diff options
| author | drochner <drochner@NetBSD.org> | 2002-07-07 12:56:34 +0000 |
|---|---|---|
| committer | drochner <drochner@NetBSD.org> | 2002-07-07 12:56:34 +0000 |
| commit | 3b22d7973b6a4e488bd5bbfef7daf9b0dd975a9b (patch) | |
| tree | 41b1da5404d74aca07a43b50e71da5270a2a8da9 /sys | |
| parent | f6f0c7616b567c74fa2f0a5be181b5f72ed46d67 (diff) | |
This is a (still experimental) framework to run BIOS code in a virtual
8086 machine. Ifff it works, it is much easier and more elegant than
going to real real mode:
-simpler code
-no need for "identity" memory mappings
-easy passing of buffers for bulk data to functions
-some more control
There is no interrupt support ATM, and it lacks a function to access
random virtual memory of the VM. MP issues to consider.
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/arch/i386/i386/kvm86.c | 327 | ||||
| -rw-r--r-- | sys/arch/i386/i386/kvm86call.s | 183 | ||||
| -rw-r--r-- | sys/arch/i386/include/kvm86.h | 13 |
3 files changed, 523 insertions, 0 deletions
diff --git a/sys/arch/i386/i386/kvm86.c b/sys/arch/i386/i386/kvm86.c new file mode 100644 index 00000000000..241554ee9df --- /dev/null +++ b/sys/arch/i386/i386/kvm86.c @@ -0,0 +1,327 @@ +/* $NetBSD: kvm86.c,v 1.1 2002/07/07 12:56:34 drochner Exp $ */ + +/* + * Copyright (c) 2002 + * Matthias Drochner. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: kvm86.c,v 1.1 2002/07/07 12:56:34 drochner Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/malloc.h> +#include <uvm/uvm.h> +#include <machine/pcb.h> +#include <machine/pte.h> +#include <machine/pmap.h> +#include <machine/kvm86.h> + +/* assembler functions in kvm86call.s */ +extern int kvm86_call(struct trapframe *); +extern void kvm86_ret(struct trapframe *, int); + +struct kvm86_data { +#define PGTABLE_SIZE ((1024 + 64) * 1024 / NBPG) + pt_entry_t pgtbl[PGTABLE_SIZE]; /* must be aliged */ + + struct segment_descriptor sd; + + struct pcb pcb; /* contains TSS */ + u_long iomap[0x10000/32]; /* full size io permission map */ +}; + +static void kvm86_map(struct kvm86_data *, paddr_t, u_int32_t); +static void kvm86_mapbios(struct kvm86_data *); + +/* + * global VM for BIOS calls + */ +struct kvm86_data *bioscallvmd; +/* page for trampoline and stack */ +void *bioscallscratchpage; +/* where this page is mapped in the vm86 */ +#define BIOSCALLSCRATCHPAGE_VMVA 0x1000 + +#define KVM86_IOPL3 /* not strictly necessary, saves a lot of traps */ + +void +kvm86_init() +{ + size_t vmdsize; + char *buf; + struct kvm86_data *vmd; + struct pcb *pcb; + int i; + + vmdsize = round_page(sizeof(struct kvm86_data)) + NBPG; + + buf = malloc(vmdsize, M_DEVBUF, M_NOWAIT); + if ((u_long)buf & (NBPG - 1)) { + printf("struct kvm86_data unaligned\n"); + return; + } + memset(buf, 0, vmdsize); + /* first page is stack */ + vmd = (struct kvm86_data *)(buf + NBPG); + pcb = &vmd->pcb; + + /* + * derive pcb and TSS from proc0 + * we want to access all IO ports, so we need a full-size + * permission bitmap + * XXX do we really need the pcb or just the TSS? + */ + memcpy(pcb, &proc0.p_addr->u_pcb, sizeof(struct pcb)); + pcb->pcb_tss.tss_esp0 = (int)vmd; + pcb->pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); + for (i = 0; i < sizeof(vmd->iomap) / 4; i++) + vmd->iomap[i] = 0; + pcb->pcb_tss.tss_ioopt = + ((caddr_t)vmd->iomap - (caddr_t)&pcb->pcb_tss) << 16; + + /* setup TSS descriptor (including our iomap) */ + setsegment(&vmd->sd, &pcb->pcb_tss, + sizeof(struct pcb) + sizeof(vmd->iomap) - 1, + SDT_SYS386TSS, SEL_KPL, 0, 0); + + /* prepare VM for BIOS calls */ + kvm86_mapbios(vmd); + bioscallscratchpage = malloc(NBPG, M_DEVBUF, M_NOWAIT); + kvm86_map(vmd, vtophys((vaddr_t)bioscallscratchpage), + BIOSCALLSCRATCHPAGE_VMVA); + bioscallvmd = vmd; +} + +/* + * XXX pass some stuff to the assembler code + * XXX this should be done cleanly (in call argument to kvm86_call()) + */ +static void kvm86_prepare(struct kvm86_data *); +static void +kvm86_prepare(vmd) + struct kvm86_data *vmd; +{ + extern struct pcb *vm86pcb; + extern int vm86tssd0, vm86tssd1; + extern paddr_t vm86newptd; + extern struct trapframe *vm86frame; + extern pt_entry_t *vm86pgtableva; + + vm86newptd = vtophys((vaddr_t)vmd) | PG_V | PG_RW | PG_U | PG_u; + vm86pgtableva = vmd->pgtbl; + vm86frame = (struct trapframe *)vmd - 1; + vm86pcb = &vmd->pcb; + vm86tssd0 = *(int*)&vmd->sd; + vm86tssd1 = *((int*)&vmd->sd + 1); +} + +static void +kvm86_map(vmd, pa, vmva) + struct kvm86_data *vmd; + paddr_t pa; + u_int32_t vmva; +{ + + vmd->pgtbl[vmva >> 12] = pa | PG_V | PG_RW | PG_U | PG_u; +} + +static void +kvm86_mapbios(vmd) + struct kvm86_data *vmd; +{ + paddr_t pa; + + /* map first physical page (vector table, BIOS data) */ + kvm86_map(vmd, 0, 0); + + /* map ISA hole */ + for (pa = 0xa0000; pa < 0x100000; pa += NBPG) + kvm86_map(vmd, pa, pa); +} + +void * +kvm86_bios_addpage(vmva) + u_int32_t vmva; +{ + void *mem; + + if (bioscallvmd->pgtbl[vmva >> 12]) /* allocated? */ + return (0); + + mem = malloc(NBPG, M_DEVBUF, M_NOWAIT); + if ((u_long)mem & (NBPG - 1)) { + printf("kvm86_bios_addpage: unaligned"); + return (0); + } + kvm86_map(bioscallvmd, vtophys((vaddr_t)mem), vmva); + + return (mem); +} + +void +kvm86_bios_delpage(vmva, kva) + u_int32_t vmva; + void *kva; +{ + + bioscallvmd->pgtbl[vmva >> 12] = 0; + free(kva, M_DEVBUF); +} + +int +kvm86_bioscall(intno, tf) + int intno; + struct trapframe *tf; +{ + static const unsigned char call[] = { + 0xfa, /* CLI */ + 0xcd, /* INTxx */ + 0, + 0xfb, /* STI */ + 0xf4 /* HLT */ + }; + + memcpy(bioscallscratchpage, call, sizeof(call)); + *((unsigned char *)bioscallscratchpage + 2) = intno; + + tf->tf_eip = BIOSCALLSCRATCHPAGE_VMVA; + tf->tf_cs = 0; + tf->tf_esp = BIOSCALLSCRATCHPAGE_VMVA + NBPG - 2; + tf->tf_ss = 0; + tf->tf_eflags = PSL_USERSET | PSL_VM; +#ifdef KVM86_IOPL3 + tf->tf_eflags |= PSL_IOPL; +#endif + + kvm86_prepare(bioscallvmd); /* XXX */ + return (kvm86_call(tf)); +} + +int +kvm86_bioscall_simple(intno, r) + int intno; + struct bioscallregs *r; +{ + struct trapframe tf; + int res; + + memset(&tf, 0, sizeof(struct trapframe)); + tf.tf_eax = r->EAX; + tf.tf_ebx = r->EBX; + tf.tf_ecx = r->ECX; + tf.tf_edx = r->EDX; + tf.tf_esi = r->ESI; + tf.tf_edi = r->EDI; + tf.tf_es = r->ES; + + res = kvm86_bioscall(intno, &tf); + + r->EAX = tf.tf_eax; + r->EBX = tf.tf_ebx; + r->ECX = tf.tf_ecx; + r->EDX = tf.tf_edx; + r->ESI = tf.tf_esi; + r->EDI = tf.tf_edi; + r->ES = tf.tf_es; + r->EFLAGS = tf.tf_eflags; + + return (res); +} + +void +kvm86_gpfault(tf) + struct trapframe *tf; +{ + unsigned char *kva, insn, trapno; + u_int16_t *sp; + + kva = (unsigned char *)((tf->tf_cs << 4) + tf->tf_eip); + insn = *kva; +#ifdef KVM86DEBUG + printf("kvm86_gpfault: cs=%x, eip=%x, insn=%x, eflags=%x\n", + tf->tf_cs, tf->tf_eip, insn, tf->tf_eflags); +#endif + + KASSERT(tf->tf_eflags & PSL_VM); + + switch (insn) { + case 0xf4: /* HLT - normal exit */ + kvm86_ret(tf, 0); + break; + case 0xcd: /* INTxx */ + /* fake a return stack frame and call real mode handler */ + trapno = *(kva + 1); + sp = (u_int16_t *)((tf->tf_ss << 4) + tf->tf_esp); + *(--sp) = tf->tf_eflags; + *(--sp) = tf->tf_cs; + *(--sp) = tf->tf_eip + 2; + tf->tf_esp -= 6; + tf->tf_cs = *(u_int16_t *)(trapno * 4 + 2); + tf->tf_eip = *(u_int16_t *)(trapno * 4); + break; + case 0xcf: /* IRET */ + sp = (u_int16_t *)((tf->tf_ss << 4) + tf->tf_esp); + tf->tf_eip = *(sp++); + tf->tf_cs = *(sp++); + tf->tf_eflags = *(sp++); + tf->tf_esp += 6; + tf->tf_eflags |= PSL_VM; /* outside of 16bit flag reg */ + break; +#ifndef KVM86_IOPL3 /* XXX check VME? */ + case 0xfa: /* CLI */ + case 0xfb: /* STI */ + /* XXX ignore for now */ + tf->tf_eip++; + break; + case 0x9c: /* PUSHF */ + sp = (u_int16_t *)((tf->tf_ss << 4) + tf->tf_esp); + *(--sp) = tf->tf_eflags; + tf->tf_esp -= 2; + tf->tf_eip++; + break; + case 0x9d: /* POPF */ + sp = (u_int16_t *)((tf->tf_ss << 4) + tf->tf_esp); + tf->tf_eflags = *(sp++); + tf->tf_esp += 2; + tf->tf_eip++; + tf->tf_eflags |= PSL_VM; /* outside of 16bit flag reg */ + break; +#endif + default: +#ifdef KVM86DEBUG + printf("kvm86_gpfault: unhandled\n"); +#else + printf("kvm86_gpfault: cs=%x, eip=%x, insn=%x, eflags=%x\n", + tf->tf_cs, tf->tf_eip, insn, tf->tf_eflags); +#endif + /* + * signal error to caller + */ + kvm86_ret(tf, -1); + break; + } +} diff --git a/sys/arch/i386/i386/kvm86call.s b/sys/arch/i386/i386/kvm86call.s new file mode 100644 index 00000000000..2edca1fa747 --- /dev/null +++ b/sys/arch/i386/i386/kvm86call.s @@ -0,0 +1,183 @@ +/* $NetBSD: kvm86call.s,v 1.1 2002/07/07 12:56:34 drochner Exp $ */ + +/*- + * Copyright (c) 1998 Jonathan Lemon + * All rights reserved. + * Copyright (c) 2002 + * Matthias Drochner. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/i386/i386/vm86bios.s,v 1.28 2001/12/11 23:33:40 jhb Exp $ + */ + +#include <machine/param.h> +#include <machine/asm.h> + +#include "assym.h" + +__KERNEL_RCSID(0, "$NetBSD: kvm86call.s,v 1.1 2002/07/07 12:56:34 drochner Exp $"); + + .data + .align 4 + +/* flag for trap() */ + .globl kvm86_incall +kvm86_incall: .long 0 + +/* XXX arguments for kvm86call() */ + .globl vm86pcb, vm86newptd, vm86frame, vm86pgtableva + .globl vm86tssd0, vm86tssd1 +vm86pcb: .long 0 +vm86newptd: .long 0 +vm86frame: .long 0 +vm86pgtableva: .long 0 +vm86tssd0: .long 0 +vm86tssd1: .long 0 + +/* locals */ +SCRARGFRAME: .long 0 +SCRSTACK: .long 0 +SCRTSS0: .long 0 +SCRTSS1: .long 0 + + .text + +/* int kvm86_call(struct trapframe *) */ +ENTRY(kvm86_call) + pushl %ebp + movl %esp,%ebp /* set up frame ptr */ + pushl %esi + pushl %edi + pushl %ebx + pushl %fs + pushl %gs + + movl 8(%ebp),%eax + movl %eax,SCRARGFRAME /* save argument pointer */ + + movl vm86frame,%edi /* target frame location */ + movl SCRARGFRAME,%esi /* source (set on entry) */ + movl $FRAMESIZE/4,%ecx /* sizeof(struct trapframe)/4 */ + cld + rep + movsl /* copy frame to new stack */ + + movl _C_LABEL(curproc),%ecx + movl P_ADDR(%ecx),%eax + pushl %eax /* save curpcb */ + movl vm86pcb,%eax + movl %eax,P_ADDR(%ecx) /* set curpcb to vm86pcb */ + + movl _C_LABEL(gdt),%eax + movl P_MD_TSS_SEL(%ecx),%edi + + andl $~0x0200,4(%eax,%edi,1) /* reset "task busy" */ + + movl 0(%eax,%edi,1),%edx + movl %edx,SCRTSS0 /* save first word */ + movl 4(%eax,%edi,1),%edx + movl %edx,SCRTSS1 /* save second word */ + + movl vm86tssd0,%edx /* vm86 tssd entry */ + movl %edx,0(%eax,%edi,1) + movl vm86tssd1,%edx /* vm86 tssd entry */ + movl %edx,4(%eax,%edi,1) + ltr %di + + movl %cr3,%eax + pushl %eax /* save address space */ + movl PTDpaddr,%ecx + movl %ecx,%ebx + addl $KERNBASE,%ebx /* va of Idle PTD */ + movl 0(%ebx),%eax + pushl %eax /* old pde */ + pushl %ebx /* keep address for reuse */ + + movl %esp,SCRSTACK /* save current stack location */ + + movl vm86newptd,%eax /* mapping for vm86 page table */ + movl %eax,0(%ebx) /* ... install as PTD entry 0 */ + + movl %ecx,%cr3 /* new page tables */ + movl vm86frame,%esp /* switch to new stack */ + + movl $1,kvm86_incall /* set flag for trap() */ + + /* INTRFASTEXIT */ + popl %gs + popl %fs + popl %es + popl %ds + popl %edi + popl %esi + popl %ebp + popl %ebx + popl %edx + popl %ecx + popl %eax + addl $8,%esp + iret + + +/* void kvm86_ret(struct trapframe *, int) */ +ENTRY(kvm86_ret) + pushl %ebp + movl %esp,%ebp /* set up frame ptr */ + + movl 8(%ebp),%esi /* source */ + movl SCRARGFRAME,%edi /* destination */ + movl $FRAMESIZE/4,%ecx /* size */ + cld + rep + movsl /* copy frame to original frame */ + + movl SCRSTACK,%esp /* back to old stack */ + popl %ebx /* saved va of Idle PTD */ + popl %eax + movl %eax,0(%ebx) /* restore old pde */ + popl %eax + movl %eax,%cr3 /* install old page table */ + + movl $0,kvm86_incall /* reset trapflag */ + + movl _C_LABEL(gdt),%eax + movl _C_LABEL(curproc),%ecx + movl P_MD_TSS_SEL(%ecx),%edi + movl SCRTSS0, %edx + movl %edx, 0(%eax,%edi,1) /* restore first word */ + movl SCRTSS1, %edx + movl %edx, 4(%eax,%edi,1) /* restore second word */ + ltr %di + + popl %eax /* restore curpcb */ + movl %eax, P_ADDR(%ecx) + + movl 12(%ebp), %eax /* will be kvm86_call()'s retval */ + + popl %gs + popl %fs + popl %ebx + popl %edi + popl %esi + popl %ebp + ret /* back to kvm86_call()'s caller */ diff --git a/sys/arch/i386/include/kvm86.h b/sys/arch/i386/include/kvm86.h new file mode 100644 index 00000000000..29a5b8f40c4 --- /dev/null +++ b/sys/arch/i386/include/kvm86.h @@ -0,0 +1,13 @@ +/* $NetBSD: kvm86.h,v 1.1 2002/07/07 12:56:34 drochner Exp $ */ + +void kvm86_init(void); +void kvm86_gpfault(struct trapframe *); +extern int kvm86_incall; + +void *kvm86_bios_addpage(u_int32_t); +void kvm86_bios_delpage(u_int32_t, void *); +int kvm86_bioscall(int, struct trapframe *); + +/* for migration from bioscall() */ +#include <machine/bioscall.h> +int kvm86_bioscall_simple(int, struct bioscallregs *); |
