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.
This commit is contained in:
drochner 2002-07-07 12:56:34 +00:00
parent f6f0c7616b
commit 3b22d7973b
3 changed files with 523 additions and 0 deletions

327
sys/arch/i386/i386/kvm86.c Normal file
View File

@ -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;
}
}

View File

@ -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 */

View File

@ -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 *);