/* $NetBSD: cdboot.S,v 1.13 2021/12/05 02:47:01 msaitoh Exp $ */ /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Bang Jun-Young. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * This is a primary boot loader that loads a secondary boot loader * directly from CD without performing floppy/hard disk emulation as * described by the El Torito specification. */ #include #include #define BOOT_ADDR 0x7c00 #define BLOCK_SIZE 2048 /* Default for ISO 9660 */ #define VD_LBA 16 /* LBA of Volume Descriptor (VD) */ #define PVD_ADDR end /* Where Primary VD is loaded */ #define ROOTDIR_ADDR end+BLOCK_SIZE /* Where Root Directory is loaded */ #define LOADER_ADDR SECONDARY_LOAD_ADDRESS #ifdef BOOT_FROM_FAT #define MBR_AFTERBPB 90 /* BPB size in FAT32 partition BR */ #else #define MBR_AFTERBPB 62 /* BPB size in floppy master BR */ #endif /* * See src/sys/sys/bootblock.h for details. */ #define MBR_PART_COUNT 4 #define MBR_PART_OFFSET 446 #define MBR_PART_SIZE 16 /* sizeof(struct mbr_partition) */ /* * Disk error codes */ #define ERROR_TIMEOUT 0x80 /* * Volume Descriptor types. */ #define VD_PRIMARY 1 #define VD_SUPPLEMENTARY 2 #define VD_TERMINATOR 255 /* Only actually used entries are listed below */ /* * Format of Primary Volume Descriptor (8.4) */ #define PVD_ROOT_DR 156 /* Offset of Root Directory Record */ /* * Format of Directory Record (9.1) */ #define DR_LEN 0 #define DR_EXTENT 2 #define DR_DATA_LEN 10 #define DR_NAME_LEN 32 #define DR_NAME 33 .text .code16 ENTRY(start) jmp start1 . = start + MBR_AFTERBPB /* skip BPB */ . = start + MBR_DSN_OFFSET .long 0 /* mbr_bootsel_magic (not used here) */ . = start + MBR_BS_MAGIC_OFFSET .word 0 . = start + MBR_PART_OFFSET . = start + MBR_MAGIC_OFFSET pbr_magic: .word MBR_MAGIC .fill 512 /* reserve space for disklabel */ start1: jmp 1f .balign 4 .long X86_BOOT_MAGIC_1 /* checked by installboot & pbr code */ boot_params: /* space for patchable variables */ .long 1f - boot_params /* length of this data area */ #include . = start1 + 0x80 /* Space for patching unknown params */ 1: xorw %ax, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw $BOOT_ADDR, %sp movw %sp, %si movw $start, %di movw $BLOCK_SIZE/2, %cx rep movsw ljmp $0, $real_start real_start: movb %dl, boot_drive /* Save boot drive number */ #ifndef DISABLE_KEYPRESS /* * We can skip boot wait when: * - there's no hard disk present. * - there's no active partition in the MBR of the 1st hard disk. */ /* * Check presence of hard disks. */ movw $0x475, %si movb (%si), %al testb %al, %al jz boot_cdrom /* * Find the active partition from the MBR. */ movw $0x0201, %ax /* %al = number of sectors to read */ movw $BOOT_ADDR, %bx /* %es:%bx = data buffer */ movw $0x0001, %cx /* %ch = low 8 bits of cylinder no */ /* %cl = high 2 bits of cyl no & */ /* sector number */ movw $0x0080, %dx /* %dh = head number */ /* %dl = disk number */ int $0x13 /* Read MBR into memory */ jc boot_cdrom /* CF set on error */ movb $1, mbr_loaded movb $MBR_PART_COUNT, %cl movw $BOOT_ADDR+MBR_PART_OFFSET, %si 1: movb (%si), %al testb $0x80, %al jnz found_active addw $MBR_PART_SIZE, %si decb %cl testb %cl, %cl jnz 1b /* If 0, no active partition found */ jmp boot_cdrom found_active: movw $str_press_key, %si call message next_second: movw $str_dot, %si call message decb wait_count jz boot_hard_disk xorb %ah, %ah /* Get system time */ int $0x1a movw %dx, %di /* %cx:%dx = number of clock ticks */ addw $19, %di /* 19 ~= 18.2 Hz */ wait_key: movb $1, %ah /* Check for keystroke */ int $0x16 jz not_avail /* ZF clear if keystroke available */ xorb %ah, %ah /* Read key to flush keyboard buf */ int $0x16 jmp boot_cdrom not_avail: xorb %ah, %ah /* Get system time */ int $0x1a cmpw %dx, %di /* Compare with saved time */ jnz wait_key jmp next_second boot_hard_disk: movw $str_crlf, %si call message cmpb $1, mbr_loaded jz 1f movw $0x0201, %ax /* %al = number of sectors to read */ movw $BOOT_ADDR, %bx /* %es:%bx = data buffer */ movw $0x0001, %cx /* %ch = low 8 bits of cylinder no */ /* %cl = high 2 bits of cyl no & */ /* sector number */ movw $0x0080, %dx /* %dh = head number */ /* %dl = disk number */ int $0x13 /* Read MBR into memory */ jc panic /* CF set on error */ 1: movw %cs, %ax /* Restore initial state */ movw %ax, %ds movw %ax, %es movw $0x0080, %dx /* %dl = boot drive number */ jmp $0, $BOOT_ADDR /* Jump to MBR! */ jmp panic /* This should be never executed */ #endif /* !DISABLE_KEYPRESS */ boot_cdrom: movw $str_banner, %si call message /* Read volume descriptor sectors until Primary descriptor found */ movl $VD_LBA, %eax next_block: movb $1, %dh /* Number of sectors to read */ movl $PVD_ADDR, %ebx call read_sectors cmpb $VD_PRIMARY, (%bx) /* Is it Primary Volume Descriptor? */ jz pvd_found incl %eax cmpb $VD_TERMINATOR, (%bx) jnz next_block movw $str_no_pvd, %si call message jmp panic /* Read all of root directory */ pvd_found: movw $PVD_ADDR+PVD_ROOT_DR, %bx movl DR_EXTENT(%bx), %eax /* LBA of the root directory */ movl DR_DATA_LEN(%bx), %edx shrl $11, %edx /* Convert to number of sectors */ movb %dl, %dh /* ... and load it to %dh */ movl $ROOTDIR_ADDR, %ebx call read_sectors /* Scan directory entries searching for /boot */ next_entry: cmpb $0, DR_LEN(%bx) jz last_entry movw %bx, %si addw $DR_NAME, %si movb DR_NAME_LEN(%bx), %cl movw $str_loader, %di 1: movb (%si), %al cmpb %al, (%di) jnz fail incw %si incw %di decb %cl jnz 1b jmp load_loader fail: addw DR_LEN(%bx), %bx jmp next_entry last_entry: movw $str_no_loader, %si call message jmp panic /* Found /boot, read contents to 0x1000:0 */ load_loader: movl DR_EXTENT(%bx), %eax movl DR_DATA_LEN(%bx), %edx addl $(BLOCK_SIZE-1), %edx /* Convert file length to */ shrl $11, %edx /* ... number of sectors */ movb %dl, %dh movl $LOADER_ADDR, %ebx call read_sectors /* Finally call into code of /boot */ movl $boot_params, %esi /* Provide boot_params */ xorl %edx, %edx movb boot_drive, %dl xorl %ebx, %ebx /* Zero sector number */ lcall $LOADER_ADDR/16, $0 /* fall through on load failure */ panic: hlt jmp panic /* * Read disk sector(s) into memory * * %eax = LBA of starting sector * %ebx = buffer to store sectors * %dh = number of sectors to read * * Long transfers are split onto multiple 64k reads */ #define MAX_SECTORS (0x10000/BLOCK_SIZE) read_sectors: pushal movl %eax, edd_lba shrl $4, %ebx /* Convert buffer addr to seg:0 */ movw %bx, edd_segment 1: movb %dh, edd_nsecs cmpb $MAX_SECTORS, %dh jle 2f /* j if less than 64k */ movb $MAX_SECTORS, edd_nsecs /* Read 32 sectors - 64k bytes */ 2: movb boot_drive, %dl movw $edd_packet, %si read_again: movb $0x42, %ah push %dx /* bios shouldn't kill %dh, but ... */ int $0x13 pop %dx /* ... better safe than sorry! */ jc read_fail addw $0x1000, edd_segment /* Advance segment addr by 64k bytes */ addl $MAX_SECTORS, edd_lba /* And sector number to match */ sub edd_nsecs, %dh /* Number of sectors remaining */ jnz 1b popal ret read_fail: cmpb $ERROR_TIMEOUT, %ah jz read_again movw $str_read_error, %si call message jmp panic #include edd_packet: edd_len: .word 16 edd_nsecs: .word 0 /* Number of sectors to transfer */ edd_offset: .word 0 edd_segment: .word 0 edd_lba: .quad 0 wait_count: .byte 6 boot_drive: .byte 0 mbr_loaded: .byte 0 str_banner: .ascii "\r\nNetBSD/x86 cd9660 Primary Bootstrap" str_crlf: .asciz "\r\n" str_press_key: .asciz "\r\nPress any key to boot from CD" str_dot: .asciz "." str_read_error: .asciz "Can't read CD" str_no_pvd: .asciz "Can't find Primary Volume Descriptor" str_no_loader: .asciz "Can't find /boot" str_loader: .asciz "BOOT.;1" /* Used to calculate free bytes */ free_space = end - . . = start + BLOCK_SIZE end: