xref: /plan9-contrib/sys/src/9/pcboot/ldecomp.s (revision bca3d52ede6f3fef26e87fbd029fd128e6ad9c87)
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