--- /dev/null
+/* $NetBSD: cdboot.S,v 1.12 2011/01/04 16:53:05 jakllsch 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 <machine/asm.h>
+#include <sys/bootblock.h>
+
+#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 <boot_params.S>
+ . = 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 decriptor 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 <message.S>
+
+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: