|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1995-1994 The University of Utah and ! 3: * the Computer Systems Laboratory at the University of Utah (CSL). ! 4: * All rights reserved. ! 5: * ! 6: * Permission to use, copy, modify and distribute this software is hereby ! 7: * granted provided that (1) source code retains these copyright, permission, ! 8: * and disclaimer notices, and (2) redistributions including binaries ! 9: * reproduce the notices in supporting documentation, and (3) all advertising ! 10: * materials mentioning features or use of this software display the following ! 11: * acknowledgement: ``This product includes software developed by the ! 12: * Computer Systems Laboratory at the University of Utah.'' ! 13: * ! 14: * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS ! 15: * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF ! 16: * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 17: * ! 18: * CSL requests users of this software to return to [email protected] any ! 19: * improvements that they make and grant CSL redistribution rights. ! 20: * ! 21: * Author: Bryan Ford, University of Utah CSL ! 22: */ ! 23: ! 24: #include <mach/boolean.h> ! 25: #include <mach/vm_param.h> ! 26: #include <mach/machine/code16.h> ! 27: #include <mach/machine/vm_types.h> ! 28: #include <mach/machine/paging.h> ! 29: #include <mach/machine/eflags.h> ! 30: #include <mach/machine/proc_reg.h> ! 31: #include <mach/machine/far_ptr.h> ! 32: #include <mach/machine/vcpi.h> ! 33: #include <mach/machine/asm.h> ! 34: ! 35: #include "config.h" ! 36: #include "i16.h" ! 37: #include "i16_dos.h" ! 38: #include "cpu.h" ! 39: #include "real.h" ! 40: #include "debug.h" ! 41: #include "vm_param.h" ! 42: ! 43: #ifdef ENABLE_VCPI ! 44: ! 45: static boolean_t ems_page_allocated; ! 46: static unsigned short ems_handle; ! 47: ! 48: static vm_offset_t vcpi_pdir, vcpi_ptable0; ! 49: ! 50: struct far_pointer_32 vcpi_pmode_entry = {0, VCPI_CS}; ! 51: struct vcpi_switch_data vcpi_switch_data; ! 52: ! 53: static struct pseudo_descriptor gdt_pdesc, idt_pdesc; ! 54: ! 55: static boolean_t pic_reprogrammed; ! 56: ! 57: /* Save area for the DOS interrupt vectors ! 58: that used to be in the place we relocated the master PIC to. */ ! 59: static struct far_pointer_16 master_save_vecs[8]; ! 60: ! 61: ! 62: #ifdef ENABLE_PAGING ! 63: #define VCPI_PAGING_INIT(pdir_pa, first_unmapped_pa) vcpi_paging_init(pdir_pa, first_unmapped_pa) ! 64: #else ! 65: #define VCPI_PAGING_INIT(pdir_pa, first_unmapped_pa) ((void)0) ! 66: #endif ! 67: ! 68: #ifdef ENABLE_KERNEL_LDT ! 69: #define KERNEL_LDT_INIT() (vcpi_switch_data.ldt_sel = KERNEL_LDT) ! 70: #else ! 71: #define KERNEL_LDT_INIT() ((void)0) ! 72: #endif ! 73: ! 74: ! 75: CODE16 ! 76: ! 77: static void i16_vcpi_switch_to_pmode() ! 78: { ! 79: extern vm_offset_t boot_image_pa; ! 80: ! 81: i16_cli(); ! 82: ! 83: i16_assert(i16_get_ds() == i16_get_cs()); ! 84: i16_assert(i16_get_es() == i16_get_cs()); ! 85: i16_assert(i16_get_ss() == i16_get_cs()); ! 86: ! 87: /* Make sure the TSS isn't marked busy. */ ! 88: cpu[0].tables.gdt[KERNEL_TSS_IDX].access &= ~ACC_TSS_BUSY; ! 89: ! 90: /* Ask the VCPI server to switch to protected mode. */ ! 91: asm volatile(" ! 92: movl %%esp,%%edx ! 93: int $0x67 ! 94: "SEXT(pmode_return)": ! 95: movl %%edx,%%esp ! 96: movw %2,%%dx ! 97: movw %%dx,%%ss ! 98: movw %%dx,%%ds ! 99: movw %%dx,%%es ! 100: xorw %%dx,%%dx ! 101: movw %%dx,%%fs ! 102: movw %%dx,%%gs ! 103: " : ! 104: : "a" ((unsigned short)0xde0c), ! 105: "S" (boot_image_pa + (vm_offset_t)&vcpi_switch_data), ! 106: "i" (KERNEL_DS) ! 107: : "eax", "edx", "esi"); ! 108: ! 109: /* Make sure the direction flag is still clear. */ ! 110: i16_cld(); ! 111: } ! 112: ! 113: static void i16_vcpi_switch_to_real_mode() ! 114: { ! 115: i16_cli(); ! 116: ! 117: /* As requested by VCPI spec... */ ! 118: i16_clts(); ! 119: ! 120: /* Perform the switch. */ ! 121: asm volatile(" ! 122: movl %%esp,%%edx ! 123: pushl %1 ! 124: pushl %1 ! 125: pushl %1 ! 126: pushl %1 ! 127: pushl %1 ! 128: pushl %%edx ! 129: pushl $0 ! 130: pushl %1 ! 131: pushl $1f ! 132: movw %2,%%ds ! 133: lcall %%ss:"SEXT(vcpi_pmode_entry)" ! 134: 1: ! 135: " : ! 136: : "a" ((unsigned short)0xde0c), ! 137: "r" ((unsigned)real_cs), ! 138: "r" ((unsigned short)LINEAR_DS) ! 139: : "eax", "edx"); ! 140: ! 141: i16_assert(!(i16_get_eflags() & EFL_IF)); ! 142: i16_assert(i16_get_ds() == i16_get_cs()); ! 143: i16_assert(i16_get_es() == i16_get_cs()); ! 144: i16_assert(i16_get_ss() == i16_get_cs()); ! 145: ! 146: /* Make sure the direction flag is still clear. */ ! 147: i16_cld(); ! 148: } ! 149: ! 150: CODE32 ! 151: ! 152: static void vcpi_real_int(int intnum, struct real_call_data *rcd) ! 153: { ! 154: do_16bit( ! 155: unsigned int eflags; ! 156: ! 157: i16_vcpi_switch_to_real_mode(); ! 158: i16_real_int(intnum, rcd); ! 159: i16_vcpi_switch_to_pmode(); ! 160: ); ! 161: } ! 162: ! 163: static void vcpi_exit(int rc) ! 164: { ! 165: do_16bit( ! 166: i16_vcpi_switch_to_real_mode(); ! 167: i16_exit(rc); ! 168: while (1); ! 169: ); ! 170: } ! 171: ! 172: CODE16 ! 173: ! 174: static inline void ! 175: i16_vcpi_set_int_vecs(unsigned short master, unsigned short slave) ! 176: { ! 177: unsigned short rc; ! 178: ! 179: i16_assert(!(get_eflags() & EFL_IF)); ! 180: asm volatile("int $0x67" ! 181: : "=a" (rc) ! 182: : "a" ((unsigned short)0xde0b), ! 183: "b" ((unsigned short)master), ! 184: "c" ((unsigned short)slave)); ! 185: i16_assert((rc & 0xff00) == 0); ! 186: i16_assert(!(get_eflags() & EFL_IF)); ! 187: } ! 188: ! 189: /* Find a (hopefully) empty set of interrupt vectors ! 190: to use for the master hardware interrupts. ! 191: We assume that eight interrupt vectors in a row ! 192: that all have the same value are unused. ! 193: If VCPI servers weren't so brain-damaged ! 194: and took care of this during interrupt reflection ! 195: (like we do when running in raw mode), ! 196: this kludgery wouldn't be needed... */ ! 197: static int i16_find_free_vec_range() ! 198: { ! 199: /* i will track the first vector in a range; ! 200: j will track the last. */ ! 201: int i, j; ! 202: struct far_pointer_16 iv, jv; ! 203: ! 204: j = 0xff; ! 205: i16_dos_get_int_vec(j, &jv); ! 206: ! 207: for (i = j-1; ; i--) ! 208: { ! 209: if (i == 0x50) ! 210: { ! 211: /* No completely free sets found. ! 212: Stop here and just use 0x50-0x57. */ ! 213: break; ! 214: } ! 215: ! 216: i16_dos_get_int_vec(i, &iv); ! 217: if ((iv.ofs != jv.ofs) || (iv.seg != jv.seg)) ! 218: { ! 219: /* Vector contents changed. */ ! 220: j = i; ! 221: jv = iv; ! 222: continue; ! 223: } ! 224: ! 225: if ((j-i+1 >= 8) && ((i & 7) == 0)) ! 226: { ! 227: /* Found a free range. */ ! 228: break; ! 229: } ! 230: } ! 231: ! 232: return i; ! 233: } ! 234: ! 235: void i16_vcpi_check() ! 236: { ! 237: extern vm_offset_t dos_mem_phys_free_mem; ! 238: extern vm_offset_t dos_mem_phys_free_size; ! 239: extern void pmode_return(); ! 240: extern vm_offset_t boot_image_pa; ! 241: extern void (*i16_switch_to_real_mode)(); ! 242: extern void (*i16_switch_to_pmode)(); ! 243: ! 244: unsigned short rc; ! 245: unsigned short first_free_pte; ! 246: unsigned short vcpi_ver; ! 247: ! 248: i16_assert(boot_image_pa == kvtophys(0)); ! 249: ! 250: /* Check for presence of EMM driver. */ ! 251: { ! 252: int dev_info, out_status; ! 253: int fh; ! 254: ! 255: fh = i16_dos_open("EMMXXXX0", 0); ! 256: if (fh < 0) ! 257: return; ! 258: dev_info = i16_dos_get_device_info(fh); ! 259: out_status = i16_dos_get_output_status(fh); ! 260: i16_dos_close(fh); ! 261: if ((dev_info < 0) || !(dev_info & 0x80) ! 262: || (out_status != 0xff)) ! 263: return; ! 264: } ! 265: ! 266: /* Allocate an EMS page to force the EMM to be turned on. ! 267: If it fails, keep going anyway - ! 268: it may simply mean all the EMS pages are allocated. */ ! 269: asm volatile("int $0x67" ! 270: : "=a" (rc), ! 271: "=d" (ems_handle) ! 272: : "a" ((unsigned short)0x4300), ! 273: "b" ((unsigned short)1)); ! 274: if (!(rc & 0xff00)) ! 275: ems_page_allocated = TRUE; ! 276: ! 277: /* Check for VCPI. */ ! 278: asm volatile("int $0x67" : "=a" (rc), "=b" (vcpi_ver) : "a" ((unsigned short)0xde00)); ! 279: if (rc & 0xff00) ! 280: return; ! 281: i16_assert(vcpi_ver >= 0x0100); ! 282: ! 283: /* OK, it's there - we're now committed to using VCPI. */ ! 284: i16_switch_to_real_mode = i16_vcpi_switch_to_real_mode; ! 285: i16_switch_to_pmode = i16_vcpi_switch_to_pmode; ! 286: real_int = vcpi_real_int; ! 287: real_exit = vcpi_exit; ! 288: ! 289: do_debug(i16_puts("VCPI detected")); ! 290: ! 291: /* Allocate a page directory and page table from low DOS memory. */ ! 292: { ! 293: vm_offset_t new_dos_mem; ! 294: ! 295: new_dos_mem = ((dos_mem_phys_free_mem + PAGE_MASK) & ~PAGE_MASK) ! 296: + PAGE_SIZE*2; ! 297: if ((!dos_mem_phys_free_mem) ! 298: || (new_dos_mem - dos_mem_phys_free_mem ! 299: > dos_mem_phys_free_size)) ! 300: i16_die("not enough low DOS memory available"); ! 301: dos_mem_phys_free_size -= new_dos_mem - dos_mem_phys_free_mem; ! 302: dos_mem_phys_free_mem = new_dos_mem; ! 303: vcpi_pdir = new_dos_mem - PAGE_SIZE*2; ! 304: vcpi_ptable0 = vcpi_pdir + PAGE_SIZE; ! 305: } ! 306: ! 307: /* Initialize them. */ ! 308: { ! 309: int i; ! 310: pt_entry_t pde0 = vcpi_ptable0 ! 311: | INTEL_PTE_VALID | INTEL_PTE_WRITE | INTEL_PTE_USER; ! 312: ! 313: set_fs(vcpi_pdir >> 4); ! 314: asm volatile("movl %0,%%fs:(0)" : : "r" (pde0)); ! 315: for (i = 1; i < NPDES + NPTES; i++) ! 316: asm volatile("movl $0,%%fs:(,%0,4)" : : "r" (i)); ! 317: } ! 318: ! 319: /* Initialize the protected-mode interface. */ ! 320: asm volatile(" ! 321: pushw %%es ! 322: movw %4,%%es ! 323: int $0x67 ! 324: popw %%es ! 325: " ! 326: : "=a" (rc), ! 327: "=b" (vcpi_pmode_entry.ofs), ! 328: "=D" (first_free_pte) ! 329: : "a" ((unsigned short)0xde01), ! 330: "r" ((unsigned short)(vcpi_ptable0 >> 4)), ! 331: "D" (0), ! 332: "S" (&cpu[0].tables.gdt[VCPI_CS_IDX])); ! 333: i16_assert((rc & 0xff00) == 0); ! 334: i16_assert(get_ds() == get_cs()); ! 335: i16_assert(get_es() == get_cs()); ! 336: ! 337: #ifdef DEBUG ! 338: /* Sanity check: make sure the server did what it was supposed to do. */ ! 339: ! 340: i16_assert((cpu[0].tables.gdt[VCPI_CS_IDX].access & ACC_P|ACC_CODE) == ACC_P|ACC_CODE); ! 341: if (cpu[0].tables.gdt[VCPI_CS_IDX].granularity & SZ_G) ! 342: i16_assert(vcpi_pmode_entry.ofs < ! 343: (((vm_offset_t)cpu[0].tables.gdt[VCPI_CS_IDX].limit_high << 28) ! 344: | ((vm_offset_t)cpu[0].tables.gdt[VCPI_CS_IDX].limit_low << 12) ! 345: | (vm_offset_t)0xfff)); ! 346: else ! 347: i16_assert(vcpi_pmode_entry.ofs < ! 348: (((vm_offset_t)cpu[0].tables.gdt[VCPI_CS_IDX].limit_high << 16) ! 349: | (vm_offset_t)cpu[0].tables.gdt[VCPI_CS_IDX].limit_low)); ! 350: ! 351: i16_assert(first_free_pte/sizeof(pt_entry_t) >= 1*1024*1024/PAGE_SIZE); ! 352: i16_assert(first_free_pte/sizeof(pt_entry_t) <= 4*1024*1024/PAGE_SIZE); ! 353: ! 354: { ! 355: int i; ! 356: ! 357: for (i = 0; i < 1*1024*1024/PAGE_SIZE; i++) ! 358: { ! 359: pt_entry_t entry; ! 360: ! 361: set_ds(vcpi_ptable0 >> 4); ! 362: entry = ((pt_entry_t*)0)[i]; ! 363: set_ds(get_cs()); ! 364: i16_assert(entry & INTEL_PTE_VALID); ! 365: if (i < 0xf0000/PAGE_SIZE) ! 366: i16_assert(entry & INTEL_PTE_WRITE); ! 367: i16_assert(entry & INTEL_PTE_USER); ! 368: i16_assert(!(entry & INTEL_PTE_AVAIL)); ! 369: } ! 370: } ! 371: #endif /* DEBUG */ ! 372: ! 373: /* Find the VCPI server's hardware interrupt vector mappings. */ ! 374: asm volatile("int $0x67" ! 375: : "=a" (rc), ! 376: "=b" (irq_master_base), ! 377: "=c" (irq_slave_base) ! 378: : "a" ((unsigned short)0xde0a)); ! 379: i16_assert((rc & 0xff00) == 0); ! 380: irq_master_base &= 0xffff; ! 381: irq_slave_base &= 0xffff; ! 382: i16_assert((irq_master_base & 7) == 0); ! 383: i16_assert((irq_master_base == 0x08) || (irq_master_base >= 0x20)); ! 384: i16_assert((irq_slave_base & 7) == 0); ! 385: i16_assert(irq_slave_base >= 0x20); ! 386: ! 387: /* If they're the usual DOS values, change them. */ ! 388: if (irq_master_base == 0x08) ! 389: { ! 390: pic_reprogrammed = TRUE; ! 391: ! 392: i16_cli(); ! 393: ! 394: irq_master_base = i16_find_free_vec_range(); ! 395: ! 396: /* Save the old vectors in that range ! 397: and set them to a copy of vectors 8-15. */ ! 398: { ! 399: int i; ! 400: ! 401: for (i = 0; i < 8; i++) ! 402: { ! 403: struct far_pointer_16 hw_vec; ! 404: ! 405: i16_dos_get_int_vec(irq_master_base+i, ! 406: &master_save_vecs[i]); ! 407: i16_dos_get_int_vec(0x08+i, &hw_vec); ! 408: i16_dos_set_int_vec(irq_master_base+i, &hw_vec); ! 409: } ! 410: } ! 411: ! 412: /* Reprogram the PIC. */ ! 413: i16_pic_set_master(irq_master_base); ! 414: ! 415: /* Inform the VCPI server. */ ! 416: i16_vcpi_set_int_vecs(irq_master_base, irq_slave_base); ! 417: } ! 418: ! 419: /* Initialize the switch-to-pmode data structure. */ ! 420: vcpi_switch_data.phys_pdir = vcpi_pdir; ! 421: vcpi_switch_data.lin_gdt = boot_image_pa+(vm_offset_t)&gdt_pdesc.limit; ! 422: vcpi_switch_data.lin_idt = boot_image_pa+(vm_offset_t)&idt_pdesc.limit; ! 423: vcpi_switch_data.tss_sel = KERNEL_TSS; ! 424: vcpi_switch_data.entry_eip = (unsigned short)(vm_offset_t)&pmode_return; ! 425: vcpi_switch_data.entry_cs = KERNEL_16_CS; ! 426: ! 427: /* Initialize the GDT and IDT pseudo-descriptors. */ ! 428: gdt_pdesc.limit = sizeof(cpu[0].tables.gdt)-1; ! 429: gdt_pdesc.linear_base = boot_image_pa + (vm_offset_t)&cpu[0].tables.gdt; ! 430: idt_pdesc.limit = sizeof(cpu[0].tables.idt)-1; ! 431: idt_pdesc.linear_base = boot_image_pa + (vm_offset_t)&cpu[0].tables.idt; ! 432: ! 433: /* Set the GDT to temporary settings ! 434: just for getting into pmode the first time. */ ! 435: i16_gdt_init_temp(); ! 436: ! 437: /* VCPI insists on loading a TSS immediately on entering pmode, ! 438: so initialize the KERNEL_TSS descriptor in the GDT. */ ! 439: i16_fill_gdt_descriptor(&cpu[0], KERNEL_TSS, ! 440: boot_image_pa + (vm_offset_t)&cpu[0].tables.tss, ! 441: sizeof(cpu[0].tables.tss)-1, ! 442: ACC_PL_K|ACC_TSS, 0); ! 443: cpu[0].tables.tss.io_bit_map_offset = sizeof(cpu[0].tables.tss); ! 444: ! 445: #if 0 ! 446: /* Dump the various VCPI data structures, for debugging. */ ! 447: { ! 448: int i; ! 449: ! 450: i16_puts("Switch data"); ! 451: i16_writehexl(switch_data.phys_pdir); i16_putchar(' '); ! 452: i16_writehexl(switch_data.lin_gdt); i16_putchar(' '); ! 453: i16_writehexl(switch_data.lin_idt); i16_putchar(' '); ! 454: i16_writehexw(switch_data.ldt_sel); i16_putchar(' '); ! 455: i16_writehexw(switch_data.tss_sel); i16_putchar(' '); ! 456: i16_writehexl(switch_data.entry_eip); i16_putchar(' '); ! 457: i16_writehexw(switch_data.entry_cs); i16_puts(""); ! 458: ! 459: i16_puts("GDT pdesc"); ! 460: i16_writehexw(gdt_pdesc.limit); i16_putchar(' '); ! 461: i16_writehexl(gdt_pdesc.linear_base); i16_puts(""); ! 462: ! 463: i16_puts("IDT pdesc"); ! 464: i16_writehexw(idt_pdesc.limit); i16_putchar(' '); ! 465: i16_writehexl(idt_pdesc.linear_base); i16_puts(""); ! 466: ! 467: i16_puts("GDT"); ! 468: for (i = 0; i < GDTSZ; i++) ! 469: { ! 470: i16_writehexw(i*8); i16_putchar(' '); ! 471: i16_writehexll(*((long long*)&cpu[0].tables.gdt[i])); ! 472: i16_puts(""); ! 473: } ! 474: } ! 475: #endif ! 476: ! 477: /* Switch into pmode briefly to initialize the CPU tables and such. */ ! 478: i16_vcpi_switch_to_pmode(); ! 479: i16_do_32bit( ! 480: ! 481: /* Note that right now we can only access the first 1MB of memory, ! 482: because paging is enabled and that's the only memory region that's been mapped. ! 483: The rest of physical memory won't be mapped until VCPI_PAGING_INIT, ! 484: but VCPI_PAGING_INIT requires allocating memory for page tables, ! 485: and we can't call phys_mem_collect() to provide memory to the allocator ! 486: until all physical memory can be read and written. ! 487: To get out of this catch-22, ! 488: we call dos_mem_collect() beforehand here ! 489: to make low DOS memory available for allocation by VCPI_PAGING_INIT. ! 490: The call to phys_mem_collect() later will cause dos_mem_collect ! 491: to be called a second time, but it'll just do nothing then. */ ! 492: dos_mem_collect(); ! 493: ! 494: /* Initialize the basic CPU tables. */ ! 495: cpu_init(&cpu[0]); ! 496: ! 497: /* Initialize the paging system. */ ! 498: VCPI_PAGING_INIT(vcpi_pdir, (vm_offset_t)first_free_pte / 4 * PAGE_SIZE); ! 499: ! 500: /* Now that we can access all physical memory, ! 501: collect the remaining memory regions we discovered while in 16-bit mode ! 502: and add them to our free memory list. */ ! 503: phys_mem_collect(); ! 504: ! 505: /* Initialize the hardware interrupt vectors in the IDT. */ ! 506: idt_irq_init(); ! 507: ! 508: /* Now that we have an initialized LDT descriptor, start using it. */ ! 509: KERNEL_LDT_INIT(); ! 510: ! 511: /* Switch to real mode and back again once more, ! 512: to make sure everything's loaded properly. */ ! 513: do_16bit( ! 514: i16_vcpi_switch_to_real_mode(); ! 515: i16_vcpi_switch_to_pmode(); ! 516: ); ! 517: ! 518: vcpi_start(); ! 519: ); ! 520: } ! 521: ! 522: /* Shouldn't be necessary, but just in case the end of the above function, ! 523: containing the .code16, gets "optimized away"... */ ! 524: CODE16 ! 525: ! 526: void i16_vcpi_shutdown() ! 527: { ! 528: if (pic_reprogrammed) ! 529: { ! 530: pic_reprogrammed = FALSE; ! 531: ! 532: i16_cli(); ! 533: ! 534: i16_assert(irq_master_base >= 0x20); ! 535: ! 536: /* Reprogram the PIC. */ ! 537: i16_pic_set_master(0x08); ! 538: ! 539: /* Inform the VCPI server. */ ! 540: i16_vcpi_set_int_vecs(0x08, irq_slave_base); ! 541: ! 542: /* Restore the old interrupt vectors. */ ! 543: { ! 544: int i; ! 545: ! 546: for (i = 0; i < 8; i++) ! 547: { ! 548: i16_dos_set_int_vec(irq_master_base+i, ! 549: &master_save_vecs[i]); ! 550: } ! 551: } ! 552: ! 553: i16_sti(); ! 554: } ! 555: ! 556: if (ems_page_allocated) ! 557: { ! 558: ems_page_allocated = 0; ! 559: asm volatile("int $0x67" : : "a" (0x4500), "d" (ems_handle)); ! 560: } ! 561: } ! 562: ! 563: #endif ENABLE_VCPI ! 564:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.