1/*
2 *  head.s contains the 32-bit startup code.
3 *
4 * NOTE!!! Startup happens at absolute address 0x00000000, which is also where
5 * the page directory will exist. The startup code will be overwritten by
6 * the page directory.
7 */
8.text
9.globl _idt,_gdt,_pg_dir
10_pg_dir:
11startup_32:
12	movl $0x10,%eax
13	mov %ax,%ds
14	mov %ax,%es
15	mov %ax,%fs
16	mov %ax,%gs
17	lss _stack_start,%esp
18	call setup_idt
19	call setup_gdt
20	movl $0x10,%eax		# reload all the segment registers
21	mov %ax,%ds		# after changing gdt. CS was already
22	mov %ax,%es		# reloaded in 'setup_gdt'
23	mov %ax,%fs
24	mov %ax,%gs
25	lss _stack_start,%esp
26	xorl %eax,%eax
271:	incl %eax		# check that A20 really IS enabled
28	movl %eax,0x000000
29	cmpl %eax,0x100000
30	je 1b
31	movl %cr0,%eax		# check math chip
32	andl $0x80000011,%eax	# Save PG,ET,PE
33	testl $0x10,%eax
34	jne 1f			# ET is set - 387 is present
35	orl $4,%eax		# else set emulate bit
361:	movl %eax,%cr0
37	jmp after_page_tables
38
39/*
40 *  setup_idt
41 *
42 *  sets up a idt with 256 entries pointing to
43 *  ignore_int, interrupt gates. It then loads
44 *  idt. Everything that wants to install itself
45 *  in the idt-table may do so themselves. Interrupts
46 *  are enabled elsewhere, when we can be relatively
47 *  sure everything is ok. This routine will be over-
48 *  written by the page tables.
49 */
50setup_idt:
51	lea ignore_int,%edx
52	movl $0x00080000,%eax
53	movw %dx,%ax		/* selector = 0x0008 = cs */
54	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
55
56	lea _idt,%edi
57	mov $256,%ecx
58rp_sidt:
59	movl %eax,(%edi)
60	movl %edx,4(%edi)
61	addl $8,%edi
62	dec %ecx
63	jne rp_sidt
64	lidt idt_descr
65	ret
66
67/*
68 *  setup_gdt
69 *
70 *  This routines sets up a new gdt and loads it.
71 *  Only two entries are currently built, the same
72 *  ones that were built in init.s. The routine
73 *  is VERY complicated at two whole lines, so this
74 *  rather long comment is certainly needed :-).
75 *  This routine will beoverwritten by the page tables.
76 */
77setup_gdt:
78	lgdt gdt_descr
79	ret
80
81.org 0x1000
82pg0:
83
84.org 0x2000
85pg1:
86
87.org 0x3000
88pg2:		# This is not used yet, but if you
89		# want to expand past 8 Mb, you'll have
90		# to use it.
91
92.org 0x4000
93after_page_tables:
94	pushl $0		# These are the parameters to main :-)
95	pushl $0
96	pushl $0
97	pushl $L6		# return address for main, if it decides to.
98	pushl $_main
99	jmp setup_paging
100L6:
101	jmp L6			# main should never return here, but
102				# just in case, we know what happens.
103
104/* This is the default interrupt "handler" :-) */
105.align 2
106ignore_int:
107	incb 0xb8000+160		# put something on the screen
108	movb $2,0xb8000+161		# so that we know something
109	iret				# happened
110
111
112/*
113 * Setup_paging
114 *
115 * This routine sets up paging by setting the page bit
116 * in cr0. The page tables are set up, identity-mapping
117 * the first 8MB. The pager assumes that no illegal
118 * addresses are produced (ie >4Mb on a 4Mb machine).
119 *
120 * NOTE! Although all physical memory should be identity
121 * mapped by this routine, only the kernel page functions
122 * use the >1Mb addresses directly. All "normal" functions
123 * use just the lower 1Mb, or the local data space, which
124 * will be mapped to some other place - mm keeps track of
125 * that.
126 *
127 * For those with more memory than 8 Mb - tough luck. I've
128 * not got it, why should you :-) The source is here. Change
129 * it. (Seriously - it shouldn't be too difficult. Mostly
130 * change some constants etc. I left it at 8Mb, as my machine
131 * even cannot be extended past that (ok, but it was cheap :-)
132 * I've tried to show which constants to change by having
133 * some kind of marker at them (search for "8Mb"), but I
134 * won't guarantee that's all :-( )
135 */
136.align 2
137setup_paging:
138	movl $1024*3,%ecx
139	xorl %eax,%eax
140	xorl %edi,%edi			/* pg_dir is at 0x000 */
141	cld;rep;stosl
142	movl $pg0+7,_pg_dir		/* set present bit/user r/w */
143	movl $pg1+7,_pg_dir+4		/*  --------- " " --------- */
144	movl $pg1+4092,%edi
145	movl $0x7ff007,%eax		/*  8Mb - 4096 + 7 (r/w user,p) */
146	std
1471:	stosl			/* fill pages backwards - more efficient :-) */
148	subl $0x1000,%eax
149	jge 1b
150	xorl %eax,%eax		/* pg_dir is at 0x0000 */
151	movl %eax,%cr3		/* cr3 - page directory start */
152	movl %cr0,%eax
153	orl $0x80000000,%eax
154	movl %eax,%cr0		/* set paging (PG) bit */
155	ret			/* this also flushes prefetch-queue */
156
157.align 2
158.word 0
159idt_descr:
160	.word 256*8-1		# idt contains 256 entries
161	.long _idt
162.align 2
163.word 0
164gdt_descr:
165	.word 256*8-1		# so does gdt (not that that's any
166	.long _gdt		# magic number, but it works for me :^)
167
168	.align 3
169_idt:	.fill 256,8,0		# idt is uninitialized
170
171_gdt:	.quad 0x0000000000000000	/* NULL descriptor */
172	.quad 0x00c09a00000007ff	/* 8Mb */
173	.quad 0x00c09200000007ff	/* 8Mb */
174	.quad 0x0000000000000000	/* TEMPORARY - don't use */
175	.fill 252,8,0			/* space for LDT's and TSS's etc */
176