1/* 2 * Bootstrap loader decompressor. Starts at 0x10000 (where pbs puts it) 3 * or 0x7c00 (where pxe puts it) and memmoves kernel (immediately following) 4 * into standard kernel location. 5 */ 6#include "mem.h" 7#include "/sys/src/boot/pc/x16.h" 8 9#undef BIOSCALL /* we don't know what evil the bios gets up to */ 10#define BIOSCALL(b) INT $(b); CLI 11 12#define CMPL(r0, r1) BYTE $0x66; CMP(r0, r1) 13#define LLI(i, rX) BYTE $0x66; /* i -> rX */ \ 14 BYTE $(0xB8+rX); \ 15 LONG $i; 16#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */ 17#define WBINVD BYTE $0x0F; BYTE $0x09 18 19TEXT origin(SB), $0 20/* 21 * turn off interrupts 22 */ 23 CLI 24 25 /* 26 * This part of l.s is used only in the boot kernel. 27 * It assumes that we are in real address mode, i.e., 28 * that we look like an 8086. 29 * 30 * Make sure the segments are reasonable. 31 * If we were started directly from the BIOS 32 * (i.e. no MS-DOS) then DS may not be 33 * right. 34 */ 35 MOVW CS, AX 36 MOVW AX, DS 37 38 LWI(0, rAX) /* always put stack in first 64k */ 39 MTSR(rAX, rSS) 40 LWI(origin(SB), rSP) /* set the stack pointer */ 41 42 LWI(0x2401, rAX) /* enable a20 line */ 43 BIOSCALL(0x15) 44 45 XORL AX, AX 46 MOVB $0x03, AL 47// LWI(3, rAX) 48 INT $0x10 /* set video mode in AL */ 49 50/* 51 * Check for CGA mode. 52 */ 53_cgastart: 54 LWI(0x0F00, rAX) /* get current video mode in AL */ 55 BIOSCALL(0x10) 56 ANDI(0x007F, rAX) 57 SUBI(0x0003, rAX) /* is it mode 3? */ 58 JEQ _cgamode3 59 60 LWI(0x0003, rAX) /* turn on text mode 3 */ 61 BIOSCALL(0x10) 62_cgamode3: 63 LWI(_hello(SB), rSI) 64 CALL _cgaputs(SB) 65 66 LLI(BIOSTABLES, rAX) /* tables in low memory, not after end */ 67 OPSIZE; ANDL $~(BY2PG-1), AX 68 OPSIZE; SHRL $4, AX 69 SW(rAX, _ES(SB)) 70 CLR(rDI) 71 SW(rDI, _DI(SB)) 72 73 MTSR(rAX, rES) 74 75/* 76 * Check for APM1.2 BIOS support. 77 */ 78 LWI(0x5304, rAX) /* disconnect anyone else */ 79 CLR(rBX) 80 BIOSCALL(0x15) 81 JCS _apmfail 82 83 LWI(0x5303, rAX) /* connect */ 84 CLR(rBX) 85 CLC 86 BIOSCALL(0x15) 87 JCC _apmpush 88_apmfail: 89 LW(_ES(SB), rAX) /* no support */ 90 MTSR(rAX, rES) 91 LW(_DI(SB), rDI) 92 JCS _apmend 93 94_apmpush: 95 OPSIZE; PUSHR(rSI) /* save returned APM data on stack */ 96 OPSIZE; PUSHR(rBX) 97 PUSHR(rDI) 98 PUSHR(rDX) 99 PUSHR(rCX) 100 PUSHR(rAX) 101 102 LW(_ES(SB), rAX) 103 MTSR(rAX, rES) 104 LW(_DI(SB), rDI) 105 106 LWI(0x5041, rAX) /* first 4 bytes are APM\0 */ 107 STOSW 108 LWI(0x004D, rAX) 109 STOSW 110 111 LWI(8, rCX) /* pop the saved APM data */ 112_apmpop: 113 POPR(rAX) 114 STOSW 115 LOOP _apmpop 116_apmend: 117 118/* 119 * Try to retrieve the 0xE820 memory map. 120 * This is weird because some BIOS do not seem to preserve 121 * ES/DI on failure. Consequently they may not be valid 122 * at _e820end:. 123 */ 124 SW(rDI, _DI(SB)) /* save DI */ 125 CLR(rAX) /* write terminator */ 126 STOSW 127 STOSW 128 129 CLR(rBX) 130 PUSHR(rBX) /* keep loop count on stack */ 131 /* BX is the continuation value */ 132_e820loop: 133 POPR(rAX) 134 INC(rAX) 135 PUSHR(rAX) /* doesn't affect FLAGS */ 136 CMPI(32, rAX) /* mmap[32+1] in C code */ 137 JGT _e820pop 138 139 LLI(20, rCX) /* buffer size */ 140 LLI(0x534D4150, rDX) /* signature - ASCII "SMAP" */ 141 LLI(0x0000E820, rAX) /* function code */ 142 143 BIOSCALL(0x15) /* writes 20 bytes at (es,di) */ 144 145 JCS _e820pop /* some kind of error */ 146 LLI(0x534D4150, rDX) 147 CMPL(rDX, rAX) /* verify correct BIOS version */ 148 JNE _e820pop 149 LLI(20, rDX) 150 CMPL(rDX, rCX) /* verify correct count */ 151 JNE _e820pop 152 153 SUBI(4, rDI) /* overwrite terminator */ 154 LWI(0x414D, rAX) /* first 4 bytes are "MAP\0" */ 155 STOSW 156 LWI(0x0050, rAX) 157 STOSW 158 159 ADDI(20, rDI) /* bump to next entry */ 160 161 SW(rDI, _DI(SB)) /* save DI */ 162 CLR(rAX) /* write terminator */ 163 STOSW 164 STOSW 165 166 OR(rBX, rBX) /* zero if last entry */ 167 JNE _e820loop 168 169_e820pop: 170 POPR(rAX) /* loop count */ 171 LW(_DI(SB), rDI) 172 CLR(rAX) 173 MTSR(rAX, rES) 174_e820end: 175 176/* 177 * goto protected mode 178 */ 179/* MOVL loadgdtptr(SB),GDTR /**/ 180 BYTE $0x0f 181 BYTE $0x01 182 BYTE $0x16 183 WORD $loadgdtptr(SB) 184 185 DELAY 186 LWI(1, rAX) 187 /* MOV AX,MSW */ 188 BYTE $0x0F; BYTE $0x01; BYTE $0xF0 189 190/* 191 * clear prefetch queue (weird code to avoid optimizations) 192 */ 193 DELAY 194 195/* 196 * set all segs 197 */ 198/* MOVW $KDSEL,AX /**/ 199 BYTE $0xc7 200 BYTE $0xc0 201 WORD $KDSEL 202 MOVW AX,DS 203 MOVW AX,SS 204 MOVW AX,ES 205 MOVW AX,FS 206 MOVW AX,GS 207 208 MOVW $(20*1024*1024-4), SP /* new stack pointer */ 209 DELAY 210 211 /* god only knows what the damned bios has been up to... */ 212 CLI 213 214 /* jump to C (main) */ 215/* JMPFAR KESEL:$main(SB) /**/ 216 BYTE $0x66 217 BYTE $0xEA 218 LONG $_main(SB) 219 WORD $KESEL 220 221/* output a cheery wee message (rSI) */ 222TEXT _cgaputs(SB), $0 223//_cgaputs: 224 CLR(rBX) 225_cgaputsloop: 226 LODSB 227 ORB(rAL, rAL) 228 JEQ _cgaend 229 230 LBI(0x0E,rAH) 231 BIOSCALL(0x10) 232 JMP _cgaputsloop 233_cgaend: 234 RET 235 236TEXT _hello(SB), $0 237 BYTE $'\r'; BYTE $'\n' 238 BYTE $'9'; BYTE $'b'; BYTE $'o'; BYTE $'o' 239 BYTE $'t'; BYTE $' ' 240 BYTE $'\z' 241 242/* offset into bios tables using segment ES. stos? use (es,di) */ 243TEXT _DI(SB), $0 244 LONG $0 245 246/* segment address of bios tables (BIOSTABLES >> 4) */ 247TEXT _ES(SB), $0 248 LONG $0 249 250/* 251 * pointer to initial gdt 252 */ 253TEXT loadgdtptr(SB),$0 254 WORD $(4*8) 255 LONG $loadgdt(SB) 256 257/* 258 * gdt to get us to 32-bit/segmented/unpaged mode 259 */ 260TEXT loadgdt(SB),$0 261 262 /* null descriptor */ 263 LONG $0 264 LONG $0 265 266 /* data segment descriptor for 4 gigabytes (PL 0) */ 267 LONG $(0xFFFF) 268 LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) 269 270 /* exec segment descriptor for 4 gigabytes (PL 0) */ 271 LONG $(0xFFFF) 272 LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) 273 274 /* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */ 275 LONG $(0xFFFF) 276 LONG $(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) 277 278/* 279 * output a byte 280 */ 281TEXT outb(SB),$0 282 MOVL p+0(FP),DX 283 MOVL b+4(FP),AX 284 OUTB 285 RET 286 287/* 288 * input a byte 289 */ 290TEXT inb(SB),$0 291 MOVL p+0(FP),DX 292 XORL AX,AX 293 INB 294 RET 295 296TEXT mb586(SB), $0 297 XORL AX, AX 298 CPUID 299 RET 300 301TEXT wbinvd(SB), $0 302 WBINVD 303 RET 304 305TEXT splhi(SB),$0 306 PUSHFL 307 POPL AX 308 CLI 309 RET 310