1843e1988Sjohnlev /*
2843e1988Sjohnlev * CDDL HEADER START
3843e1988Sjohnlev *
4843e1988Sjohnlev * The contents of this file are subject to the terms of the
5843e1988Sjohnlev * Common Development and Distribution License (the "License").
6843e1988Sjohnlev * You may not use this file except in compliance with the License.
7843e1988Sjohnlev *
8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev * See the License for the specific language governing permissions
11843e1988Sjohnlev * and limitations under the License.
12843e1988Sjohnlev *
13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev *
19843e1988Sjohnlev * CDDL HEADER END
20843e1988Sjohnlev */
21843e1988Sjohnlev
22843e1988Sjohnlev /*
237f0b8309SEdward Pilatowicz * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24843e1988Sjohnlev * Use is subject to license terms.
25843e1988Sjohnlev */
26843e1988Sjohnlev
273599414cSJeremy Jones /*
283599414cSJeremy Jones * Copyright (c) 2014 by Delphix. All rights reserved.
296d1e6c90SYuri Pankov * Copyright 2018 Nexenta Systems, Inc.
303599414cSJeremy Jones */
313599414cSJeremy Jones
32843e1988Sjohnlev /*
33843e1988Sjohnlev * Xen virtual device driver interfaces
34843e1988Sjohnlev */
35843e1988Sjohnlev
36843e1988Sjohnlev /*
37843e1988Sjohnlev * todo:
38843e1988Sjohnlev * + name space clean up:
39843e1988Sjohnlev * xvdi_* - public xen interfaces, for use by all leaf drivers
40843e1988Sjohnlev * xd_* - public xen data structures
41843e1988Sjohnlev * i_xvdi_* - implementation private functions
42843e1988Sjohnlev * xendev_* - xendev driver interfaces, both internal and in cb_ops/bus_ops
43843e1988Sjohnlev * + add mdb dcmds to dump ring status
44843e1988Sjohnlev * + implement xvdi_xxx to wrap xenbus_xxx read/write function
45843e1988Sjohnlev * + convert (xendev_ring_t *) into xvdi_ring_handle_t
46843e1988Sjohnlev */
47843e1988Sjohnlev #include <sys/conf.h>
48843e1988Sjohnlev #include <sys/param.h>
49843e1988Sjohnlev #include <sys/kmem.h>
50843e1988Sjohnlev #include <vm/seg_kmem.h>
51843e1988Sjohnlev #include <sys/debug.h>
52843e1988Sjohnlev #include <sys/modctl.h>
53843e1988Sjohnlev #include <sys/autoconf.h>
54843e1988Sjohnlev #include <sys/ddi_impldefs.h>
55843e1988Sjohnlev #include <sys/ddi_subrdefs.h>
56843e1988Sjohnlev #include <sys/ddi.h>
57843e1988Sjohnlev #include <sys/sunddi.h>
58843e1988Sjohnlev #include <sys/sunndi.h>
59843e1988Sjohnlev #include <sys/sunldi.h>
60843e1988Sjohnlev #include <sys/fs/dv_node.h>
61843e1988Sjohnlev #include <sys/avintr.h>
62843e1988Sjohnlev #include <sys/psm.h>
63843e1988Sjohnlev #include <sys/spl.h>
64843e1988Sjohnlev #include <sys/promif.h>
65843e1988Sjohnlev #include <sys/list.h>
66843e1988Sjohnlev #include <sys/bootconf.h>
67843e1988Sjohnlev #include <sys/bootsvcs.h>
68843e1988Sjohnlev #include <sys/bootinfo.h>
69843e1988Sjohnlev #include <sys/note.h>
707f0b8309SEdward Pilatowicz #include <sys/sysmacros.h>
71551bc2a6Smrj #ifdef XPV_HVM_DRIVER
72551bc2a6Smrj #include <sys/xpv_support.h>
73551bc2a6Smrj #include <sys/hypervisor.h>
74551bc2a6Smrj #include <public/grant_table.h>
75551bc2a6Smrj #include <public/xen.h>
76551bc2a6Smrj #include <public/io/xenbus.h>
77551bc2a6Smrj #include <public/io/xs_wire.h>
78551bc2a6Smrj #include <public/event_channel.h>
79551bc2a6Smrj #include <public/io/xenbus.h>
80551bc2a6Smrj #else /* XPV_HVM_DRIVER */
81551bc2a6Smrj #include <sys/hypervisor.h>
82843e1988Sjohnlev #include <sys/xen_mmu.h>
83843e1988Sjohnlev #include <xen/sys/xenbus_impl.h>
84551bc2a6Smrj #include <sys/evtchn_impl.h>
85551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
86551bc2a6Smrj #include <sys/gnttab.h>
87843e1988Sjohnlev #include <xen/sys/xendev.h>
88843e1988Sjohnlev #include <vm/hat_i86.h>
89843e1988Sjohnlev #include <sys/scsi/generic/inquiry.h>
90843e1988Sjohnlev #include <util/sscanf.h>
91843e1988Sjohnlev #include <xen/public/io/xs_wire.h>
92843e1988Sjohnlev
93843e1988Sjohnlev
9406bbe1e0Sedp #define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
9506bbe1e0Sedp #define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
9606bbe1e0Sedp ((ch) >= 'A' && (ch) <= 'F'))
9706bbe1e0Sedp
98843e1988Sjohnlev static void xvdi_ring_init_sring(xendev_ring_t *);
99843e1988Sjohnlev static void xvdi_ring_init_front_ring(xendev_ring_t *, size_t, size_t);
100551bc2a6Smrj #ifndef XPV_HVM_DRIVER
101843e1988Sjohnlev static void xvdi_ring_init_back_ring(xendev_ring_t *, size_t, size_t);
102551bc2a6Smrj #endif
103843e1988Sjohnlev static void xvdi_reinit_ring(dev_info_t *, grant_ref_t *, xendev_ring_t *);
104843e1988Sjohnlev
105843e1988Sjohnlev static int i_xvdi_add_watches(dev_info_t *);
106843e1988Sjohnlev static void i_xvdi_rem_watches(dev_info_t *);
107843e1988Sjohnlev
108843e1988Sjohnlev static int i_xvdi_add_watch_oestate(dev_info_t *);
109843e1988Sjohnlev static void i_xvdi_rem_watch_oestate(dev_info_t *);
110843e1988Sjohnlev static void i_xvdi_oestate_cb(struct xenbus_device *, XenbusState);
111843e1988Sjohnlev static void i_xvdi_oestate_handler(void *);
112843e1988Sjohnlev
113843e1988Sjohnlev static int i_xvdi_add_watch_hpstate(dev_info_t *);
114843e1988Sjohnlev static void i_xvdi_rem_watch_hpstate(dev_info_t *);
115843e1988Sjohnlev static void i_xvdi_hpstate_cb(struct xenbus_watch *, const char **,
116843e1988Sjohnlev unsigned int);
117843e1988Sjohnlev static void i_xvdi_hpstate_handler(void *);
118843e1988Sjohnlev
119843e1988Sjohnlev static int i_xvdi_add_watch_bepath(dev_info_t *);
120843e1988Sjohnlev static void i_xvdi_rem_watch_bepath(dev_info_t *);
121843e1988Sjohnlev static void i_xvdi_bepath_cb(struct xenbus_watch *, const char **,
122843e1988Sjohnlev unsigned in);
123843e1988Sjohnlev
124843e1988Sjohnlev static void xendev_offline_device(void *);
125843e1988Sjohnlev
126843e1988Sjohnlev static void i_xvdi_probe_path_cb(struct xenbus_watch *, const char **,
127843e1988Sjohnlev unsigned int);
128843e1988Sjohnlev static void i_xvdi_probe_path_handler(void *);
129843e1988Sjohnlev
130eea6c6b9SMax zhen typedef struct oestate_evt {
131eea6c6b9SMax zhen dev_info_t *dip;
132eea6c6b9SMax zhen XenbusState state;
133eea6c6b9SMax zhen } i_oestate_evt_t;
134eea6c6b9SMax zhen
135843e1988Sjohnlev typedef struct xd_cfg {
136843e1988Sjohnlev xendev_devclass_t devclass;
137843e1988Sjohnlev char *xsdev;
138843e1988Sjohnlev char *xs_path_fe;
139843e1988Sjohnlev char *xs_path_be;
140843e1988Sjohnlev char *node_fe;
141843e1988Sjohnlev char *node_be;
142843e1988Sjohnlev char *device_type;
143843e1988Sjohnlev int xd_ipl;
144843e1988Sjohnlev int flags;
145843e1988Sjohnlev } i_xd_cfg_t;
146843e1988Sjohnlev
147843e1988Sjohnlev #define XD_DOM_ZERO 0x01 /* dom0 only. */
148843e1988Sjohnlev #define XD_DOM_GUEST 0x02 /* Guest domains (i.e. non-dom0). */
149843e1988Sjohnlev #define XD_DOM_IO 0x04 /* IO domains. */
150843e1988Sjohnlev
151843e1988Sjohnlev #define XD_DOM_ALL (XD_DOM_ZERO | XD_DOM_GUEST)
152843e1988Sjohnlev
153843e1988Sjohnlev static i_xd_cfg_t xdci[] = {
1543599414cSJeremy Jones #ifndef XPV_HVM_DRIVER
155843e1988Sjohnlev { XEN_CONSOLE, NULL, NULL, NULL, "xencons", NULL,
156843e1988Sjohnlev "console", IPL_CONS, XD_DOM_ALL, },
1573599414cSJeremy Jones #endif
158843e1988Sjohnlev
159843e1988Sjohnlev { XEN_VNET, "vif", "device/vif", "backend/vif", "xnf", "xnb",
160843e1988Sjohnlev "network", IPL_VIF, XD_DOM_ALL, },
161843e1988Sjohnlev
162843e1988Sjohnlev { XEN_VBLK, "vbd", "device/vbd", "backend/vbd", "xdf", "xdb",
163843e1988Sjohnlev "block", IPL_VBD, XD_DOM_ALL, },
164843e1988Sjohnlev
1657eea693dSMark Johnson { XEN_BLKTAP, "tap", NULL, "backend/tap", NULL, "xpvtap",
1667eea693dSMark Johnson "block", IPL_VBD, XD_DOM_ALL, },
1677eea693dSMark Johnson
1683599414cSJeremy Jones #ifndef XPV_HVM_DRIVER
169843e1988Sjohnlev { XEN_XENBUS, NULL, NULL, NULL, "xenbus", NULL,
170843e1988Sjohnlev NULL, 0, XD_DOM_ALL, },
171843e1988Sjohnlev
172843e1988Sjohnlev { XEN_DOMCAPS, NULL, NULL, NULL, "domcaps", NULL,
173843e1988Sjohnlev NULL, 0, XD_DOM_ALL, },
174843e1988Sjohnlev
175843e1988Sjohnlev { XEN_BALLOON, NULL, NULL, NULL, "balloon", NULL,
176843e1988Sjohnlev NULL, 0, XD_DOM_ALL, },
1773599414cSJeremy Jones #endif
178843e1988Sjohnlev
179843e1988Sjohnlev { XEN_EVTCHN, NULL, NULL, NULL, "evtchn", NULL,
180843e1988Sjohnlev NULL, 0, XD_DOM_ZERO, },
181843e1988Sjohnlev
182843e1988Sjohnlev { XEN_PRIVCMD, NULL, NULL, NULL, "privcmd", NULL,
183843e1988Sjohnlev NULL, 0, XD_DOM_ZERO, },
184843e1988Sjohnlev };
185843e1988Sjohnlev #define NXDC (sizeof (xdci) / sizeof (xdci[0]))
186843e1988Sjohnlev
187843e1988Sjohnlev static void i_xvdi_enum_fe(dev_info_t *, i_xd_cfg_t *);
188843e1988Sjohnlev static void i_xvdi_enum_be(dev_info_t *, i_xd_cfg_t *);
189843e1988Sjohnlev static void i_xvdi_enum_worker(dev_info_t *, i_xd_cfg_t *, char *);
190843e1988Sjohnlev
191843e1988Sjohnlev /*
192843e1988Sjohnlev * Xen device channel device access and DMA attributes
193843e1988Sjohnlev */
194843e1988Sjohnlev static ddi_device_acc_attr_t xendev_dc_accattr = {
195843e1988Sjohnlev DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC
196843e1988Sjohnlev };
197843e1988Sjohnlev
198843e1988Sjohnlev static ddi_dma_attr_t xendev_dc_dmaattr = {
199843e1988Sjohnlev DMA_ATTR_V0, /* version of this structure */
200843e1988Sjohnlev 0, /* lowest usable address */
201843e1988Sjohnlev 0xffffffffffffffffULL, /* highest usable address */
202843e1988Sjohnlev 0x7fffffff, /* maximum DMAable byte count */
203843e1988Sjohnlev MMU_PAGESIZE, /* alignment in bytes */
204843e1988Sjohnlev 0x7ff, /* bitmap of burst sizes */
205843e1988Sjohnlev 1, /* minimum transfer */
206843e1988Sjohnlev 0xffffffffU, /* maximum transfer */
207843e1988Sjohnlev 0xffffffffffffffffULL, /* maximum segment length */
208843e1988Sjohnlev 1, /* maximum number of segments */
209843e1988Sjohnlev 1, /* granularity */
210843e1988Sjohnlev 0, /* flags (reserved) */
211843e1988Sjohnlev };
212843e1988Sjohnlev
213843e1988Sjohnlev static dev_info_t *xendev_dip = NULL;
214843e1988Sjohnlev
215843e1988Sjohnlev #define XVDI_DBG_STATE 0x01
216843e1988Sjohnlev #define XVDI_DBG_PROBE 0x02
217843e1988Sjohnlev
218843e1988Sjohnlev #ifdef DEBUG
219ab4a9bebSjohnlev int i_xvdi_debug = 0;
220843e1988Sjohnlev
221843e1988Sjohnlev #define XVDI_DPRINTF(flag, format, ...) \
222843e1988Sjohnlev { \
223843e1988Sjohnlev if (i_xvdi_debug & (flag)) \
224843e1988Sjohnlev prom_printf((format), __VA_ARGS__); \
225843e1988Sjohnlev }
226843e1988Sjohnlev #else
227843e1988Sjohnlev #define XVDI_DPRINTF(flag, format, ...)
228843e1988Sjohnlev #endif /* DEBUG */
229843e1988Sjohnlev
230843e1988Sjohnlev static i_xd_cfg_t *
i_xvdi_devclass2cfg(xendev_devclass_t devclass)231843e1988Sjohnlev i_xvdi_devclass2cfg(xendev_devclass_t devclass)
232843e1988Sjohnlev {
233843e1988Sjohnlev i_xd_cfg_t *xdcp;
234843e1988Sjohnlev int i;
235843e1988Sjohnlev
236843e1988Sjohnlev for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++)
237843e1988Sjohnlev if (xdcp->devclass == devclass)
238843e1988Sjohnlev return (xdcp);
239843e1988Sjohnlev
240843e1988Sjohnlev return (NULL);
241843e1988Sjohnlev }
242843e1988Sjohnlev
243843e1988Sjohnlev int
xvdi_init_dev(dev_info_t * dip)244843e1988Sjohnlev xvdi_init_dev(dev_info_t *dip)
245843e1988Sjohnlev {
246843e1988Sjohnlev xendev_devclass_t devcls;
247843e1988Sjohnlev int vdevnum;
248843e1988Sjohnlev domid_t domid;
249843e1988Sjohnlev struct xendev_ppd *pdp;
250843e1988Sjohnlev i_xd_cfg_t *xdcp;
251843e1988Sjohnlev boolean_t backend;
252843e1988Sjohnlev char xsnamebuf[TYPICALMAXPATHLEN];
253843e1988Sjohnlev char *xsname;
25497869ac5Sjhd void *prop_str;
2553de3be76Sjhd unsigned int prop_len;
2566d1e6c90SYuri Pankov char unitaddr[16];
257843e1988Sjohnlev
258843e1988Sjohnlev devcls = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
259843e1988Sjohnlev DDI_PROP_DONTPASS, "devclass", XEN_INVAL);
260843e1988Sjohnlev vdevnum = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2611ca30e39Sjohnlev DDI_PROP_DONTPASS, "vdev", VDEV_NOXS);
262843e1988Sjohnlev domid = (domid_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
263843e1988Sjohnlev DDI_PROP_DONTPASS, "domain", DOMID_SELF);
264843e1988Sjohnlev
265843e1988Sjohnlev backend = (domid != DOMID_SELF);
266843e1988Sjohnlev xdcp = i_xvdi_devclass2cfg(devcls);
267843e1988Sjohnlev if (xdcp->device_type != NULL)
268843e1988Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
269843e1988Sjohnlev "device_type", xdcp->device_type);
270843e1988Sjohnlev
271843e1988Sjohnlev pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
272843e1988Sjohnlev pdp->xd_domain = domid;
273843e1988Sjohnlev pdp->xd_vdevnum = vdevnum;
274843e1988Sjohnlev pdp->xd_devclass = devcls;
275843e1988Sjohnlev pdp->xd_evtchn = INVALID_EVTCHN;
2767f0b8309SEdward Pilatowicz list_create(&pdp->xd_xb_watches, sizeof (xd_xb_watches_t),
2777f0b8309SEdward Pilatowicz offsetof(xd_xb_watches_t, xxw_list));
2787eea693dSMark Johnson mutex_init(&pdp->xd_evt_lk, NULL, MUTEX_DRIVER, NULL);
2797eea693dSMark Johnson mutex_init(&pdp->xd_ndi_lk, NULL, MUTEX_DRIVER, NULL);
280843e1988Sjohnlev ddi_set_parent_data(dip, pdp);
281843e1988Sjohnlev
282843e1988Sjohnlev /*
283843e1988Sjohnlev * devices that do not need to interact with xenstore
284843e1988Sjohnlev */
2851ca30e39Sjohnlev if (vdevnum == VDEV_NOXS) {
286843e1988Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
287843e1988Sjohnlev "unit-address", "0");
288843e1988Sjohnlev if (devcls == XEN_CONSOLE)
289843e1988Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
290843e1988Sjohnlev "pm-hardware-state", "needs-suspend-resume");
291843e1988Sjohnlev return (DDI_SUCCESS);
292843e1988Sjohnlev }
293843e1988Sjohnlev
294843e1988Sjohnlev /*
295843e1988Sjohnlev * PV devices that need to probe xenstore
296843e1988Sjohnlev */
297843e1988Sjohnlev
298843e1988Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
299843e1988Sjohnlev "pm-hardware-state", "needs-suspend-resume");
300843e1988Sjohnlev
301843e1988Sjohnlev xsname = xsnamebuf;
302843e1988Sjohnlev if (!backend)
303843e1988Sjohnlev (void) snprintf(xsnamebuf, sizeof (xsnamebuf),
304843e1988Sjohnlev "%s/%d", xdcp->xs_path_fe, vdevnum);
305843e1988Sjohnlev else
306843e1988Sjohnlev (void) snprintf(xsnamebuf, sizeof (xsnamebuf),
307843e1988Sjohnlev "%s/%d/%d", xdcp->xs_path_be, domid, vdevnum);
3081d03c31eSjohnlev if ((xenbus_read_driver_state(xsname) >= XenbusStateClosing)) {
3091d03c31eSjohnlev /* Don't try to init a dev that may be closing */
3107eea693dSMark Johnson mutex_destroy(&pdp->xd_ndi_lk);
3117eea693dSMark Johnson mutex_destroy(&pdp->xd_evt_lk);
3121d03c31eSjohnlev kmem_free(pdp, sizeof (*pdp));
3131d03c31eSjohnlev ddi_set_parent_data(dip, NULL);
3141d03c31eSjohnlev return (DDI_FAILURE);
3151d03c31eSjohnlev }
316843e1988Sjohnlev
317843e1988Sjohnlev pdp->xd_xsdev.nodename = i_ddi_strdup(xsname, KM_SLEEP);
318843e1988Sjohnlev pdp->xd_xsdev.devicetype = xdcp->xsdev;
319843e1988Sjohnlev pdp->xd_xsdev.frontend = (backend ? 0 : 1);
320843e1988Sjohnlev pdp->xd_xsdev.data = dip;
321843e1988Sjohnlev pdp->xd_xsdev.otherend_id = (backend ? domid : -1);
322843e1988Sjohnlev if (i_xvdi_add_watches(dip) != DDI_SUCCESS) {
323843e1988Sjohnlev cmn_err(CE_WARN, "xvdi_init_dev: "
324843e1988Sjohnlev "cannot add watches for %s", xsname);
325843e1988Sjohnlev xvdi_uninit_dev(dip);
326843e1988Sjohnlev return (DDI_FAILURE);
327843e1988Sjohnlev }
328843e1988Sjohnlev
32997869ac5Sjhd if (backend)
33097869ac5Sjhd return (DDI_SUCCESS);
33197869ac5Sjhd
332843e1988Sjohnlev /*
33397869ac5Sjhd * The unit-address for frontend devices is the name of the
33497869ac5Sjhd * of the xenstore node containing the device configuration
33597869ac5Sjhd * and is contained in the 'vdev' property.
33697869ac5Sjhd * VIF devices are named using an incrementing integer.
3376d1e6c90SYuri Pankov * VBD devices are either named using the 32-bit dev_t value
33897869ac5Sjhd * for linux 'hd' and 'xvd' devices, or a simple integer value
33997869ac5Sjhd * in the range 0..767. 768 is the base value of the linux
34097869ac5Sjhd * dev_t namespace, the dev_t value for 'hda'.
341843e1988Sjohnlev */
34297869ac5Sjhd (void) snprintf(unitaddr, sizeof (unitaddr), "%d", vdevnum);
34397869ac5Sjhd (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "unit-address",
34497869ac5Sjhd unitaddr);
34597869ac5Sjhd
34697869ac5Sjhd switch (devcls) {
34797869ac5Sjhd case XEN_VNET:
34897869ac5Sjhd if (xenbus_read(XBT_NULL, xsname, "mac", (void *)&prop_str,
34997869ac5Sjhd &prop_len) != 0)
350843e1988Sjohnlev break;
35197869ac5Sjhd (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "mac",
35297869ac5Sjhd prop_str);
35397869ac5Sjhd kmem_free(prop_str, prop_len);
35497869ac5Sjhd break;
35597869ac5Sjhd case XEN_VBLK:
35697869ac5Sjhd /*
35797869ac5Sjhd * cache a copy of the otherend name
35897869ac5Sjhd * for ease of observeability
35997869ac5Sjhd */
36097869ac5Sjhd if (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, "dev",
36197869ac5Sjhd &prop_str, &prop_len) != 0)
362843e1988Sjohnlev break;
36397869ac5Sjhd (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
36497869ac5Sjhd "dev-address", prop_str);
36597869ac5Sjhd kmem_free(prop_str, prop_len);
36697869ac5Sjhd break;
36797869ac5Sjhd default:
36897869ac5Sjhd break;
369843e1988Sjohnlev }
370843e1988Sjohnlev
371