summaryrefslogtreecommitdiff
path: root/os/boot/pc/l.s
diff options
context:
space:
mode:
Diffstat (limited to 'os/boot/pc/l.s')
-rw-r--r--os/boot/pc/l.s1079
1 files changed, 1079 insertions, 0 deletions
diff --git a/os/boot/pc/l.s b/os/boot/pc/l.s
new file mode 100644
index 00000000..0269e92e
--- /dev/null
+++ b/os/boot/pc/l.s
@@ -0,0 +1,1079 @@
+#include "x16.h"
+#include "mem.h"
+
+#define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */
+#define RDTSC BYTE $0x0F; BYTE $0x31 /* RDTSC, result in AX/DX (lo/hi) */
+#define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */
+
+#ifdef PXE
+#define PDB 0x90000 /* temporary page tables (24KB) */
+#else
+#define PDB 0x08000
+#endif PXE
+
+#define NoScreenBlank 1
+/*#define ResetDiscs 1*/
+
+TEXT origin(SB), $0
+ /*
+ * This part of l.s is used only in the boot kernel.
+ * It assumes that we are in real address mode, i.e.,
+ * that we look like an 8086.
+ *
+ * Make sure the segments are reasonable.
+ * If we were started directly from the BIOS
+ * (i.e. no MS-DOS) then DS may not be
+ * right.
+ */
+ MOVW CS, AX
+ MOVW AX, DS
+
+#ifdef NoScreenBlank
+ /*
+ * Get the current video mode. If it isn't mode 3,
+ * set text mode 3.
+ * Well, no. Windows95 won't co-operate here so we have
+ * to explicitly set mode 3.
+ */
+ XORL AX, AX
+ MOVB $0x0F, AH
+ INT $0x10 /* get current video mode in AL */
+ CMPB AL, $03
+ JEQ sayhello
+#endif /* NoScreenBlank */
+ XORL AX, AX
+ MOVB $0x03, AL
+ INT $0x10 /* set video mode in AL */
+
+sayhello:
+ LWI(hello(SB), rSI)
+ CALL16(biosputs(SB))
+
+#ifdef ResetDiscs
+ XORL AX, AX /* reset disc system */
+ XORL DX, DX
+ MOVB $0x80, DL
+ INT $0x13
+#endif /* ResetDiscs */
+
+#ifdef DOTCOM
+/*
+ * relocate everything to a half meg and jump there
+ * - looks weird because it is being assembled by a 32 bit
+ * assembler for a 16 bit world
+ *
+ * only b.com does this - not 9load
+ */
+ MOVL $0,BX
+ INCL BX
+ SHLL $15,BX
+ MOVL BX,CX
+ MOVW BX,ES
+ MOVL $0,SI
+ MOVL SI,DI
+ CLD
+ REP
+ MOVSL
+
+ /*
+ * Jump to the copied image;
+ * fix up the DS for the new location.
+ */
+ FARJUMP16(0x8000, _start8000(SB))
+
+TEXT _start8000(SB), $0
+ MFSR(rCS, rAX) /* fix up DS, ES (0x8000) */
+ MTSR(rAX, rDS)
+ MTSR(rAX, rES)
+
+ /*
+ * If we are already in protected mode, have to get back
+ * to real mode before trying any privileged operations
+ * (like going into protected mode...).
+ * Try to reset with a restart vector.
+ */
+ MFCR(rCR0, rAX) /* are we in protected mode? */
+ ANDI(0x0001, rAX)
+ JEQ _real
+
+ CLR(rBX)
+ MTSR(rBX, rES)
+
+ LWI(0x0467, rBX) /* reset entry point */
+ LWI(_start8000(SB), rAX) /* offset within segment */
+ BYTE $0x26
+ BYTE $0x89
+ BYTE $0x07 /* MOVW AX, ES:[BX] */
+ LBI(0x69, rBL)
+ MFSR(rCS, rAX) /* segment */
+ BYTE $0x26
+ BYTE $0x89
+ BYTE $0x07 /* MOVW AX, ES:[BX] */
+
+ CLR(rDX)
+ OUTPORTB(0x70, 0x8F)
+ OUTPORTB(0x71, 0x0A)
+
+ FARJUMP16(0xFFFF, 0x0000) /* reset */
+#endif /* DOTCOM */
+
+_real:
+
+/*
+ * do things that need to be done in real mode.
+ * the results get written to CONFADDR (0x1200)
+ * in a series of <4-byte-magic-number><block-of-data>
+ * the data length is dependent on the magic number.
+ *
+ * this gets parsed by conf.c:/^readlsconf
+ *
+ * N.B. CALL16 kills rDI, so we can't call anything.
+ */
+ LWI(0x0000, rAX)
+ MTSR(rAX, rES)
+ LWI(0x1200, rDI)
+
+/*
+ * turn off interrupts
+ */
+ CLI
+
+/*
+ * detect APM1.2 bios support
+ */
+ /* save DI */
+ SW(rDI, rock(SB))
+
+ /* disconnect anyone else */
+ LWI(0x5304, rAX)
+ LWI(0x0000, rBX)
+ INT $0x15
+
+ /* connect */
+ CLC
+ LWI(0x5303, rAX)
+ LWI(0x0000, rBX)
+ INT $0x15
+ CLI /* apm put interrupts back? */
+
+ JC noapm
+
+ OPSIZE; PUSHR(rSI)
+ OPSIZE; PUSHR(rBX)
+ PUSHR(rDI)
+ PUSHR(rDX)
+ PUSHR(rCX)
+ PUSHR(rAX)
+
+ /* put DI, ES back */
+ LW(rock(SB), rDI)
+ LWI(0x0000, rAX)
+ MTSR(rAX, rES)
+
+ /*
+ * write APM data. first four bytes are APM\0.
+ */
+ LWI(0x5041, rAX)
+ STOSW
+
+ LWI(0x004d, rAX)
+ STOSW
+
+ LWI(8, rCX)
+apmmove:
+ POPR(rAX)
+ STOSW
+ LOOP apmmove
+
+noapm:
+
+/*
+ * end of real mode hacks: write terminator, put ES back.
+ */
+ LWI(0x0000, rAX)
+ STOSW
+ STOSW
+
+ MFSR(rCS, rAX) /* fix up ES (0x8000) */
+ MTSR(rAX, rES)
+
+/*
+ * goto protected mode
+ */
+/* MOVL tgdtptr(SB),GDTR /**/
+ BYTE $0x0f
+ BYTE $0x01
+ BYTE $0x16
+ WORD $tgdtptr(SB)
+
+ LWI(1, rAX)
+ /* MOV AX,MSW */
+ BYTE $0x0F; BYTE $0x01; BYTE $0xF0
+
+/*
+ * clear prefetch queue (weird code to avoid optimizations)
+ */
+ /* JMP .+2 */
+ BYTE $0xEB
+ BYTE $0x00
+
+/*
+ * set all segs
+ */
+/* MOVW $SELECTOR(1, SELGDT, 0),AX /**/
+ BYTE $0xc7
+ BYTE $0xc0
+ WORD $SELECTOR(1, SELGDT, 0)
+ MOVW AX,DS
+ MOVW AX,SS
+ MOVW AX,ES
+ MOVW AX,FS
+ MOVW AX,GS
+
+/* JMPFAR SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
+ BYTE $0x66
+ BYTE $0xEA
+ LONG $mode32bit-KZERO(SB)
+ WORD $SELECTOR(2, SELGDT, 0)
+
+TEXT mode32bit(SB),$0
+ /*
+ * make a bottom level page table page that maps the first
+ * 16 meg of physical memory
+ */
+ MOVL $PDB, DI /* clear 6 pages for the tables etc. */
+ XORL AX, AX
+ MOVL $(6*BY2PG), CX
+ SHRL $2, CX
+
+ CLD
+ REP; STOSL
+
+ MOVL $PDB, AX /* phys addr of temporary page table */
+ MOVL $(4*1024),CX /* pte's per page */
+ MOVL $((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX
+setpte:
+ MOVL BX,-4(AX)(CX*4)
+ SUBL $(1<<PGSHIFT),BX
+ LOOP setpte
+
+ /*
+ * make a top level page table page that maps the first
+ * 16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg
+ */
+ MOVL AX,BX
+ ADDL $(4*BY2PG),AX
+ ADDL $(PTEVALID|PTEKERNEL|PTEWRITE),BX
+ MOVL BX,0(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
+ ADDL $BY2PG,BX
+ MOVL BX,4(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
+ ADDL $BY2PG,BX
+ MOVL BX,8(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
+ ADDL $BY2PG,BX
+ MOVL BX,12(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
+
+ /*
+ * point processor to top level page & turn on paging
+ *
+ * this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0"
+ * message in the VMware log.
+ */
+ MOVL AX,CR3
+ MOVL CR0,AX
+ ORL $0X80000000,AX
+ MOVL AX,CR0
+
+ /*
+ * use a jump to an absolute location to get the PC into
+ * KZERO.
+ */
+ LEAL tokzero(SB),AX
+ JMP* AX
+
+/*
+ * When we load 9load from DOS, the bootstrap jumps
+ * to the instruction right after `JUMP', which gets
+ * us into kzero.
+ *
+ * The name prevents it from being optimized away.
+ */
+TEXT jumplabel(SB), $0
+ BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P'
+
+ LEAL tokzero(SB),AX
+ JMP* AX
+
+TEXT tokzero(SB),$0
+ /*
+ * Clear BSS
+ */
+ LEAL edata(SB),SI
+ MOVL SI,DI
+ ADDL $4,DI
+ MOVL $0,AX
+ MOVL AX,(SI)
+ LEAL end(SB),CX
+ SUBL DI,CX
+ SHRL $2,CX
+ CLD
+ REP
+ MOVSL
+
+ /*
+ * stack and mach
+ */
+ MOVL $mach0(SB),SP
+ MOVL SP,m(SB)
+ MOVL $0,0(SP)
+ ADDL $(MACHSIZE-4),SP /* start stack above machine struct */
+
+ CALL main(SB)
+
+loop:
+ JMP loop
+
+GLOBL mach0+0(SB), $MACHSIZE
+GLOBL m(SB), $4
+
+/*
+ * gdt to get us to 32-bit/segmented/unpaged mode
+ */
+TEXT tgdt(SB),$0
+
+ /* null descriptor */
+ LONG $0
+ LONG $0
+
+ /* data segment descriptor for 4 gigabytes (PL 0) */
+ LONG $(0xFFFF)
+ LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
+
+ /* exec segment descriptor for 4 gigabytes (PL 0) */
+ LONG $(0xFFFF)
+ LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+ /* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */
+ LONG $(0xFFFF)
+ LONG $(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+/*
+ * pointer to initial gdt
+ */
+TEXT tgdtptr(SB),$0
+ WORD $(4*8)
+ LONG $tgdt-KZERO(SB)
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT biosputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ BIOSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/*
+ * input a byte
+ */
+TEXT inb(SB),$0
+
+ MOVL p+0(FP),DX
+ XORL AX,AX
+ INB
+ RET
+
+/*
+ * input a short from a port
+ */
+TEXT ins(SB), $0
+
+ MOVL p+0(FP), DX
+ XORL AX, AX
+ OPSIZE; INL
+ RET
+
+/*
+ * input a long from a port
+ */
+TEXT inl(SB), $0
+
+ MOVL p+0(FP), DX
+ XORL AX, AX
+ INL
+ RET
+
+/*
+ * output a byte
+ */
+TEXT outb(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL b+4(FP),AX
+ OUTB
+ RET
+
+/*
+ * output a short to a port
+ */
+TEXT outs(SB), $0
+ MOVL p+0(FP), DX
+ MOVL s+4(FP), AX
+ OPSIZE; OUTL
+ RET
+
+/*
+ * output a long to a port
+ */
+TEXT outl(SB), $0
+ MOVL p+0(FP), DX
+ MOVL s+4(FP), AX
+ OUTL
+ RET
+
+/*
+ * input a string of bytes from a port
+ */
+TEXT insb(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),DI
+ MOVL c+8(FP),CX
+ CLD; REP; INSB
+ RET
+
+/*
+ * input a string of shorts from a port
+ */
+TEXT inss(SB),$0
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),DI
+ MOVL c+8(FP),CX
+ CLD
+ REP; OPSIZE; INSL
+ RET
+
+/*
+ * output a string of bytes to a port
+ */
+TEXT outsb(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),SI
+ MOVL c+8(FP),CX
+ CLD; REP; OUTSB
+ RET
+
+/*
+ * output a string of shorts to a port
+ */
+TEXT outss(SB),$0
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),SI
+ MOVL c+8(FP),CX
+ CLD
+ REP; OPSIZE; OUTSL
+ RET
+
+/*
+ * input a string of longs from a port
+ */
+TEXT insl(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),DI
+ MOVL c+8(FP),CX
+ CLD; REP; INSL
+ RET
+
+/*
+ * output a string of longs to a port
+ */
+TEXT outsl(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),SI
+ MOVL c+8(FP),CX
+ CLD; REP; OUTSL
+ RET
+
+/*
+ * routines to load/read various system registers
+ */
+GLOBL idtptr(SB),$6
+TEXT putidt(SB),$0 /* interrupt descriptor table */
+ MOVL t+0(FP),AX
+ MOVL AX,idtptr+2(SB)
+ MOVL l+4(FP),AX
+ MOVW AX,idtptr(SB)
+ MOVL idtptr(SB),IDTR
+ RET
+
+TEXT putcr3(SB),$0 /* top level page table pointer */
+ MOVL t+0(FP),AX
+ MOVL AX,CR3
+ RET
+
+TEXT getcr0(SB),$0 /* coprocessor bits */
+ MOVL CR0,AX
+ RET
+
+TEXT getcr2(SB),$0 /* fault address */
+ MOVL CR2,AX
+ RET
+
+TEXT getcr3(SB),$0 /* page directory base */
+ MOVL CR3,AX
+ RET
+
+TEXT getcr4(SB), $0 /* CR4 - extensions */
+ MOVL CR4, AX
+ RET
+
+TEXT _cycles(SB), $0 /* time stamp counter */
+ RDTSC
+ MOVL vlong+0(FP), CX /* &vlong */
+ MOVL AX, 0(CX) /* lo */
+ MOVL DX, 4(CX) /* hi */
+ RET
+
+TEXT rdmsr(SB), $0 /* model-specific register */
+ MOVL index+0(FP), CX
+ RDMSR
+ MOVL vlong+4(FP), CX /* &vlong */
+ MOVL AX, 0(CX) /* lo */
+ MOVL DX, 4(CX) /* hi */
+ RET
+
+TEXT wrmsr(SB), $0
+ MOVL index+0(FP), CX
+ MOVL lo+4(FP), AX
+ MOVL hi+8(FP), DX
+ WRMSR
+ RET
+
+TEXT mb386(SB), $0
+ POPL AX /* return PC */
+ PUSHFL
+ PUSHL CS
+ PUSHL AX
+ IRETL
+
+/*
+ * special traps
+ */
+TEXT intr0(SB),$0
+ PUSHL $0
+ PUSHL $0
+ JMP intrcommon
+TEXT intr1(SB),$0
+ PUSHL $0
+ PUSHL $1
+ JMP intrcommon
+TEXT intr2(SB),$0
+ PUSHL $0
+ PUSHL $2
+ JMP intrcommon
+TEXT intr3(SB),$0
+ PUSHL $0
+ PUSHL $3
+ JMP intrcommon
+TEXT intr4(SB),$0
+ PUSHL $0
+ PUSHL $4
+ JMP intrcommon
+TEXT intr5(SB),$0
+ PUSHL $0
+ PUSHL $5
+ JMP intrcommon
+TEXT intr6(SB),$0
+ PUSHL $0
+ PUSHL $6
+ JMP intrcommon
+TEXT intr7(SB),$0
+ PUSHL $0
+ PUSHL $7
+ JMP intrcommon
+TEXT intr8(SB),$0
+ PUSHL $8
+ JMP intrcommon
+TEXT intr9(SB),$0
+ PUSHL $0
+ PUSHL $9
+ JMP intrcommon
+TEXT intr10(SB),$0
+ PUSHL $10
+ JMP intrcommon
+TEXT intr11(SB),$0
+ PUSHL $11
+ JMP intrcommon
+TEXT intr12(SB),$0
+ PUSHL $12
+ JMP intrcommon
+TEXT intr13(SB),$0
+ PUSHL $13
+ JMP intrcommon
+TEXT intr14(SB),$0
+ PUSHL $14
+ JMP intrcommon
+TEXT intr15(SB),$0
+ PUSHL $0
+ PUSHL $15
+ JMP intrcommon
+TEXT intr16(SB),$0
+ PUSHL $0
+ PUSHL $16
+ JMP intrcommon
+TEXT intr24(SB),$0
+ PUSHL $0
+ PUSHL $24
+ JMP intrcommon
+TEXT intr25(SB),$0
+ PUSHL $0
+ PUSHL $25
+ JMP intrcommon
+TEXT intr26(SB),$0
+ PUSHL $0
+ PUSHL $26
+ JMP intrcommon
+TEXT intr27(SB),$0
+ PUSHL $0
+ PUSHL $27
+ JMP intrcommon
+TEXT intr28(SB),$0
+ PUSHL $0
+ PUSHL $28
+ JMP intrcommon
+TEXT intr29(SB),$0
+ PUSHL $0
+ PUSHL $29
+ JMP intrcommon
+TEXT intr30(SB),$0
+ PUSHL $0
+ PUSHL $30
+ JMP intrcommon
+TEXT intr31(SB),$0
+ PUSHL $0
+ PUSHL $31
+ JMP intrcommon
+TEXT intr32(SB),$0
+ PUSHL $0
+ PUSHL $32
+ JMP intrcommon
+TEXT intr33(SB),$0
+ PUSHL $0
+ PUSHL $33
+ JMP intrcommon
+TEXT intr34(SB),$0
+ PUSHL $0
+ PUSHL $34
+ JMP intrcommon
+TEXT intr35(SB),$0
+ PUSHL $0
+ PUSHL $35
+ JMP intrcommon
+TEXT intr36(SB),$0
+ PUSHL $0
+ PUSHL $36
+ JMP intrcommon
+TEXT intr37(SB),$0
+ PUSHL $0
+ PUSHL $37
+ JMP intrcommon
+TEXT intr38(SB),$0
+ PUSHL $0
+ PUSHL $38
+ JMP intrcommon
+TEXT intr39(SB),$0
+ PUSHL $0
+ PUSHL $39
+ JMP intrcommon
+TEXT intr64(SB),$0
+ PUSHL $0
+ PUSHL $64
+ JMP intrcommon
+TEXT intrbad(SB),$0
+ PUSHL $0
+ PUSHL $0x1ff
+ JMP intrcommon
+
+intrcommon:
+ PUSHL DS
+ PUSHL ES
+ PUSHL FS
+ PUSHL GS
+ PUSHAL
+ MOVL $(KDSEL),AX
+ MOVW AX,DS
+ MOVW AX,ES
+ LEAL 0(SP),AX
+ PUSHL AX
+ CALL trap(SB)
+ POPL AX
+ POPAL
+ POPL GS
+ POPL FS
+ POPL ES
+ POPL DS
+ ADDL $8,SP /* error code and trap type */
+ IRETL
+
+
+/*
+ * interrupt level is interrupts on or off
+ */
+TEXT spllo(SB),$0
+ PUSHFL
+ POPL AX
+ STI
+ RET
+
+TEXT splhi(SB),$0
+ PUSHFL
+ POPL AX
+ CLI
+ RET
+
+TEXT splx(SB),$0
+ MOVL s+0(FP),AX
+ PUSHL AX
+ POPFL
+ RET
+
+/*
+ * do nothing whatsoever till interrupt happens
+ */
+TEXT idle(SB),$0
+ HLT
+ RET
+
+/*
+ * Try to determine the CPU type which requires fiddling with EFLAGS.
+ * If the Id bit can be toggled then the CPUID instruciton can be used
+ * to determine CPU identity and features. First have to check if it's
+ * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
+ * toggled then it's an older 486 of some kind.
+ *
+ * cpuid(id[], &ax, &dx);
+ */
+#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */
+TEXT cpuid(SB), $0
+ MOVL $0x240000, AX
+ PUSHL AX
+ POPFL /* set Id|Ac */
+
+ PUSHFL
+ POPL BX /* retrieve value */
+
+ MOVL $0, AX
+ PUSHL AX
+ POPFL /* clear Id|Ac, EFLAGS initialised */
+
+ PUSHFL
+ POPL AX /* retrieve value */
+ XORL BX, AX
+ TESTL $0x040000, AX /* Ac */
+ JZ _cpu386 /* can't set this bit on 386 */
+ TESTL $0x200000, AX /* Id */
+ JZ _cpu486 /* can't toggle this bit on some 486 */
+
+ MOVL $0, AX
+ CPUID
+ MOVL id+0(FP), BP
+ MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */
+ MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */
+ MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */
+
+ MOVL $1, AX
+ CPUID
+ JMP _cpuid
+
+_cpu486:
+ MOVL $0x400, AX
+ MOVL $0, DX
+ JMP _cpuid
+
+_cpu386:
+ MOVL $0x300, AX
+ MOVL $0, DX
+
+_cpuid:
+ MOVL ax+4(FP), BP
+ MOVL AX, 0(BP)
+ MOVL dx+8(FP), BP
+ MOVL DX, 0(BP)
+ RET
+
+
+/*
+ * basic timing loop to determine CPU frequency
+ */
+TEXT aamloop(SB),$0
+
+ MOVL c+0(FP),CX
+aaml1:
+ AAM
+ LOOP aaml1
+ RET
+
+TEXT hello(SB), $0
+ BYTE $'P'; BYTE $'l'; BYTE $'a'; BYTE $'n';
+ BYTE $' '; BYTE $'9'; BYTE $' '; BYTE $'f';
+ BYTE $'r'; BYTE $'o'; BYTE $'m'; BYTE $' ';
+ BYTE $'B'; BYTE $'e'; BYTE $'l'; BYTE $'l';
+ BYTE $' '; BYTE $'L'; BYTE $'a'; BYTE $'b';
+ BYTE $'s';
+ BYTE $'\r';
+ BYTE $'\n';
+ BYTE $'\z';
+
+TEXT rock(SB), $0
+ BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+
+GLOBL pxe(SB), $4
+#ifdef PXE
+DATA pxe+0(SB)/4, $1
+#else
+DATA pxe+0(SB)/4, $0
+#endif /* PXE */
+
+/*
+ * Save registers.
+ */
+TEXT saveregs(SB), $0
+ /* appease 8l */
+ SUBL $32, SP
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+
+ PUSHL AX
+ PUSHL BX
+ PUSHL CX
+ PUSHL DX
+ PUSHL BP
+ PUSHL DI
+ PUSHL SI
+ PUSHFL
+
+ XCHGL 32(SP), AX /* swap return PC and saved flags */
+ XCHGL 0(SP), AX
+ XCHGL 32(SP), AX
+ RET
+
+TEXT restoreregs(SB), $0
+ /* appease 8l */
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ ADDL $32, SP
+
+ XCHGL 32(SP), AX /* swap return PC and saved flags */
+ XCHGL 0(SP), AX
+ XCHGL 32(SP), AX
+
+ POPFL
+ POPL SI
+ POPL DI
+ POPL BP
+ POPL DX
+ POPL CX
+ POPL BX
+ POPL AX
+ RET
+
+/*
+ * Assumed to be in protected mode at time of call.
+ * Switch to real mode, execute an interrupt, and
+ * then switch back to protected mode.
+ *
+ * Assumes:
+ *
+ * - no device interrupts are going to come in
+ * - 0-16MB is identity mapped in page tables
+ * - can use code segment 0x1000 in real mode
+ * to get at l.s code
+ */
+TEXT realmodeidtptr(SB), $0
+ WORD $(4*256-1)
+ LONG $0
+
+TEXT realmode0(SB), $0
+ CALL saveregs(SB)
+
+ /* switch to low code address */
+ LEAL physcode-KZERO(SB), AX
+ JMP *AX
+
+TEXT physcode(SB), $0
+
+ /* switch to low stack */
+ MOVL SP, AX
+ MOVL $0x7C00, SP
+ PUSHL AX
+
+ /* load IDT with real-mode version; GDT already fine */
+ MOVL realmodeidtptr(SB), IDTR
+
+ /* edit INT $0x00 instruction below */
+ MOVL realmodeintr(SB), AX
+ MOVB AX, realmodeintrinst+1(SB)
+
+ /* disable paging */
+ MOVL CR0, AX
+ ANDL $0x7FFFFFFF, AX
+ MOVL AX, CR0
+ /* JMP .+2 to clear prefetch queue*/
+ BYTE $0xEB; BYTE $0x00
+
+ /* jump to 16-bit code segment */
+/* JMPFAR SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/
+ BYTE $0xEA
+ LONG $again16bit-KZERO(SB)
+ WORD $SELECTOR(3, SELGDT, 0)
+
+TEXT again16bit(SB), $0
+ /*
+ * Now in 16-bit compatibility mode.
+ * These are 32-bit instructions being interpreted
+ * as 16-bit instructions. I'm being lazy and
+ * not using the macros because I know when
+ * the 16- and 32-bit instructions look the same
+ * or close enough.
+ */
+
+ /* disable protected mode and jump to real mode cs */
+ OPSIZE; MOVL CR0, AX
+ OPSIZE; XORL BX, BX
+ OPSIZE; INCL BX
+ OPSIZE; XORL BX, AX
+ OPSIZE; MOVL AX, CR0
+
+ /* JMPFAR 0x1000:now16real */
+ BYTE $0xEA
+ WORD $now16real-KZERO(SB)
+ WORD $0x1000
+
+TEXT now16real(SB), $0
+ /* copy the registers for the bios call */
+ LWI(0x1000, rAX)
+ MOVW AX,SS
+ LWI(realmoderegs(SB), rBP)
+
+ /* offsets are in Ureg */
+ LXW(44, xBP, rAX)
+ MOVW AX, DS
+ LXW(40, xBP, rAX)
+ MOVW AX, ES
+
+ OPSIZE; LXW(0, xBP, rDI)
+ OPSIZE; LXW(4, xBP, rSI)
+ OPSIZE; LXW(16, xBP, rBX)
+ OPSIZE; LXW(20, xBP, rDX)
+ OPSIZE; LXW(24, xBP, rCX)
+ OPSIZE; LXW(28, xBP, rAX)
+
+ CLC
+
+TEXT realmodeintrinst(SB), $0
+ INT $0x00
+
+ /* save the registers after the call */
+
+ LWI(0x7bfc, rSP)
+ OPSIZE; PUSHFL
+ OPSIZE; PUSHL AX
+
+ LWI(0x1000, rAX)
+ MOVW AX,SS
+ LWI(realmoderegs(SB), rBP)
+
+ OPSIZE; SXW(rDI, 0, xBP)
+ OPSIZE; SXW(rSI, 4, xBP)
+ OPSIZE; SXW(rBX, 16, xBP)
+ OPSIZE; SXW(rDX, 20, xBP)
+ OPSIZE; SXW(rCX, 24, xBP)
+ OPSIZE; POPL AX
+ OPSIZE; SXW(rAX, 28, xBP)
+
+ MOVW DS, AX
+ OPSIZE; SXW(rAX, 44, xBP)
+ MOVW ES, AX
+ OPSIZE; SXW(rAX, 40, xBP)
+
+ OPSIZE; POPL AX
+ OPSIZE; SXW(rAX, 64, xBP) /* flags */
+
+ /* re-enter protected mode and jump to 32-bit code */
+ OPSIZE; MOVL $1, AX
+ OPSIZE; MOVL AX, CR0
+
+/* JMPFAR SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/
+ OPSIZE
+ BYTE $0xEA
+ LONG $again32bit-KZERO(SB)
+ WORD $SELECTOR(2, SELGDT, 0)
+
+TEXT again32bit(SB), $0
+ MOVW $SELECTOR(1, SELGDT, 0),AX
+ MOVW AX,DS
+ MOVW AX,SS
+ MOVW AX,ES
+ MOVW AX,FS
+ MOVW AX,GS
+
+ /* enable paging and jump to kzero-address code */
+ MOVL CR0, AX
+ ORL $0x80000000, AX
+ MOVL AX, CR0
+ LEAL again32kzero(SB), AX
+ JMP* AX
+
+TEXT again32kzero(SB), $0
+ /* breathe a sigh of relief - back in 32-bit protected mode */
+
+ /* switch to old stack */
+ PUSHL AX /* match popl below for 8l */
+ MOVL $0x7BFC, SP
+ POPL SP
+
+ /* restore idt */
+ MOVL idtptr(SB),IDTR
+
+ CALL restoreregs(SB)
+ RET
+
+TEXT realmoderegs(SB), $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+
+TEXT realmodeintr(SB), $0
+ LONG $0
+