]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/l.s
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / boot / pc / l.s
1 #include "x16.h"
2 #include "mem.h"
3
4 #define WRMSR           BYTE $0x0F; BYTE $0x30  /* WRMSR, argument in AX/DX (lo/hi) */
5 #define RDTSC           BYTE $0x0F; BYTE $0x31  /* RDTSC, result in AX/DX (lo/hi) */
6 #define RDMSR           BYTE $0x0F; BYTE $0x32  /* RDMSR, result in AX/DX (lo/hi) */
7
8 #ifdef PXE
9 #define PDB             0x90000         /* temporary page tables (24KB) */
10 #else
11 #define PDB             0x08000
12 #endif PXE
13
14 /*#define VGA           1 */
15 #define NoScreenBlank   1
16 /*#define ResetDiscs    1*/
17
18 TEXT origin(SB), $0
19         /*
20          * This part of l.s is used only in the boot kernel.
21          * It assumes that we are in real address mode, i.e.,
22          * that we look like an 8086.
23          *
24          * Make sure the segments are reasonable.
25          * If we were started directly from the BIOS
26          * (i.e. no MS-DOS) then DS may not be
27          * right.
28          */
29         MOVW    CS, AX
30         MOVW    AX, DS
31
32 #ifdef VGA
33 #ifdef NoScreenBlank
34         /*
35          * Get the current video mode. If it isn't mode 3,
36          * set text mode 3.
37          * Well, no. Windows95 won't co-operate here so we have
38          * to explicitly set mode 3.
39          */
40         XORL    AX, AX
41         MOVB    $0x0F, AH
42         INT     $0x10                   /* get current video mode in AL */
43         CMPB    AL, $03
44         JEQ     sayhello
45 #endif /* NoScreenBlank */
46         XORL    AX, AX
47         MOVB    $0x03, AL
48         INT     $0x10                   /* set video mode in AL */
49
50 sayhello:
51         LWI(hello(SB), rSI)
52         CALL16(biosputs(SB))
53 #endif  /* VGA */
54 #ifdef ResetDiscs
55         XORL    AX, AX                  /* reset disc system */
56         XORL    DX, DX
57         MOVB    $0x80, DL
58         INT     $0x13
59 #endif /* ResetDiscs */
60
61 #ifdef DOTCOM
62 /*
63  *      relocate everything to a half meg and jump there
64  *      - looks weird because it is being assembled by a 32 bit
65  *        assembler for a 16 bit world
66  *
67  *      only b.com does this - not 9load
68  */
69         MOVL    $0,BX
70         INCL    BX
71         SHLL    $15,BX
72         MOVL    BX,CX
73         MOVW    BX,ES
74         MOVL    $0,SI
75         MOVL    SI,DI
76         CLD
77         REP
78         MOVSL
79
80         /*
81          * Jump to the copied image;
82          * fix up the DS for the new location.
83          */
84         FARJUMP16(0x8000, _start8000(SB))
85
86 TEXT _start8000(SB), $0
87         MFSR(rCS, rAX)                  /* fix up DS, ES (0x8000) */
88         MTSR(rAX, rDS)
89         MTSR(rAX, rES)
90
91         /*
92          * If we are already in protected mode, have to get back
93          * to real mode before trying any privileged operations
94          * (like going into protected mode...).
95          * Try to reset with a restart vector.
96          */
97         MFCR(rCR0, rAX)                 /* are we in protected mode? */
98         ANDI(0x0001, rAX)
99         JEQ     _real
100
101         CLR(rBX)
102         MTSR(rBX, rES)
103
104         LWI(0x0467, rBX)                /* reset entry point */
105         LWI(_start8000(SB), rAX)        /* offset within segment */
106         BYTE    $0x26
107         BYTE    $0x89
108         BYTE    $0x07                   /* MOVW AX, ES:[BX] */
109         LBI(0x69, rBL)
110         MFSR(rCS, rAX)                  /* segment */
111         BYTE    $0x26
112         BYTE    $0x89
113         BYTE    $0x07                   /* MOVW AX, ES:[BX] */
114
115         CLR(rDX)
116         OUTPORTB(0x70, 0x8F)
117         OUTPORTB(0x71, 0x0A)
118
119         FARJUMP16(0xFFFF, 0x0000)               /* reset */
120 #endif /* DOTCOM */
121
122 _real:
123
124 /*
125  *      do things that need to be done in real mode.
126  *      the results get written to CONFADDR (0x1200)
127  *      in a series of <4-byte-magic-number><block-of-data>
128  *      the data length is dependent on the magic number.
129  *
130  *      this gets parsed by conf.c:/^readlsconf
131  *
132  *      N.B. CALL16 kills rDI, so we can't call anything.
133  */
134         LWI(0x0000, rAX)
135         MTSR(rAX, rES)
136         LWI(0x1200, rDI)
137
138 /*
139  *      turn off interrupts
140  */
141         CLI
142
143 /*
144  *      detect APM1.2 bios support
145  */
146         /* save DI */
147         SW(rDI, rock(SB))
148
149         /* disconnect anyone else */
150         LWI(0x5304, rAX)
151         LWI(0x0000, rBX)
152         INT $0x15
153
154         /* connect */
155         CLC
156         LWI(0x5303, rAX)
157         LWI(0x0000, rBX)
158         INT $0x15
159         CLI     /* apm put interrupts back? */
160
161         JC noapm
162
163         OPSIZE; PUSHR(rSI)
164         OPSIZE; PUSHR(rBX)
165         PUSHR(rDI)
166         PUSHR(rDX)
167         PUSHR(rCX)
168         PUSHR(rAX)
169
170         /* put DI, ES back */
171         LW(rock(SB), rDI)
172         LWI(0x0000, rAX)
173         MTSR(rAX, rES)
174
175         /*
176          * write APM data.  first four bytes are APM\0.
177          */
178         LWI(0x5041, rAX)
179         STOSW
180
181         LWI(0x004d, rAX)
182         STOSW
183
184         LWI(8, rCX)
185 apmmove:
186         POPR(rAX)
187         STOSW
188         LOOP apmmove
189
190 noapm:
191
192 /*
193  *      end of real mode hacks: write terminator, put ES back.
194  */
195         LWI(0x0000, rAX)
196         STOSW
197         STOSW
198
199         MFSR(rCS, rAX)                  /* fix up ES (0x8000) */
200         MTSR(rAX, rES)
201
202 /*
203  *      goto protected mode
204  */
205 /*      MOVL    tgdtptr(SB),GDTR /**/
206          BYTE   $0x0f
207          BYTE   $0x01
208          BYTE   $0x16
209          WORD   $tgdtptr(SB)
210
211         LWI(1, rAX)
212         /* MOV AX,MSW */
213         BYTE $0x0F; BYTE $0x01; BYTE $0xF0
214
215 /*
216  *      clear prefetch queue (weird code to avoid optimizations)
217  */
218         /* JMP .+2 */
219         BYTE $0xEB
220         BYTE $0x00
221
222 /*
223  *      set all segs
224  */
225 /*      MOVW    $SELECTOR(1, SELGDT, 0),AX      /**/
226          BYTE   $0xc7
227          BYTE   $0xc0
228          WORD   $SELECTOR(1, SELGDT, 0)
229         MOVW    AX,DS
230         MOVW    AX,SS
231         MOVW    AX,ES
232         MOVW    AX,FS
233         MOVW    AX,GS
234
235 /*      JMPFAR  SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
236          BYTE   $0x66
237          BYTE   $0xEA
238          LONG   $mode32bit-KZERO(SB)
239          WORD   $SELECTOR(2, SELGDT, 0)
240
241 TEXT    mode32bit(SB),$0
242         /*
243          *  make a bottom level page table page that maps the first
244          *  16 meg of physical memory
245          */
246         MOVL    $PDB, DI                        /* clear 6 pages for the tables etc. */
247         XORL    AX, AX
248         MOVL    $(6*BY2PG), CX
249         SHRL    $2, CX
250
251         CLD
252         REP;    STOSL
253
254         MOVL    $PDB, AX                /* phys addr of temporary page table */
255         MOVL    $(4*1024),CX            /* pte's per page */
256         MOVL    $((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX
257 setpte:
258         MOVL    BX,-4(AX)(CX*4)
259         SUBL    $(1<<PGSHIFT),BX
260         LOOP    setpte
261
262         /*
263          *  make a top level page table page that maps the first
264          *  16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg
265          */
266         MOVL    AX,BX
267         ADDL    $(4*BY2PG),AX
268         ADDL    $(PTEVALID|PTEKERNEL|PTEWRITE),BX
269         MOVL    BX,0(AX)
270         MOVL    BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
271         ADDL    $BY2PG,BX
272         MOVL    BX,4(AX)
273         MOVL    BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
274         ADDL    $BY2PG,BX
275         MOVL    BX,8(AX)
276         MOVL    BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
277         ADDL    $BY2PG,BX
278         MOVL    BX,12(AX)
279         MOVL    BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
280
281         /*
282          *  point processor to top level page & turn on paging
283          *
284          *  this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0"
285          *  message in the VMware log.
286          */
287         MOVL    AX,CR3
288         MOVL    CR0,AX
289         ORL     $0X80000000,AX
290         MOVL    AX,CR0
291
292         /*
293          *  use a jump to an absolute location to get the PC into
294          *  KZERO.
295          */
296         LEAL    tokzero(SB),AX
297         JMP*    AX
298
299 /*
300  * When we load 9load from DOS, the bootstrap jumps
301  * to the instruction right after `JUMP', which gets
302  * us into kzero.
303  *
304  * The name prevents it from being optimized away.
305  */
306 TEXT jumplabel(SB), $0
307         BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P'
308
309         LEAL    tokzero(SB),AX
310         JMP*    AX
311
312 TEXT    tokzero(SB),$0
313         /*
314          * Clear BSS
315          */
316         LEAL    edata(SB),SI
317         MOVL    SI,DI
318         ADDL    $4,DI
319         MOVL    $0,AX
320         MOVL    AX,(SI)
321         LEAL    end(SB),CX
322         SUBL    DI,CX
323         SHRL    $2,CX
324         CLD
325         REP
326         MOVSL
327
328         /*
329          *  stack and mach
330          */
331         MOVL    $mach0(SB),SP
332         MOVL    SP,m(SB)
333         MOVL    $0,0(SP)
334         ADDL    $(MACHSIZE-4),SP        /* start stack above machine struct */
335
336         CALL    main(SB)
337
338 loop:
339         JMP     loop
340
341 GLOBL   mach0+0(SB), $MACHSIZE
342 GLOBL   m(SB), $4
343
344 /*
345  *  gdt to get us to 32-bit/segmented/unpaged mode
346  */
347 TEXT    tgdt(SB),$0
348
349         /* null descriptor */
350         LONG    $0
351         LONG    $0
352
353         /* data segment descriptor for 4 gigabytes (PL 0) */
354         LONG    $(0xFFFF)
355         LONG    $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
356
357         /* exec segment descriptor for 4 gigabytes (PL 0) */
358         LONG    $(0xFFFF)
359         LONG    $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
360
361         /* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */
362         LONG    $(0xFFFF)
363         LONG    $(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
364
365 /*
366  *  pointer to initial gdt
367  */
368 TEXT    tgdtptr(SB),$0
369         WORD    $(4*8)
370         LONG    $tgdt-KZERO(SB)
371
372 /*
373  * Output a string to the display.
374  * String argument is in rSI.
375  */
376 TEXT biosputs(SB), $0
377         PUSHA
378         CLR(rBX)
379 _BIOSputs:
380         LODSB
381         ORB(rAL, rAL)
382         JEQ _BIOSputsret
383
384         LBI(0x0E, rAH)
385         BIOSCALL(0x10)
386         JMP _BIOSputs
387
388 _BIOSputsret:
389         POPA
390         RET
391
392 /*
393  *  input a byte
394  */
395 TEXT    inb(SB),$0
396
397         MOVL    p+0(FP),DX
398         XORL    AX,AX
399         INB
400         RET
401
402 /*
403  * input a short from a port
404  */
405 TEXT    ins(SB), $0
406
407         MOVL    p+0(FP), DX
408         XORL    AX, AX
409         OPSIZE; INL
410         RET
411
412 /*
413  * input a long from a port
414  */
415 TEXT    inl(SB), $0
416
417         MOVL    p+0(FP), DX
418         XORL    AX, AX
419         INL
420         RET
421
422 /*
423  *  output a byte
424  */
425 TEXT    outb(SB),$0
426
427         MOVL    p+0(FP),DX
428         MOVL    b+4(FP),AX
429         OUTB
430         RET
431
432 /*
433  * output a short to a port
434  */
435 TEXT    outs(SB), $0
436         MOVL    p+0(FP), DX
437         MOVL    s+4(FP), AX
438         OPSIZE; OUTL
439         RET
440
441 /*
442  * output a long to a port
443  */
444 TEXT    outl(SB), $0
445         MOVL    p+0(FP), DX
446         MOVL    s+4(FP), AX
447         OUTL
448         RET
449
450 /*
451  *  input a string of bytes from a port
452  */
453 TEXT    insb(SB),$0
454
455         MOVL    p+0(FP),DX
456         MOVL    a+4(FP),DI
457         MOVL    c+8(FP),CX
458         CLD; REP; INSB
459         RET
460
461 /*
462  *  input a string of shorts from a port
463  */
464 TEXT    inss(SB),$0
465         MOVL    p+0(FP),DX
466         MOVL    a+4(FP),DI
467         MOVL    c+8(FP),CX
468         CLD
469         REP; OPSIZE; INSL
470         RET
471
472 /*
473  *  output a string of bytes to a port
474  */
475 TEXT    outsb(SB),$0
476
477         MOVL    p+0(FP),DX
478         MOVL    a+4(FP),SI
479         MOVL    c+8(FP),CX
480         CLD; REP; OUTSB
481         RET
482
483 /*
484  *  output a string of shorts to a port
485  */
486 TEXT    outss(SB),$0
487         MOVL    p+0(FP),DX
488         MOVL    a+4(FP),SI
489         MOVL    c+8(FP),CX
490         CLD
491         REP; OPSIZE; OUTSL
492         RET
493
494 /*
495  *  input a string of longs from a port
496  */
497 TEXT    insl(SB),$0
498
499         MOVL    p+0(FP),DX
500         MOVL    a+4(FP),DI
501         MOVL    c+8(FP),CX
502         CLD; REP; INSL
503         RET
504
505 /*
506  *  output a string of longs to a port
507  */
508 TEXT    outsl(SB),$0
509
510         MOVL    p+0(FP),DX
511         MOVL    a+4(FP),SI
512         MOVL    c+8(FP),CX
513         CLD; REP; OUTSL
514         RET
515
516 /*
517  *  routines to load/read various system registers
518  */
519 GLOBL   idtptr(SB),$6
520 TEXT    putidt(SB),$0           /* interrupt descriptor table */
521         MOVL    t+0(FP),AX
522         MOVL    AX,idtptr+2(SB)
523         MOVL    l+4(FP),AX
524         MOVW    AX,idtptr(SB)
525         MOVL    idtptr(SB),IDTR
526         RET
527
528 TEXT    putcr3(SB),$0           /* top level page table pointer */
529         MOVL    t+0(FP),AX
530         MOVL    AX,CR3
531         RET
532
533 TEXT    getcr0(SB),$0           /* coprocessor bits */
534         MOVL    CR0,AX
535         RET
536
537 TEXT    getcr2(SB),$0           /* fault address */
538         MOVL    CR2,AX
539         RET
540
541 TEXT    getcr3(SB),$0           /* page directory base */
542         MOVL    CR3,AX
543         RET
544
545 TEXT    getcr4(SB), $0          /* CR4 - extensions */
546         MOVL    CR4, AX
547         RET
548
549 TEXT _cycles(SB), $0                            /* time stamp counter */
550         RDTSC
551         MOVL    vlong+0(FP), CX                 /* &vlong */
552         MOVL    AX, 0(CX)                       /* lo */
553         MOVL    DX, 4(CX)                       /* hi */
554         RET
555
556 TEXT rdmsr(SB), $0                              /* model-specific register */
557         MOVL    index+0(FP), CX
558         RDMSR
559         MOVL    vlong+4(FP), CX                 /* &vlong */
560         MOVL    AX, 0(CX)                       /* lo */
561         MOVL    DX, 4(CX)                       /* hi */
562         RET
563         
564 TEXT wrmsr(SB), $0
565         MOVL    index+0(FP), CX
566         MOVL    lo+4(FP), AX
567         MOVL    hi+8(FP), DX
568         WRMSR
569         RET
570
571 TEXT mb386(SB), $0
572         POPL    AX                              /* return PC */
573         PUSHFL
574         PUSHL   CS
575         PUSHL   AX
576         IRETL
577
578 /*
579  *  special traps
580  */
581 TEXT    intr0(SB),$0
582         PUSHL   $0
583         PUSHL   $0
584         JMP     intrcommon
585 TEXT    intr1(SB),$0
586         PUSHL   $0
587         PUSHL   $1
588         JMP     intrcommon
589 TEXT    intr2(SB),$0
590         PUSHL   $0
591         PUSHL   $2
592         JMP     intrcommon
593 TEXT    intr3(SB),$0
594         PUSHL   $0
595         PUSHL   $3
596         JMP     intrcommon
597 TEXT    intr4(SB),$0
598         PUSHL   $0
599         PUSHL   $4
600         JMP     intrcommon
601 TEXT    intr5(SB),$0
602         PUSHL   $0
603         PUSHL   $5
604         JMP     intrcommon
605 TEXT    intr6(SB),$0
606         PUSHL   $0
607         PUSHL   $6
608         JMP     intrcommon
609 TEXT    intr7(SB),$0
610         PUSHL   $0
611         PUSHL   $7
612         JMP     intrcommon
613 TEXT    intr8(SB),$0
614         PUSHL   $8
615         JMP     intrcommon
616 TEXT    intr9(SB),$0
617         PUSHL   $0
618         PUSHL   $9
619         JMP     intrcommon
620 TEXT    intr10(SB),$0
621         PUSHL   $10
622         JMP     intrcommon
623 TEXT    intr11(SB),$0
624         PUSHL   $11
625         JMP     intrcommon
626 TEXT    intr12(SB),$0
627         PUSHL   $12
628         JMP     intrcommon
629 TEXT    intr13(SB),$0
630         PUSHL   $13
631         JMP     intrcommon
632 TEXT    intr14(SB),$0
633         PUSHL   $14
634         JMP     intrcommon
635 TEXT    intr15(SB),$0
636         PUSHL   $0
637         PUSHL   $15
638         JMP     intrcommon
639 TEXT    intr16(SB),$0
640         PUSHL   $0
641         PUSHL   $16
642         JMP     intrcommon
643 TEXT    intr24(SB),$0
644         PUSHL   $0
645         PUSHL   $24
646         JMP     intrcommon
647 TEXT    intr25(SB),$0
648         PUSHL   $0
649         PUSHL   $25
650         JMP     intrcommon
651 TEXT    intr26(SB),$0
652         PUSHL   $0
653         PUSHL   $26
654         JMP     intrcommon
655 TEXT    intr27(SB),$0
656         PUSHL   $0
657         PUSHL   $27
658         JMP     intrcommon
659 TEXT    intr28(SB),$0
660         PUSHL   $0
661         PUSHL   $28
662         JMP     intrcommon
663 TEXT    intr29(SB),$0
664         PUSHL   $0
665         PUSHL   $29
666         JMP     intrcommon
667 TEXT    intr30(SB),$0
668         PUSHL   $0
669         PUSHL   $30
670         JMP     intrcommon
671 TEXT    intr31(SB),$0
672         PUSHL   $0
673         PUSHL   $31
674         JMP     intrcommon
675 TEXT    intr32(SB),$0
676         PUSHL   $0
677         PUSHL   $32
678         JMP     intrcommon
679 TEXT    intr33(SB),$0
680         PUSHL   $0
681         PUSHL   $33
682         JMP     intrcommon
683 TEXT    intr34(SB),$0
684         PUSHL   $0
685         PUSHL   $34
686         JMP     intrcommon
687 TEXT    intr35(SB),$0
688         PUSHL   $0
689         PUSHL   $35
690         JMP     intrcommon
691 TEXT    intr36(SB),$0
692         PUSHL   $0
693         PUSHL   $36
694         JMP     intrcommon
695 TEXT    intr37(SB),$0
696         PUSHL   $0
697         PUSHL   $37
698         JMP     intrcommon
699 TEXT    intr38(SB),$0
700         PUSHL   $0
701         PUSHL   $38
702         JMP     intrcommon
703 TEXT    intr39(SB),$0
704         PUSHL   $0
705         PUSHL   $39
706         JMP     intrcommon
707 TEXT    intr64(SB),$0
708         PUSHL   $0
709         PUSHL   $64
710         JMP     intrcommon
711 TEXT    intrbad(SB),$0
712         PUSHL   $0
713         PUSHL   $0x1ff
714         JMP     intrcommon
715
716 intrcommon:
717         PUSHL   DS
718         PUSHL   ES
719         PUSHL   FS
720         PUSHL   GS
721         PUSHAL
722         MOVL    $(KDSEL),AX
723         MOVW    AX,DS
724         MOVW    AX,ES
725         LEAL    0(SP),AX
726         PUSHL   AX
727         CALL    trap(SB)
728         POPL    AX
729         POPAL
730         POPL    GS
731         POPL    FS
732         POPL    ES
733         POPL    DS
734         ADDL    $8,SP   /* error code and trap type */
735         IRETL
736
737
738 /*
739  *  interrupt level is interrupts on or off
740  */
741 TEXT    spllo(SB),$0
742         PUSHFL
743         POPL    AX
744         STI
745         RET
746
747 TEXT    splhi(SB),$0
748         PUSHFL
749         POPL    AX
750         CLI
751         RET
752
753 TEXT    splx(SB),$0
754         MOVL    s+0(FP),AX
755         PUSHL   AX
756         POPFL
757         RET
758
759 /*
760  *  do nothing whatsoever till interrupt happens
761  */
762 TEXT    idle(SB),$0
763         HLT
764         RET
765
766 /*
767  * Try to determine the CPU type which requires fiddling with EFLAGS.
768  * If the Id bit can be toggled then the CPUID instruciton can be used
769  * to determine CPU identity and features. First have to check if it's
770  * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
771  * toggled then it's an older 486 of some kind.
772  *
773  *      cpuid(id[], &ax, &dx);
774  */
775 #define CPUID           BYTE $0x0F; BYTE $0xA2  /* CPUID, argument in AX */
776 TEXT cpuid(SB), $0
777         MOVL    $0x240000, AX
778         PUSHL   AX
779         POPFL                                   /* set Id|Ac */
780
781         PUSHFL
782         POPL    BX                              /* retrieve value */
783
784         MOVL    $0, AX
785         PUSHL   AX
786         POPFL                                   /* clear Id|Ac, EFLAGS initialised */
787
788         PUSHFL
789         POPL    AX                              /* retrieve value */
790         XORL    BX, AX
791         TESTL   $0x040000, AX                   /* Ac */
792         JZ      _cpu386                         /* can't set this bit on 386 */
793         TESTL   $0x200000, AX                   /* Id */
794         JZ      _cpu486                         /* can't toggle this bit on some 486 */
795
796         MOVL    $0, AX
797         CPUID
798         MOVL    id+0(FP), BP
799         MOVL    BX, 0(BP)                       /* "Genu" "Auth" "Cyri" */
800         MOVL    DX, 4(BP)                       /* "ineI" "enti" "xIns" */
801         MOVL    CX, 8(BP)                       /* "ntel" "cAMD" "tead" */
802
803         MOVL    $1, AX
804         CPUID
805         JMP     _cpuid
806
807 _cpu486:
808         MOVL    $0x400, AX
809         MOVL    $0, DX
810         JMP     _cpuid
811
812 _cpu386:
813         MOVL    $0x300, AX
814         MOVL    $0, DX
815
816 _cpuid:
817         MOVL    ax+4(FP), BP
818         MOVL    AX, 0(BP)
819         MOVL    dx+8(FP), BP
820         MOVL    DX, 0(BP)
821         RET
822
823
824 /*
825  *  basic timing loop to determine CPU frequency
826  */
827 TEXT    aamloop(SB),$0
828
829         MOVL    c+0(FP),CX
830 aaml1:
831         AAM
832         LOOP    aaml1
833         RET
834
835 TEXT hello(SB), $0
836         BYTE $'\r';
837         BYTE $'\n';
838         BYTE $'P'; BYTE $'l'; BYTE $'a'; BYTE $'n';
839         BYTE $' '; BYTE $'9'; BYTE $' '; BYTE $'f';
840         BYTE $'r'; BYTE $'o'; BYTE $'m'; BYTE $' ';
841         BYTE $'B'; BYTE $'e'; BYTE $'l'; BYTE $'l';
842         BYTE $' '; BYTE $'L'; BYTE $'a'; BYTE $'b';
843         BYTE $'s'; 
844 #ifdef PXE
845         BYTE $' '; BYTE $'b'; BYTE $'y'; BYTE $' ';
846         BYTE $'P'; BYTE $'X'; BYTE $'E';
847 #endif
848         BYTE $'\r';
849         BYTE $'\n';
850         BYTE $'\z';
851
852 TEXT rock(SB), $0
853         BYTE $0; BYTE $0; BYTE $0; BYTE $0;
854
855 GLOBL pxe(SB), $4
856 #ifdef PXE
857 DATA    pxe+0(SB)/4, $1
858 #else
859 DATA    pxe+0(SB)/4, $0
860 #endif /* PXE */
861
862 /*
863  * Save registers.
864  */
865 TEXT saveregs(SB), $0
866         /* appease 8l */
867         SUBL $32, SP
868         POPL AX
869         POPL AX
870         POPL AX
871         POPL AX
872         POPL AX
873         POPL AX
874         POPL AX
875         POPL AX
876         
877         PUSHL   AX
878         PUSHL   BX
879         PUSHL   CX
880         PUSHL   DX
881         PUSHL   BP
882         PUSHL   DI
883         PUSHL   SI
884         PUSHFL
885
886         XCHGL   32(SP), AX      /* swap return PC and saved flags */
887         XCHGL   0(SP), AX
888         XCHGL   32(SP), AX
889         RET
890
891 TEXT restoreregs(SB), $0
892         /* appease 8l */
893         PUSHL   AX
894         PUSHL   AX
895         PUSHL   AX
896         PUSHL   AX
897         PUSHL   AX
898         PUSHL   AX
899         PUSHL   AX
900         PUSHL   AX
901         ADDL    $32, SP
902         
903         XCHGL   32(SP), AX      /* swap return PC and saved flags */
904         XCHGL   0(SP), AX
905         XCHGL   32(SP), AX
906
907         POPFL
908         POPL    SI
909         POPL    DI
910         POPL    BP
911         POPL    DX
912         POPL    CX
913         POPL    BX
914         POPL    AX
915         RET
916
917 /*
918  * Assumed to be in protected mode at time of call.
919  * Switch to real mode, execute an interrupt, and
920  * then switch back to protected mode.  
921  *
922  * Assumes:
923  *
924  *      - no device interrupts are going to come in
925  *      - 0-16MB is identity mapped in page tables
926  *      - can use code segment 0x1000 in real mode
927  *              to get at l.s code
928  */
929 TEXT realmodeidtptr(SB), $0
930         WORD    $(4*256-1)
931         LONG    $0
932
933 TEXT realmode0(SB), $0
934         CALL    saveregs(SB)
935
936         /* switch to low code address */
937         LEAL    physcode-KZERO(SB), AX
938         JMP *AX
939
940 TEXT physcode(SB), $0
941
942         /* switch to low stack */
943         MOVL    SP, AX
944         MOVL    $0x7C00, SP
945         PUSHL   AX
946
947         /* load IDT with real-mode version; GDT already fine */
948         MOVL    realmodeidtptr(SB), IDTR
949
950         /* edit INT $0x00 instruction below */
951         MOVL    realmodeintr(SB), AX
952         MOVB    AX, realmodeintrinst+1(SB)
953
954         /* disable paging */
955         MOVL    CR0, AX
956         ANDL    $0x7FFFFFFF, AX
957         MOVL    AX, CR0
958         /* JMP .+2 to clear prefetch queue*/
959         BYTE $0xEB; BYTE $0x00
960
961         /* jump to 16-bit code segment */
962 /*      JMPFAR  SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/
963          BYTE   $0xEA
964          LONG   $again16bit-KZERO(SB)
965          WORD   $SELECTOR(3, SELGDT, 0)
966
967 TEXT again16bit(SB), $0
968         /*
969          * Now in 16-bit compatibility mode.
970          * These are 32-bit instructions being interpreted
971          * as 16-bit instructions.  I'm being lazy and
972          * not using the macros because I know when
973          * the 16- and 32-bit instructions look the same
974          * or close enough.
975          */
976
977         /* disable protected mode and jump to real mode cs */
978         OPSIZE; MOVL CR0, AX
979         OPSIZE; XORL BX, BX
980         OPSIZE; INCL BX
981         OPSIZE; XORL BX, AX
982         OPSIZE; MOVL AX, CR0
983
984         /* JMPFAR 0x1000:now16real */
985          BYTE $0xEA
986          WORD   $now16real-KZERO(SB)
987          WORD   $0x1000
988
989 TEXT now16real(SB), $0
990         /* copy the registers for the bios call */
991         LWI(0x1000, rAX)
992         MOVW    AX,SS
993         LWI(realmoderegs(SB), rBP)
994         
995         /* offsets are in Ureg */
996         LXW(44, xBP, rAX)
997         MOVW    AX, DS
998         LXW(40, xBP, rAX)
999         MOVW    AX, ES
1000
1001         OPSIZE; LXW(0, xBP, rDI)
1002         OPSIZE; LXW(4, xBP, rSI)
1003         OPSIZE; LXW(16, xBP, rBX)
1004         OPSIZE; LXW(20, xBP, rDX)
1005         OPSIZE; LXW(24, xBP, rCX)
1006         OPSIZE; LXW(28, xBP, rAX)
1007
1008         CLC
1009
1010 TEXT realmodeintrinst(SB), $0
1011         INT $0x00
1012
1013
1014         /* save the registers after the call */
1015
1016         LWI(0x7bfc, rSP)
1017         OPSIZE; PUSHFL
1018         OPSIZE; PUSHL AX
1019
1020         LWI(0x1000, rAX)
1021         MOVW    AX,SS
1022         LWI(realmoderegs(SB), rBP)
1023         
1024         OPSIZE; SXW(rDI, 0, xBP)
1025         OPSIZE; SXW(rSI, 4, xBP)
1026         OPSIZE; SXW(rBX, 16, xBP)
1027         OPSIZE; SXW(rDX, 20, xBP)
1028         OPSIZE; SXW(rCX, 24, xBP)
1029         OPSIZE; POPL AX
1030         OPSIZE; SXW(rAX, 28, xBP)
1031
1032         MOVW    DS, AX
1033         OPSIZE; SXW(rAX, 44, xBP)
1034         MOVW    ES, AX
1035         OPSIZE; SXW(rAX, 40, xBP)
1036
1037         OPSIZE; POPL AX
1038         OPSIZE; SXW(rAX, 64, xBP)       /* flags */
1039
1040         /* re-enter protected mode and jump to 32-bit code */
1041         OPSIZE; MOVL $1, AX
1042         OPSIZE; MOVL AX, CR0
1043         
1044 /*      JMPFAR  SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/
1045          OPSIZE
1046          BYTE $0xEA
1047          LONG   $again32bit-KZERO(SB)
1048          WORD   $SELECTOR(2, SELGDT, 0)
1049
1050 TEXT again32bit(SB), $0
1051         MOVW    $SELECTOR(1, SELGDT, 0),AX
1052         MOVW    AX,DS
1053         MOVW    AX,SS
1054         MOVW    AX,ES
1055         MOVW    AX,FS
1056         MOVW    AX,GS
1057
1058         /* enable paging and jump to kzero-address code */
1059         MOVL    CR0, AX
1060         ORL     $0x80000000, AX
1061         MOVL    AX, CR0
1062         LEAL    again32kzero(SB), AX
1063         JMP*    AX
1064
1065 TEXT again32kzero(SB), $0
1066         /* breathe a sigh of relief - back in 32-bit protected mode */
1067
1068         /* switch to old stack */       
1069         PUSHL   AX      /* match popl below for 8l */
1070         MOVL    $0x7BFC, SP
1071         POPL    SP
1072
1073         /* restore idt */
1074         MOVL    idtptr(SB),IDTR
1075
1076         CALL    restoreregs(SB)
1077         RET
1078
1079 TEXT realmoderegs(SB), $0
1080         LONG $0; LONG $0; LONG $0; LONG $0
1081         LONG $0; LONG $0; LONG $0; LONG $0
1082         LONG $0; LONG $0; LONG $0; LONG $0
1083         LONG $0; LONG $0; LONG $0; LONG $0
1084         LONG $0; LONG $0; LONG $0; LONG $0
1085
1086 TEXT realmodeintr(SB), $0
1087         LONG $0
1088