1/* $NetBSD: kvm86.c,v 1.20 2009/11/07 07:27:44 cegger Exp $ */ 2 3/* 4 * Copyright (c) 2002 5 * Matthias Drochner. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: kvm86.c,v 1.20 2009/11/07 07:27:44 cegger Exp $"); 31 32#include "opt_multiprocessor.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/proc.h> 37#include <sys/malloc.h> 38#include <sys/mutex.h> 39#include <sys/cpu.h> 40 41#include <uvm/uvm.h> 42 43#include <machine/tss.h> 44#include <machine/gdt.h> 45#include <machine/pte.h> 46#include <machine/pmap.h> 47#include <machine/kvm86.h> 48 49/* assembler functions in kvm86call.s */ 50extern int kvm86_call(struct trapframe *); 51extern void kvm86_ret(struct trapframe *, int); 52 53struct kvm86_data { 54#define PGTABLE_SIZE ((1024 + 64) * 1024 / PAGE_SIZE) 55 pt_entry_t pgtbl[PGTABLE_SIZE]; /* must be aliged */ 56 57 struct segment_descriptor sd; 58 59 struct i386tss tss; 60 u_long iomap[0x10000/32]; /* full size io permission map */ 61}; 62 63static void kvm86_map(struct kvm86_data *, paddr_t, uint32_t); 64static void kvm86_mapbios(struct kvm86_data *); 65 66/* 67 * global VM for BIOS calls 68 */ 69struct kvm86_data *bioscallvmd; 70/* page for trampoline and stack */ 71void *bioscallscratchpage; 72/* where this page is mapped in the vm86 */ 73#define BIOSCALLSCRATCHPAGE_VMVA 0x1000 74/* a virtual page to map in vm86 memory temporarily */ 75vaddr_t bioscalltmpva; 76 77int kvm86_tss_sel; 78 79kmutex_t kvm86_mp_lock; 80 81#define KVM86_IOPL3 /* not strictly necessary, saves a lot of traps */ 82 83void 84kvm86_init(void) 85{ 86 size_t vmdsize; 87 char *buf; 88 struct kvm86_data *vmd; 89 struct i386tss *tss; 90 int i; 91 int slot; 92 93 vmdsize = round_page(sizeof(struct kvm86_data)) + PAGE_SIZE; 94 95 buf = malloc(vmdsize, M_DEVBUF, M_NOWAIT | M_ZERO); 96 if ((u_long)buf & (PAGE_SIZE - 1)) { 97 printf("struct kvm86_data unaligned\n"); 98 return; 99 } 100 /* first page is stack */ 101 vmd = (struct kvm86_data *)(buf + PAGE_SIZE); 102 tss = &vmd->tss; 103 104 /* 105 * we want to access all IO ports, so we need a full-size 106 * permission bitmap 107 */ 108 memcpy(tss, &curcpu()->ci_tss, sizeof(*tss)); 109 tss->tss_esp0 = (int)vmd; 110 tss->tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); 111 for (i = 0; i < sizeof(vmd->iomap) / 4; i++) 112 vmd->iomap[i] = 0; 113 tss->tss_iobase = ((char *)vmd->iomap - (char *)tss) << 16; 114 115 /* setup TSS descriptor (including our iomap) */ 116 mutex_enter(&cpu_lock); 117 slot = gdt_get_slot(); 118 kvm86_tss_sel = GSEL(slot, SEL_KPL); 119 setgdt(slot, tss, sizeof(*tss) + sizeof(vmd->iomap) - 1, 120 SDT_SYS386TSS, SEL_KPL, 0, 0); 121 mutex_exit(&cpu_lock); 122 123 /* prepare VM for BIOS calls */ 124 kvm86_mapbios(vmd); 125 bioscallscratchpage = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT); 126 kvm86_map(vmd, vtophys((vaddr_t)bioscallscratchpage), 127 BIOSCALLSCRATCHPAGE_VMVA); 128 bioscallvmd = vmd; 129 bioscalltmpva = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY); 130 mutex_init(&kvm86_mp_lock, MUTEX_DEFAULT, IPL_NONE); 131} 132 133/* 134 * XXX pass some stuff to the assembler code 135 * XXX this should be done cleanly (in call argument to kvm86_call()) 136 */ 137static void kvm86_prepare(struct kvm86_data *); 138static void 139kvm86_prepare(struct kvm86_data *vmd) 140{ 141 extern paddr_t vm86newptd; 142 extern struct trapframe *vm86frame; 143 extern pt_entry_t *vm86pgtableva; 144 145 vm86newptd = vtophys((vaddr_t)vmd) | PG_V | PG_RW | PG_U | PG_u; 146 vm86pgtableva = vmd->pgtbl; 147 vm86frame = (struct trapframe *)vmd - 1; 148} 149 150static void 151kvm86_map(struct kvm86_data *vmd, paddr_t pa, uint32_t vmva) 152{ 153 154 vmd->pgtbl[vmva >> 12] = pa | PG_V | PG_RW | PG_U | PG_u; 155} 156 157static void 158kvm86_mapbios(struct kvm86_data *vmd) 159{ 160 paddr_t pa; 161 162 /* map first physical page (vector table, BIOS data) */ 163 kvm86_map(vmd, 0, 0); 164 165 /* map ISA hole */ 166 for (pa = 0xa0000; pa < 0x100000; pa += PAGE_SIZE) 167 kvm86_map(vmd, pa, pa); 168} 169 170void * 171kvm86_bios_addpage(uint32_t vmva) 172{ 173 void *mem; 174 175 if (bioscallvmd->pgtbl[vmva >> 12]) /* allocated? */ 176 return (0); 177 178 mem = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT); 179 if ((u_long)mem & (PAGE_SIZE - 1)) { 180 printf("kvm86_bios_addpage: unaligned"); 181 return (0); 182 } 183 kvm86_map(bioscallvmd, vtophys((vaddr_t)mem), vmva); 184 185 return (mem); 186} 187 188void 189kvm86_bios_delpage(uint32_t vmva, void *kva) 190{ 191 192 bioscallvmd->pgtbl[vmva >> 12] = 0; 193 free(kva, M_DEVBUF); 194} 195 196size_t 197kvm86_bios_read(uint32_t vmva, char *buf, size_t len) 198{ 199 size_t todo, now; 200 paddr_t vmpa; 201 202 todo = len; 203 while (todo > 0) { 204 now = min(todo, PAGE_SIZE - (vmva & (PAGE_SIZE - 1))); 205 206 if (!bioscallvmd->pgtbl[vmva >> 12]) 207 break; 208 vmpa = bioscallvmd->pgtbl[vmva >> 12] & ~(PAGE_SIZE - 1); 209 pmap_kenter_pa(bioscalltmpva, vmpa, VM_PROT_READ, 0); 210 pmap_update(pmap_kernel()); 211 212 memcpy(buf, (void *)(bioscalltmpva + (vmva & (PAGE_SIZE - 1))), 213 now); 214 buf += now; 215 todo -= now; 216 vmva += now; 217 } 218 return (len - todo); 219} 220 221int 222kvm86_bioscall(int intno, struct trapframe *tf) 223{ 224 static const unsigned char call[] = { 225 0xfa, /* CLI */ 226 0xcd, /* INTxx */ 227 0, 228 0xfb, /* STI */ 229 0xf4 /* HLT */ 230 }; 231 int ret; 232 233 mutex_enter(&kvm86_mp_lock); 234 memcpy(bioscallscratchpage, call, sizeof(call)); 235 *((unsigned char *)bioscallscratchpage + 2) = intno; 236 237 tf->tf_eip = BIOSCALLSCRATCHPAGE_VMVA; 238 tf->tf_cs = 0; 239 tf->tf_esp = BIOSCALLSCRATCHPAGE_VMVA + PAGE_SIZE - 2; 240 tf->tf_ss = 0; 241 tf->tf_eflags = PSL_USERSET | PSL_VM; 242#ifdef KVM86_IOPL3 243 tf->tf_eflags |= PSL_IOPL; 244#endif 245 tf->tf_ds = tf->tf_es = tf->tf_fs = tf->tf_gs = 0; 246 247 kvm86_prepare(bioscallvmd); /* XXX */ 248 kpreempt_disable(); 249 ret = kvm86_call(tf); 250 kpreempt_enable(); 251 mutex_exit(&kvm86_mp_lock); 252 return ret; 253} 254 255int 256kvm86_bioscall_simple(int intno, struct bioscallregs *r) 257{ 258 struct trapframe tf; 259 int res; 260 261 memset(&tf, 0, sizeof(struct trapframe)); 262 tf.tf_eax = r->EAX; 263 tf.tf_ebx = r->EBX; 264 tf.tf_ecx = r->ECX; 265 tf.tf_edx = r->EDX; 266 tf.tf_esi = r->ESI; 267 tf.tf_edi = r->EDI; 268 tf.tf_vm86_es = r->ES; 269 270 res = kvm86_bioscall(intno, &tf); 271 272 r->EAX = tf.tf_eax; 273 r->EBX = tf.tf_ebx; 274 r->ECX = tf.tf_ecx; 275 r->EDX = tf.tf_edx; 276 r->ESI = tf.tf_esi; 277 r->EDI = tf.tf_edi; 278 r->ES = tf.tf_vm86_es; 279 r->EFLAGS = tf.tf_eflags; 280 281 return (res); 282} 283 284void 285kvm86_gpfault(struct trapframe *tf) 286{ 287 unsigned char *kva, insn, trapno; 288 uint16_t *sp; 289 290 kva = (unsigned char *)((tf->tf_cs << 4) + tf->tf_eip); 291 insn = *kva; 292#ifdef KVM86DEBUG 293 printf("kvm86_gpfault: cs=%x, eip=%x, insn=%x, eflags=%x\n", 294 tf->tf_cs, tf->tf_eip, insn, tf->tf_eflags); 295#endif 296 297 KASSERT(tf->tf_eflags & PSL_VM); 298 299 switch (insn) { 300 case 0xf4: /* HLT - normal exit */ 301 kvm86_ret(tf, 0); 302 break; 303 case 0xcd: /* INTxx */ 304 /* fake a return stack frame and call real mode handler */ 305 trapno = *(kva + 1); 306 sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp); 307 *(--sp) = tf->tf_eflags; 308 *(--sp) = tf->tf_cs; 309 *(--sp) = tf->tf_eip + 2; 310 tf->tf_esp -= 6; 311 tf->tf_cs = *(uint16_t *)(trapno * 4 + 2); 312 tf->tf_eip = *(uint16_t *)(trapno * 4); 313 break; 314 case 0xcf: /* IRET */ 315 sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp); 316 tf->tf_eip = *(sp++); 317 tf->tf_cs = *(sp++); 318 tf->tf_eflags = *(sp++); 319 tf->tf_esp += 6; 320 tf->tf_eflags |= PSL_VM; /* outside of 16bit flag reg */ 321 break; 322#ifndef KVM86_IOPL3 /* XXX check VME? */ 323 case 0xfa: /* CLI */ 324 case 0xfb: /* STI */ 325 /* XXX ignore for now */ 326 tf->tf_eip++; 327 break; 328 case 0x9c: /* PUSHF */ 329 sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp); 330 *(--sp) = tf->tf_eflags; 331 tf->tf_esp -= 2; 332 tf->tf_eip++; 333 break; 334 case 0x9d: /* POPF */ 335 sp = (uint16_t *)((tf->tf_ss << 4) + tf->tf_esp); 336 tf->tf_eflags = *(sp++); 337 tf->tf_esp += 2; 338 tf->tf_eip++; 339 tf->tf_eflags |= PSL_VM; /* outside of 16bit flag reg */ 340 break; 341#endif 342 default: 343#ifdef KVM86DEBUG 344 printf("kvm86_gpfault: unhandled\n"); 345#else 346 printf("kvm86_gpfault: cs=%x, eip=%x, insn=%x, eflags=%x\n", 347 tf->tf_cs, tf->tf_eip, insn, tf->tf_eflags); 348#endif 349 /* 350 * signal error to caller 351 */ 352 kvm86_ret(tf, -1); 353 break; 354 } 355} 356