2 * protmod.c Protected Mode Utilities
\r
4 * (C) 1994 by Christian Gusenbauer (cg@fimp01.fim.uni-linz.ac.at)
\r
5 * All Rights Reserved.
\r
7 * Permission to use, copy, modify and distribute this software and its
\r
8 * documentation is hereby granted, provided that both the copyright
\r
9 * notice and this permission notice appear in all copies of the
\r
10 * software, derivative works or modified versions, and any portions
\r
11 * thereof, and that both notices appear in supporting documentation.
\r
13 * I ALLOW YOU USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. I DISCLAIM
\r
14 * ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE
\r
15 * USE OF THIS SOFTWARE.
\r
22 #include <process.h>
\r
24 #include "bootinfo.h"
\r
25 #include "protmod.h"
\r
27 #define data32 _emit 0x66
\r
28 #define addr32 _emit 0x67
\r
30 #define SEG(a) ((unsigned int)(((long)(a))>>16l))
\r
31 #define OFF(a) ((unsigned int)((long)(a)))
\r
32 #define ptr2lin(a) ((unsigned long)(SEG(a)*0x10l+(long)OFF(a)))
\r
35 unsigned short limit; /* Segment limit */
\r
36 unsigned long addr:24; /* address */
\r
37 unsigned long rights:8; /* access rights */
\r
38 unsigned short reserved; /* reserved on 80286 */
\r
42 unsigned short limit;
\r
48 unsigned long GdtrAddress;
\r
49 unsigned long IdtrAddress;
\r
50 unsigned short LDTR;
\r
56 static DTENTRY gdt[] =
\r
58 { 0, 0, 0, 0 }, /* Dummy */
\r
59 { 0, 0, 0, 0 }, /* GDT itself */
\r
60 { 0, 0, 0, 0 }, /* FROM */
\r
61 { 0, 0, 0, 0 }, /* TO */
\r
62 { 0, 0, 0, 0 }, /* BIOS CS */
\r
63 { 0, 0, 0, 0 } /* SS */
\r
66 static DTENTRY gdt2[] =
\r
68 { 0, 0, 0, 0 }, /* Dummy */
\r
69 { 0, 0, 0, 0 }, /* GDT itself */
\r
70 { 0, 0, 0, 0 }, /* IDT */
\r
71 { 0, 0, 0, 0 }, /* DS */
\r
72 { 0, 0, 0, 0 }, /* ES */
\r
73 { 0, 0, 0, 0 }, /* SS */
\r
74 { 0, 0, 0, 0 }, /* CS */
\r
75 { 0, 0, 0, 0 }, /* BIOS CS, uninitialized */
\r
76 { 0, 0, 0, 0 } /* VCPI: TSS */
\r
79 static DTENTRY FreeBSDGdt[] = {
\r
80 { 0x0000, 0, 0x00, 0x0000 }, /* 0: empty */
\r
81 { 0xffff, 0, 0x9f, 0x00cf }, /* 1: kernel code */
\r
82 { 0xffff, 0, 0x93, 0x00cf }, /* 2: kernel data */
\r
83 { 0xffff, 0, 0x9e, 0x0040 }, /* 3: boot code */
\r
84 { 0xffff, 0, 0x92, 0x0040 }, /* 4: boot data */
\r
85 { 0xffff, 0, 0x9e, 0x0000 }, /* 5: 16bit boot code */
\r
88 static DTENTRY Ldt[] = {
\r
89 { 0x0000, 0, 0x00, 0x0000 }, /* 0: empty */
\r
92 static DTENTRY idt2[256] = { 0 };
\r
93 static unsigned char Tss[256];
\r
95 static struct dtr FreeBSDGdtr = { sizeof FreeBSDGdt - 1, 0 };
\r
96 static struct dtr Gdtr = { sizeof gdt2 - 1, 0 };
\r
97 static struct dtr Idtr = { sizeof idt2 - 1, 0 };
\r
99 struct bootinfo bootinfo;
\r
102 int pm_copy(char far *from, unsigned long to, unsigned long count)
\r
104 unsigned char status;
\r
105 unsigned short cnt = (unsigned short) count;
\r
107 if (count == 0l) return -1; /* count has to be > 0!! */
\r
108 gdt[2].limit = cnt-1; /* so much bytes to receive */
\r
109 gdt[2].addr = _FP_SEG(from)*0x10l+_FP_OFF(from);
\r
110 gdt[2].rights = 0x92; /* Data Segment: r/w */
\r
112 gdt[3].limit = cnt-1; /* so much bytes to read */
\r
113 gdt[3].addr = to; /* from HiMem */
\r
114 gdt[3].rights = 0x92; /* Data Segment: r/w */
\r
120 mov ah,87h ; move words
\r
121 mov cx,cnt ; that many
\r
122 mov bx,seg gdt ; es:si points to the GDT
\r
125 int 15h ; now move the memory block
\r
126 mov status,ah ; status is the return value:
\r
128 ; 1 .. parity error,
\r
129 ; 2 .. exception interrupt
\r
130 ; 3 .. gate A20 failed
\r
134 return (int) status;
\r
137 static int pm_enter(void)
\r
139 unsigned char status;
\r
140 unsigned int segment;
\r
142 /* setup GDT entry 1: GDT */
\r
143 gdt2[1].limit = sizeof(gdt2)-1;
\r
144 gdt2[1].addr = ptr2lin(gdt2);
\r
145 gdt2[1].rights = 0x92; /* Data Segment: r/w */
\r
147 /* setup GDT entry 2: IDT */
\r
148 gdt2[2].limit = sizeof(idt2)-1;
\r
149 gdt2[2].addr = ptr2lin(idt2);
\r
150 gdt2[2].rights = 0x92; /* Data Segment: r/w */
\r
152 /* setup GDT entry 3: DS */
\r
153 _asm mov segment,ds
\r
154 gdt2[3].limit = 0xffff; /* max. offset */
\r
155 gdt2[3].addr = segment*0x10l; /* segment starts at */
\r
156 gdt2[3].rights = 0x92; /* Data Segment: r/w */
\r
158 /* setup GDT entry 4: ES */
\r
159 _asm mov segment,es
\r
160 gdt2[4].limit = 0xffff; /* max. offset */
\r
161 gdt2[4].addr = segment*0x10l; /* segment starts at */
\r
162 gdt2[4].rights = 0x92; /* Data Segment: r/w */
\r
164 /* setup GDT entry 5: SS */
\r
165 _asm mov segment,ss
\r
166 gdt2[5].limit = 0; /* max. offset = 64 K!! */
\r
167 gdt2[5].addr = segment*0x10l; /* segment starts at */
\r
168 gdt2[5].rights = 0x96; /* Stack Segment: r/w, expansion direction=down */
\r
170 /* setup GDT entry 7: uninitialized! */
\r
172 /* setup GDT entry 6: CS */
\r
173 _asm mov segment,cs
\r
174 gdt2[6].limit = 0xffff; /* max. offset */
\r
175 gdt2[6].addr = segment*0x10l; /* segment starts at */
\r
176 gdt2[6].rights = 0x9a; /* Code Segment: execute only */
\r
180 mov ah,89h ; enter protected mode
\r
181 mov bx,seg gdt2 ; es:si points to the GDT
\r
184 mov bx,2820h ; setup Interrupt Levels
\r
185 int 15h ; now move the memory block
\r
186 mov status,ah ; status is the return value and 0 if no error occurred
\r
190 if (status) return (int) status;/* no protected mode; return status */
\r
194 mov word ptr ss:[bp+4],ax ; patch code selector
\r
199 static void setupVCPI(void)
\r
201 unsigned int segment;
\r
203 /* setup GDT entry 1: VCPI 1 (code) */
\r
204 gdt2[1].limit = 0; /* max. offset */
\r
205 gdt2[1].addr = 0; /* segment starts at */
\r
206 gdt2[1].rights = 0; /* Data Segment: r/w */
\r
208 /* setup GDT entry 2: VCPI 2 */
\r
209 gdt2[2].limit = 0; /* max. offset */
\r
210 gdt2[2].addr = 0; /* segment starts at */
\r
211 gdt2[2].rights = 0; /* Data Segment: r/w */
\r
213 /* setup GDT entry 3: VCPI 3 */
\r
214 gdt2[3].limit = 0; /* max. offset */
\r
215 gdt2[3].addr = 0; /* segment starts at */
\r
216 gdt2[3].rights = 0; /* Data Segment: r/w */
\r
218 /* setup GDT entry 4: code segment (use16) */
\r
219 _asm mov segment,cs
\r
220 gdt2[4].limit = 0xffff; /* max. offset */
\r
221 gdt2[4].addr = segment*0x10l; /* segment starts at */
\r
222 gdt2[4].rights = 0x9a; /* Code Segment */
\r
224 /* setup GDT entry 5: data segment (use16) */
\r
225 _asm mov segment,ds
\r
226 gdt2[5].limit = 0xffff; /* max. offset */
\r
227 gdt2[5].addr = segment*0x10l; /* segment starts at */
\r
228 gdt2[5].rights = 0x92; /* Data Segment: r/w */
\r
230 /* setup GDT entry 6: stack segment */
\r
231 _asm mov segment,ss
\r
232 gdt2[6].limit = 0; /* max. offset */
\r
233 gdt2[6].addr = segment*0x10l; /* segment starts at */
\r
234 gdt2[6].rights = 0x96; /* Stack Segment: r/w */
\r
236 /* setup GDT entry 7: LDT selector */
\r
237 gdt2[7].limit = 7; /* max. offset */
\r
238 gdt2[7].addr = ptr2lin(Ldt); /* segment starts at */
\r
239 gdt2[7].rights = 0x82; /* Data Segment: r/w */
\r
241 /* setup GDT entry 8: 286-TSS */
\r
242 gdt2[8].limit = 43; /* max. offset */
\r
243 gdt2[8].addr = ptr2lin(Tss); /* segment starts at */
\r
244 gdt2[8].rights = 0x81; /* TSS */
\r
247 long get_high_memory(long size)
\r
249 int kb = ((int) (size/1024l)+3)&0xfffc; /* we need this much KB */
\r
250 int lo, hi, vcpiVer, vcpiStatus;
\r
251 int (far *xms_entry)();
\r
255 * Let's check for VCPI services.
\r
258 fp = fopen("EMMXXXX0", "rb");
\r
269 if (!(vcpiStatus&0xff00)) {
\r
271 printf("VCPI services Version %d.%d detected!\n", vcpiVer>>8, vcpiVer&0xff);
\r
276 * I don't know why, but 386max seems to use the first 64 KB of that
\r
277 * XMS area?! So I allocate more ram than I need!
\r
284 int 2fh ; let's look if we have XMS
\r
286 je wehaveit ; ok, we have it
\r
289 return 0x110000l; /* default load address */
\r
295 wehaveit: mov ax,4310h
\r
296 int 2fh ; get xms entry point
\r
297 mov word ptr [xms_entry],bx
\r
298 mov word ptr [xms_entry+2],es
\r
308 call [xms_entry] ; get memory
\r
310 je no ; sorry, no memory
\r
313 call [xms_entry] ; lock memory block (dx = handle)
\r
320 return (long)hi*0x10000l+(long)lo + 128l*1024l;
\r
323 void startprog(long hmaddress, long hmsize, long startaddr, long loadflags,
\r
326 long GDTaddr=ptr2lin(FreeBSDGdt);
\r
327 long *stack=_MK_FP(0x9f00, 0); /* prepare stack for starting the kernel */
\r
328 unsigned int pmseg, pmoff;
\r
329 unsigned int segment, pcxoff, psioff, pdioff;
\r
330 long h, BOOTaddr, ourret;
\r
331 unsigned char *page;
\r
335 * The MSVC 1.5 inline assembler is not able to work with
\r
336 * 386 opcodes (ie. extended registers like eax). So we have
\r
337 * to use a workaround (god save Micro$oft and their customers ;)
\r
342 mov ax, offset our_return
\r
345 BOOTaddr = segment*0x10l;
\r
346 ourret = BOOTaddr + (long) pmoff;
\r
353 mov bx,offset lab ; patch the far jump after
\r
354 mov byte ptr ds:[patch],bl ; switching gdt for FreeBSD
\r
355 mov byte ptr ds:[patch+1],bh
\r
368 *((long *)_MK_FP(segment, pcxoff+1)) = hmsize;
\r
369 *((long *)_MK_FP(segment, psioff+1)) = hmaddress;
\r
370 *((long *)_MK_FP(segment, pdioff+1)) = startaddr;
\r
372 h = ptr2lin(&VCPI);
\r
379 mov bx,word ptr ss:[h]
\r
380 mov cx,word ptr ss:[h+2]
\r
382 mov byte ptr ds:[patch2+1],bl
\r
383 mov byte ptr ds:[patch2+2],bh
\r
384 mov byte ptr ds:[patch2+3],cl
\r
385 mov byte ptr ds:[patch2+4],ch
\r
391 * Setup the stack for executing the kernel. These parameters are
\r
392 * put on the stack in reversed order (addresses are INCREMENTED)!
\r
395 *stack++ = startaddr; /* that's the startaddress */
\r
396 *stack++ = 8l; /* new CS */
\r
397 *stack++ = ourret; /* ourreturn */
\r
398 *stack++ = loadflags; /* howto */
\r
399 *stack++ = bootdev; /* bootdev */
\r
400 *stack++ = 0l; /* Parameter 4 */
\r
401 *stack++ = 0l; /* Parameter 5 */
\r
402 *stack++ = 0l; /* Parameter 6 */
\r
403 *stack++ = ptr2lin(&bootinfo); /* bootinfo */
\r
406 * Initialize FreeBSD GDT and GDTR
\r
409 FreeBSDGdtr.base = GDTaddr;
\r
411 FreeBSDGdt[3].addr = BOOTaddr;
\r
414 * Now, we have to start the kernel at the given startaddress. To do this, we must
\r
415 * switch to protected mode using INT15 with AH=0x89. This call uses its own layout
\r
416 * of the GDT, so we switch to our own GDT after we return from the INT15 call. But
\r
417 * before we do this, we must copy the 64 K which overwrites the HIMEM at 0x100000.
\r
421 if (!(status=pm_enter())) {
\r
429 fprintf(stderr, "Can't switch to protected mode!\n");
\r
430 fprintf(stderr, "Giving up :-(!\n");
\r
435 * OK. Let's use VCPI services.
\r
438 Gdtr.base = ptr2lin(gdt2);
\r
439 Idtr.base = ptr2lin(idt2);
\r
442 page = malloc(8192); /* allocate 8 KB */
\r
444 fprintf(stderr, "not enough memory!\n");
\r
447 memset(page, 0, 8192);
\r
449 h = (ptr2lin(page)+4095l) & 0xfffff000l;
\r
450 pmseg = (unsigned short) (h>>4l);
\r
453 * We *do* have VCPI services, so let's get the protected mode
\r
454 * interface and page table 0 from the server.
\r
477 * setup values for the mode change call
\r
480 *((unsigned long *) MK_FP(pmseg,0x1000)) = h+3l;
\r
482 VCPI.cr3 = h+0x1000l; /* page dir is the next page */
\r
483 VCPI.GdtrAddress = ptr2lin(&Gdtr);
\r
484 VCPI.IdtrAddress = ptr2lin(&Idtr);
\r
489 mov ax,offset nowgoVCPI
\r
493 VCPI.EIP = (long) pmoff;
\r
505 nowgoVCPI: ; we are now executing in protected mode
\r
506 ; first, we turn paging off!
\r
509 _emit 0fh ; this is "mov eax,CR0"
\r
519 _emit 0fh ; this is "mov CR0,eax"
\r
520 _emit 22h ; and turns paging off
\r
527 _emit 0fh ; this is "mov CR3,eax"
\r
528 _emit 22h ; and clears the page cache
\r
532 mov ds,ax ; load new DS
\r
538 /*******************************************************************************
\r
539 * now this is all executed in protected mode!!!
\r
542 /* setup new gdt for the FreeBSD kernel */
\r
548 _emit 0eah ; far jump to "lab" (switch cs)
\r
549 patch: _emit 0 ; these two bytes are patched with the
\r
550 _emit 0 ; correct offset of "lab"
\r
556 ; Setup SS, DS and ES registers with correct values, initialize the
\r
557 ; stackpointer to the correct value and execute kernel
\r
566 ; move kernel to its correct address
\r
568 pcx: _emit 0b9h ; Micro$oft knows, why "mov cx,0" does not
\r
569 _emit 0 ; work here
\r
573 psi: _emit 0beh ; mov si,0
\r
578 pdi: _emit 0bfh ; mov di,0
\r
586 ; MSVC is unable to assemble this instruction: mov esp,09f000h
\r
591 retf ; execute kernel
\r
592 our_return: jmp our_return
\r