1 |
/* |
/* |
2 |
* PUPA -- Preliminary Universal Programming Architecture for GRUB |
* PUPA -- Preliminary Universal Programming Architecture for GRUB |
3 |
* Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. |
* Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. |
4 |
* Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org> |
* Copyright (C) 2002,2003 Yoshinori K. Okuji <okuji@enbug.org> |
5 |
* |
* |
6 |
* This program is free software; you can redistribute it and/or modify |
* This program is free software; you can redistribute it and/or modify |
7 |
* it under the terms of the GNU General Public License as published by |
* it under the terms of the GNU General Public License as published by |
50 |
#include <pupa/machine/memory.h> |
#include <pupa/machine/memory.h> |
51 |
#include <pupa/machine/console.h> |
#include <pupa/machine/console.h> |
52 |
#include <pupa/machine/linux.h> |
#include <pupa/machine/linux.h> |
53 |
|
#include <pupa/machine/kernel.h> |
54 |
|
|
55 |
#define ABS(x) ((x) - EXT_C(start) + PUPA_BOOT_MACHINE_KERNEL_ADDR + 0x200) |
#define ABS(x) ((x) - EXT_C(start) + PUPA_BOOT_MACHINE_KERNEL_ADDR + 0x200) |
56 |
|
|
57 |
.file "startup.S" |
.file "startup.S" |
89 |
.long 0 |
.long 0 |
90 |
VARIABLE(pupa_kernel_image_size) |
VARIABLE(pupa_kernel_image_size) |
91 |
.long 0 |
.long 0 |
92 |
|
VARIABLE(pupa_compressed_size) |
93 |
|
.long 0 |
94 |
VARIABLE(pupa_install_dos_part) |
VARIABLE(pupa_install_dos_part) |
95 |
.long 0xFFFFFFFF |
.long 0xFFFFFFFF |
96 |
VARIABLE(pupa_install_bsd_part) |
VARIABLE(pupa_install_bsd_part) |
132 |
/* The ".code32" directive takes GAS out of 16-bit mode. */ |
/* The ".code32" directive takes GAS out of 16-bit mode. */ |
133 |
.code32 |
.code32 |
134 |
|
|
135 |
|
incl %eax |
136 |
|
call EXT_C(pupa_gate_a20) |
137 |
|
|
138 |
|
/* decompress the compressed part and put the result at 1MB */ |
139 |
|
movl $0x100000, %esi |
140 |
|
movl $(START_SYMBOL + PUPA_KERNEL_MACHINE_RAW_SIZE), %edi |
141 |
|
|
142 |
|
pushl %esi |
143 |
|
pushl EXT_C(pupa_compressed_size) |
144 |
|
pushl %edi |
145 |
|
call lzo1x_decompress |
146 |
|
addl $12, %esp |
147 |
|
|
148 |
|
/* copy back the decompressed part */ |
149 |
|
movl %eax, %ecx |
150 |
|
cld |
151 |
|
rep |
152 |
|
movsb |
153 |
|
|
154 |
/* copy modules before cleaning out the bss */ |
/* copy modules before cleaning out the bss */ |
155 |
movl EXT_C(pupa_total_module_size), %ecx |
movl EXT_C(pupa_total_module_size), %ecx |
156 |
movl EXT_C(pupa_kernel_image_size), %esi |
movl EXT_C(pupa_kernel_image_size), %esi |
170 |
/* compute the bss length */ |
/* compute the bss length */ |
171 |
movl $END_SYMBOL, %ecx |
movl $END_SYMBOL, %ecx |
172 |
subl %edi, %ecx |
subl %edi, %ecx |
173 |
|
|
174 |
/* clean out */ |
/* clean out */ |
175 |
|
xorl %eax, %eax |
176 |
cld |
cld |
177 |
rep |
rep |
178 |
stosb |
stosb |
184 |
|
|
185 |
|
|
186 |
/* |
/* |
187 |
|
* This is the area for all of the special variables. |
188 |
|
*/ |
189 |
|
|
190 |
|
.p2align 2 /* force 4-byte alignment */ |
191 |
|
|
192 |
|
protstack: |
193 |
|
.long PUPA_MEMORY_MACHINE_PROT_STACK |
194 |
|
|
195 |
|
VARIABLE(pupa_boot_drive) |
196 |
|
.long 0 |
197 |
|
|
198 |
|
VARIABLE(pupa_start_addr) |
199 |
|
.long START_SYMBOL |
200 |
|
|
201 |
|
VARIABLE(pupa_end_addr) |
202 |
|
.long END_SYMBOL |
203 |
|
|
204 |
|
VARIABLE(pupa_apm_bios_info) |
205 |
|
.word 0 /* version */ |
206 |
|
.word 0 /* cseg */ |
207 |
|
.long 0 /* offset */ |
208 |
|
.word 0 /* cseg_16 */ |
209 |
|
.word 0 /* dseg_16 */ |
210 |
|
.word 0 /* cseg_len */ |
211 |
|
.word 0 /* cseg_16_len */ |
212 |
|
.word 0 /* dseg_16_len */ |
213 |
|
|
214 |
|
/* |
215 |
|
* This is the Global Descriptor Table |
216 |
|
* |
217 |
|
* An entry, a "Segment Descriptor", looks like this: |
218 |
|
* |
219 |
|
* 31 24 19 16 7 0 |
220 |
|
* ------------------------------------------------------------ |
221 |
|
* | | |B| |A| | | |1|0|E|W|A| | |
222 |
|
* | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 | |
223 |
|
* | | |D| |L| 19..16| | |1|1|C|R|A| | |
224 |
|
* ------------------------------------------------------------ |
225 |
|
* | | | |
226 |
|
* | BASE 15..0 | LIMIT 15..0 | |
227 |
|
* | | | |
228 |
|
* ------------------------------------------------------------ |
229 |
|
* |
230 |
|
* Note the ordering of the data items is reversed from the above |
231 |
|
* description. |
232 |
|
*/ |
233 |
|
|
234 |
|
.p2align 2 /* force 4-byte alignment */ |
235 |
|
gdt: |
236 |
|
.word 0, 0 |
237 |
|
.byte 0, 0, 0, 0 |
238 |
|
|
239 |
|
/* code segment */ |
240 |
|
.word 0xFFFF, 0 |
241 |
|
.byte 0, 0x9A, 0xCF, 0 |
242 |
|
|
243 |
|
/* data segment */ |
244 |
|
.word 0xFFFF, 0 |
245 |
|
.byte 0, 0x92, 0xCF, 0 |
246 |
|
|
247 |
|
/* 16 bit real mode CS */ |
248 |
|
.word 0xFFFF, 0 |
249 |
|
.byte 0, 0x9E, 0, 0 |
250 |
|
|
251 |
|
/* 16 bit real mode DS */ |
252 |
|
.word 0xFFFF, 0 |
253 |
|
.byte 0, 0x92, 0, 0 |
254 |
|
|
255 |
|
|
256 |
|
/* this is the GDT descriptor */ |
257 |
|
gdtdesc: |
258 |
|
.word 0x27 /* limit */ |
259 |
|
.long gdt /* addr */ |
260 |
|
|
261 |
|
|
262 |
|
/* |
263 |
|
* These next two routines, "real_to_prot" and "prot_to_real" are structured |
264 |
|
* in a very specific way. Be very careful when changing them. |
265 |
|
* |
266 |
|
* NOTE: Use of either one messes up %eax and %ebp. |
267 |
|
*/ |
268 |
|
|
269 |
|
real_to_prot: |
270 |
|
.code16 |
271 |
|
cli |
272 |
|
|
273 |
|
/* load the GDT register */ |
274 |
|
DATA32 ADDR32 lgdt gdtdesc |
275 |
|
|
276 |
|
/* turn on protected mode */ |
277 |
|
movl %cr0, %eax |
278 |
|
orl $PUPA_MEMORY_MACHINE_CR0_PE_ON, %eax |
279 |
|
movl %eax, %cr0 |
280 |
|
|
281 |
|
/* jump to relocation, flush prefetch queue, and reload %cs */ |
282 |
|
DATA32 ljmp $PUPA_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg |
283 |
|
|
284 |
|
.code32 |
285 |
|
protcseg: |
286 |
|
/* reload other segment registers */ |
287 |
|
movw $PUPA_MEMORY_MACHINE_PROT_MODE_DSEG, %ax |
288 |
|
movw %ax, %ds |
289 |
|
movw %ax, %es |
290 |
|
movw %ax, %fs |
291 |
|
movw %ax, %gs |
292 |
|
movw %ax, %ss |
293 |
|
|
294 |
|
/* put the return address in a known safe location */ |
295 |
|
movl (%esp), %eax |
296 |
|
movl %eax, PUPA_MEMORY_MACHINE_REAL_STACK |
297 |
|
|
298 |
|
/* get protected mode stack */ |
299 |
|
movl protstack, %eax |
300 |
|
movl %eax, %esp |
301 |
|
movl %eax, %ebp |
302 |
|
|
303 |
|
/* get return address onto the right stack */ |
304 |
|
movl PUPA_MEMORY_MACHINE_REAL_STACK, %eax |
305 |
|
movl %eax, (%esp) |
306 |
|
|
307 |
|
/* zero %eax */ |
308 |
|
xorl %eax, %eax |
309 |
|
|
310 |
|
/* return on the old (or initialized) stack! */ |
311 |
|
ret |
312 |
|
|
313 |
|
|
314 |
|
prot_to_real: |
315 |
|
/* just in case, set GDT */ |
316 |
|
lgdt gdtdesc |
317 |
|
|
318 |
|
/* save the protected mode stack */ |
319 |
|
movl %esp, %eax |
320 |
|
movl %eax, protstack |
321 |
|
|
322 |
|
/* get the return address */ |
323 |
|
movl (%esp), %eax |
324 |
|
movl %eax, PUPA_MEMORY_MACHINE_REAL_STACK |
325 |
|
|
326 |
|
/* set up new stack */ |
327 |
|
movl $PUPA_MEMORY_MACHINE_REAL_STACK, %eax |
328 |
|
movl %eax, %esp |
329 |
|
movl %eax, %ebp |
330 |
|
|
331 |
|
/* set up segment limits */ |
332 |
|
movw $PUPA_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax |
333 |
|
movw %ax, %ds |
334 |
|
movw %ax, %es |
335 |
|
movw %ax, %fs |
336 |
|
movw %ax, %gs |
337 |
|
movw %ax, %ss |
338 |
|
|
339 |
|
/* this might be an extra step */ |
340 |
|
/* jump to a 16 bit segment */ |
341 |
|
ljmp $PUPA_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg |
342 |
|
|
343 |
|
tmpcseg: |
344 |
|
.code16 |
345 |
|
|
346 |
|
/* clear the PE bit of CR0 */ |
347 |
|
movl %cr0, %eax |
348 |
|
andl $(~PUPA_MEMORY_MACHINE_CR0_PE_ON), %eax |
349 |
|
movl %eax, %cr0 |
350 |
|
|
351 |
|
/* flush prefetch queue, reload %cs */ |
352 |
|
DATA32 ljmp $0, $realcseg |
353 |
|
|
354 |
|
realcseg: |
355 |
|
/* we are in real mode now |
356 |
|
* set up the real mode segment registers : DS, SS, ES |
357 |
|
*/ |
358 |
|
/* zero %eax */ |
359 |
|
xorl %eax, %eax |
360 |
|
|
361 |
|
movw %ax, %ds |
362 |
|
movw %ax, %es |
363 |
|
movw %ax, %fs |
364 |
|
movw %ax, %gs |
365 |
|
movw %ax, %ss |
366 |
|
|
367 |
|
/* restore interrupts */ |
368 |
|
sti |
369 |
|
|
370 |
|
/* return on new stack! */ |
371 |
|
DATA32 ret |
372 |
|
|
373 |
|
.code32 |
374 |
|
|
375 |
|
|
376 |
|
/* |
377 |
|
* pupa_gate_a20(int on) |
378 |
|
* |
379 |
|
* Gate address-line 20 for high memory. |
380 |
|
* |
381 |
|
* This routine is probably overconservative in what it does, but so what? |
382 |
|
* |
383 |
|
* It also eats any keystrokes in the keyboard buffer. :-( |
384 |
|
*/ |
385 |
|
|
386 |
|
FUNCTION(pupa_gate_a20) |
387 |
|
movl %eax, %ecx |
388 |
|
|
389 |
|
call gloop1 |
390 |
|
|
391 |
|
movb $0xd1, %al |
392 |
|
outb $0x64 |
393 |
|
|
394 |
|
gloopint1: |
395 |
|
inb $0x64 |
396 |
|
andb $0x02, %al |
397 |
|
jnz gloopint1 |
398 |
|
|
399 |
|
movb $0xdd, %al |
400 |
|
testl %ecx, %ecx |
401 |
|
jz gdoit |
402 |
|
|
403 |
|
orb $0x02, %al |
404 |
|
gdoit: |
405 |
|
outb $0x60 |
406 |
|
|
407 |
|
call gloop1 |
408 |
|
|
409 |
|
/* output a dummy command (USB keyboard hack) */ |
410 |
|
movb $0xff, %al |
411 |
|
outb $0x64 |
412 |
|
call gloop1 |
413 |
|
|
414 |
|
ret |
415 |
|
|
416 |
|
gloop1: |
417 |
|
inb $0x64 |
418 |
|
andb $0x02, %al |
419 |
|
jnz gloop1 |
420 |
|
|
421 |
|
gloop2: |
422 |
|
inb $0x64 |
423 |
|
andb $0x01, %al |
424 |
|
jz gloop2ret |
425 |
|
inb $0x60 |
426 |
|
jmp gloop2 |
427 |
|
|
428 |
|
gloop2ret: |
429 |
|
ret |
430 |
|
|
431 |
|
|
432 |
|
#include "lzo1x.S" |
433 |
|
|
434 |
|
|
435 |
|
/* |
436 |
* This call is special... it never returns... in fact it should simply |
* This call is special... it never returns... in fact it should simply |
437 |
* hang at this point! |
* hang at this point! |
438 |
*/ |
*/ |
621 |
|
|
622 |
|
|
623 |
/* |
/* |
|
* These next two routines, "real_to_prot" and "prot_to_real" are structured |
|
|
* in a very specific way. Be very careful when changing them. |
|
|
* |
|
|
* NOTE: Use of either one messes up %eax and %ebp. |
|
|
*/ |
|
|
|
|
|
real_to_prot: |
|
|
.code16 |
|
|
cli |
|
|
|
|
|
/* load the GDT register */ |
|
|
DATA32 ADDR32 lgdt gdtdesc |
|
|
|
|
|
/* turn on protected mode */ |
|
|
movl %cr0, %eax |
|
|
orl $PUPA_MEMORY_MACHINE_CR0_PE_ON, %eax |
|
|
movl %eax, %cr0 |
|
|
|
|
|
/* jump to relocation, flush prefetch queue, and reload %cs */ |
|
|
DATA32 ljmp $PUPA_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg |
|
|
|
|
|
.code32 |
|
|
protcseg: |
|
|
/* reload other segment registers */ |
|
|
movw $PUPA_MEMORY_MACHINE_PROT_MODE_DSEG, %ax |
|
|
movw %ax, %ds |
|
|
movw %ax, %es |
|
|
movw %ax, %fs |
|
|
movw %ax, %gs |
|
|
movw %ax, %ss |
|
|
|
|
|
/* put the return address in a known safe location */ |
|
|
movl (%esp), %eax |
|
|
movl %eax, PUPA_MEMORY_MACHINE_REAL_STACK |
|
|
|
|
|
/* get protected mode stack */ |
|
|
movl protstack, %eax |
|
|
movl %eax, %esp |
|
|
movl %eax, %ebp |
|
|
|
|
|
/* get return address onto the right stack */ |
|
|
movl PUPA_MEMORY_MACHINE_REAL_STACK, %eax |
|
|
movl %eax, (%esp) |
|
|
|
|
|
/* zero %eax */ |
|
|
xorl %eax, %eax |
|
|
|
|
|
/* return on the old (or initialized) stack! */ |
|
|
ret |
|
|
|
|
|
|
|
|
prot_to_real: |
|
|
/* just in case, set GDT */ |
|
|
lgdt gdtdesc |
|
|
|
|
|
/* save the protected mode stack */ |
|
|
movl %esp, %eax |
|
|
movl %eax, protstack |
|
|
|
|
|
/* get the return address */ |
|
|
movl (%esp), %eax |
|
|
movl %eax, PUPA_MEMORY_MACHINE_REAL_STACK |
|
|
|
|
|
/* set up new stack */ |
|
|
movl $PUPA_MEMORY_MACHINE_REAL_STACK, %eax |
|
|
movl %eax, %esp |
|
|
movl %eax, %ebp |
|
|
|
|
|
/* set up segment limits */ |
|
|
movw $PUPA_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax |
|
|
movw %ax, %ds |
|
|
movw %ax, %es |
|
|
movw %ax, %fs |
|
|
movw %ax, %gs |
|
|
movw %ax, %ss |
|
|
|
|
|
/* this might be an extra step */ |
|
|
/* jump to a 16 bit segment */ |
|
|
ljmp $PUPA_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg |
|
|
|
|
|
tmpcseg: |
|
|
.code16 |
|
|
|
|
|
/* clear the PE bit of CR0 */ |
|
|
movl %cr0, %eax |
|
|
andl $(~PUPA_MEMORY_MACHINE_CR0_PE_ON), %eax |
|
|
movl %eax, %cr0 |
|
|
|
|
|
/* flush prefetch queue, reload %cs */ |
|
|
DATA32 ljmp $0, $realcseg |
|
|
|
|
|
realcseg: |
|
|
/* we are in real mode now |
|
|
* set up the real mode segment registers : DS, SS, ES |
|
|
*/ |
|
|
/* zero %eax */ |
|
|
xorl %eax, %eax |
|
|
|
|
|
movw %ax, %ds |
|
|
movw %ax, %es |
|
|
movw %ax, %fs |
|
|
movw %ax, %gs |
|
|
movw %ax, %ss |
|
|
|
|
|
/* restore interrupts */ |
|
|
sti |
|
|
|
|
|
/* return on new stack! */ |
|
|
DATA32 ret |
|
|
|
|
|
.code32 |
|
|
|
|
|
|
|
|
/* |
|
624 |
* int pupa_biosdisk_rw_int13_extensions (int ah, int drive, void *dap) |
* int pupa_biosdisk_rw_int13_extensions (int ah, int drive, void *dap) |
625 |
* |
* |
626 |
* Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP |
* Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP |
1090 |
|
|
1091 |
|
|
1092 |
/* |
/* |
|
* pupa_gate_a20(int on) |
|
|
* |
|
|
* Gate address-line 20 for high memory. |
|
|
* |
|
|
* This routine is probably overconservative in what it does, but so what? |
|
|
* |
|
|
* It also eats any keystrokes in the keyboard buffer. :-( |
|
|
*/ |
|
|
|
|
|
FUNCTION(pupa_gate_a20) |
|
|
pushl %eax |
|
|
|
|
|
call gloop1 |
|
|
|
|
|
movb $0xd1, %al |
|
|
outb $0x64 |
|
|
|
|
|
gloopint1: |
|
|
inb $0x64 |
|
|
andb $0x02, %al |
|
|
jnz gloopint1 |
|
|
|
|
|
movb $0xdd, %al |
|
|
cmpb $0, 0x8(%esp) |
|
|
jz gdoit |
|
|
|
|
|
orb $0x02, %al |
|
|
gdoit: |
|
|
outb $0x60 |
|
|
|
|
|
call gloop1 |
|
|
|
|
|
/* output a dummy command (USB keyboard hack) */ |
|
|
movb $0xff, %al |
|
|
outb $0x64 |
|
|
call gloop1 |
|
|
|
|
|
popl %eax |
|
|
ret |
|
|
|
|
|
gloop1: |
|
|
inb $0x64 |
|
|
andb $0x02, %al |
|
|
jnz gloop1 |
|
|
|
|
|
gloop2: |
|
|
inb $0x64 |
|
|
andb $0x01, %al |
|
|
jz gloop2ret |
|
|
inb $0x60 |
|
|
jmp gloop2 |
|
|
|
|
|
gloop2ret: |
|
|
ret |
|
|
|
|
|
|
|
|
/* |
|
1093 |
* void pupa_console_putchar (int c) |
* void pupa_console_putchar (int c) |
1094 |
* |
* |
1095 |
* Put the character C on the console. Because GRUB wants to write a |
* Put the character C on the console. Because GRUB wants to write a |
1514 |
|
|
1515 |
popl %ebp |
popl %ebp |
1516 |
ret |
ret |
|
|
|
|
|
|
|
/* |
|
|
* This is the area for all of the special variables. |
|
|
*/ |
|
|
|
|
|
.p2align 2 /* force 4-byte alignment */ |
|
|
|
|
|
protstack: |
|
|
.long PUPA_MEMORY_MACHINE_PROT_STACK |
|
|
|
|
|
VARIABLE(pupa_boot_drive) |
|
|
.long 0 |
|
|
|
|
|
VARIABLE(pupa_start_addr) |
|
|
.long START_SYMBOL |
|
|
|
|
|
VARIABLE(pupa_end_addr) |
|
|
.long END_SYMBOL |
|
|
|
|
|
VARIABLE(pupa_apm_bios_info) |
|
|
.word 0 /* version */ |
|
|
.word 0 /* cseg */ |
|
|
.long 0 /* offset */ |
|
|
.word 0 /* cseg_16 */ |
|
|
.word 0 /* dseg_16 */ |
|
|
.word 0 /* cseg_len */ |
|
|
.word 0 /* cseg_16_len */ |
|
|
.word 0 /* dseg_16_len */ |
|
|
|
|
|
/* |
|
|
* This is the Global Descriptor Table |
|
|
* |
|
|
* An entry, a "Segment Descriptor", looks like this: |
|
|
* |
|
|
* 31 24 19 16 7 0 |
|
|
* ------------------------------------------------------------ |
|
|
* | | |B| |A| | | |1|0|E|W|A| | |
|
|
* | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 | |
|
|
* | | |D| |L| 19..16| | |1|1|C|R|A| | |
|
|
* ------------------------------------------------------------ |
|
|
* | | | |
|
|
* | BASE 15..0 | LIMIT 15..0 | |
|
|
* | | | |
|
|
* ------------------------------------------------------------ |
|
|
* |
|
|
* Note the ordering of the data items is reversed from the above |
|
|
* description. |
|
|
*/ |
|
|
|
|
|
.p2align 2 /* force 4-byte alignment */ |
|
|
gdt: |
|
|
.word 0, 0 |
|
|
.byte 0, 0, 0, 0 |
|
|
|
|
|
/* code segment */ |
|
|
.word 0xFFFF, 0 |
|
|
.byte 0, 0x9A, 0xCF, 0 |
|
|
|
|
|
/* data segment */ |
|
|
.word 0xFFFF, 0 |
|
|
.byte 0, 0x92, 0xCF, 0 |
|
|
|
|
|
/* 16 bit real mode CS */ |
|
|
.word 0xFFFF, 0 |
|
|
.byte 0, 0x9E, 0, 0 |
|
|
|
|
|
/* 16 bit real mode DS */ |
|
|
.word 0xFFFF, 0 |
|
|
.byte 0, 0x92, 0, 0 |
|
|
|
|
|
|
|
|
/* this is the GDT descriptor */ |
|
|
gdtdesc: |
|
|
.word 0x27 /* limit */ |
|
|
.long gdt /* addr */ |
|