diff options
Diffstat (limited to 'src/boot/setup.s')
-rw-r--r-- | src/boot/setup.s | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/boot/setup.s b/src/boot/setup.s new file mode 100644 index 0000000..b8aae75 --- /dev/null +++ b/src/boot/setup.s | |||
@@ -0,0 +1,242 @@ | |||
1 | .code16 | ||
2 | # rewrite with AT&T syntax by falcon <wuzhangjin@gmail.com> at 081012 | ||
3 | # | ||
4 | # setup.s (C) 1991 Linus Torvalds | ||
5 | # | ||
6 | # setup.s is responsible for getting the system data from the BIOS, | ||
7 | # and putting them into the appropriate places in system memory. | ||
8 | # both setup.s and system has been loaded by the bootblock. | ||
9 | # | ||
10 | # This code asks the bios for memory/disk/other parameters, and | ||
11 | # puts them in a "safe" place: 0x90000-0x901FF, ie where the | ||
12 | # boot-block used to be. It is then up to the protected mode | ||
13 | # system to read them from there before the area is overwritten | ||
14 | # for buffer-blocks. | ||
15 | # | ||
16 | |||
17 | # NOTE! These had better be the same as in bootsect.s! | ||
18 | |||
19 | .equ INITSEG, 0x9000 # we move boot here - out of the way | ||
20 | .equ SYSSEG, 0x1000 # system loaded at 0x10000 (65536). | ||
21 | .equ SETUPSEG, 0x9020 # this is the current segment | ||
22 | |||
23 | .global _start, begtext, begdata, begbss, endtext, enddata, endbss | ||
24 | .text | ||
25 | begtext: | ||
26 | .data | ||
27 | begdata: | ||
28 | .bss | ||
29 | begbss: | ||
30 | .text | ||
31 | |||
32 | ljmp $SETUPSEG, $_start | ||
33 | _start: | ||
34 | |||
35 | # ok, the read went well so we get current cursor position and save it for | ||
36 | # posterity. | ||
37 | |||
38 | mov $INITSEG, %ax # this is done in bootsect already, but... | ||
39 | mov %ax, %ds | ||
40 | mov $0x03, %ah # read cursor pos | ||
41 | xor %bh, %bh | ||
42 | int $0x10 # save it in known place, con_init fetches | ||
43 | mov %dx, %ds:0 # it from 0x90000. | ||
44 | # Get memory size (extended mem, kB) | ||
45 | |||
46 | mov $0x88, %ah | ||
47 | int $0x15 | ||
48 | mov %ax, %ds:2 | ||
49 | |||
50 | # Get video-card data: | ||
51 | |||
52 | mov $0x0f, %ah | ||
53 | int $0x10 | ||
54 | mov %bx, %ds:4 # bh = display page | ||
55 | mov %ax, %ds:6 # al = video mode, ah = window width | ||
56 | |||
57 | # check for EGA/VGA and some config parameters | ||
58 | |||
59 | mov $0x12, %ah | ||
60 | mov $0x10, %bl | ||
61 | int $0x10 | ||
62 | mov %ax, %ds:8 | ||
63 | mov %bx, %ds:10 | ||
64 | mov %cx, %ds:12 | ||
65 | |||
66 | # Get hd0 data | ||
67 | |||
68 | mov $0x0000, %ax | ||
69 | mov %ax, %ds | ||
70 | lds %ds:4*0x41, %si | ||
71 | mov $INITSEG, %ax | ||
72 | mov %ax, %es | ||
73 | mov $0x0080, %di | ||
74 | mov $0x10, %cx | ||
75 | rep | ||
76 | movsb | ||
77 | |||
78 | # Get hd1 data | ||
79 | |||
80 | mov $0x0000, %ax | ||
81 | mov %ax, %ds | ||
82 | lds %ds:4*0x46, %si | ||
83 | mov $INITSEG, %ax | ||
84 | mov %ax, %es | ||
85 | mov $0x0090, %di | ||
86 | mov $0x10, %cx | ||
87 | rep | ||
88 | movsb | ||
89 | |||
90 | # Check that there IS a hd1 :-) | ||
91 | |||
92 | mov $0x01500, %ax | ||
93 | mov $0x81, %dl | ||
94 | int $0x13 | ||
95 | jc no_disk1 | ||
96 | cmp $3, %ah | ||
97 | je is_disk1 | ||
98 | no_disk1: | ||
99 | mov $INITSEG, %ax | ||
100 | mov %ax, %es | ||
101 | mov $0x0090, %di | ||
102 | mov $0x10, %cx | ||
103 | mov $0x00, %ax | ||
104 | rep | ||
105 | stosb | ||
106 | is_disk1: | ||
107 | |||
108 | # now we want to move to protected mode ... | ||
109 | |||
110 | cli # no interrupts allowed ! | ||
111 | |||
112 | # first we move the system to it's rightful place | ||
113 | |||
114 | mov $0x0000, %ax | ||
115 | cld # 'direction'=0, movs moves forward | ||
116 | do_move: | ||
117 | mov %ax, %es # destination segment | ||
118 | add $0x1000, %ax | ||
119 | cmp $0x9000, %ax | ||
120 | jz end_move | ||
121 | mov %ax, %ds # source segment | ||
122 | sub %di, %di | ||
123 | sub %si, %si | ||
124 | mov $0x8000, %cx | ||
125 | rep | ||
126 | movsw | ||
127 | jmp do_move | ||
128 | |||
129 | # then we load the segment descriptors | ||
130 | |||
131 | end_move: | ||
132 | mov $SETUPSEG, %ax # right, forgot this at first. didn't work :-) | ||
133 | mov %ax, %ds | ||
134 | lidt idt_48 # load idt with 0,0 | ||
135 | lgdt gdt_48 # load gdt with whatever appropriate | ||
136 | |||
137 | # that was painless, now we enable A20 | ||
138 | |||
139 | #call empty_8042 # 8042 is the keyboard controller | ||
140 | #mov $0xD1, %al # command write | ||
141 | #out %al, $0x64 | ||
142 | #call empty_8042 | ||
143 | #mov $0xDF, %al # A20 on | ||
144 | #out %al, $0x60 | ||
145 | #call empty_8042 | ||
146 | inb $0x92, %al # open A20 line(Fast Gate A20). | ||
147 | orb $0b00000010, %al | ||
148 | outb %al, $0x92 | ||
149 | |||
150 | # well, that went ok, I hope. Now we have to reprogram the interrupts :-( | ||
151 | # we put them right after the intel-reserved hardware interrupts, at | ||
152 | # int 0x20-0x2F. There they won't mess up anything. Sadly IBM really | ||
153 | # messed this up with the original PC, and they haven't been able to | ||
154 | # rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, | ||
155 | # which is used for the internal hardware interrupts as well. We just | ||
156 | # have to reprogram the 8259's, and it isn't fun. | ||
157 | |||
158 | mov $0x11, %al # initialization sequence(ICW1) | ||
159 | # ICW4 needed(1),CASCADE mode,Level-triggered | ||
160 | out %al, $0x20 # send it to 8259A-1 | ||
161 | .word 0x00eb,0x00eb # jmp $+2, jmp $+2 | ||
162 | out %al, $0xA0 # and to 8259A-2 | ||
163 | .word 0x00eb,0x00eb | ||
164 | mov $0x20, %al # start of hardware int's (0x20)(ICW2) | ||
165 | out %al, $0x21 # from 0x20-0x27 | ||
166 | .word 0x00eb,0x00eb | ||
167 | mov $0x28, %al # start of hardware int's 2 (0x28) | ||
168 | out %al, $0xA1 # from 0x28-0x2F | ||
169 | .word 0x00eb,0x00eb # IR 7654 3210 | ||
170 | mov $0x04, %al # 8259-1 is master(0000 0100) --\ | ||
171 | out %al, $0x21 # | | ||
172 | .word 0x00eb,0x00eb # INT / | ||
173 | mov $0x02, %al # 8259-2 is slave( 010 --> 2) | ||
174 | out %al, $0xA1 | ||
175 | .word 0x00eb,0x00eb | ||
176 | mov $0x01, %al # 8086 mode for both | ||
177 | out %al, $0x21 | ||
178 | .word 0x00eb,0x00eb | ||
179 | out %al, $0xA1 | ||
180 | .word 0x00eb,0x00eb | ||
181 | mov $0xFF, %al # mask off all interrupts for now | ||
182 | out %al, $0x21 | ||
183 | .word 0x00eb,0x00eb | ||
184 | out %al, $0xA1 | ||
185 | |||
186 | # well, that certainly wasn't fun :-(. Hopefully it works, and we don't | ||
187 | # need no steenking BIOS anyway (except for the initial loading :-). | ||
188 | # The BIOS-routine wants lots of unnecessary data, and it's less | ||
189 | # "interesting" anyway. This is how REAL programmers do it. | ||
190 | # | ||
191 | # Well, now's the time to actually move into protected mode. To make | ||
192 | # things as simple as possible, we do no register set-up or anything, | ||
193 | # we let the gnu-compiled 32-bit programs do that. We just jump to | ||
194 | # absolute address 0x00000, in 32-bit protected mode. | ||
195 | #mov $0x0001, %ax # protected mode (PE) bit | ||
196 | #lmsw %ax # This is it! | ||
197 | mov %cr0, %eax # get machine status(cr0|MSW) | ||
198 | bts $0, %eax # turn on the PE-bit | ||
199 | mov %eax, %cr0 # protection enabled | ||
200 | |||
201 | # segment-descriptor (INDEX:TI:RPL) | ||
202 | .equ sel_cs0, 0x0008 # select for code segment 0 ( 001:0 :00) | ||
203 | ljmp $sel_cs0, $0 # jmp offset 0 of code segment 0 in gdt | ||
204 | |||
205 | # This routine checks that the keyboard command queue is empty | ||
206 | # No timeout is used - if this hangs there is something wrong with | ||
207 | # the machine, and we probably couldn't proceed anyway. | ||
208 | empty_8042: | ||
209 | .word 0x00eb,0x00eb | ||
210 | in $0x64, %al # 8042 status port | ||
211 | test $2, %al # is input buffer full? | ||
212 | jnz empty_8042 # yes - loop | ||
213 | ret | ||
214 | |||
215 | gdt: | ||
216 | .word 0,0,0,0 # dummy | ||
217 | |||
218 | .word 0x07FF # 8Mb - limit=2047 (2048*4096=8Mb) | ||
219 | .word 0x0000 # base address=0 | ||
220 | .word 0x9A00 # code read/exec | ||
221 | .word 0x00C0 # granularity=4096, 386 | ||
222 | |||
223 | .word 0x07FF # 8Mb - limit=2047 (2048*4096=8Mb) | ||
224 | .word 0x0000 # base address=0 | ||
225 | .word 0x9200 # data read/write | ||
226 | .word 0x00C0 # granularity=4096, 386 | ||
227 | |||
228 | idt_48: | ||
229 | .word 0 # idt limit=0 | ||
230 | .word 0,0 # idt base=0L | ||
231 | |||
232 | gdt_48: | ||
233 | .word 0x800 # gdt limit=2048, 256 GDT entries | ||
234 | .word 512+gdt, 0x9 # gdt base = 0X9xxxx, | ||
235 | # 512+gdt is the real gdt after setup is moved to 0x9020 * 0x10 | ||
236 | |||
237 | .text | ||
238 | endtext: | ||
239 | .data | ||
240 | enddata: | ||
241 | .bss | ||
242 | endbss: | ||