1#include "mem.h" 2#include "/sys/src/boot/pc/x16.h" 3#undef DELAY 4 5#define PADDR(a) ((a) & ~KZERO) 6#define KADDR(a) (KZERO|(a)) 7 8/* 9 * Some machine instructions not handled by 8[al]. 10 */ 11#define OP16 BYTE $0x66 12#define DELAY BYTE $0xEB; BYTE $0x00 /* JMP .+2 */ 13#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */ 14#define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */ 15#define RDTSC BYTE $0x0F; BYTE $0x31 /* RDTSC, result in AX/DX (lo/hi) */ 16#define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */ 17#define HLT BYTE $0xF4 18#define INVLPG BYTE $0x0F; BYTE $0x01; BYTE $0x39 /* INVLPG (%ecx) */ 19#define WBINVD BYTE $0x0F; BYTE $0x09 20 21/* 22 * Macros for calculating offsets within the page directory base 23 * and page tables. Note that these are assembler-specific hence 24 * the '<<2'. 25 */ 26#define PDO(a) (((((a))>>22) & 0x03FF)<<2) 27#define PTO(a) (((((a))>>12) & 0x03FF)<<2) 28 29TEXT m0rgdtptr(SB), $0 30 WORD $(NGDT*8-1) 31 LONG $(CPU0GDT-KZERO) 32 33TEXT m0gdtptr(SB), $0 34 WORD $(NGDT*8-1) 35 LONG $CPU0GDT 36 37TEXT m0idtptr(SB), $0 38 WORD $(256*8-1) 39 LONG $IDTADDR 40 41/* 42 * Save registers. 43 */ 44TEXT saveregs(SB), $0 45 /* appease 8l */ 46 SUBL $32, SP 47 POPL AX 48 POPL AX 49 POPL AX 50 POPL AX 51 POPL AX 52 POPL AX 53 POPL AX 54 POPL AX 55 56 PUSHL AX 57 PUSHL BX 58 PUSHL CX 59 PUSHL DX 60 PUSHL BP 61 PUSHL DI 62 PUSHL SI 63 PUSHFL 64 65 XCHGL 32(SP), AX /* swap return PC and saved flags */ 66 XCHGL 0(SP), AX 67 XCHGL 32(SP), AX 68 RET 69 70TEXT restoreregs(SB), $0 71 /* appease 8l */ 72 PUSHL AX 73 PUSHL AX 74 PUSHL AX 75 PUSHL AX 76 PUSHL AX 77 PUSHL AX 78 PUSHL AX 79 PUSHL AX 80 ADDL $32, SP 81 82 XCHGL 32(SP), AX /* swap return PC and saved flags */ 83 XCHGL 0(SP), AX 84 XCHGL 32(SP), AX 85 86 POPFL 87 POPL SI 88 POPL DI 89 POPL BP 90 POPL DX 91 POPL CX 92 POPL BX 93 POPL AX 94 RET 95 96/* 97 * Assumed to be in protected mode at time of call. 98 * Switch to real mode, execute an interrupt, and 99 * then switch back to protected mode. 100 * 101 * Assumes: 102 * - no device interrupts are going to come in 103 * - 0-16MB is identity mapped in page tables 104 * - l.s real-mode code is in low memory already but 105 * may need to be copied into the first 64K (if loaded by pbs) 106 * - can use code segment rmseg in real mode to get at l.s code 107 * - the above Chinese puzzle pretty much forces RMUADDR to be 0x8000 or 0 108 * and rmseg to be 0x800 or 0. 109 */ 110 111TEXT realmodeidtptr(SB), $0 112 WORD $(4*256-1) 113 LONG $0 114 115TEXT realmode0(SB), $0 116 CALL saveregs(SB) 117 118 /* switch to low code address */ 119 LEAL physcode-KZERO(SB), AX 120 JMP *AX 121 122TEXT physcode(SB), $0 123 124 /* switch to low stack */ 125 MOVL SP, AX 126 MOVL $RMSTACK, SP 127 PUSHL AX 128 129 /* paranoia: make sure modified INT & JMPFAR instr.s are seen below */ 130 BYTE $0x0f; BYTE $0xae; BYTE $0xf8 /* SFENCE */ 131 BYTE $0x0f; BYTE $0xae; BYTE $0xe8 /* LFENCE */ 132 BYTE $0x0f; BYTE $0xae; BYTE $0xf0 /* MFENCE */ 133 134 /* change gdt to physical pointer */ 135 MOVL m0rgdtptr-KZERO(SB), GDTR 136 137 /* load IDT with real-mode version*/ 138 MOVL realmodeidtptr-KZERO(SB), IDTR 139 140 /* disable paging */ 141 MOVL CR0, AX 142 ANDL $(~PG), AX 143 MOVL AX, CR0 144 /* JMP .+2 to clear prefetch queue*/ 145 BYTE $0xEB; BYTE $0x00 146 147 /* jump to 16-bit code segment */ 148/* JMPFAR SELECTOR(KESEG16, SELGDT, 0):$again16bit(SB) /**/ 149 BYTE $0xEA 150 LONG $again16bit-KZERO(SB) 151 WORD $SELECTOR(KESEG16, SELGDT, 0) 152 153TEXT again16bit(SB), $0 154 /* 155 * Now in 16-bit compatibility mode. 156 * These are 32-bit instructions being interpreted 157 * as 16-bit instructions. I'm being lazy and 158 * not using the macros because I know when 159 * the 16- and 32-bit instructions look the same 160 * or close enough. 161 */ 162 163 /* disable protected mode and jump to real mode cs */ 164 OPSIZE; MOVL CR0, AX 165 OPSIZE; XORL BX, BX 166 OPSIZE; INCL BX 167 OPSIZE; XORL BX, AX 168 OPSIZE; MOVL AX, CR0 169 170 /* JMPFAR rmseg:now16real */ 171 BYTE $0xEA 172 WORD $now16real-KZERO(SB) 173TEXT rmseg(SB), $0 174 WORD $0 175 176TEXT now16real(SB), $0 177 /* copy the registers for the bios call */ 178 CLR(rAX) 179 MTSR(rAX, rSS) /* 0000 -> rSS */ 180 LWI((RMSTACK-4), rSP) /* preserve AX pushed in physcode */ 181 182 LWI((RMUADDR-KZERO), rBP) 183 184 /* offsets are in Ureg */ 185 LXW(44, xBP, rAX) 186 MOVW AX, DS 187 LXW(40, xBP, rAX) 188 MOVW AX, ES 189 190 OPSIZE; LXW(0, xBP, rDI) 191 OPSIZE; LXW(4, xBP, rSI) 192 OPSIZE; LXW(16, xBP, rBX) 193 OPSIZE; LXW(20, xBP, rDX) 194 OPSIZE; LXW(24, xBP, rCX) 195 OPSIZE; LXW(28, xBP, rAX) 196 197 CLC 198 199 /* assume that SP and SS persist across INT */ 200 201TEXT realmodeintrinst(SB), $0 202 INT $0x00 203 CLI /* who knows what evil the bios got up to */ 204 /* save the registers after the call */ 205 206// CLR(rBP) 207// MTSR(rBP, rSS) /* 0000 -> rSS */ 208// LWI((RMSTACK-4), rSP) 209 210 OPSIZE; PUSHFL 211 OPSIZE; PUSHL AX 212 213 LWI((RMUADDR-KZERO), rBP) 214 OPSIZE; SXW(rDI, 0, xBP) 215 OPSIZE; SXW(rSI, 4, xBP) 216 OPSIZE; SXW(rBX, 16, xBP) 217 OPSIZE; SXW(rDX, 20, xBP) 218 OPSIZE; SXW(rCX, 24, xBP) 219 OPSIZE; POPL AX 220 OPSIZE; SXW(rAX, 28, xBP) 221 222 MOVW DS, AX 223 OPSIZE; SXW(rAX, 44, xBP) 224 MOVW ES, AX 225 OPSIZE; SXW(rAX, 40, xBP) 226 227 OPSIZE; POPL AX 228 OPSIZE; SXW(rAX, 64, xBP) /* flags */ 229 230 /* re-enter protected mode and jump to 32-bit code */ 231 OPSIZE; MOVL $1, AX 232 OPSIZE; MOVL AX, CR0 233 234/* JMPFAR SELECTOR(KESEG, SELGDT, 0):$again32bit(SB) /**/ 235 OPSIZE 236 BYTE $0xEA 237 LONG $again32bit-KZERO(SB) 238 WORD $SELECTOR(KESEG, SELGDT, 0) 239 240TEXT again32bit(SB), $0 241 MOVW $SELECTOR(KDSEG, SELGDT, 0),AX 242 MOVW AX,DS 243 MOVW AX,SS 244 MOVW AX,ES 245 MOVW AX,FS 246 MOVW AX,GS 247 248 /* enable paging and jump to kzero-address code */ 249 MOVL CR0, AX 250 ORL $(PG|0x10000), AX /* PG|WP */ 251 MOVL AX, CR0 252 LEAL again32kzero(SB), AX 253 JMP* AX 254 255TEXT again32kzero(SB), $0 256 /* breathe a sigh of relief - back in 32-bit protected mode */ 257 258 /* switch to old stack */ 259 PUSHL AX /* match popl below for 8l */ 260 MOVL $(RMSTACK-4), SP 261 POPL SP 262 263 /* restore idt */ 264 MOVL m0idtptr(SB),IDTR 265 266 /* restore gdt */ 267 MOVL m0gdtptr(SB), GDTR 268 269 CALL restoreregs(SB) 270 RET 271 272TEXT realmodeend(SB), $0 273 274/* 275 * Must be 4-byte aligned & within 8K of the image's start to be seen. 276 */ 277 NOP 278 NOP 279 NOP 280TEXT _multibootheader(SB), $0 /* CHECK alignment (4) */ 281 LONG $0x1BADB002 /* magic */ 282 LONG $0x00010003 /* flags */ 283 LONG $-(0x1BADB002 + 0x00010003) /* checksum */ 284 LONG $_multibootheader-KZERO(SB) /* header_addr */ 285 LONG $_start32p-KZERO(SB) /* load_addr */ 286 LONG $edata-KZERO(SB) /* load_end_addr */ 287 LONG $end-KZERO(SB) /* bss_end_addr */ 288 LONG $_start32p-KZERO(SB) /* entry_addr */ 289 LONG $0 /* mode_type */ 290 LONG $0 /* width */ 291 LONG $0 /* height */ 292 LONG $0 /* depth */ 293 294 LONG $0 /* +48: saved AX - magic */ 295 LONG $0 /* +52: saved BX - info* */ 296 297/* 298 * There's no way with 8[al] to make this into local data, hence 299 * the TEXT definitions. Also, it should be in the same segment as 300 * the LGDT instruction. 301 * In real mode only 24-bits of the descriptor are loaded so the 302 * -KZERO is superfluous for the usual mappings. 303 * The segments are 304 * NULL 305 * DATA 32b 4GB PL0 306 * EXEC 32b 4GB PL0 307 * EXEC 16b 4GB PL0 308 */ 309TEXT _gdt16r(SB), $0 310 LONG $0x0000; LONG $0 311 LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) 312 LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) 313 LONG $0xFFFF; LONG $(SEGG |(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) 314TEXT _gdtptr16r(SB), $0 315 WORD $(4*8) 316 LONG $_gdt16r-KZERO(SB) 317