xref: /inferno-os/os/boot/pc/l.s (revision 8a8c2d742b51525f66c2210e3c8a251de10022ff)
1#include "x16.h"
2#include "mem.h"
3
4#define WRMSR		BYTE $0x0F; BYTE $0x30	/* WRMSR, argument in AX/DX (lo/hi) */
5#define RDTSC 		BYTE $0x0F; BYTE $0x31	/* RDTSC, result in AX/DX (lo/hi) */
6#define RDMSR		BYTE $0x0F; BYTE $0x32	/* RDMSR, result in AX/DX (lo/hi) */
7
8#ifdef PXE
9#define PDB		0x90000		/* temporary page tables (24KB) */
10#else
11#define PDB		0x08000
12#endif PXE
13
14#define NoScreenBlank	1
15/*#define ResetDiscs	1*/
16
17TEXT origin(SB), $0
18	/*
19	 * This part of l.s is used only in the boot kernel.
20	 * It assumes that we are in real address mode, i.e.,
21	 * that we look like an 8086.
22	 *
23	 * Make sure the segments are reasonable.
24	 * If we were started directly from the BIOS
25	 * (i.e. no MS-DOS) then DS may not be
26	 * right.
27	 */
28	MOVW	CS, AX
29	MOVW	AX, DS
30
31#ifdef NoScreenBlank
32	/*
33	 * Get the current video mode. If it isn't mode 3,
34	 * set text mode 3.
35	 * Well, no. Windows95 won't co-operate here so we have
36	 * to explicitly set mode 3.
37	 */
38	XORL	AX, AX
39	MOVB	$0x0F, AH
40	INT	$0x10			/* get current video mode in AL */
41	CMPB	AL, $03
42	JEQ	sayhello
43#endif /* NoScreenBlank */
44	XORL	AX, AX
45	MOVB	$0x03, AL
46	INT	$0x10			/* set video mode in AL */
47
48sayhello:
49	LWI(hello(SB), rSI)
50	CALL16(biosputs(SB))
51
52#ifdef ResetDiscs
53	XORL	AX, AX			/* reset disc system */
54	XORL	DX, DX
55	MOVB	$0x80, DL
56	INT	$0x13
57#endif /* ResetDiscs */
58
59#ifdef DOTCOM
60/*
61 *	relocate everything to a half meg and jump there
62 *	- looks weird because it is being assembled by a 32 bit
63 *	  assembler for a 16 bit world
64 *
65 *	only b.com does this - not 9load
66 */
67	MOVL	$0,BX
68	INCL	BX
69	SHLL	$15,BX
70	MOVL	BX,CX
71	MOVW	BX,ES
72	MOVL	$0,SI
73	MOVL	SI,DI
74	CLD
75	REP
76	MOVSL
77
78	/*
79	 * Jump to the copied image;
80	 * fix up the DS for the new location.
81	 */
82	FARJUMP16(0x8000, _start8000(SB))
83
84TEXT _start8000(SB), $0
85	MFSR(rCS, rAX)			/* fix up DS, ES (0x8000) */
86	MTSR(rAX, rDS)
87	MTSR(rAX, rES)
88
89	/*
90	 * If we are already in protected mode, have to get back
91	 * to real mode before trying any privileged operations
92	 * (like going into protected mode...).
93	 * Try to reset with a restart vector.
94	 */
95	MFCR(rCR0, rAX)			/* are we in protected mode? */
96	ANDI(0x0001, rAX)
97	JEQ	_real
98
99	CLR(rBX)
100	MTSR(rBX, rES)
101
102	LWI(0x0467, rBX)		/* reset entry point */
103	LWI(_start8000(SB), rAX)	/* offset within segment */
104	BYTE	$0x26
105	BYTE	$0x89
106	BYTE	$0x07			/* MOVW	AX, ES:[BX] */
107	LBI(0x69, rBL)
108	MFSR(rCS, rAX)			/* segment */
109	BYTE	$0x26
110	BYTE	$0x89
111	BYTE	$0x07			/* MOVW	AX, ES:[BX] */
112
113	CLR(rDX)
114	OUTPORTB(0x70, 0x8F)
115	OUTPORTB(0x71, 0x0A)
116
117	FARJUMP16(0xFFFF, 0x0000)		/* reset */
118#endif /* DOTCOM */
119
120_real:
121
122/*
123 *	do things that need to be done in real mode.
124 *	the results get written to CONFADDR (0x1200)
125 *	in a series of <4-byte-magic-number><block-of-data>
126 *	the data length is dependent on the magic number.
127 *
128 *	this gets parsed by conf.c:/^readlsconf
129 *
130 *	N.B. CALL16 kills rDI, so we can't call anything.
131 */
132	LWI(0x0000, rAX)
133	MTSR(rAX, rES)
134	LWI(0x1200, rDI)
135
136/*
137 *	turn off interrupts
138 */
139	CLI
140
141/*
142 *	detect APM1.2 bios support
143 */
144	/* save DI */
145	SW(rDI, rock(SB))
146
147	/* disconnect anyone else */
148	LWI(0x5304, rAX)
149	LWI(0x0000, rBX)
150	INT $0x15
151
152	/* connect */
153	CLC
154	LWI(0x5303, rAX)
155	LWI(0x0000, rBX)
156	INT $0x15
157	CLI	/* apm put interrupts back? */
158
159	JC noapm
160
161	OPSIZE; PUSHR(rSI)
162	OPSIZE; PUSHR(rBX)
163	PUSHR(rDI)
164	PUSHR(rDX)
165	PUSHR(rCX)
166	PUSHR(rAX)
167
168	/* put DI, ES back */
169	LW(rock(SB), rDI)
170	LWI(0x0000, rAX)
171	MTSR(rAX, rES)
172
173	/*
174	 * write APM data.  first four bytes are APM\0.
175	 */
176	LWI(0x5041, rAX)
177	STOSW
178
179	LWI(0x004d, rAX)
180	STOSW
181
182	LWI(8, rCX)
183apmmove:
184	POPR(rAX)
185	STOSW
186	LOOP apmmove
187
188noapm:
189
190/*
191 *	end of real mode hacks: write terminator, put ES back.
192 */
193	LWI(0x0000, rAX)
194	STOSW
195	STOSW
196
197	MFSR(rCS, rAX)			/* fix up ES (0x8000) */
198	MTSR(rAX, rES)
199
200/*
201 * 	goto protected mode
202 */
203/*	MOVL	tgdtptr(SB),GDTR /**/
204	 BYTE	$0x0f
205	 BYTE	$0x01
206	 BYTE	$0x16
207	 WORD	$tgdtptr(SB)
208
209	LWI(1, rAX)
210	/* MOV AX,MSW */
211	BYTE $0x0F; BYTE $0x01; BYTE $0xF0
212
213/*
214 *	clear prefetch queue (weird code to avoid optimizations)
215 */
216	/* JMP .+2 */
217	BYTE $0xEB
218	BYTE $0x00
219
220/*
221 *	set all segs
222 */
223/*	MOVW	$SELECTOR(1, SELGDT, 0),AX	/**/
224	 BYTE	$0xc7
225	 BYTE	$0xc0
226	 WORD	$SELECTOR(1, SELGDT, 0)
227	MOVW	AX,DS
228	MOVW	AX,SS
229	MOVW	AX,ES
230	MOVW	AX,FS
231	MOVW	AX,GS
232
233/*	JMPFAR	SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
234	 BYTE	$0x66
235	 BYTE	$0xEA
236	 LONG	$mode32bit-KZERO(SB)
237	 WORD	$SELECTOR(2, SELGDT, 0)
238
239TEXT	mode32bit(SB),$0
240	/*
241	 *  make a bottom level page table page that maps the first
242	 *  16 meg of physical memory
243	 */
244	MOVL	$PDB, DI			/* clear 6 pages for the tables etc. */
245	XORL	AX, AX
246	MOVL	$(6*BY2PG), CX
247	SHRL	$2, CX
248
249	CLD
250	REP;	STOSL
251
252	MOVL	$PDB, AX		/* phys addr of temporary page table */
253	MOVL	$(4*1024),CX		/* pte's per page */
254	MOVL	$((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX
255setpte:
256	MOVL	BX,-4(AX)(CX*4)
257	SUBL	$(1<<PGSHIFT),BX
258	LOOP	setpte
259
260	/*
261	 *  make a top level page table page that maps the first
262	 *  16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg
263	 */
264	MOVL	AX,BX
265	ADDL	$(4*BY2PG),AX
266	ADDL	$(PTEVALID|PTEKERNEL|PTEWRITE),BX
267	MOVL	BX,0(AX)
268	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
269	ADDL	$BY2PG,BX
270	MOVL	BX,4(AX)
271	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
272	ADDL	$BY2PG,BX
273	MOVL	BX,8(AX)
274	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
275	ADDL	$BY2PG,BX
276	MOVL	BX,12(AX)
277	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
278
279	/*
280	 *  point processor to top level page & turn on paging
281	 *
282	 *  this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0"
283	 *  message in the VMware log.
284	 */
285	MOVL	AX,CR3
286	MOVL	CR0,AX
287	ORL	$0X80000000,AX
288	MOVL	AX,CR0
289
290	/*
291	 *  use a jump to an absolute location to get the PC into
292	 *  KZERO.
293	 */
294	LEAL	tokzero(SB),AX
295	JMP*	AX
296
297/*
298 * When we load 9load from DOS, the bootstrap jumps
299 * to the instruction right after `JUMP', which gets
300 * us into kzero.
301 *
302 * The name prevents it from being optimized away.
303 */
304TEXT jumplabel(SB), $0
305	BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P'
306
307	LEAL	tokzero(SB),AX
308	JMP*	AX
309
310TEXT	tokzero(SB),$0
311	/*
312	 * Clear BSS
313	 */
314	LEAL	edata(SB),SI
315	MOVL	SI,DI
316	ADDL	$4,DI
317	MOVL	$0,AX
318	MOVL	AX,(SI)
319	LEAL	end(SB),CX
320	SUBL	DI,CX
321	SHRL	$2,CX
322	CLD
323	REP
324	MOVSL
325
326	/*
327	 *  stack and mach
328	 */
329	MOVL	$mach0(SB),SP
330	MOVL	SP,m(SB)
331	MOVL	$0,0(SP)
332	ADDL	$(MACHSIZE-4),SP	/* start stack above machine struct */
333
334	CALL	main(SB)
335
336loop:
337	JMP	loop
338
339GLOBL	mach0+0(SB), $MACHSIZE
340GLOBL	m(SB), $4
341
342/*
343 *  gdt to get us to 32-bit/segmented/unpaged mode
344 */
345TEXT	tgdt(SB),$0
346
347	/* null descriptor */
348	LONG	$0
349	LONG	$0
350
351	/* data segment descriptor for 4 gigabytes (PL 0) */
352	LONG	$(0xFFFF)
353	LONG	$(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
354
355	/* exec segment descriptor for 4 gigabytes (PL 0) */
356	LONG	$(0xFFFF)
357	LONG	$(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
358
359	/* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */
360	LONG	$(0xFFFF)
361	LONG	$(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
362
363/*
364 *  pointer to initial gdt
365 */
366TEXT	tgdtptr(SB),$0
367	WORD	$(4*8)
368	LONG	$tgdt-KZERO(SB)
369
370/*
371 * Output a string to the display.
372 * String argument is in rSI.
373 */
374TEXT biosputs(SB), $0
375	PUSHA
376	CLR(rBX)
377_BIOSputs:
378	LODSB
379	ORB(rAL, rAL)
380	JEQ _BIOSputsret
381
382	LBI(0x0E, rAH)
383	BIOSCALL(0x10)
384	JMP _BIOSputs
385
386_BIOSputsret:
387	POPA
388	RET
389
390/*
391 *  input a byte
392 */
393TEXT	inb(SB),$0
394
395	MOVL	p+0(FP),DX
396	XORL	AX,AX
397	INB
398	RET
399
400/*
401 * input a short from a port
402 */
403TEXT	ins(SB), $0
404
405	MOVL	p+0(FP), DX
406	XORL	AX, AX
407	OPSIZE; INL
408	RET
409
410/*
411 * input a long from a port
412 */
413TEXT	inl(SB), $0
414
415	MOVL	p+0(FP), DX
416	XORL	AX, AX
417	INL
418	RET
419
420/*
421 *  output a byte
422 */
423TEXT	outb(SB),$0
424
425	MOVL	p+0(FP),DX
426	MOVL	b+4(FP),AX
427	OUTB
428	RET
429
430/*
431 * output a short to a port
432 */
433TEXT	outs(SB), $0
434	MOVL	p+0(FP), DX
435	MOVL	s+4(FP), AX
436	OPSIZE; OUTL
437	RET
438
439/*
440 * output a long to a port
441 */
442TEXT	outl(SB), $0
443	MOVL	p+0(FP), DX
444	MOVL	s+4(FP), AX
445	OUTL
446	RET
447
448/*
449 *  input a string of bytes from a port
450 */
451TEXT	insb(SB),$0
452
453	MOVL	p+0(FP),DX
454	MOVL	a+4(FP),DI
455	MOVL	c+8(FP),CX
456	CLD; REP; INSB
457	RET
458
459/*
460 *  input a string of shorts from a port
461 */
462TEXT	inss(SB),$0
463	MOVL	p+0(FP),DX
464	MOVL	a+4(FP),DI
465	MOVL	c+8(FP),CX
466	CLD
467	REP; OPSIZE; INSL
468	RET
469
470/*
471 *  output a string of bytes to a port
472 */
473TEXT	outsb(SB),$0
474
475	MOVL	p+0(FP),DX
476	MOVL	a+4(FP),SI
477	MOVL	c+8(FP),CX
478	CLD; REP; OUTSB
479	RET
480
481/*
482 *  output a string of shorts to a port
483 */
484TEXT	outss(SB),$0
485	MOVL	p+0(FP),DX
486	MOVL	a+4(FP),SI
487	MOVL	c+8(FP),CX
488	CLD
489	REP; OPSIZE; OUTSL
490	RET
491
492/*
493 *  input a string of longs from a port
494 */
495TEXT	insl(SB),$0
496
497	MOVL	p+0(FP),DX
498	MOVL	a+4(FP),DI
499	MOVL	c+8(FP),CX
500	CLD; REP; INSL
501	RET
502
503/*
504 *  output a string of longs to a port
505 */
506TEXT	outsl(SB),$0
507
508	MOVL	p+0(FP),DX
509	MOVL	a+4(FP),SI
510	MOVL	c+8(FP),CX
511	CLD; REP; OUTSL
512	RET
513
514/*
515 *  routines to load/read various system registers
516 */
517GLOBL	idtptr(SB),$6
518TEXT	putidt(SB),$0		/* interrupt descriptor table */
519	MOVL	t+0(FP),AX
520	MOVL	AX,idtptr+2(SB)
521	MOVL	l+4(FP),AX
522	MOVW	AX,idtptr(SB)
523	MOVL	idtptr(SB),IDTR
524	RET
525
526TEXT	putcr3(SB),$0		/* top level page table pointer */
527	MOVL	t+0(FP),AX
528	MOVL	AX,CR3
529	RET
530
531TEXT	getcr0(SB),$0		/* coprocessor bits */
532	MOVL	CR0,AX
533	RET
534
535TEXT	getcr2(SB),$0		/* fault address */
536	MOVL	CR2,AX
537	RET
538
539TEXT	getcr3(SB),$0		/* page directory base */
540	MOVL	CR3,AX
541	RET
542
543TEXT	getcr4(SB), $0		/* CR4 - extensions */
544	MOVL	CR4, AX
545	RET
546
547TEXT _cycles(SB), $0				/* time stamp counter */
548	RDTSC
549	MOVL	vlong+0(FP), CX			/* &vlong */
550	MOVL	AX, 0(CX)			/* lo */
551	MOVL	DX, 4(CX)			/* hi */
552	RET
553
554TEXT rdmsr(SB), $0				/* model-specific register */
555	MOVL	index+0(FP), CX
556	RDMSR
557	MOVL	vlong+4(FP), CX			/* &vlong */
558	MOVL	AX, 0(CX)			/* lo */
559	MOVL	DX, 4(CX)			/* hi */
560	RET
561
562TEXT wrmsr(SB), $0
563	MOVL	index+0(FP), CX
564	MOVL	lo+4(FP), AX
565	MOVL	hi+8(FP), DX
566	WRMSR
567	RET
568
569TEXT mb386(SB), $0
570	POPL	AX				/* return PC */
571	PUSHFL
572	PUSHL	CS
573	PUSHL	AX
574	IRETL
575
576/*
577 *  special traps
578 */
579TEXT	intr0(SB),$0
580	PUSHL	$0
581	PUSHL	$0
582	JMP	intrcommon
583TEXT	intr1(SB),$0
584	PUSHL	$0
585	PUSHL	$1
586	JMP	intrcommon
587TEXT	intr2(SB),$0
588	PUSHL	$0
589	PUSHL	$2
590	JMP	intrcommon
591TEXT	intr3(SB),$0
592	PUSHL	$0
593	PUSHL	$3
594	JMP	intrcommon
595TEXT	intr4(SB),$0
596	PUSHL	$0
597	PUSHL	$4
598	JMP	intrcommon
599TEXT	intr5(SB),$0
600	PUSHL	$0
601	PUSHL	$5
602	JMP	intrcommon
603TEXT	intr6(SB),$0
604	PUSHL	$0
605	PUSHL	$6
606	JMP	intrcommon
607TEXT	intr7(SB),$0
608	PUSHL	$0
609	PUSHL	$7
610	JMP	intrcommon
611TEXT	intr8(SB),$0
612	PUSHL	$8
613	JMP	intrcommon
614TEXT	intr9(SB),$0
615	PUSHL	$0
616	PUSHL	$9
617	JMP	intrcommon
618TEXT	intr10(SB),$0
619	PUSHL	$10
620	JMP	intrcommon
621TEXT	intr11(SB),$0
622	PUSHL	$11
623	JMP	intrcommon
624TEXT	intr12(SB),$0
625	PUSHL	$12
626	JMP	intrcommon
627TEXT	intr13(SB),$0
628	PUSHL	$13
629	JMP	intrcommon
630TEXT	intr14(SB),$0
631	PUSHL	$14
632	JMP	intrcommon
633TEXT	intr15(SB),$0
634	PUSHL	$0
635	PUSHL	$15
636	JMP	intrcommon
637TEXT	intr16(SB),$0
638	PUSHL	$0
639	PUSHL	$16
640	JMP	intrcommon
641TEXT	intr24(SB),$0
642	PUSHL	$0
643	PUSHL	$24
644	JMP	intrcommon
645TEXT	intr25(SB),$0
646	PUSHL	$0
647	PUSHL	$25
648	JMP	intrcommon
649TEXT	intr26(SB),$0
650	PUSHL	$0
651	PUSHL	$26
652	JMP	intrcommon
653TEXT	intr27(SB),$0
654	PUSHL	$0
655	PUSHL	$27
656	JMP	intrcommon
657TEXT	intr28(SB),$0
658	PUSHL	$0
659	PUSHL	$28
660	JMP	intrcommon
661TEXT	intr29(SB),$0
662	PUSHL	$0
663	PUSHL	$29
664	JMP	intrcommon
665TEXT	intr30(SB),$0
666	PUSHL	$0
667	PUSHL	$30
668	JMP	intrcommon
669TEXT	intr31(SB),$0
670	PUSHL	$0
671	PUSHL	$31
672	JMP	intrcommon
673TEXT	intr32(SB),$0
674	PUSHL	$0
675	PUSHL	$32
676	JMP	intrcommon
677TEXT	intr33(SB),$0
678	PUSHL	$0
679	PUSHL	$33
680	JMP	intrcommon
681TEXT	intr34(SB),$0
682	PUSHL	$0
683	PUSHL	$34
684	JMP	intrcommon
685TEXT	intr35(SB),$0
686	PUSHL	$0
687	PUSHL	$35
688	JMP	intrcommon
689TEXT	intr36(SB),$0
690	PUSHL	$0
691	PUSHL	$36
692	JMP	intrcommon
693TEXT	intr37(SB),$0
694	PUSHL	$0
695	PUSHL	$37
696	JMP	intrcommon
697TEXT	intr38(SB),$0
698	PUSHL	$0
699	PUSHL	$38
700	JMP	intrcommon
701TEXT	intr39(SB),$0
702	PUSHL	$0
703	PUSHL	$39
704	JMP	intrcommon
705TEXT	intr64(SB),$0
706	PUSHL	$0
707	PUSHL	$64
708	JMP	intrcommon
709TEXT	intrbad(SB),$0
710	PUSHL	$0
711	PUSHL	$0x1ff
712	JMP	intrcommon
713
714intrcommon:
715	PUSHL	DS
716	PUSHL	ES
717	PUSHL	FS
718	PUSHL	GS
719	PUSHAL
720	MOVL	$(KDSEL),AX
721	MOVW	AX,DS
722	MOVW	AX,ES
723	LEAL	0(SP),AX
724	PUSHL	AX
725	CALL	trap(SB)
726	POPL	AX
727	POPAL
728	POPL	GS
729	POPL	FS
730	POPL	ES
731	POPL	DS
732	ADDL	$8,SP	/* error code and trap type */
733	IRETL
734
735
736/*
737 *  interrupt level is interrupts on or off
738 */
739TEXT	spllo(SB),$0
740	PUSHFL
741	POPL	AX
742	STI
743	RET
744
745TEXT	splhi(SB),$0
746	PUSHFL
747	POPL	AX
748	CLI
749	RET
750
751TEXT	splx(SB),$0
752	MOVL	s+0(FP),AX
753	PUSHL	AX
754	POPFL
755	RET
756
757/*
758 *  do nothing whatsoever till interrupt happens
759 */
760TEXT	idle(SB),$0
761	HLT
762	RET
763
764/*
765 * Try to determine the CPU type which requires fiddling with EFLAGS.
766 * If the Id bit can be toggled then the CPUID instruciton can be used
767 * to determine CPU identity and features. First have to check if it's
768 * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
769 * toggled then it's an older 486 of some kind.
770 *
771 *	cpuid(id[], &ax, &dx);
772 */
773#define CPUID		BYTE $0x0F; BYTE $0xA2	/* CPUID, argument in AX */
774TEXT cpuid(SB), $0
775	MOVL	$0x240000, AX
776	PUSHL	AX
777	POPFL					/* set Id|Ac */
778
779	PUSHFL
780	POPL	BX				/* retrieve value */
781
782	MOVL	$0, AX
783	PUSHL	AX
784	POPFL					/* clear Id|Ac, EFLAGS initialised */
785
786	PUSHFL
787	POPL	AX				/* retrieve value */
788	XORL	BX, AX
789	TESTL	$0x040000, AX			/* Ac */
790	JZ	_cpu386				/* can't set this bit on 386 */
791	TESTL	$0x200000, AX			/* Id */
792	JZ	_cpu486				/* can't toggle this bit on some 486 */
793
794	MOVL	$0, AX
795	CPUID
796	MOVL	id+0(FP), BP
797	MOVL	BX, 0(BP)			/* "Genu" "Auth" "Cyri" */
798	MOVL	DX, 4(BP)			/* "ineI" "enti" "xIns" */
799	MOVL	CX, 8(BP)			/* "ntel" "cAMD" "tead" */
800
801	MOVL	$1, AX
802	CPUID
803	JMP	_cpuid
804
805_cpu486:
806	MOVL	$0x400, AX
807	MOVL	$0, DX
808	JMP	_cpuid
809
810_cpu386:
811	MOVL	$0x300, AX
812	MOVL	$0, DX
813
814_cpuid:
815	MOVL	ax+4(FP), BP
816	MOVL	AX, 0(BP)
817	MOVL	dx+8(FP), BP
818	MOVL	DX, 0(BP)
819	RET
820
821
822/*
823 *  basic timing loop to determine CPU frequency
824 */
825TEXT	aamloop(SB),$0
826
827	MOVL	c+0(FP),CX
828aaml1:
829	AAM
830	LOOP	aaml1
831	RET
832
833TEXT hello(SB), $0
834	BYTE $'P'; BYTE $'l'; BYTE $'a'; BYTE $'n';
835	BYTE $' '; BYTE $'9'; BYTE $' '; BYTE $'f';
836	BYTE $'r'; BYTE $'o'; BYTE $'m'; BYTE $' ';
837	BYTE $'B'; BYTE $'e'; BYTE $'l'; BYTE $'l';
838	BYTE $' '; BYTE $'L'; BYTE $'a'; BYTE $'b';
839	BYTE $'s';
840#ifdef PXE
841	BYTE $' '; BYTE $'b'; BYTE $'y'; BYTE $' ';
842	BYTE $'P'; BYTE $'X'; BYTE $'E';
843#endif
844	BYTE $'\r';
845	BYTE $'\n';
846	BYTE $'\z';
847
848TEXT rock(SB), $0
849	BYTE $0; BYTE $0; BYTE $0; BYTE $0;
850
851GLOBL pxe(SB), $4
852#ifdef PXE
853DATA	pxe+0(SB)/4, $1
854#else
855DATA	pxe+0(SB)/4, $0
856#endif /* PXE */
857
858/*
859 * Save registers.
860 */
861TEXT saveregs(SB), $0
862	/* appease 8l */
863	SUBL $32, SP
864	POPL AX
865	POPL AX
866	POPL AX
867	POPL AX
868	POPL AX
869	POPL AX
870	POPL AX
871	POPL AX
872
873	PUSHL	AX
874	PUSHL	BX
875	PUSHL	CX
876	PUSHL	DX
877	PUSHL	BP
878	PUSHL	DI
879	PUSHL	SI
880	PUSHFL
881
882	XCHGL	32(SP), AX	/* swap return PC and saved flags */
883	XCHGL	0(SP), AX
884	XCHGL	32(SP), AX
885	RET
886
887TEXT restoreregs(SB), $0
888	/* appease 8l */
889	PUSHL	AX
890	PUSHL	AX
891	PUSHL	AX
892	PUSHL	AX
893	PUSHL	AX
894	PUSHL	AX
895	PUSHL	AX
896	PUSHL	AX
897	ADDL	$32, SP
898
899	XCHGL	32(SP), AX	/* swap return PC and saved flags */
900	XCHGL	0(SP), AX
901	XCHGL	32(SP), AX
902
903	POPFL
904	POPL	SI
905	POPL	DI
906	POPL	BP
907	POPL	DX
908	POPL	CX
909	POPL	BX
910	POPL	AX
911	RET
912
913/*
914 * Assumed to be in protected mode at time of call.
915 * Switch to real mode, execute an interrupt, and
916 * then switch back to protected mode.
917 *
918 * Assumes:
919 *
920 *	- no device interrupts are going to come in
921 *	- 0-16MB is identity mapped in page tables
922 *	- can use code segment 0x1000 in real mode
923 *		to get at l.s code
924 */
925TEXT realmodeidtptr(SB), $0
926	WORD	$(4*256-1)
927	LONG	$0
928
929TEXT realmode0(SB), $0
930	CALL	saveregs(SB)
931
932	/* switch to low code address */
933	LEAL	physcode-KZERO(SB), AX
934	JMP *AX
935
936TEXT physcode(SB), $0
937
938	/* switch to low stack */
939	MOVL	SP, AX
940	MOVL	$0x7C00, SP
941	PUSHL	AX
942
943	/* load IDT with real-mode version; GDT already fine */
944	MOVL	realmodeidtptr(SB), IDTR
945
946	/* edit INT $0x00 instruction below */
947	MOVL	realmodeintr(SB), AX
948	MOVB	AX, realmodeintrinst+1(SB)
949
950	/* disable paging */
951	MOVL	CR0, AX
952	ANDL	$0x7FFFFFFF, AX
953	MOVL	AX, CR0
954	/* JMP .+2 to clear prefetch queue*/
955	BYTE $0xEB; BYTE $0x00
956
957	/* jump to 16-bit code segment */
958/*	JMPFAR	SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/
959	 BYTE	$0xEA
960	 LONG	$again16bit-KZERO(SB)
961	 WORD	$SELECTOR(3, SELGDT, 0)
962
963TEXT again16bit(SB), $0
964	/*
965	 * Now in 16-bit compatibility mode.
966	 * These are 32-bit instructions being interpreted
967	 * as 16-bit instructions.  I'm being lazy and
968	 * not using the macros because I know when
969	 * the 16- and 32-bit instructions look the same
970	 * or close enough.
971	 */
972
973	/* disable protected mode and jump to real mode cs */
974	OPSIZE; MOVL CR0, AX
975	OPSIZE; XORL BX, BX
976	OPSIZE; INCL BX
977	OPSIZE; XORL BX, AX
978	OPSIZE; MOVL AX, CR0
979
980	/* JMPFAR 0x1000:now16real */
981	 BYTE $0xEA
982	 WORD	$now16real-KZERO(SB)
983	 WORD	$0x1000
984
985TEXT now16real(SB), $0
986	/* copy the registers for the bios call */
987	LWI(0x1000, rAX)
988	MOVW	AX,SS
989	LWI(realmoderegs(SB), rBP)
990
991	/* offsets are in Ureg */
992	LXW(44, xBP, rAX)
993	MOVW	AX, DS
994	LXW(40, xBP, rAX)
995	MOVW	AX, ES
996
997	OPSIZE; LXW(0, xBP, rDI)
998	OPSIZE; LXW(4, xBP, rSI)
999	OPSIZE; LXW(16, xBP, rBX)
1000	OPSIZE; LXW(20, xBP, rDX)
1001	OPSIZE; LXW(24, xBP, rCX)
1002	OPSIZE; LXW(28, xBP, rAX)
1003
1004	CLC
1005
1006TEXT realmodeintrinst(SB), $0
1007	INT $0x00
1008
1009
1010	/* save the registers after the call */
1011
1012	LWI(0x7bfc, rSP)
1013	OPSIZE; PUSHFL
1014	OPSIZE; PUSHL AX
1015
1016	LWI(0x1000, rAX)
1017	MOVW	AX,SS
1018	LWI(realmoderegs(SB), rBP)
1019
1020	OPSIZE; SXW(rDI, 0, xBP)
1021	OPSIZE; SXW(rSI, 4, xBP)
1022	OPSIZE; SXW(rBX, 16, xBP)
1023	OPSIZE; SXW(rDX, 20, xBP)
1024	OPSIZE; SXW(rCX, 24, xBP)
1025	OPSIZE; POPL AX
1026	OPSIZE; SXW(rAX, 28, xBP)
1027
1028	MOVW	DS, AX
1029	OPSIZE; SXW(rAX, 44, xBP)
1030	MOVW	ES, AX
1031	OPSIZE; SXW(rAX, 40, xBP)
1032
1033	OPSIZE; POPL AX
1034	OPSIZE; SXW(rAX, 64, xBP)	/* flags */
1035
1036	/* re-enter protected mode and jump to 32-bit code */
1037	OPSIZE; MOVL $1, AX
1038	OPSIZE; MOVL AX, CR0
1039
1040/*	JMPFAR	SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/
1041	 OPSIZE
1042	 BYTE $0xEA
1043	 LONG	$again32bit-KZERO(SB)
1044	 WORD	$SELECTOR(2, SELGDT, 0)
1045
1046TEXT again32bit(SB), $0
1047	MOVW	$SELECTOR(1, SELGDT, 0),AX
1048	MOVW	AX,DS
1049	MOVW	AX,SS
1050	MOVW	AX,ES
1051	MOVW	AX,FS
1052	MOVW	AX,GS
1053
1054	/* enable paging and jump to kzero-address code */
1055	MOVL	CR0, AX
1056	ORL	$0x80000000, AX
1057	MOVL	AX, CR0
1058	LEAL	again32kzero(SB), AX
1059	JMP*	AX
1060
1061TEXT again32kzero(SB), $0
1062	/* breathe a sigh of relief - back in 32-bit protected mode */
1063
1064	/* switch to old stack */
1065	PUSHL	AX	/* match popl below for 8l */
1066	MOVL	$0x7BFC, SP
1067	POPL	SP
1068
1069	/* restore idt */
1070	MOVL	idtptr(SB),IDTR
1071
1072	CALL	restoreregs(SB)
1073	RET
1074
1075TEXT realmoderegs(SB), $0
1076	LONG $0; LONG $0; LONG $0; LONG $0
1077	LONG $0; LONG $0; LONG $0; LONG $0
1078	LONG $0; LONG $0; LONG $0; LONG $0
1079	LONG $0; LONG $0; LONG $0; LONG $0
1080	LONG $0; LONG $0; LONG $0; LONG $0
1081
1082TEXT realmodeintr(SB), $0
1083	LONG $0
1084
1085