| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 1994-2002 H. Peter Anvin |
| * Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| * |
| */ |
| |
| /* |
| Most of this file was originally "isolinux.asm" from SYSLINUX package. |
| It has been very heavily modified. |
| */ |
| |
| #define ASM_FILE |
| #include "stage1.h" |
| #include "shared.h" |
| #include "iso9660.h" |
| |
| #ifndef STAGE1_5 |
| #include "stage2_size.h" |
| #endif |
| |
| |
| /* Absolute addresses |
| This makes the assembler generate the address without support |
| from the linker. (ELF can't relocate 16-bit addresses!) */ |
| #define ABS(x) (x-_start+BOOTSEC_LOCATION) |
| |
| #ifdef STAGE1_5 |
| # define STAGE_ADDR 0x2000 |
| #else |
| # define STAGE_ADDR 0x8000 |
| #endif /* STAGE1_5 */ |
| |
| /* Print message string */ |
| #define MSG(x) mov $ABS(x), %si; call message; |
| |
| .file "start_eltorito.S" |
| |
| .text |
| |
| /* Tell GAS to generate 16-bit instructions so that this code works |
| in real mode. */ |
| .code16 |
| |
| .globl start, _start |
| |
| /* |
| * Primary entry point. Because BIOSes are buggy, we only load the first |
| * CD-ROM sector (2K) of the file, so the number one priority is actually |
| * loading the rest. |
| */ |
| start: |
| _start: |
| cli |
| ljmp $0, $ABS(real_start) |
| |
| . = _start + 8 /* Pad to file offset 8 */ |
| |
| /* This table gets filled in by mkisofs using the |
| -boot-info-table option */ |
| bi_pvd: .long 0xDEADBEEF /* LBA of primary volume descript */ |
| bi_file: .long 0xDEADBEEF /* LBA of boot file */ |
| bi_length: .long 0xDEADBEEF /* Length of boot file */ |
| bi_csum: .long 0xDEADBEEF /* Checksum of boot file */ |
| bi_reserved: .space (10*4) /* Reserved */ |
| |
| real_start: |
| xor %ax, %ax |
| mov %ax, %ss |
| mov %ax, %ds |
| mov %ax, %es |
| mov %ax, %fs |
| mov %ax, %gs |
| mov $STAGE1_STACKSEG, %sp /* set up the REAL stack */ |
| sti |
| cld |
| |
| /* save drive reference first thing! */ |
| mov %dl, ABS(EXT_C(BootDrive)) |
| |
| /* print a notification message on the screen */ |
| MSG(notification_string) |
| |
| load_image: |
| /* Set up boot file sector, size, load address */ |
| mov ABS(bi_length), %eax |
| add $(ISO_SECTOR_SIZE-1), %eax |
| shr $ISO_SECTOR_BITS, %eax /* dwords->sectors */ |
| mov %ax, %bp /* boot file sectors */ |
| mov $(STAGE_ADDR >> 4), %bx |
| mov %bx, %es |
| xor %bx, %bx |
| mov ABS(bi_file), %eax |
| call getlinsec |
| mov %ds, %ax |
| mov %ax, %es |
| |
| MSG(notification_done) |
| bootit: |
| /* save the sector number of the second sector in %ebp */ |
| mov $ABS(firstlist - BOOTSEC_LISTSIZE), %si |
| mov (%si), %ebp |
| mov ABS(EXT_C(BootDrive)), %dl /* this makes sure %dl is our "boot" drive */ |
| ljmp $0, $(STAGE_ADDR+SECTOR_SIZE) /* jump to main() in asm.S */ |
| |
| /* go here when you need to stop the machine hard after an error condition */ |
| stop: jmp stop |
| |
| |
| /* |
| * Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. |
| * |
| * Note that we can't always do this as a single request, because at least |
| * Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick |
| * to 16 sectors (32K) per request. |
| * |
| * Input: |
| * EAX - Linear sector number |
| * ES:BX - Target buffer |
| * BP - Sector count |
| */ |
| getlinsec: |
| mov $ABS(dapa), %si /* Load up the DAPA */ |
| mov %bx, 4(%si) |
| mov %es, %bx |
| mov %bx, 6(%si) |
| mov %eax, 8(%si) |
| 1: |
| push %bp |
| push %si |
| cmp ABS(EXT_C(MaxTransfer)), %bp |
| jbe 2f |
| mov ABS(EXT_C(MaxTransfer)), %bp |
| 2: |
| mov %bp, 2(%si) |
| mov ABS(EXT_C(BootDrive)), %dl |
| mov $0x42, %ah /* Extended Read */ |
| call xint13 |
| pop %si |
| pop %bp |
| movzwl 2(%si), %eax /* Sectors we read */ |
| add %eax, 8(%si) /* Advance sector pointer */ |
| sub %ax, %bp /* Sectors left */ |
| shl $(ISO_SECTOR_BITS-4), %ax /* 2048-byte sectors -> segment */ |
| add %ax, 6(%si) /* Advance buffer pointer */ |
| |
| pushal |
| MSG(notification_step) |
| popal |
| cmp $0, %bp |
| ja 1b |
| mov 8(%si), %eax /* Return next sector */ |
| ret |
| |
| /* |
| * INT 13h with retry |
| */ |
| xint13: |
| movb $6, ABS(EXT_C(RetryCount)) |
| pushal |
| .try: |
| int $0x13 |
| jc 1f |
| add $(8*4), %sp /* Clean up stack */ |
| ret |
| 1: |
| mov %ah, %dl /* Save error code */ |
| decb ABS(EXT_C(RetryCount)) |
| jz .real_error |
| mov ABS(EXT_C(RetryCount)), %al |
| mov ABS(dapa+2), %ah /* Sector transfer count */ |
| cmp $2, %al /* Only 2 attempts left */ |
| ja 2f |
| mov $1, %ah /* Drop transfer size to 1 */ |
| jmp .setmaxtr |
| 2: |
| cmp $3, %al |
| ja 3f /* First time, just try again */ |
| shr $1, %ah /* Otherwise, try to reduce */ |
| adc $0, %ah /* the max transfer size, but not */ |
| .setmaxtr: |
| mov %ah, ABS(EXT_C(MaxTransfer)) |
| mov %ah, ABS(dapa+2) |
| 3: |
| popal |
| jmp .try |
| |
| .real_error: |
| MSG(read_error_string) |
| mov %dl, %al |
| call printhex2 |
| popal |
| jmp stop |
| |
| |
| |
| /* |
| * message: write the string pointed to by %si |
| * |
| * WARNING: trashes %si, %ax, and %bx |
| */ |
| |
| /* |
| * Use BIOS "int 10H Function 0Eh" to write character in teletype mode |
| * %ah = 0xe %al = character |
| * %bh = page %bl = foreground color (graphics modes) |
| */ |
| 1: |
| mov $0x0001, %bx |
| mov $0x0E, %ah |
| int $0x10 /* display a byte */ |
| |
| message: |
| lodsb |
| or %al, %al |
| jne 1b /* if not end of string, jmp to display */ |
| ret |
| |
| /* |
| * printhex[248]: Write a hex number in (AL, AX, EAX) to the console |
| */ |
| printhex2: |
| pushal |
| rol $24, %eax |
| mov $2, %cx |
| jmp 1f |
| printhex4: |
| pushal |
| rol $16, %eax |
| mov $4, %cx |
| jmp 1f |
| printhex8: |
| pushal |
| mov $8, %cx |
| 1: |
| rol $4, %eax |
| push %eax |
| and $0x0F, %al |
| cmp $10, %al |
| jae .high |
| .low: add $('0'), %al |
| jmp 2f |
| .high: add $('A'-10), %al |
| 2: |
| mov $0x0001, %bx |
| mov $0x0E, %ah |
| int $0x10 /* display a char */ |
| pop %eax |
| loop 1b |
| popal |
| ret |
| |
| /**************************************************************************/ |
| #ifdef STAGE1_5 |
| notification_string: .string "Loading stage1.5 " |
| #else |
| notification_string: .string "Loading stage2 " |
| #endif |
| |
| notification_step: .string "." |
| notification_done: .string "\r\n" |
| |
| read_error_string: .string "Read error 0x" |
| |
| /* |
| * EBIOS disk address packet |
| */ |
| .align 8 |
| dapa: .byte 16 /* Packet size */ |
| .byte 0 /* reserved */ |
| .word 0 /* +2 Block count */ |
| .word 0 /* +4 Offset of buffer */ |
| .word 0 /* +6 Segment of buffer */ |
| .long 0 /* +8 LBA (LSW) */ |
| .long 0 /* +C LBA (MSW) */ |
| |
| VARIABLE(BootDrive) |
| .byte 0xFF |
| VARIABLE(MaxTransfer) |
| .word 16 /* Max sectors per transfer (32Kb) */ |
| VARIABLE(RetryCount) |
| .byte 0 |
| |
| |
| /* |
| * This area is an empty space between the main body of code below which |
| * grows up (fixed after compilation, but between releases it may change |
| * in size easily), and the lists of sectors to read, which grows down |
| * from a fixed top location. |
| */ |
| |
| .word 0 |
| .word 0 |
| |
| . = _start + SECTOR_SIZE - BOOTSEC_LISTSIZE |
| |
| /* fill the first data listing with the default */ |
| blocklist_default_start:/* this is the sector start parameter, in logical |
| sectors from the start of the disk, sector 0 */ |
| .long 0 |
| |
| blocklist_default_len: /* this is the number of sectors to read */ |
| #ifdef STAGE1_5 |
| .word 0 |
| #else |
| .word (STAGE2_SIZE + ISO_SECTOR_SIZE - 1) >> ISO_SECTOR_BITS |
| #endif |
| blocklist_default_seg: /* this is the segment of the starting address |
| to load the data into */ |
| .word (STAGE_ADDR + SECTOR_SIZE) >> 4 |
| |
| firstlist: /* this label has to be after the list data!!! */ |