309 lines · 5.6 KB · Assembly
RawDownload
/* * Bootstrap loader decompressor.  Starts at 0x10000 (where pbs puts it) * or 0x7c00 (where pxe puts it) and memmoves kernel (immediately following) * into standard kernel location. */#include "mem.h"#include "/sys/src/boot/pc/x16.h" #undef BIOSCALL		/* we don't know what evil the bios gets up to */#define BIOSCALL(b)	INT $(b); CLI #define CMPL(r0, r1)	BYTE $0x66; CMP(r0, r1)#define LLI(i, rX)	BYTE $0x66;		/* i -> rX */		\			BYTE $(0xB8+rX);				\			LONG $i;#define CPUID		BYTE $0x0F; BYTE $0xA2	/* CPUID, argument in AX */#define WBINVD		BYTE $0x0F; BYTE $0x09 TEXT origin(SB), $0/* *	turn off interrupts */	CLI 	/*	 * 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 	LWI(0, rAX)			/* always put stack in first 64k */	MTSR(rAX, rSS)	LWI(origin(SB), rSP)		/* set the stack pointer */ 	LWI(0x2401, rAX)		/* enable a20 line */	BIOSCALL(0x15) 	XORL	AX, AX	MOVB	$0x03, AL//	LWI(3, rAX)	INT	$0x10			/* set video mode in AL */ /* * Check for CGA mode. */_cgastart:	LWI(0x0F00, rAX)		/* get current video mode in AL */	BIOSCALL(0x10)	ANDI(0x007F, rAX)	SUBI(0x0003, rAX)		/* is it mode 3? */	JEQ	_cgamode3 	LWI(0x0003, rAX)		/* turn on text mode 3 */	BIOSCALL(0x10)_cgamode3:	LWI(_hello(SB), rSI)	CALL	_cgaputs(SB) 	LLI(BIOSTABLES, rAX)	/* tables in low memory, not after end */	OPSIZE; ANDL $~(BY2PG-1), AX	OPSIZE; SHRL $4, AX	SW(rAX, _ES(SB))	CLR(rDI)	SW(rDI, _DI(SB)) 	MTSR(rAX, rES)	/* * Check for APM1.2 BIOS support. */	LWI(0x5304, rAX)		/* disconnect anyone else */	CLR(rBX)	BIOSCALL(0x15)	JCS	_apmfail 	LWI(0x5303, rAX)		/* connect */	CLR(rBX)	CLC	BIOSCALL(0x15)	JCC	_apmpush_apmfail:	LW(_ES(SB), rAX)		/* no support */	MTSR(rAX, rES)	LW(_DI(SB), rDI)	JCS	_apmend _apmpush:	OPSIZE; PUSHR(rSI)		/* save returned APM data on stack */	OPSIZE; PUSHR(rBX)	PUSHR(rDI)	PUSHR(rDX)	PUSHR(rCX)	PUSHR(rAX) 	LW(_ES(SB), rAX)	MTSR(rAX, rES)	LW(_DI(SB), rDI) 	LWI(0x5041, rAX)		/* first 4 bytes are APM\0 */	STOSW	LWI(0x004D, rAX)	STOSW 	LWI(8, rCX)			/* pop the saved APM data */_apmpop:	POPR(rAX)	STOSW	LOOP	_apmpop_apmend: /* * Try to retrieve the 0xE820 memory map. * This is weird because some BIOS do not seem to preserve * ES/DI on failure. Consequently they may not be valid * at _e820end:. */	SW(rDI, _DI(SB))		/* save DI */	CLR(rAX)			/* write terminator */	STOSW	STOSW 	CLR(rBX)	PUSHR(rBX)			/* keep loop count on stack */					/* BX is the continuation value */_e820loop:	POPR(rAX)	INC(rAX)	PUSHR(rAX)			/* doesn't affect FLAGS */	CMPI(32, rAX)			/* mmap[32+1] in C code */	JGT	_e820pop 	LLI(20, rCX)			/* buffer size */	LLI(0x534D4150, rDX)		/* signature - ASCII "SMAP" */	LLI(0x0000E820, rAX)		/* function code */ 	BIOSCALL(0x15)			/* writes 20 bytes at (es,di) */ 	JCS	_e820pop		/* some kind of error */	LLI(0x534D4150, rDX)	CMPL(rDX, rAX)			/* verify correct BIOS version */	JNE	_e820pop	LLI(20, rDX)	CMPL(rDX, rCX)			/* verify correct count */	JNE	_e820pop 	SUBI(4, rDI)			/* overwrite terminator */	LWI(0x414D, rAX)		/* first 4 bytes are "MAP\0" */	STOSW	LWI(0x0050, rAX)	STOSW 	ADDI(20, rDI)			/* bump to next entry */ 	SW(rDI, _DI(SB))		/* save DI */	CLR(rAX)			/* write terminator */	STOSW	STOSW 	OR(rBX, rBX)			/* zero if last entry */	JNE	_e820loop _e820pop:	POPR(rAX)			/* loop count */	LW(_DI(SB), rDI)	CLR(rAX)	MTSR(rAX, rES)_e820end: /* * 	goto protected mode *//*	MOVL	loadgdtptr(SB),GDTR /**/	 BYTE	$0x0f	 BYTE	$0x01	 BYTE	$0x16	 WORD	$loadgdtptr(SB) 	DELAY	LWI(1, rAX)	/* MOV AX,MSW */	BYTE $0x0F; BYTE $0x01; BYTE $0xF0 /* *	clear prefetch queue (weird code to avoid optimizations) */	DELAY /* *	set all segs *//*	MOVW	$KDSEL,AX	/**/	 BYTE	$0xc7	 BYTE	$0xc0	 WORD	$KDSEL	MOVW	AX,DS	MOVW	AX,SS	MOVW	AX,ES	MOVW	AX,FS	MOVW	AX,GS 	MOVW	$(20*1024*1024-4), SP		/* new stack pointer */	DELAY 	/* god only knows what the damned bios has been up to... */	CLI 	/* jump to C (main) *//*	JMPFAR	KESEL:$main(SB) /**/	 BYTE	$0x66	 BYTE	$0xEA	 LONG	$_main(SB)	 WORD	$KESEL /* output a cheery wee message (rSI) */TEXT _cgaputs(SB), $0//_cgaputs:	CLR(rBX)_cgaputsloop:	LODSB	ORB(rAL, rAL)	JEQ	_cgaend 	LBI(0x0E,rAH)	BIOSCALL(0x10)	JMP	_cgaputsloop_cgaend:	RET TEXT _hello(SB), $0	BYTE $'\r'; BYTE $'\n'	BYTE $'9'; BYTE $'b'; BYTE $'o'; BYTE $'o'	BYTE $'t'; BYTE $' '	BYTE $'\z' /* offset into bios tables using segment ES.  stos? use (es,di) */TEXT _DI(SB), $0	LONG	$0 /* segment address of bios tables (BIOSTABLES >> 4) */TEXT _ES(SB), $0	LONG	$0 /* *  pointer to initial gdt */TEXT	loadgdtptr(SB),$0	WORD	$(4*8)	LONG	$loadgdt(SB) /* *  gdt to get us to 32-bit/segmented/unpaged mode */TEXT	loadgdt(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) /* *  output a byte */TEXT	outb(SB),$0	MOVL	p+0(FP),DX	MOVL	b+4(FP),AX	OUTB	RET /* *  input a byte */TEXT	inb(SB),$0	MOVL	p+0(FP),DX	XORL	AX,AX	INB	RET TEXT mb586(SB), $0	XORL	AX, AX	CPUID	RET TEXT wbinvd(SB), $0	WBINVD	RET TEXT	splhi(SB),$0	PUSHFL	POPL	AX	CLI	RET