From 36020a912d537779c3bf4a74537c1a5ef2040dec Mon Sep 17 00:00:00 2001 From: Marcus Overhagen Date: Mon, 25 Sep 2006 14:25:58 +0000 Subject: [PATCH] copied from src/system/boot/platform/bios_ia32/shell.S git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18917 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../boot/platform/pxe_ia32/pxe_stage2.S | 376 ++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 src/system/boot/platform/pxe_ia32/pxe_stage2.S diff --git a/src/system/boot/platform/pxe_ia32/pxe_stage2.S b/src/system/boot/platform/pxe_ia32/pxe_stage2.S new file mode 100644 index 0000000000..ff3130fb69 --- /dev/null +++ b/src/system/boot/platform/pxe_ia32/pxe_stage2.S @@ -0,0 +1,376 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +/** This file contains the boot floppy and BFS boot block entry points for + * the stage 2 boot loader. + * The floppy entry point is at offset 0. It's loaded at 0x07c0:0x000. It + * will load the rest of the loader to 0x1000:0x0200 and execute it. + * The BFS boot block will load the whole stage 2 loader to 0x1000:0x0000 + * and will then jump to 0x1000:0x0200 as its entry point. + * This code will then switch to protected mode and will directly call + * the entry function of the embedded ELF part of the loader. + */ + +#define GLOBAL(x) .globl x ; x + +// this saves us some trouble with relocation (I didn't manage GAS to +// create 32 bit references to labels) +#define FAILURE_STRING 0x1d0 +#define DOT_STRING 0x1fc + +#define DRIVE_RETRIES 3 + // when the drive reading fails for some reason, it will + // retry this many times until it will report a failure + +.text +.code16 + +/** This is the entry point when we were written directly to a floppy disk */ + + jmp floppy_start + +sNumSectors: + // this location will contain the length of the boot loader as + // written by the "makeflop" command in 512 byte blocks + // 0x180 is the allowed maximum, as the zipped TAR with the + // kernel and the boot module might start at offset 192 kB + .word 0x0180 + +floppy_start: + cli + cld + + // set up the stack to 0x0000:0x9000 + xor %ax, %ax + mov %ax, %ss + mov $0x9000, %sp + + push $0x07c0 + pop %ds + push $0x1000 + pop %es + + // load the rest of the boot loader to 0x1000:0x0200 + .code32 // we need to create a 32-bit relocation entry for the linker... + .byte 0x67 + movw sNumSectors - 0x10000, %di + // the loader symbols are located at offset 0x10000 + .code16 + xor %dh, %dh // head 0, don't change BIOS boot device + mov $0x2, %cx // sector 2 + mov $0x200, %bx // to 0x1000:0x0200 + call load_sectors + + // ToDo: this seems to be problematic, at least under Bochs (reboot will fail) +#if 0 + or %dl, %dl // if it's a floppy, turn off its motor + jnz start_loader + call disable_floppy_motor +#endif + +start_loader: + // indicate that we were booted from CD/floppy/whatever + .code32 + .byte 0x67 + movb $1, gBootedFromImage - 0x7c00 + // %ds is 0x7c0 right now, but the symbol were loaded + // to offset 0x10000 + .code16 + + // set our environment and jump to the standard BFS boot block entry point + xor %dx, %dx // boot device ID and partition offset to 0 + xor %eax, %eax + ljmp $0x1000, $0x0200 + + +/** Loads %di sectors from floppy disk, starting at head %dh, sector %cx. + * The data is loaded to %es:%bx. On exit, %es:%bx will point immediately + * behind the loaded data, so that you can continue to read in data. + * %ax, %cx, %dx, %bp, %di and %si will be clobbered. + */ + +load_sectors: + // first, get information about the drive as we intend to read whole tracks + push %bx + push %cx + push %dx + push %di + push %es + + movb $8, %ah // get drive parameters - changes a lot of registers + int $0x13 + + pop %es + pop %di + // ToDo: store the number of heads somewhere (it's in %dh) + pop %dx + and $63, %cx // mask out max. sector number (bit 0-5) + mov %cx, %si // and remember it + pop %cx + pop %bx + +load_track: + mov %di, %ax // limit the sector count to track boundaries + add %cl, %al + dec %ax + cmp %si, %ax + jbe matches_track_boundary + mov %si, %ax +matches_track_boundary: + inc %ax // take the current sector offset into account + sub %cl, %al + + // make sure we don't cross a 64kB address boundary or else the read will fail + // (this small piece of knowledge took me some time to accept :)) + shl $9, %ax + mov %ax, %bp + add %bx, %bp + jnc respects_boundary + xor %ax, %ax // only read up to the 64kB boundary + sub %bx, %ax +respects_boundary: + shr $9, %ax + mov DRIVE_RETRIES, %bp + +try_to_read: + pusha + movb $2, %ah // load sectors from drive + int $0x13 + jnc read_succeeded + + xor %ax, %ax + int $0x13 // reset drive + popa + + dec %bp + jz load_failed // if already retried often enough, bail out + jmp try_to_read + +read_succeeded: + mov $DOT_STRING, %si + call print_string + popa + + xor %ah, %ah + add %ax, %cx // next sector start + sub %ax, %di // update sectors left to be read + + shl $9, %ax // get byte offset + add %ax, %bx // update target address + jnz check_sector_start + + mov %es, %ax // overflow to the next 64kB, %bx is already zero + add $0x1000, %ax + mov %ax, %es + +check_sector_start: + mov %si, %ax // compare the sectors, not the cylinders + cmp %al, %cl + jbe continue_reading + + sub %si, %cx + inc %dh // next head + cmp $1, %dh + // ToDo: check max. number of heads! + jbe check_sector_start + + xor %dh, %dh // next cylinder + inc %ch + jmp check_sector_start + +continue_reading: + or %di, %di + jnz load_track + ret + +load_failed: + mov $FAILURE_STRING, %si + call print_string + + xor %ax, %ax + int $0x16 // wait for key + int $0x19 // and reboot + +disable_floppy_motor: + xor %al, %al + mov $0x3f2, %dx + out %al, %dx + ret + +print_string: + movb $0x0e, %ah + xor %bx, %bx +print_char: + lodsb + orb %al, %al // are there still characters left? + jz no_more_chars + int $0x10 + jmp print_char +no_more_chars: + ret + +floppy_end: + .org FAILURE_STRING + .string " Loading failed! Press key to reboot.\r\n" + .org DOT_STRING + .string "." + + .org 0x01fe + .word 0xaa55 + // this bumps the "start" label to offset 0x0200 as + // expected by the BFS boot loader, and also marks + // this block as valid boot block for the BIOS + + +//-------------------------------------------------------------- + +/** This is the entry point of the stage2 bootloader when it has + * been loaded from the stage1 loader from a BFS disk. + */ + +bfs_start: + cld // set the data, and extra segment to our code start + pushw $0x1000 + pop %ds + push %ds + pop %es + + .code32 // save knowledge from the BFS boot block for later use + .byte 0x67 + movb %dl, gBootDriveID - 0x10000 + .byte 0x67 + .byte 0x66 + movl %eax, gBootPartitionOffset - 0x10000 + .code16 + + xor %ax, %ax // set up stack at 0x0000:0x9000 + mov %ax, %ss + mov $0x9000, %sp + + cli // no interrupts please + call enable_a20 // enable a20 gate + + .code32 // This forces a 32 bit relocation entry + .byte 0x66 // that allows linking with others + .byte 0x67 + lgdt gdt_descriptor - 0x10000 + // load global descriptor table; we're still in real mode segment + // 0x1000 so we have to manually correct the address + + .code16 + movl %cr0, %eax // set the PE bit of cr0 to switch to protected mode + orb $0x1, %al + movl %eax, %cr0 + + .code32 + .byte 0x66 + ljmp $0x8, $_protected_code_segment +_protected_code_segment: + mov $0x10, %ax // load descriptor 2 in the data and stack segment selectors + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + mov $0x10000, %ebp // setup new stack + mov %ebp, %esp + + call _start + +//-------------------------------------------------------------- + +/** Enables the a20 gate. It will first try to enable it through + * the BIOS, and, if that fails, will use the old style AT mechanism + * using the keyboard port. + * ToDo: it no longer does this! Now, it just uses the "fast A20" + * mechanism using port 0x92. This does work on all systems + * I have access to. + */ + +enable_a20: + inb $0x92, %al + testb $0x02, %al + jnz _a20_out + orb $0x02, %al + andb $0xfe, %al + outb %al, $0x92 +_a20_out: + ret + +// ToDo: the code below didn't seem to work properly on all machines +/* movw $0x2402, %ax // first, query the a20 status + int $0x15 + jc _a20_old_method // if that fails, use the old AT method + test $0x1, %al + jnz _a20_done // Is a20 gate already enabled? + movw $0x2401, %ax + int $0x15 + jnc _a20_done +_a20_old_method: + call _a20_loop1 // empty the keyboard buffer + jnz _a20_done + movb $0xd1, %al + outb %al, $0x64 + call _a20_loop1 // empty the keyboard buffer + jnz _a20_done + movb $0xdf, %al + outb %al, $0x60 +_a20_loop1: + movl $0x20000, %ecx +_a20_loop2: + inb $0x64, %al + test $0x2, %al + loopne _a20_loop2 +_a20_done: + ret +*/ + +//-------------------------------------------------------------- + +.org 896 + // since we don't need the above space when the boot loader is + // running, it is used as a real mode scratch buffer (as our + // boot loader spans over the whole real mode 0x1000 segment) + +/* global data table */ + +gdt: + // null descriptor + .long 0 + .long 0 + + // kernel code segment + .long 0x0000ffff // base: 0, limit: 4 GB + .long 0x00cf9e00 // type: 32 bit, exec-only conforming, privilege 0 + // kernel data and stack segment + .long 0x0000ffff // base: 0, limit: 4 GB + .long 0x00cf9200 // type: 32 bit, data read/write, privilege 0 + + // real mode 16 bit code segment + .long 0x0000ffff // base: 0x10000, limit: 64 kB + .long 0x00009e01 + // real mode 16 bit data and stack segment + .long 0x0000ffff // base: 0x10000, limit: 64 kB + .long 0x00009201 + // real mode 16 bit stack segment + .long 0x0000ffff // base: 0, limit: 64 kB + .long 0x00009200 + +gdt_descriptor: + .word 0x2f // 6 entries in the GDT (8 bytes each) + .long gdt + +GLOBAL(gBootedFromImage): + .byte 0 + +GLOBAL(gBootDriveID): + .byte 0 + +GLOBAL(gBootPartitionOffset): + .long 0 + +.org 1024