#include "mem.h"
#include "/sys/src/boot/pc/x16.h"
#undef DELAY
#define PADDR(a) ((a) & ~KZERO)
#define KADDR(a) (KZERO|(a))
/*
* Some machine instructions not handled by 8[al].
*/
#define OP16 BYTE $0x66
#define DELAY BYTE $0xEB; BYTE $0x00 /* JMP .+2 */
#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */
#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) */
#define HLT BYTE $0xF4
#define INVLPG BYTE $0x0F; BYTE $0x01; BYTE $0x39 /* INVLPG (%ecx) */
#define WBINVD BYTE $0x0F; BYTE $0x09
/*
* Macros for calculating offsets within the page directory base
* and page tables. Note that these are assembler-specific hence
* the '<<2'.
*/
#define PDO(a) (((((a))>>22) & 0x03FF)<<2)
#define PTO(a) (((((a))>>12) & 0x03FF)<<2)
TEXT m0rgdtptr(SB), $0
WORD $(NGDT*8-1)
LONG $(CPU0GDT-KZERO)
TEXT m0gdtptr(SB), $0
WORD $(NGDT*8-1)
LONG $CPU0GDT
TEXT m0idtptr(SB), $0
WORD $(256*8-1)
LONG $IDTADDR
/*
* 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
* - l.s real-mode code is in low memory already but
* may need to be copied into the first 64K (if loaded by pbs)
* - can use code segment rmseg in real mode to get at l.s code
* - the above Chinese puzzle pretty much forces RMUADDR to be 0x8000 or 0
* and rmseg to be 0x800 or 0.
*/
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 $RMSTACK, SP
PUSHL AX
/* paranoia: make sure modified INT & JMPFAR instr.s are seen below */
BYTE $0x0f; BYTE $0xae; BYTE $0xf8 /* SFENCE */
BYTE $0x0f; BYTE $0xae; BYTE $0xe8 /* LFENCE */
BYTE $0x0f; BYTE $0xae; BYTE $0xf0 /* MFENCE */
/* change gdt to physical pointer */
MOVL m0rgdtptr-KZERO(SB), GDTR
/* load IDT with real-mode version*/
MOVL realmodeidtptr-KZERO(SB), IDTR
/* disable paging */
MOVL CR0, AX
ANDL $(~PG), AX
MOVL AX, CR0
/* JMP .+2 to clear prefetch queue*/
BYTE $0xEB; BYTE $0x00
/* jump to 16-bit code segment */
/* JMPFAR SELECTOR(KESEG16, SELGDT, 0):$again16bit(SB) /**/
BYTE $0xEA
LONG $again16bit-KZERO(SB)
WORD $SELECTOR(KESEG16, 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 rmseg:now16real */
BYTE $0xEA
WORD $now16real-KZERO(SB)
TEXT rmseg(SB), $0
WORD $0
TEXT now16real(SB), $0
/* copy the registers for the bios call */
CLR(rAX)
MTSR(rAX, rSS) /* 0000 -> rSS */
LWI((RMSTACK-4), rSP) /* preserve AX pushed in physcode */
LWI((RMUADDR-KZERO), 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
/* assume that SP and SS persist across INT */
TEXT realmodeintrinst(SB), $0
INT $0x00
CLI /* who knows what evil the bios got up to */
/* save the registers after the call */
// CLR(rBP)
// MTSR(rBP, rSS) /* 0000 -> rSS */
// LWI((RMSTACK-4), rSP)
OPSIZE; PUSHFL
OPSIZE; PUSHL AX
LWI((RMUADDR-KZERO), 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(KESEG, SELGDT, 0):$again32bit(SB) /**/
OPSIZE
BYTE $0xEA
LONG $again32bit-KZERO(SB)
WORD $SELECTOR(KESEG, SELGDT, 0)
TEXT again32bit(SB), $0
MOVW $SELECTOR(KDSEG, 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 $(PG|0x10000), AX /* PG|WP */
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 $(RMSTACK-4), SP
POPL SP
/* restore idt */
MOVL m0idtptr(SB),IDTR
/* restore gdt */
MOVL m0gdtptr(SB), GDTR
CALL restoreregs(SB)
RET
TEXT realmodeend(SB), $0
/*
* Must be 4-byte aligned & within 8K of the image's start to be seen.
*/
NOP
NOP
NOP
TEXT _multibootheader(SB), $0 /* CHECK alignment (4) */
LONG $0x1BADB002 /* magic */
LONG $0x00010003 /* flags */
LONG $-(0x1BADB002 + 0x00010003) /* checksum */
LONG $_multibootheader-KZERO(SB) /* header_addr */
LONG $_start32p-KZERO(SB) /* load_addr */
LONG $edata-KZERO(SB) /* load_end_addr */
LONG $end-KZERO(SB) /* bss_end_addr */
LONG $_start32p-KZERO(SB) /* entry_addr */
LONG $0 /* mode_type */
LONG $0 /* width */
LONG $0 /* height */
LONG $0 /* depth */
LONG $0 /* +48: saved AX - magic */
LONG $0 /* +52: saved BX - info* */
/*
* There's no way with 8[al] to make this into local data, hence
* the TEXT definitions. Also, it should be in the same segment as
* the LGDT instruction.
* In real mode only 24-bits of the descriptor are loaded so the
* -KZERO is superfluous for the usual mappings.
* The segments are
* NULL
* DATA 32b 4GB PL0
* EXEC 32b 4GB PL0
* EXEC 16b 4GB PL0
*/
TEXT _gdt16r(SB), $0
LONG $0x0000; LONG $0
LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
LONG $0xFFFF; LONG $(SEGG |(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
TEXT _gdtptr16r(SB), $0
WORD $(4*8)
LONG $_gdt16r-KZERO(SB)
|