|
|
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.