From 7d044ed089b88dcf2975af28013b1a923a2c436f Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Thu, 9 Jul 2020 15:53:44 +0100 Subject: [PATCH] Add support for USB legacy boot on hybrid ISO. --- .gitignore | 1 + boot/mbr.S | 179 ++++++++++++++++++++++++++++++ build32/Makefile | 7 +- build32/ldscripts/memtest_mbr.lds | 16 +++ build64/Makefile | 7 +- build64/ldscripts/memtest_mbr.lds | 16 +++ 6 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 boot/mbr.S create mode 100644 build32/ldscripts/memtest_mbr.lds create mode 100644 build64/ldscripts/memtest_mbr.lds diff --git a/.gitignore b/.gitignore index 0caab12..57f6761 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ memtest_shared.bin *.efi *.img *.iso +*.mbr diff --git a/boot/mbr.S b/boot/mbr.S new file mode 100644 index 0000000..756a934 --- /dev/null +++ b/boot/mbr.S @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mbr.S supports booting directly from the BIOS when a memtest binary image is +// stored on a hard disk or USB stick. When booted by the BIOS, it is loaded +// at address 0x7c00. It then loads the setup code immediately after itself +// (address 0x7e00) and the main program code at segment MAIN_SEG, using BIOS +// interrupts to read the data from disk. It locates the start of the memtest +// image containing the setup and main program code from the LBA stored at +// offset 0x1b0. The LBA value is assumed to be offset by 4, for compatibility +// with the xorrisofs --grub2-mbr option. +// +// The first 512B of the memtest binary image is not used, so either of the +// memtest.bin or memtest.efi images may be used. +// +// Copyright (C) 2020 Martin Whitaker. + +#define __ASSEMBLY__ + +#include "boot.h" + + .section ".mbr", "ax", @progbits + .code16 + +# The BIOS boot entry point. This will be located at 0x7c00. + + .globl boot +boot: + # Initialise the segment registers and the stack. + + ljmp $BOOT_SEG, $init +init: + movw %cs, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw $BOOT_STACK_TOP, %ax + movw %ax, %sp + + # Check we have a valid drive number. If not, assume we are + # booting from the first hard drive. + + testb $0x80, %dl + jz 0f + testb $0x70, %dl + jz 1f +0: movb $0x80, %dl +1: + + # Load the setup code. + + movw $SETUP_SECS, dap_length + movw $SETUP_SEG, dap_segment + movl image_base, %eax + subl $3, %eax + movl %eax, dap_lba_start + movw $dap, %si + movb $0x42, %ah + int $0x13 + jc load_error + + # Print a message. + + pushw %dx + + movb $0x03, %ah # read cursor pos + xorb %bh, %bh + int $0x10 + + leaw boot_msg, %bp + movw $(boot_msg_end - boot_msg), %cx + movw $0x0007, %bx # page 0, attribute 7 (normal) + movw $0x1301, %ax # write string, move cursor + int $0x10 + + popw %dx + + # Load the main test program. + + movw $MAIN_SEG, dap_segment + addl $SETUP_SECS, dap_lba_start + movw $_sys_size, %cx # length in 16B chunks + addw $31, %cx # convert to sectors (rounding up) + shrw $5, %cx +0: movw $64, %bx # load either 64 sectors or remaining + cmpw %bx, %cx # length if less than 64 sectors + ja 1f + movw %cx, %bx +1: movw %bx, dap_length + movw $dap, %si + movb $0x42, %ah + int $0x13 + jc load_error + addw $0x800, dap_segment + addl $0x40, dap_lba_start + subw %bx, %cx + jg 0b + + # Turn off the floppy drive motor. + + movw $0x3f2, %dx + xorb %al, %al + outb %al, %dx + + # Turn off the text display cursor. + + movb $0x01, %ah + movb $0x00, %bh + movw $0x2000, %cx + int $0x10 + + # Fix up the Linux boot header to indicate we've loaded into low memory. + + movl $LOW_LOAD_ADDR, 0x214 + + # After that (everything loaded), we jump to the setup code loaded + # directly after the boot block. + + ljmp $SETUP_SEG, $0 + +load_error: + movb %ah, %al + call hex_to_ascii + movb %al, error_msg+1 + movb %ah, %al + shrb $4, %al + call hex_to_ascii + movb %al, error_msg+0 + + movb $0x03, %ah # read cursor pos + xorb %bh, %bh + int $0x10 + + leaw error_msg, %bp + movw $(error_msg_end - error_msg), %cx + movw $0x0007, %bx # page 0, attribute 7 (normal) + movw $0x1301, %ax # write string, move cursor + int $0x10 + +0: hlt + jmp 0b + + +hex_to_ascii: + andb $0xf, %al + addb $'0', %al + cmpb $'9', %al + jle 1f + addb $('A' - '0' - 10), %al +1: ret + +# Local variables. + +dap: + .byte 0x10 + .byte 0 +dap_length: + .word 0 +dap_offset: + .word 0 +dap_segment: + .word 0 +dap_lba_start: + .quad 0 + +boot_msg: + .ascii "Loading PCMemTest\r\n" +boot_msg_end: + +error_msg: + .ascii " Disk read failed\r\n" +error_msg_end: + + .org 0x1b0 +image_base: + .quad 5 # default to sector 1, offset by 4 + + .org 0x1fe +boot_flag: + .word 0xAA55 diff --git a/build32/Makefile b/build32/Makefile index 582075c..206dfb7 100644 --- a/build32/Makefile +++ b/build32/Makefile @@ -108,6 +108,9 @@ memtest.efi: memtest_shared.bin boot/header.o boot/setup.o ldscripts/memtest_efi $(eval SIZES=$(shell size -G -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_efi.lds boot/header.o boot/setup.o -b binary memtest_shared.bin -o memtest.efi +memtest.mbr: memtest_shared.bin boot/mbr.o ldscripts/memtest_mbr.lds + $(LD) -T ldscripts/memtest_mbr.lds boot/mbr.o -b binary memtest_shared.bin -o memtest.mbr + floppy.img: memtest.bin dd if=/dev/zero of=floppy.img bs=1474560 count=1 dd if=memtest.bin of=floppy.img bs=1474560 conv=notrunc @@ -119,10 +122,10 @@ esp.img: memtest.efi /sbin/mkdosfs -n MEMTEST-ESP -F12 -C esp.img 4096 mcopy -s -i esp.img iso/EFI :: -iso: floppy.img esp.img +iso: memtest.mbr floppy.img esp.img @mkdir -p iso/boot cp floppy.img iso/boot/floppy.img - xorrisofs -pad -R -J -volid PCMemTest32 -graft-points -hide-rr-moved \ + xorrisofs -pad -R -J -volid PCMemTest32 -graft-points -hide-rr-moved --grub2-mbr memtest.mbr \ -b /boot/floppy.img --efi-boot --interval:appended_partition_2:all:: \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./esp.img \ -o ./memtest.iso /boot=./iso/boot diff --git a/build32/ldscripts/memtest_mbr.lds b/build32/ldscripts/memtest_mbr.lds new file mode 100644 index 0000000..b28976d --- /dev/null +++ b/build32/ldscripts/memtest_mbr.lds @@ -0,0 +1,16 @@ +OUTPUT_FORMAT("binary") +OUTPUT_ARCH("i386") + +ENTRY(boot); +SECTIONS { + . = 0; + .mbr : { + *(.mbr) + } + .memtest (NOLOAD) : { + _start = . ; + *(.data) + _end = . ; + } + _sys_size = (_end - _start + 15) >> 4; +} diff --git a/build64/Makefile b/build64/Makefile index 312e37e..0271bd0 100644 --- a/build64/Makefile +++ b/build64/Makefile @@ -107,6 +107,9 @@ memtest.efi: memtest_shared.bin boot/header.o boot/setup.o ldscripts/memtest_efi $(eval SIZES=$(shell size -G -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_efi.lds boot/header.o boot/setup.o -b binary memtest_shared.bin -o memtest.efi +memtest.mbr: memtest_shared.bin boot/mbr.o ldscripts/memtest_mbr.lds + $(LD) -T ldscripts/memtest_mbr.lds boot/mbr.o -b binary memtest_shared.bin -o memtest.mbr + floppy.img: memtest.bin dd if=/dev/zero of=floppy.img bs=1474560 count=1 dd if=memtest.bin of=floppy.img bs=1474560 conv=notrunc @@ -118,10 +121,10 @@ esp.img: memtest.efi /sbin/mkdosfs -n MEMTEST-ESP -F12 -C esp.img 4096 mcopy -s -i esp.img iso/EFI :: -iso: floppy.img esp.img +iso: memtest.mbr floppy.img esp.img @mkdir -p iso/boot cp floppy.img iso/boot/floppy.img - xorrisofs -pad -R -J -volid PCMemTest64 -graft-points -hide-rr-moved \ + xorrisofs -pad -R -J -volid PCMemTest64 -graft-points -hide-rr-moved --grub2-mbr memtest.mbr \ -b /boot/floppy.img --efi-boot --interval:appended_partition_2:all:: \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./esp.img \ -o ./memtest.iso /boot=./iso/boot diff --git a/build64/ldscripts/memtest_mbr.lds b/build64/ldscripts/memtest_mbr.lds new file mode 100644 index 0000000..8d4dbb7 --- /dev/null +++ b/build64/ldscripts/memtest_mbr.lds @@ -0,0 +1,16 @@ +OUTPUT_FORMAT("binary") +OUTPUT_ARCH(i386:x86-64) + +ENTRY(boot); +SECTIONS { + . = 0; + .mbr : { + *(.mbr) + } + .memtest (NOLOAD) : { + _start = . ; + *(.data) + _end = . ; + } + _sys_size = (_end - _start + 15) >> 4; +}