what the heck one last one before i go take a nap...
[dragonfly.git] / sys / i386 / boot / dosboot / protmod.c
1 /*\r
2  *      protmod.c               Protected Mode Utilities\r
3  *\r
4  *      (C) 1994 by Christian Gusenbauer (cg@fimp01.fim.uni-linz.ac.at)\r
5  *      All Rights Reserved.\r
6  * \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
12  * \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
16  * \r
17  */\r
18 #include <stdio.h>\r
19 #include <stdlib.h>\r
20 #include <dos.h>\r
21 #include <memory.h>\r
22 #include <process.h>\r
23 #include "boot.h"\r
24 #include "bootinfo.h"\r
25 #include "protmod.h"\r
26 \r
27 #define data32  _emit 0x66\r
28 #define addr32  _emit 0x67\r
29 \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
33 \r
34 typedef struct {\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
39 } DTENTRY;\r
40 \r
41 struct dtr {\r
42         unsigned short limit;\r
43         unsigned long base;\r
44 };\r
45 \r
46 struct {\r
47         unsigned long cr3;\r
48         unsigned long GdtrAddress;\r
49         unsigned long IdtrAddress;\r
50         unsigned short LDTR;\r
51         unsigned short TR;\r
52         unsigned long EIP;\r
53         unsigned short CS;\r
54 } VCPI;\r
55 \r
56 static DTENTRY gdt[] =\r
57 {\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
64 };\r
65 \r
66 static DTENTRY gdt2[] =\r
67 {\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
77 };\r
78 \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
86 };\r
87 \r
88 static DTENTRY Ldt[] = {\r
89         { 0x0000, 0, 0x00, 0x0000 },    /* 0: empty */\r
90 };\r
91 \r
92 static DTENTRY idt2[256] = { 0 };\r
93 static unsigned char Tss[256];\r
94 \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
98 \r
99 struct bootinfo bootinfo;\r
100 int VCPIboot;\r
101 \r
102 int pm_copy(char far *from, unsigned long to, unsigned long count)\r
103 {\r
104         unsigned char status;\r
105         unsigned short cnt = (unsigned short) count;\r
106 \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
111 \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
115 \r
116         cnt >>= 1;\r
117 \r
118         _asm {\r
119                 pusha\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
123                 mov es,bx\r
124                 mov si,offset gdt\r
125                 int 15h                 ; now move the memory block\r
126                 mov status,ah           ; status is the return value:\r
127                                         ;       0 .. no error,\r
128                                         ;       1 .. parity error,\r
129                                         ;       2 .. exception interrupt\r
130                                         ;       3 .. gate A20 failed\r
131                 popa\r
132         }\r
133 \r
134     return (int) status;\r
135 }\r
136 \r
137 static int pm_enter(void)\r
138 {\r
139         unsigned char status;\r
140         unsigned int segment;\r
141 \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
146 \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
151 \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
157 \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
163 \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
169 \r
170         /* setup GDT entry 7: uninitialized! */\r
171 \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
177 \r
178         _asm {\r
179                 pusha\r
180                 mov ah,89h              ; enter protected mode\r
181                 mov bx,seg gdt2         ; es:si points to the GDT\r
182                 mov es,bx\r
183                 mov si,offset gdt2\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
187                 popa\r
188         }\r
189 \r
190         if (status) return (int) status;/* no protected mode; return status */\r
191 \r
192         _asm {\r
193                 mov ax,30h\r
194                 mov word ptr ss:[bp+4],ax       ; patch code selector\r
195         }\r
196         return 0;\r
197 }\r
198 \r
199 static void setupVCPI(void)\r
200 {\r
201         unsigned int segment;\r
202 \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
207 \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
212 \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
217 \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
223 \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
229 \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
235 \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
240 \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
245 }\r
246 \r
247 long get_high_memory(long size)\r
248 {\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
252         FILE *fp;\r
253 \r
254         /*\r
255          * Let's check for VCPI services.\r
256          */\r
257 \r
258         fp = fopen("EMMXXXX0", "rb");\r
259         if (fp) {\r
260                 fclose(fp);\r
261                 _asm {\r
262                         pusha\r
263                         mov ax,0de00h\r
264                         int 67h\r
265                         mov vcpiVer,bx\r
266                         mov vcpiStatus,ax\r
267                         popa\r
268                 }\r
269                 if (!(vcpiStatus&0xff00)) {\r
270                         VCPIboot = 1;\r
271                         printf("VCPI services Version %d.%d detected!\n", vcpiVer>>8, vcpiVer&0xff);\r
272                 }\r
273         }\r
274 \r
275         /*\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
278          */\r
279         kb += 128;\r
280 \r
281         _asm {\r
282                 pusha\r
283                 mov ax,4300h\r
284                 int 2fh                 ; let's look if we have XMS\r
285                 cmp al,80h\r
286                 je wehaveit             ; ok, we have it\r
287                 popa\r
288         }\r
289         return 0x110000l;               /* default load address */\r
290 \r
291 no:     _asm popa\r
292         return 0l;\r
293 \r
294         _asm {\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
299 \r
300                 mov ah,8h\r
301                 call [xms_entry]\r
302 \r
303                 cmp ax,kb\r
304                 jb no\r
305 \r
306                 mov dx,kb\r
307                 mov ah,9h\r
308                 call [xms_entry]        ; get memory\r
309                 cmp ax,0\r
310                 je no                   ; sorry, no memory\r
311 \r
312                 mov ah,0ch\r
313                 call [xms_entry]        ; lock memory block (dx = handle)\r
314                 cmp ax,0\r
315                 je no\r
316                 mov lo,bx\r
317                 mov hi,dx\r
318                 popa\r
319         }\r
320         return (long)hi*0x10000l+(long)lo + 128l*1024l;\r
321 }\r
322 \r
323 void startprog(long hmaddress, long hmsize, long startaddr, long loadflags,\r
324                            long bootdev)\r
325 {\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
332         int status;\r
333 \r
334         /*\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
338          */\r
339 \r
340         _asm {\r
341                 mov segment,cs\r
342                 mov ax, offset our_return\r
343                 mov pmoff,ax\r
344         }\r
345         BOOTaddr = segment*0x10l;\r
346         ourret = BOOTaddr + (long) pmoff;\r
347 \r
348         _asm {\r
349                 push ds\r
350 \r
351                 mov ax,cs\r
352                 mov ds,ax\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
356 \r
357                 mov bx,offset pcx\r
358                 mov pcxoff,bx\r
359                 mov bx,offset psi\r
360                 mov psioff,bx\r
361                 mov bx,offset pdi\r
362                 mov pdioff,bx\r
363                 mov segment,ds\r
364 \r
365                 pop ds\r
366         }\r
367 \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
371 \r
372         h = ptr2lin(&VCPI);\r
373 \r
374         _asm {\r
375                 push ds\r
376                 mov ax,cs\r
377                 mov ds,ax\r
378 \r
379                 mov bx,word ptr ss:[h]\r
380                 mov cx,word ptr ss:[h+2]\r
381 \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
386 \r
387                 pop ds\r
388         }\r
389 \r
390         /*\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
393          */\r
394 \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
404 \r
405         /*\r
406          * Initialize FreeBSD GDT and GDTR\r
407          */\r
408 \r
409         FreeBSDGdtr.base = GDTaddr;\r
410 \r
411         FreeBSDGdt[3].addr = BOOTaddr;\r
412 \r
413         /*\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
418          */\r
419 \r
420         if (!VCPIboot) {\r
421                 if (!(status=pm_enter())) {\r
422                         _asm {\r
423                                 cli\r
424                                 mov ax,18h\r
425                                 mov ds,ax\r
426                         }\r
427                         goto nowgo;\r
428                 }\r
429                 fprintf(stderr, "Can't switch to protected mode!\n");\r
430                 fprintf(stderr, "Giving up :-(!\n");\r
431                 exit(0);\r
432         }\r
433 \r
434         /*\r
435          * OK. Let's use VCPI services.\r
436          */\r
437 \r
438         Gdtr.base = ptr2lin(gdt2);\r
439         Idtr.base = ptr2lin(idt2);\r
440         setupVCPI();\r
441 \r
442         page = malloc(8192);            /* allocate 8 KB */\r
443         if (!page) {\r
444                 fprintf(stderr, "not enough memory!\n");\r
445                 exit(0);\r
446         }\r
447         memset(page, 0, 8192);\r
448 \r
449         h = (ptr2lin(page)+4095l) & 0xfffff000l;\r
450         pmseg = (unsigned short) (h>>4l);\r
451 \r
452         /*\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
455          */\r
456 \r
457         _asm {\r
458                 push ds\r
459                 push si\r
460                 push di\r
461                 mov ax,seg gdt2\r
462                 mov ds,ax\r
463                 mov ax,offset gdt2\r
464                 add ax,8\r
465                 mov si,ax\r
466                 mov ax,pmseg\r
467                 mov es,ax\r
468                 xor di,di\r
469                 mov ax,0xde01\r
470                 int 0x67\r
471                 pop di\r
472                 pop si\r
473                 pop ds\r
474         }\r
475 \r
476         /*\r
477          * setup values for the mode change call\r
478          */\r
479 \r
480         *((unsigned long *) MK_FP(pmseg,0x1000)) = h+3l;\r
481 \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
485         VCPI.LDTR = 7*8;\r
486         VCPI.TR = 8*8;\r
487 \r
488         _asm {\r
489                 mov ax,offset nowgoVCPI\r
490                 mov pmoff,ax\r
491         }\r
492 \r
493         VCPI.EIP = (long) pmoff;\r
494         VCPI.CS = 4*8;\r
495 \r
496         _asm {\r
497                 cli\r
498                 data32\r
499 patch2:         mov si,0\r
500                 _emit 0\r
501                 _emit 0\r
502                 mov ax,0de0ch\r
503                 int 67h\r
504 \r
505 nowgoVCPI:      ; we are now executing in protected mode\r
506                 ; first, we turn paging off!\r
507 \r
508                 data32\r
509                 _emit 0fh       ; this is "mov eax,CR0"\r
510                 _emit 20h       ;\r
511                 _emit 0c0h      ;\r
512 \r
513                 data32\r
514                 and ax,0ffffh\r
515                 _emit 0ffh\r
516                 _emit 7fh\r
517 \r
518                 data32\r
519                 _emit 0fh       ; this is "mov CR0,eax"\r
520                 _emit 22h       ; and turns paging off\r
521                 _emit 0c0h      ;\r
522 \r
523                 data32\r
524                 xor ax,ax\r
525 \r
526                 data32\r
527                 _emit 0fh       ; this is "mov CR3,eax"\r
528                 _emit 22h       ; and clears the page cache\r
529                 _emit 0d8h      ;\r
530 \r
531                 mov ax,28h\r
532                 mov ds,ax       ; load new DS\r
533                 mov es,ax\r
534                 mov ax,6*8\r
535                 mov ss,ax\r
536         }\r
537 \r
538 /*******************************************************************************\r
539  * now this is all executed in protected mode!!!\r
540  */\r
541 \r
542         /* setup new gdt for the FreeBSD kernel */\r
543         _asm {\r
544 nowgo:          cli\r
545                 lgdt FreeBSDGdtr\r
546 \r
547                 data32\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
551                 _emit 0\r
552                 _emit 0\r
553                 _emit 18h\r
554                 _emit 0\r
555 \r
556         ; Setup SS, DS and ES registers with correct values, initialize the\r
557         ; stackpointer to the correct value and execute kernel\r
558 \r
559 lab:            mov bx,10h\r
560                 _emit 0\r
561                 _emit 0\r
562                 mov ds,bx\r
563                 mov es,bx\r
564                 mov ss,bx\r
565 \r
566         ; move kernel to its correct address\r
567 \r
568 pcx:            _emit 0b9h      ; Micro$oft knows, why "mov cx,0" does not\r
569                 _emit 0         ; work here\r
570                 _emit 0\r
571                 _emit 0\r
572                 _emit 0\r
573 psi:            _emit 0beh      ; mov si,0\r
574                 _emit 0\r
575                 _emit 0\r
576                 _emit 0\r
577                 _emit 0\r
578 pdi:            _emit 0bfh      ; mov di,0\r
579                 _emit 0\r
580                 _emit 0\r
581                 _emit 0x10\r
582                 _emit 0\r
583 \r
584                 rep movsb\r
585 \r
586         ; MSVC is unable to assemble this instruction: mov esp,09f000h\r
587 \r
588                 mov sp,0f000h\r
589                 _emit 9h\r
590                 _emit 0\r
591                 retf                                            ; execute kernel\r
592 our_return:     jmp our_return\r
593         }\r
594         /* not reached */\r
595 }\r