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