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) */
9 #define PDB 0x90000 /* temporary page tables (24KB) */
15 #define NoScreenBlank 1
16 /*#define ResetDiscs 1*/
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.
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
35 * Get the current video mode. If it isn't mode 3,
37 * Well, no. Windows95 won't co-operate here so we have
38 * to explicitly set mode 3.
42 INT $0x10 /* get current video mode in AL */
45 #endif /* NoScreenBlank */
48 INT $0x10 /* set video mode in AL */
55 XORL AX, AX /* reset disc system */
59 #endif /* ResetDiscs */
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
67 * only b.com does this - not 9load
81 * Jump to the copied image;
82 * fix up the DS for the new location.
84 FARJUMP16(0x8000, _start8000(SB))
86 TEXT _start8000(SB), $0
87 MFSR(rCS, rAX) /* fix up DS, ES (0x8000) */
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.
97 MFCR(rCR0, rAX) /* are we in protected mode? */
104 LWI(0x0467, rBX) /* reset entry point */
105 LWI(_start8000(SB), rAX) /* offset within segment */
108 BYTE $0x07 /* MOVW AX, ES:[BX] */
110 MFSR(rCS, rAX) /* segment */
113 BYTE $0x07 /* MOVW AX, ES:[BX] */
119 FARJUMP16(0xFFFF, 0x0000) /* reset */
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.
130 * this gets parsed by conf.c:/^readlsconf
132 * N.B. CALL16 kills rDI, so we can't call anything.
139 * turn off interrupts
144 * detect APM1.2 bios support
149 /* disconnect anyone else */
159 CLI /* apm put interrupts back? */
170 /* put DI, ES back */
176 * write APM data. first four bytes are APM\0.
193 * end of real mode hacks: write terminator, put ES back.
199 MFSR(rCS, rAX) /* fix up ES (0x8000) */
203 * goto protected mode
205 /* MOVL tgdtptr(SB),GDTR /**/
213 BYTE $0x0F; BYTE $0x01; BYTE $0xF0
216 * clear prefetch queue (weird code to avoid optimizations)
225 /* MOVW $SELECTOR(1, SELGDT, 0),AX /**/
228 WORD $SELECTOR(1, SELGDT, 0)
235 /* JMPFAR SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
238 LONG $mode32bit-KZERO(SB)
239 WORD $SELECTOR(2, SELGDT, 0)
241 TEXT mode32bit(SB),$0
243 * make a bottom level page table page that maps the first
244 * 16 meg of physical memory
246 MOVL $PDB, DI /* clear 6 pages for the tables etc. */
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
259 SUBL $(1<<PGSHIFT),BX
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
268 ADDL $(PTEVALID|PTEKERNEL|PTEWRITE),BX
270 MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
273 MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
276 MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
279 MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
282 * point processor to top level page & turn on paging
284 * this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0"
285 * message in the VMware log.
293 * use a jump to an absolute location to get the PC into
300 * When we load 9load from DOS, the bootstrap jumps
301 * to the instruction right after `JUMP', which gets
304 * The name prevents it from being optimized away.
306 TEXT jumplabel(SB), $0
307 BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P'
334 ADDL $(MACHSIZE-4),SP /* start stack above machine struct */
341 GLOBL mach0+0(SB), $MACHSIZE
345 * gdt to get us to 32-bit/segmented/unpaged mode
349 /* null descriptor */
353 /* data segment descriptor for 4 gigabytes (PL 0) */
355 LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
357 /* exec segment descriptor for 4 gigabytes (PL 0) */
359 LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
361 /* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */
363 LONG $(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
366 * pointer to initial gdt
373 * Output a string to the display.
374 * String argument is in rSI.
376 TEXT biosputs(SB), $0
403 * input a short from a port
413 * input a long from a port
433 * output a short to a port
442 * output a long to a port
451 * input a string of bytes from a port
462 * input a string of shorts from a port
473 * output a string of bytes to a port
484 * output a string of shorts to a port
495 * input a string of longs from a port
506 * output a string of longs to a port
517 * routines to load/read various system registers
520 TEXT putidt(SB),$0 /* interrupt descriptor table */
528 TEXT putcr3(SB),$0 /* top level page table pointer */
533 TEXT getcr0(SB),$0 /* coprocessor bits */
537 TEXT getcr2(SB),$0 /* fault address */
541 TEXT getcr3(SB),$0 /* page directory base */
545 TEXT getcr4(SB), $0 /* CR4 - extensions */
549 TEXT _cycles(SB), $0 /* time stamp counter */
551 MOVL vlong+0(FP), CX /* &vlong */
552 MOVL AX, 0(CX) /* lo */
553 MOVL DX, 4(CX) /* hi */
556 TEXT rdmsr(SB), $0 /* model-specific register */
559 MOVL vlong+4(FP), CX /* &vlong */
560 MOVL AX, 0(CX) /* lo */
561 MOVL DX, 4(CX) /* hi */
572 POPL AX /* return PC */
734 ADDL $8,SP /* error code and trap type */
739 * interrupt level is interrupts on or off
760 * do nothing whatsoever till interrupt happens
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.
773 * cpuid(id[], &ax, &dx);
775 #define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */
779 POPFL /* set Id|Ac */
782 POPL BX /* retrieve value */
786 POPFL /* clear Id|Ac, EFLAGS initialised */
789 POPL AX /* retrieve value */
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 */
799 MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */
800 MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */
801 MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */
825 * basic timing loop to determine CPU frequency
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';
845 BYTE $' '; BYTE $'b'; BYTE $'y'; BYTE $' ';
846 BYTE $'P'; BYTE $'X'; BYTE $'E';
853 BYTE $0; BYTE $0; BYTE $0; BYTE $0;
865 TEXT saveregs(SB), $0
886 XCHGL 32(SP), AX /* swap return PC and saved flags */
891 TEXT restoreregs(SB), $0
903 XCHGL 32(SP), AX /* swap return PC and saved flags */
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.
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
929 TEXT realmodeidtptr(SB), $0
933 TEXT realmode0(SB), $0
936 /* switch to low code address */
937 LEAL physcode-KZERO(SB), AX
940 TEXT physcode(SB), $0
942 /* switch to low stack */
947 /* load IDT with real-mode version; GDT already fine */
948 MOVL realmodeidtptr(SB), IDTR
950 /* edit INT $0x00 instruction below */
951 MOVL realmodeintr(SB), AX
952 MOVB AX, realmodeintrinst+1(SB)
958 /* JMP .+2 to clear prefetch queue*/
959 BYTE $0xEB; BYTE $0x00
961 /* jump to 16-bit code segment */
962 /* JMPFAR SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/
964 LONG $again16bit-KZERO(SB)
965 WORD $SELECTOR(3, SELGDT, 0)
967 TEXT again16bit(SB), $0
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
977 /* disable protected mode and jump to real mode cs */
984 /* JMPFAR 0x1000:now16real */
986 WORD $now16real-KZERO(SB)
989 TEXT now16real(SB), $0
990 /* copy the registers for the bios call */
993 LWI(realmoderegs(SB), rBP)
995 /* offsets are in Ureg */
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)
1010 TEXT realmodeintrinst(SB), $0
1014 /* save the registers after the call */
1022 LWI(realmoderegs(SB), rBP)
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)
1030 OPSIZE; SXW(rAX, 28, xBP)
1033 OPSIZE; SXW(rAX, 44, xBP)
1035 OPSIZE; SXW(rAX, 40, xBP)
1038 OPSIZE; SXW(rAX, 64, xBP) /* flags */
1040 /* re-enter protected mode and jump to 32-bit code */
1042 OPSIZE; MOVL AX, CR0
1044 /* JMPFAR SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/
1047 LONG $again32bit-KZERO(SB)
1048 WORD $SELECTOR(2, SELGDT, 0)
1050 TEXT again32bit(SB), $0
1051 MOVW $SELECTOR(1, SELGDT, 0),AX
1058 /* enable paging and jump to kzero-address code */
1062 LEAL again32kzero(SB), AX
1065 TEXT again32kzero(SB), $0
1066 /* breathe a sigh of relief - back in 32-bit protected mode */
1068 /* switch to old stack */
1069 PUSHL AX /* match popl below for 8l */
1074 MOVL idtptr(SB),IDTR
1076 CALL restoreregs(SB)
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
1086 TEXT realmodeintr(SB), $0