2 * SPDX-License-Identifier: BSD-4-Clause
5 * Bill Paul <wpaul@windriver.com>. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Bill Paul.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/unistd.h>
41 #include <sys/types.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
46 #include <sys/mutex.h>
47 #include <sys/module.h>
52 #include <sys/sched.h>
55 #include <sys/queue.h>
58 #include <machine/segments.h>
61 #include <dev/usb/usb.h>
63 #include <compat/ndis/pe_var.h>
64 #include <compat/ndis/cfg_var.h>
65 #include <compat/ndis/resource_var.h>
66 #include <compat/ndis/ntoskrnl_var.h>
67 #include <compat/ndis/ndis_var.h>
68 #include <compat/ndis/hal_var.h>
69 #include <compat/ndis/usbd_var.h>
71 static struct mtx drvdb_mtx;
72 static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head;
74 static driver_object fake_pci_driver; /* serves both PCI and cardbus */
75 static driver_object fake_pccard_driver;
78 static void x86_oldldt(void *);
79 static void x86_newldt(void *);
82 void *tid_except_list; /* 0x00 */
83 uint32_t tid_oldfs; /* 0x04 */
84 uint32_t tid_selector; /* 0x08 */
85 struct tid *tid_self; /* 0x0C */
86 int tid_cpu; /* 0x10 */
89 static struct tid *my_tids;
92 #define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path"
97 STAILQ_INIT(&drvdb_head);
98 mtx_init(&drvdb_mtx, "Windows driver DB lock",
99 "Windows internal lock", MTX_DEF);
102 * PCI and pccard devices don't need to use IRPs to
103 * interact with their bus drivers (usually), so our
104 * emulated PCI and pccard drivers are just stubs.
105 * USB devices, on the other hand, do all their I/O
106 * by exchanging IRPs with the USB bus driver, so
107 * for that we need to provide emulator dispatcher
108 * routines, which are in a separate module.
111 windrv_bus_attach(&fake_pci_driver, "PCI Bus");
112 windrv_bus_attach(&fake_pccard_driver, "PCCARD Bus");
117 * In order to properly support SMP machines, we have
118 * to modify the GDT on each CPU, since we never know
119 * on which one we'll end up running.
122 my_tids = ExAllocatePoolWithTag(NonPagedPool,
123 sizeof(struct tid) * mp_ncpus, 0);
125 panic("failed to allocate thread info blocks");
126 smp_rendezvous(NULL, x86_newldt, NULL, NULL);
136 mtx_lock(&drvdb_mtx);
137 while(STAILQ_FIRST(&drvdb_head) != NULL) {
138 d = STAILQ_FIRST(&drvdb_head);
139 STAILQ_REMOVE_HEAD(&drvdb_head, link);
142 mtx_unlock(&drvdb_mtx);
144 RtlFreeUnicodeString(&fake_pci_driver.dro_drivername);
145 RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername);
147 mtx_destroy(&drvdb_mtx);
150 smp_rendezvous(NULL, x86_oldldt, NULL, NULL);
157 * Given the address of a driver image, find its corresponding
162 windrv_lookup(img, name)
170 bzero((char *)&us, sizeof(us));
175 RtlInitAnsiString(&as, name);
176 if (RtlAnsiStringToUnicodeString(&us, &as, TRUE))
180 mtx_lock(&drvdb_mtx);
181 STAILQ_FOREACH(d, &drvdb_head, link) {
182 if (d->windrv_object->dro_driverstart == (void *)img ||
183 (bcmp((char *)d->windrv_object->dro_drivername.us_buf,
184 (char *)us.us_buf, us.us_len) == 0 && us.us_len)) {
185 mtx_unlock(&drvdb_mtx);
187 ExFreePool(us.us_buf);
188 return (d->windrv_object);
191 mtx_unlock(&drvdb_mtx);
194 RtlFreeUnicodeString(&us);
200 windrv_match(matchfunc, ctx)
201 matchfuncptr matchfunc;
207 mtx_lock(&drvdb_mtx);
208 STAILQ_FOREACH(d, &drvdb_head, link) {
209 if (d->windrv_devlist == NULL)
211 match = matchfunc(d->windrv_bustype, d->windrv_devlist, ctx);
213 mtx_unlock(&drvdb_mtx);
217 mtx_unlock(&drvdb_mtx);
223 * Remove a driver_object from our datatabase and destroy it. Throw
224 * away any custom driver extension info that may have been added.
228 windrv_unload(mod, img, len)
233 struct drvdb_ent *db, *r = NULL;
235 device_object *d, *pdo;
239 drv = windrv_lookup(img, NULL);
242 * When we unload a driver image, we need to force a
243 * detach of any devices that might be using it. We
244 * need the PDOs of all attached devices for this.
245 * Getting at them is a little hard. We basically
246 * have to walk the device lists of all our bus
250 mtx_lock(&drvdb_mtx);
251 STAILQ_FOREACH(db, &drvdb_head, link) {
253 * Fake bus drivers have no devlist info.
254 * If this driver has devlist info, it's
255 * a loaded Windows driver and has no PDOs,
258 if (db->windrv_devlist != NULL)
260 pdo = db->windrv_object->dro_devobj;
261 while (pdo != NULL) {
262 d = pdo->do_attacheddev;
263 if (d->do_drvobj != drv) {
264 pdo = pdo->do_nextdev;
267 dev = pdo->do_devext;
268 pdo = pdo->do_nextdev;
269 mtx_unlock(&drvdb_mtx);
271 mtx_lock(&drvdb_mtx);
275 STAILQ_FOREACH(db, &drvdb_head, link) {
276 if (db->windrv_object->dro_driverstart == (void *)img) {
278 STAILQ_REMOVE(&drvdb_head, db, drvdb_ent, link);
282 mtx_unlock(&drvdb_mtx);
291 * Destroy any custom extensions that may have been added.
293 drv = r->windrv_object;
294 while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) {
295 e = RemoveHeadList(&drv->dro_driverext->dre_usrext);
299 /* Free the driver extension */
300 free(drv->dro_driverext, M_DEVBUF);
302 /* Free the driver name */
303 RtlFreeUnicodeString(&drv->dro_drivername);
305 /* Free driver object */
308 /* Free our DB handle */
314 #define WINDRV_LOADED htonl(0x42534F44)
318 patch_user_shared_data_address(vm_offset_t img, size_t len)
320 unsigned long i, n, max_addr, *addr;
322 n = len - sizeof(unsigned long);
323 max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data);
324 for (i = 0; i < n; i++) {
325 addr = (unsigned long *)(img + i);
326 if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) {
327 *addr -= KI_USER_SHARED_DATA;
328 *addr += (unsigned long)&kuser_shared_data;
335 * Loader routine for actual Windows driver modules, ultimately
336 * calls the driver's DriverEntry() routine.
340 windrv_load(mod, img, len, bustype, devlist, regvals)
344 interface_type bustype;
348 image_import_descriptor imp_desc;
349 image_optional_header opt_hdr;
351 struct drvdb_ent *new;
352 struct driver_object *drv;
358 * First step: try to relocate and dynalink the executable
362 ptr = (uint32_t *)(img + 8);
363 if (*ptr == WINDRV_LOADED)
366 /* Perform text relocation */
367 if (pe_relocate(img))
370 /* Dynamically link the NDIS.SYS routines -- required. */
371 if (pe_patch_imports(img, "NDIS", ndis_functbl))
374 /* Dynamically link the HAL.dll routines -- optional. */
375 if (pe_get_import_descriptor(img, &imp_desc, "HAL") == 0) {
376 if (pe_patch_imports(img, "HAL", hal_functbl))
380 /* Dynamically link ntoskrnl.exe -- optional. */
381 if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
382 if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
387 patch_user_shared_data_address(img, len);
390 /* Dynamically link USBD.SYS -- optional */
391 if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) {
392 if (pe_patch_imports(img, "USBD", usbd_functbl))
396 *ptr = WINDRV_LOADED;
400 /* Next step: find the driver entry point. */
402 pe_get_optional_header(img, &opt_hdr);
403 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
405 /* Next step: allocate and store a driver object. */
407 new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
411 drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
413 free (new, M_DEVBUF);
417 /* Allocate a driver extension structure too. */
419 drv->dro_driverext = malloc(sizeof(driver_extension),
420 M_DEVBUF, M_NOWAIT|M_ZERO);
422 if (drv->dro_driverext == NULL) {
428 InitializeListHead((&drv->dro_driverext->dre_usrext));
430 drv->dro_driverstart = (void *)img;
431 drv->dro_driversize = len;
433 RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH);
434 if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) {
440 new->windrv_object = drv;
441 new->windrv_regvals = regvals;
442 new->windrv_devlist = devlist;
443 new->windrv_bustype = bustype;
445 /* Now call the DriverEntry() function. */
447 status = MSCALL2(entry, drv, &drv->dro_drivername);
449 if (status != STATUS_SUCCESS) {
450 RtlFreeUnicodeString(&drv->dro_drivername);
456 mtx_lock(&drvdb_mtx);
457 STAILQ_INSERT_HEAD(&drvdb_head, new, link);
458 mtx_unlock(&drvdb_mtx);
464 * Make a new Physical Device Object for a device that was
465 * detected/plugged in. For us, the PDO is just a way to
466 * get at the device_t.
470 windrv_create_pdo(drv, bsddev)
477 * This is a new physical device object, which technically
478 * is the "top of the stack." Consequently, we don't do
479 * an IoAttachDeviceToDeviceStack() here.
482 mtx_lock(&drvdb_mtx);
483 IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev);
484 mtx_unlock(&drvdb_mtx);
486 /* Stash pointer to our BSD device handle. */
488 dev->do_devext = bsddev;
490 return (STATUS_SUCCESS);
494 windrv_destroy_pdo(drv, bsddev)
500 pdo = windrv_find_pdo(drv, bsddev);
502 /* Remove reference to device_t */
504 pdo->do_devext = NULL;
506 mtx_lock(&drvdb_mtx);
508 mtx_unlock(&drvdb_mtx);
512 * Given a device_t, find the corresponding PDO in a driver's
517 windrv_find_pdo(drv, bsddev)
523 mtx_lock(&drvdb_mtx);
524 pdo = drv->dro_devobj;
525 while (pdo != NULL) {
526 if (pdo->do_devext == bsddev) {
527 mtx_unlock(&drvdb_mtx);
530 pdo = pdo->do_nextdev;
532 mtx_unlock(&drvdb_mtx);
538 * Add an internally emulated driver to the database. We need this
539 * to set up an emulated bus driver so that it can receive IRPs.
543 windrv_bus_attach(drv, name)
547 struct drvdb_ent *new;
550 new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
554 RtlInitAnsiString(&as, name);
555 if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE))
562 * Set up a fake image pointer to avoid false matches
563 * in windrv_lookup().
565 drv->dro_driverstart = (void *)0xFFFFFFFF;
567 new->windrv_object = drv;
568 new->windrv_devlist = NULL;
569 new->windrv_regvals = NULL;
571 mtx_lock(&drvdb_mtx);
572 STAILQ_INSERT_HEAD(&drvdb_head, new, link);
573 mtx_unlock(&drvdb_mtx);
580 extern void x86_64_wrap(void);
581 extern void x86_64_wrap_call(void);
582 extern void x86_64_wrap_end(void);
585 windrv_wrap(func, wrap, argcnt, ftype)
592 vm_offset_t *calladdr;
593 vm_offset_t wrapstart, wrapend, wrapcall;
595 wrapstart = (vm_offset_t)&x86_64_wrap;
596 wrapend = (vm_offset_t)&x86_64_wrap_end;
597 wrapcall = (vm_offset_t)&x86_64_wrap_call;
599 /* Allocate a new wrapper instance. */
601 p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
605 /* Copy over the code. */
607 bcopy((char *)wrapstart, p, (wrapend - wrapstart));
609 /* Insert the function address into the new wrapper instance. */
611 calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2);
612 *calladdr = (vm_offset_t)func;
618 #endif /* __amd64__ */
635 } __attribute__((__packed__));
637 extern uint16_t x86_getfs(void);
638 extern void x86_setfs(uint16_t);
639 extern void *x86_gettid(void);
640 extern void x86_critical_enter(void);
641 extern void x86_critical_exit(void);
642 extern void x86_getldt(struct gdt *, uint16_t *);
643 extern void x86_setldt(struct gdt *, uint16_t);
645 #define SEL_LDT 4 /* local descriptor table */
646 #define SEL_TO_FS(x) (((x) << 3))
649 * FreeBSD 6.0 and later has a special GDT segment reserved
650 * specifically for us, so if GNDIS_SEL is defined, use that.
651 * If not, use GTGATE_SEL, which is uninitialized and infrequently
656 #define FREEBSD_EMPTYSEL GNDIS_SEL
658 #define FREEBSD_EMPTYSEL GTGATE_SEL /* slot 7 */
662 * The meanings of various bits in a descriptor vary a little
663 * depending on whether the descriptor will be used as a
664 * code, data or system descriptor. (And that in turn depends
665 * on which segment register selects the descriptor.)
666 * We're only trying to create a data segment, so the definitions
667 * below are the ones that apply to a data descriptor.
670 #define SEGFLAGLO_PRESENT 0x80 /* segment is present */
671 #define SEGFLAGLO_PRIVLVL 0x60 /* privlevel needed for this seg */
672 #define SEGFLAGLO_CD 0x10 /* 1 = code/data, 0 = system */
673 #define SEGFLAGLO_MBZ 0x08 /* must be zero */
674 #define SEGFLAGLO_EXPANDDOWN 0x04 /* limit expands down */
675 #define SEGFLAGLO_WRITEABLE 0x02 /* segment is writeable */
676 #define SEGGLAGLO_ACCESSED 0x01 /* segment has been accessed */
678 #define SEGFLAGHI_GRAN 0x80 /* granularity, 1 = byte, 0 = page */
679 #define SEGFLAGHI_BIG 0x40 /* 1 = 32 bit stack, 0 = 16 bit */
682 * Context switch from UNIX to Windows. Save the existing value
683 * of %fs for this processor, then change it to point to our
684 * fake TID. Note that it is also possible to pin ourselves
685 * to our current CPU, though I'm not sure this is really
686 * necessary. It depends on whether or not an interrupt might
687 * preempt us while Windows code is running and we wind up
688 * scheduled onto another CPU as a result. So far, it doesn't
689 * seem like this is what happens.
697 t = &my_tids[curthread->td_oncpu];
700 * Ugly hack. During system bootstrap (cold == 1), only CPU 0
701 * is running. So if we were loaded at bootstrap, only CPU 0
702 * will have our special GDT entry. This is a problem for SMP
703 * systems, so to deal with this, we check here to make sure
704 * the TID for this processor has been initialized, and if it
705 * hasn't, we need to do it right now or else things will
709 if (t->tid_self != t)
712 x86_critical_enter();
713 t->tid_oldfs = x86_getfs();
714 t->tid_cpu = curthread->td_oncpu;
716 x86_setfs(SEL_TO_FS(t->tid_selector));
719 /* Now entering Windows land, population: you. */
723 * Context switch from Windows back to UNIX. Restore %fs to
724 * its previous value. This always occurs after a call to
733 x86_critical_enter();
735 x86_setfs(t->tid_oldfs);
739 /* Welcome back to UNIX land, we missed you. */
742 if (t->tid_cpu != curthread->td_oncpu)
743 panic("ctxsw GOT MOVED TO OTHER CPU!");
747 static int windrv_wrap_stdcall(funcptr, funcptr *, int);
748 static int windrv_wrap_fastcall(funcptr, funcptr *, int);
749 static int windrv_wrap_regparm(funcptr, funcptr *);
751 extern void x86_fastcall_wrap(void);
752 extern void x86_fastcall_wrap_call(void);
753 extern void x86_fastcall_wrap_arg(void);
754 extern void x86_fastcall_wrap_end(void);
757 windrv_wrap_fastcall(func, wrap, argcnt)
763 vm_offset_t *calladdr;
765 vm_offset_t wrapstart, wrapend, wrapcall, wraparg;
767 wrapstart = (vm_offset_t)&x86_fastcall_wrap;
768 wrapend = (vm_offset_t)&x86_fastcall_wrap_end;
769 wrapcall = (vm_offset_t)&x86_fastcall_wrap_call;
770 wraparg = (vm_offset_t)&x86_fastcall_wrap_arg;
772 /* Allocate a new wrapper instance. */
774 p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
778 /* Copy over the code. */
780 bcopy((char *)wrapstart, p, (wrapend - wrapstart));
782 /* Insert the function address into the new wrapper instance. */
784 calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
785 *calladdr = (vm_offset_t)func;
791 argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
792 *argaddr = argcnt * sizeof(uint32_t);
799 extern void x86_stdcall_wrap(void);
800 extern void x86_stdcall_wrap_call(void);
801 extern void x86_stdcall_wrap_arg(void);
802 extern void x86_stdcall_wrap_end(void);
805 windrv_wrap_stdcall(func, wrap, argcnt)
811 vm_offset_t *calladdr;
813 vm_offset_t wrapstart, wrapend, wrapcall, wraparg;
815 wrapstart = (vm_offset_t)&x86_stdcall_wrap;
816 wrapend = (vm_offset_t)&x86_stdcall_wrap_end;
817 wrapcall = (vm_offset_t)&x86_stdcall_wrap_call;
818 wraparg = (vm_offset_t)&x86_stdcall_wrap_arg;
820 /* Allocate a new wrapper instance. */
822 p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
826 /* Copy over the code. */
828 bcopy((char *)wrapstart, p, (wrapend - wrapstart));
830 /* Insert the function address into the new wrapper instance. */
832 calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
833 *calladdr = (vm_offset_t)func;
835 argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
836 *argaddr = argcnt * sizeof(uint32_t);
843 extern void x86_regparm_wrap(void);
844 extern void x86_regparm_wrap_call(void);
845 extern void x86_regparm_wrap_end(void);
848 windrv_wrap_regparm(func, wrap)
853 vm_offset_t *calladdr;
854 vm_offset_t wrapstart, wrapend, wrapcall;
856 wrapstart = (vm_offset_t)&x86_regparm_wrap;
857 wrapend = (vm_offset_t)&x86_regparm_wrap_end;
858 wrapcall = (vm_offset_t)&x86_regparm_wrap_call;
860 /* Allocate a new wrapper instance. */
862 p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
866 /* Copy over the code. */
868 bcopy(x86_regparm_wrap, p, (wrapend - wrapstart));
870 /* Insert the function address into the new wrapper instance. */
872 calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
873 *calladdr = (vm_offset_t)func;
881 windrv_wrap(func, wrap, argcnt, ftype)
888 case WINDRV_WRAP_FASTCALL:
889 return (windrv_wrap_fastcall(func, wrap, argcnt));
890 case WINDRV_WRAP_STDCALL:
891 return (windrv_wrap_stdcall(func, wrap, argcnt));
892 case WINDRV_WRAP_REGPARM:
893 return (windrv_wrap_regparm(func, wrap));
894 case WINDRV_WRAP_CDECL:
895 return (windrv_wrap_stdcall(func, wrap, 0));
911 mtx_lock_spin(&dt_lock);
913 /* Grab location of existing GDT. */
915 x86_getldt(>able, <able);
917 /* Find the slot we updated. */
920 gdt += FREEBSD_EMPTYSEL;
924 bzero((char *)gdt, sizeof(struct x86desc));
928 x86_setldt(>able, ltable);
930 mtx_unlock_spin(&dt_lock);
944 mtx_lock_spin(&dt_lock);
946 /* Grab location of existing GDT. */
948 x86_getldt(>able, <able);
950 /* Get pointer to the GDT table. */
954 /* Get pointer to empty slot */
956 l += FREEBSD_EMPTYSEL;
958 /* Initialize TID for this CPU. */
960 my_tids[t->td_oncpu].tid_selector = FREEBSD_EMPTYSEL;
961 my_tids[t->td_oncpu].tid_self = &my_tids[t->td_oncpu];
963 /* Set up new GDT entry. */
965 l->x_lolimit = sizeof(struct tid);
966 l->x_hilimit = SEGFLAGHI_GRAN|SEGFLAGHI_BIG;
967 l->x_base0 = (vm_offset_t)(&my_tids[t->td_oncpu]) & 0xFFFF;
968 l->x_base1 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 16) & 0xFF;
969 l->x_base2 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 24) & 0xFF;
970 l->x_flags = SEGFLAGLO_PRESENT|SEGFLAGLO_CD|SEGFLAGLO_WRITEABLE;
972 /* Update the GDT. */
974 x86_setldt(>able, ltable);
976 mtx_unlock_spin(&dt_lock);
981 #endif /* __i386__ */
987 free(func, M_DEVBUF);