diff options
author | msmith <msmith@FreeBSD.org> | 1999-07-29 01:49:19 +0000 |
---|---|---|
committer | msmith <msmith@FreeBSD.org> | 1999-07-29 01:49:19 +0000 |
commit | e495b00d19aaf17fae0a67881fa9bbc27f42f10b (patch) | |
tree | 16887357d8f31adc150c8ac0f20f758b5a8bfcdf /sys/amd64 | |
parent | 0cee2525fa4800b488630501d295a4700c050a81 (diff) | |
download | FreeBSD-src-e495b00d19aaf17fae0a67881fa9bbc27f42f10b.zip FreeBSD-src-e495b00d19aaf17fae0a67881fa9bbc27f42f10b.tar.gz |
Major update to the kernel's BIOS-calling ability.
- Add support for calling 32-bit code in other segments
- Add support for calling 16-bit protected mode code
Update APM to use this facility.
Submitted by: jlemon
Diffstat (limited to 'sys/amd64')
-rw-r--r-- | sys/amd64/amd64/bios.c | 275 | ||||
-rw-r--r-- | sys/amd64/amd64/machdep.c | 32 | ||||
-rw-r--r-- | sys/amd64/include/pc/bios.h | 88 | ||||
-rw-r--r-- | sys/amd64/include/segments.h | 12 |
4 files changed, 365 insertions, 42 deletions
diff --git a/sys/amd64/amd64/bios.c b/sys/amd64/amd64/bios.c index 5029db4..82afa3f 100644 --- a/sys/amd64/amd64/bios.c +++ b/sys/amd64/amd64/bios.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 1997 Michael Smith + * Copyright (c) 1998 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bios.c,v 1.11 1998/07/15 03:58:57 bde Exp $ + * $Id: bios.c,v 1.12 1999/03/16 21:11:28 msmith Exp $ */ /* @@ -31,12 +32,17 @@ */ #include <sys/param.h> +#include <sys/proc.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <vm/vm.h> #include <vm/pmap.h> #include <machine/md_var.h> - +#include <machine/segments.h> +#include <machine/stdarg.h> +#include <machine/tss.h> +#include <machine/vmparam.h> #include <machine/pc/bios.h> #define BIOS_START 0xe0000 @@ -47,7 +53,7 @@ struct bios32_SDentry PCIbios = {entry : 0}; static struct SMBIOS_table *SMBIOStable = 0; static struct DMI_table *DMItable = 0; -static caddr_t bios32_SDCI = NULL; +static u_int bios32_SDCI = 0; static void bios32_init(void *junk); @@ -84,10 +90,10 @@ bios32_init(void *junk) } /* If checksum is OK, enable use of the entrypoint */ if ((ck == 0) && (sdh->entry < (BIOS_START + BIOS_SIZE))) { - bios32_SDCI = (caddr_t)BIOS_PADDRTOVADDR(sdh->entry); + bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry); if (bootverbose) { printf("Found BIOS32 Service Directory header at %p\n", sdh); - printf("Entry = 0x%x (%p) Rev = %d Len = %d\n", + printf("Entry = 0x%x (%x) Rev = %d Len = %d\n", sdh->entry, bios32_SDCI, sdh->revision, sdh->len); } /* See if there's a PCI BIOS entrypoint here */ @@ -168,23 +174,24 @@ bios32_init(void *junk) int bios32_SDlookup(struct bios32_SDentry *ent) { - struct bios32_args args; - - if (bios32_SDCI != NULL) { + struct bios_regs args; + + if (bios32_SDCI == 0) + return (1); args.eax = ent->ident.id; /* set up arguments */ args.ebx = args.ecx = args.edx = 0; - bios32(bios32_SDCI, &args); /* make the BIOS call */ + bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL)); if ((args.eax & 0xff) == 0) { /* success? */ - ent->base = args.ebx; - ent->len = args.ecx; - ent->entry = args.edx; - return(0); /* all OK */ + ent->base = args.ebx; + ent->len = args.ecx; + ent->entry = args.edx; + return (0); /* all OK */ } - } - return(1); /* failed */ + return (1); /* failed */ } + /* * bios_sigsearch * @@ -234,5 +241,239 @@ bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs return(0); } - - +/* + * do not staticize, used by bioscall.s + */ +union { + struct { + u_short offset; + u_short segment; + } vec16; + struct { + u_int offset; + u_short segment; + } vec32; +} bioscall_vector; /* bios jump vector */ + +void +set_bios_selectors(struct bios_segments *seg, int flags) +{ + static u_int curgen = 1; + struct soft_segment_descriptor ssd = { + 0, /* segment base address (overwritten) */ + 0, /* length (overwritten) */ + SDT_MEMERA, /* segment type (overwritten) */ + 0, /* priority level */ + 1, /* descriptor present */ + 0, 0, + 1, /* descriptor size (overwritten) */ + 0 /* granularity == byte units */ + }; + + if (seg->generation == curgen) + return; + if (++curgen == 0) + curgen = 1; + seg->generation = curgen; + + ssd.ssd_base = seg->code32.base; + ssd.ssd_limit = seg->code32.limit; + ssdtosd(&ssd, &gdt[GBIOSCODE32_SEL].sd); + + ssd.ssd_def32 = 0; + if (flags & BIOSCODE_FLAG) { + ssd.ssd_base = seg->code16.base; + ssd.ssd_limit = seg->code16.limit; + ssdtosd(&ssd, &gdt[GBIOSCODE16_SEL].sd); + } + + ssd.ssd_type = SDT_MEMRWA; + if (flags & BIOSDATA_FLAG) { + ssd.ssd_base = seg->data.base; + ssd.ssd_limit = seg->data.limit; + ssdtosd(&ssd, &gdt[GBIOSDATA_SEL].sd); + } + + if (flags & BIOSUTIL_FLAG) { + ssd.ssd_base = seg->util.base; + ssd.ssd_limit = seg->util.limit; + ssdtosd(&ssd, &gdt[GBIOSUTIL_SEL].sd); + } + + if (flags & BIOSARGS_FLAG) { + ssd.ssd_base = seg->args.base; + ssd.ssd_limit = seg->args.limit; + ssdtosd(&ssd, &gdt[GBIOSARGS_SEL].sd); + } +} + +/* + * for pointers, we don't know how much space is supposed to be allocated, + * so we assume a minimum size of 256 bytes. If more than this is needed, + * then this can be revisited, such as adding a length specifier. + */ +#define ASSUMED_ARGSIZE 256 + +extern int vm86pa; + +/* + * this routine is really greedy with selectors, and uses 5: + * + * 32-bit code selector: to return to kernel + * 16-bit code selector: for running code + * data selector: for 16-bit data + * util selector: extra utility selector + * args selector: to handle pointers + * + * the util selector is set from the util16 entry in bios16_args, if a + * "U" specifier is seen. + * + * See <machine/pc/bios.h> for description of format specifiers + */ +int +bios16(struct bios_args *args, char *fmt, ...) +{ + char *p, *stack, *stack_top; + va_list ap; + int flags = BIOSCODE_FLAG | BIOSDATA_FLAG; + u_int i, arg_start, arg_end; + u_int *pte, *ptd; + + arg_start = 0xffffffff; + arg_end = 0; + + stack = (caddr_t)PAGE_SIZE; + va_start(ap, fmt); + for (p = fmt; p && *p; p++) { + switch (*p) { + case 'p': /* 32-bit pointer */ + i = va_arg(ap, u_int); + arg_start = min(arg_start, i); + arg_end = max(arg_end, i + ASSUMED_ARGSIZE); + flags |= BIOSARGS_FLAG; + stack -= 4; + break; + + case 'i': /* 32-bit integer */ + i = va_arg(ap, u_int); + stack -= 4; + break; + + case 'U': /* 16-bit selector */ + flags |= BIOSUTIL_FLAG; + /* FALL THROUGH */ + case 'D': /* 16-bit selector */ + case 'C': /* 16-bit selector */ + case 's': /* 16-bit integer */ + i = va_arg(ap, u_short); + stack -= 2; + break; + + default: + return (EINVAL); + } + } + + if (flags & BIOSARGS_FLAG) { + if (arg_end - arg_start > ctob(16)) + return (EACCES); + args->seg.args.base = arg_start; + args->seg.args.limit = arg_end - arg_start; + } + + args->seg.code32.base = (u_int)&bios16_call & PG_FRAME; + args->seg.code32.limit = 0xffff; + + ptd = (u_int *)rcr3(); +#ifdef SMP + if (ptd == my_idlePTD) +#else + if (ptd == IdlePTD) +#endif + { + /* + * no page table, so create one and install it. + */ + pte = (u_int *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK); + ptd = (u_int *)((u_int)ptd + KERNBASE); + *ptd = vtophys(pte) | PG_RW | PG_V; + } else { + /* + * this is a user-level page table + */ + pte = (u_int *)&PTmap; + } + /* + * install pointer to page 0. we don't need to flush the tlb, + * since there should not be a previous mapping for page 0. + */ + *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V; + + stack_top = stack; + va_start(ap, fmt); + for (p = fmt; p && *p; p++) { + switch (*p) { + case 'p': /* 32-bit pointer */ + i = va_arg(ap, u_int); + *(u_int *)stack = (i - arg_start) | + (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16); + stack += 4; + break; + + case 'i': /* 32-bit integer */ + i = va_arg(ap, u_int); + *(u_int *)stack = i; + stack += 4; + break; + + case 'U': /* 16-bit selector */ + i = va_arg(ap, u_short); + *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL); + stack += 2; + break; + + case 'D': /* 16-bit selector */ + i = va_arg(ap, u_short); + *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL); + stack += 2; + break; + + case 'C': /* 16-bit selector */ + i = va_arg(ap, u_short); + *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL); + stack += 2; + break; + + case 's': /* 16-bit integer */ + i = va_arg(ap, u_short); + *(u_short *)stack = i; + stack += 2; + break; + + default: + return (EINVAL); + } + } + + args->seg.generation = 0; /* reload selectors */ + set_bios_selectors(&args->seg, flags); + bioscall_vector.vec16.offset = (u_short)args->entry; + bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL); + + i = bios16_call(&args->r, stack_top); + + if (pte == (u_int *)&PTmap) { + *pte = 0; /* remove entry */ + } else { + *ptd = 0; /* remove page table */ + free(pte, M_TEMP); /* ... and free it */ + } + + + /* + * XXX only needs to be invlpg(0) but that doesn't work on the 386 + */ + invltlb(); + + return (i); +} diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index a321bb2..c0dc2eb 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -35,7 +35,7 @@ * SUCH DAMAGE. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * $Id: machdep.c,v 1.355 1999/07/09 04:15:40 jlemon Exp $ + * $Id: machdep.c,v 1.356 1999/07/19 23:36:30 peter Exp $ */ #include "apm.h" @@ -1004,8 +1004,8 @@ struct soft_segment_descriptor gdt_segs[] = { 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, -/* GAPMCODE32_SEL 9 APM BIOS 32-bit interface (32bit Code) */ -{ 0, /* segment base address (overwritten by APM) */ +/* GBIOSCODE32_SEL 9 BIOS 32-bit interface (32bit Code) */ +{ 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ @@ -1013,8 +1013,8 @@ struct soft_segment_descriptor gdt_segs[] = { 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, -/* GAPMCODE16_SEL 10 APM BIOS 32-bit interface (16bit Code) */ -{ 0, /* segment base address (overwritten by APM) */ +/* GBIOSCODE16_SEL 10 BIOS 32-bit interface (16bit Code) */ +{ 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ @@ -1022,8 +1022,8 @@ struct soft_segment_descriptor gdt_segs[] = { 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, -/* GAPMDATA_SEL 11 APM BIOS 32-bit interface (Data) */ -{ 0, /* segment base address (overwritten by APM) */ +/* GBIOSDATA_SEL 11 BIOS 32-bit interface (Data) */ +{ 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ @@ -1031,6 +1031,24 @@ struct soft_segment_descriptor gdt_segs[] = { 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, +/* GBIOSUTIL_SEL 12 BIOS 16-bit interface (Utility) */ +{ 0, /* segment base address (overwritten) */ + 0xfffff, /* length */ + SDT_MEMRWA, /* segment type */ + 0, /* segment descriptor priority level */ + 1, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 1 /* limit granularity (byte/page units)*/ }, +/* GBIOSARGS_SEL 13 BIOS 16-bit interface (Arguments) */ +{ 0, /* segment base address (overwritten) */ + 0xfffff, /* length */ + SDT_MEMRWA, /* segment type */ + 0, /* segment descriptor priority level */ + 1, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { diff --git a/sys/amd64/include/pc/bios.h b/sys/amd64/include/pc/bios.h index 4e47eae..0c88a4d 100644 --- a/sys/amd64/include/pc/bios.h +++ b/sys/amd64/include/pc/bios.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 1997 Michael Smith + * Copyright (c) 1998 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bios.h,v 1.1 1997/08/01 06:04:59 msmith Exp $ + * $Id: bios.h,v 1.2 1997/08/04 03:31:23 msmith Exp $ */ /* @@ -59,17 +60,6 @@ extern int bios32_SDlookup(struct bios32_SDentry *ent); extern u_int32_t bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs); -/* - * Call a 32-bit BIOS function - */ -struct bios32_args { - u_long eax; - u_long ebx; - u_long ecx; - u_long edx; -}; -extern void bios32(caddr_t func_addr, struct bios32_args *args); - #define BIOS_PADDRTOVADDR(x) (((x) - ISA_HOLE_START) + atdevbase) #define BIOS_VADDRTOPADDR(x) (((x) - atdevbase) + ISA_HOLE_START) @@ -108,5 +98,77 @@ extern struct bios32_SDentry PCIbios; extern struct SMBIOS_table *SMBIOS_table; extern struct DMI_table *DMI_table; +struct segment_info { + u_int base; + u_int limit; +}; + +#define BIOSCODE_FLAG 0x01 +#define BIOSDATA_FLAG 0x02 +#define BIOSUTIL_FLAG 0x04 +#define BIOSARGS_FLAG 0x08 + +struct bios_segments { + u_int generation; + struct segment_info code32; /* 32-bit code (mandatory) */ + struct segment_info code16; /* 16-bit code */ + struct segment_info data; /* 16-bit data */ + struct segment_info util; /* 16-bit utility */ + struct segment_info args; /* 16-bit args */ +}; + +struct bios_regs { + u_int eax; + u_int ebx; + u_int ecx; + u_int edx; + u_int esi; + u_int edi; +}; + +struct bios_args { + u_int entry; /* entry point of routine */ + struct bios_regs r; + struct bios_segments seg; +}; + +/* + * format specifiers and defines for bios16() + * s = short (16 bits) + * i = int (32 bits) + * p = pointer (converted to seg:offset) + * C,D,U = selector (corresponding to code/data/utility segment) + */ +#define PNP_COUNT_DEVNODES "sppD", 0x00 +#define PNP_GET_DEVNODE "sppsD", 0x01 +#define PNP_SET_DEVNODE "sppsD", 0x02 +#define PNP_GET_EVENT "spD", 0x03 +#define PNP_SEND_MSG "ssD", 0x04 +#define PNP_GET_DOCK_INFO "spD", 0x05 + +#define PNP_SEL_PRIBOOT "ssiiisspD", 0x07 +#define PNP_GET_PRIBOOT "sspppppD", 0x08 +#define PNP_SET_RESINFO "spD", 0x09 +#define PNP_GET_RESINFO "spD", 0x0A +#define PNP_GET_APM_ID "sppD", 0x0B + +#define PNP_GET_ISA_INFO "spD", 0x40 +#define PNP_GET_ECSD_INFO "spppD", 0x41 +#define PNP_READ_ESCD "spUD", 0x42 +#define PNP_WRITE_ESCD "spUD", 0x43 + +#define PNP_GET_DMI_INFO "spppppD", 0x50 +#define PNP_GET_DMI "sppUD", 0x51 + +#define PNP_BOOT_CHECK "sp", 0x60 +#define PNP_COUNT_IPL "sppp", 0x61 +#define PNP_GET_BOOTPRI "spp", 0x62 +#define PNP_SET_BOOTPRI "sp", 0x63 +#define PNP_GET_LASTBOOT "sp", 0x64 +#define PNP_GET_BOOTFIRST "sp", 0x65 +#define PNP_SET_BOOTFIRST "sp", 0x66 - +extern int bios16(struct bios_args *, char *, ...); +extern int bios16_call(struct bios_regs *, char *); +extern int bios32(struct bios_regs *, u_int, u_short); +extern void set_bios_selectors(struct bios_segments *, int); diff --git a/sys/amd64/include/segments.h b/sys/amd64/include/segments.h index 7028185..14afa57 100644 --- a/sys/amd64/include/segments.h +++ b/sys/amd64/include/segments.h @@ -35,7 +35,7 @@ * SUCH DAMAGE. * * from: @(#)segments.h 7.1 (Berkeley) 5/9/91 - * $Id: segments.h,v 1.19 1999/04/28 01:04:06 luoqi Exp $ + * $Id: segments.h,v 1.20 1999/06/18 14:32:21 bde Exp $ */ #ifndef _MACHINE_SEGMENTS_H_ @@ -215,14 +215,16 @@ struct region_descriptor { #define GUSERLDT_SEL 6 /* User LDT */ #define GTGATE_SEL 7 /* Process task switch gate */ #define GPANIC_SEL 8 /* Task state to consider panic from */ -#define GAPMCODE32_SEL 9 /* APM BIOS 32-bit interface (32bit Code) */ -#define GAPMCODE16_SEL 10 /* APM BIOS 32-bit interface (16bit Code) */ -#define GAPMDATA_SEL 11 /* APM BIOS 32-bit interface (Data) */ +#define GBIOSCODE32_SEL 9 /* BIOS interface (32bit Code) */ +#define GBIOSCODE16_SEL 10 /* BIOS interface (16bit Code) */ +#define GBIOSDATA_SEL 11 /* BIOS interface (Data) */ +#define GBIOSUTIL_SEL 12 /* BIOS interface (Utility) */ +#define GBIOSARGS_SEL 13 /* BIOS interface (Arguments) */ #ifdef BDE_DEBUGGER #define NGDT 18 /* some of 11-17 are reserved for debugger */ #else -#define NGDT 12 +#define NGDT 14 #endif /* |