summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>1999-07-29 01:49:19 +0000
committermsmith <msmith@FreeBSD.org>1999-07-29 01:49:19 +0000
commite495b00d19aaf17fae0a67881fa9bbc27f42f10b (patch)
tree16887357d8f31adc150c8ac0f20f758b5a8bfcdf /sys/amd64
parent0cee2525fa4800b488630501d295a4700c050a81 (diff)
downloadFreeBSD-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.c275
-rw-r--r--sys/amd64/amd64/machdep.c32
-rw-r--r--sys/amd64/include/pc/bios.h88
-rw-r--r--sys/amd64/include/segments.h12
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
/*
OpenPOWER on IntegriCloud