From 0f57ed16848adf9be50f485109de63d46d8d0378 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Tue, 20 Mar 2012 02:56:34 +0000 Subject: [PATCH] [mbr] "Press any key to boot from USB..." * Adds an MBR to prompt the use about USB or HDD boot * If USB boot is not selected, an INT13h override is added to make the second bootable drive think it is first (0x81 -> 0x80) * USB drive BIOS index is left unmodified --- res/mbr/Makefile | 60 +++++++++ res/mbr/mbr.S | 333 +++++++++++++++++++++++++++++++++++++++++++++++ res/mbr/mbr.bin | Bin 0 -> 512 bytes res/mbr/mbr.ld | 7 + src/rufus.rc | 12 +- 5 files changed, 406 insertions(+), 6 deletions(-) create mode 100644 res/mbr/Makefile create mode 100644 res/mbr/mbr.S create mode 100644 res/mbr/mbr.bin create mode 100644 res/mbr/mbr.ld diff --git a/res/mbr/Makefile b/res/mbr/Makefile new file mode 100644 index 00000000..4e6067d1 --- /dev/null +++ b/res/mbr/Makefile @@ -0,0 +1,60 @@ +# +# This file is part of the Rufus project. +# +# Copyright (c) 2012 Pete Batard +# +# 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 3 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, see . +# + +OBJECTS = mbr.o +TARGET = mbr + +ASM = gcc +CC = gcc +LD = ld +OBJDUMP = objdump +OBJCOPY = objcopy +CFLAGS = -m32 +LDFLAGS = -nostartfile + +.PHONY: all clean + +all: $(TARGET).bin + +clean: + @-rm -f -v *.o *.out $(TARGET).map + +%.o: %.c Makefile + @echo "[CC] $@" + @$(CC) -c -o $*.o $(CFLAGS) $< + +%.o: %.S Makefile + @echo "[AS] $<" + @$(ASM) -c -o $*.o $(CFLAGS) $< + +# Produce a disassembly dump, for verification purposes +dis: $(TARGET).out + @echo "[DIS] $<" + @$(OBJCOPY) -O binary -j .main --set-section-flags .main=alloc,load,readonly,code $< main.bin + @$(OBJDUMP) -D -bbinary -mi8086 -Mintel main.bin | less + @-rm -f main.bin + +$(TARGET).out: $(OBJECTS) $(TARGET).ld + @echo "[LD] $@" + @$(LD) $(LDFLAGS) -T$(TARGET).ld -o $@ $(OBJECTS) -Map $(TARGET).map + +$(TARGET).bin: $(TARGET).out + @echo "[MBR] $@" + @# Note: -j only works for sections that have the 'ALLOC' flag set + @$(OBJCOPY) -O binary -j .main --gap-fill=0x00 $< $@ diff --git a/res/mbr/mbr.S b/res/mbr/mbr.S new file mode 100644 index 00000000..c263e414 --- /dev/null +++ b/res/mbr/mbr.S @@ -0,0 +1,333 @@ +/********************************************************************************/ +/* Rufus - The Reliable USB Formatting Utility, bootable USB MBR */ +/* */ +/* Copyright (c) 2011 Pete Batard */ +/* */ +/* 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 3 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, see . */ +/* */ +/********************************************************************************/ + + +/********************************************************************************/ +/* GNU Assembler Settings: */ +/********************************************************************************/ +.intel_syntax noprefix # Use Intel assembler syntax (same as IDA Pro) +.code16 # MBR code is executed in x86 CPU real/16 bit mode +/********************************************************************************/ + +/********************************************************************************/ +/* Constants: */ +/********************************************************************************/ +DOT_TIMEOUT = 0x12 # Number of RTC interrupts for ~ 1s +DOT_NUMBER = 0x04 # Number of dots to be printed before timeout +MBR_ADDR = 0x7c00 # Same address for both original MBR and copy +MBR_SIZE = 0x200 +MBR_RESERVED = 0x1b8 # Start of the reserved section (partition table, etc.) +BUF_SEGMENT = 0x3000 # Segment for the buffer +PT_MAX = 0x04 # Number of partition entries in the partition table +PT_ENTRY_SIZE = 0x10 # Size of a partition entry in the partition table +INT_RTC = 0x08 +INT_DSK = 0x13 + +/********************************************************************************/ +/* mbr: this section must reside at 0x00007c00, and be exactly 512 bytes */ +/********************************************************************************/ +.section main, "ax" +.globl mbr # label must be declared global for the linker +mbr: +# We must be able to reuse the original memory location that the BIOS copied +# the MBR to (0x00007c000), so our first task is to copy our MBR data to another +# location, and run our code from there + inc cx # Register fixup + dec bx + inc bp + dec di + cld + xor ax, ax + cli # Disable interrupts when fiddling with the stack + mov ss, ax # First order of the day it to set up the stack + mov sp, MBR_ADDR # This places the stack right before the original MBR + sti + mov si, sp # While we have that value handy + mov di, sp + push ds # Now that we have a stack... + push es + mov ds, ax # Original MBR is in segment 0 + mov bx, 0x0413 # McAfee thinks we are a virus if we use 413 directly... + mov ax,[bx+0] + dec ax # Remove 1KB from the RAM for our copy + mov [bx+0],ax + shl ax, 6 # Convert to segment address + sub ax, MBR_ADDR>>4 # For convenience of disassembly, so that irrespective + mov es, ax # of the segment, our base address remains MBR_ADDR + mov cx, MBR_SIZE # Move our code out of the way + rep movsb + push ax # Push the CS:IP for our retf + push offset 0f + retf + +# --------------------------------------------------------------------------- + +# From this point forward, we are running the copy at a different segment from seg 0 +0: mov ds, ax # AX = ES = CS, only DS points back to old seg => fix this + push es + xor ax, ax + mov es, ax + mov bx, MBR_ADDR # TODO optimize + mov ax, 0x0201 + mov cx, 0x0001 + mov dx, 0x0081 # Check next bootable disk + int 0x13 # DISK - READ SECTORS INTO MEMORY + # AL = number of sectors to read, CH = track, CL = sector + # DH = head, DL = drive, ES:BX -> buffer to fill + # Return: CF set on error, AH = status, AL = number of sectors read + jnb read_success + +read_failure: # If we couldn't get data for second bootable, just boot USB + pop es + jmp boot_usb_no_rtc + +read_success: + mov cx, PT_MAX + mov bp, offset partition_table + +check_table: # check the partition table for an active (bootable) partition + cmpb es:[bp+0], 0x00 + jl active_partition # 0x80 or greater means the partition is active + jnz invalid_table # anything between 0x01 and 0x7f is invalid + add bp, PT_ENTRY_SIZE # next partition + loop check_table + +no_active: + pop es + jmp exit + +active_partition: + push bp + +check_one_active: # check that all subsequent partitions are zero (only one active) + add bp, PT_ENTRY_SIZE + dec cx + jz valid_active + cmpb es:[bp+0], 0x00 + jz check_one_active + pop bp + +invalid_table: + pop es + jmp exit + +valid_active: + pop bp + pop es + mov si, offset prompt_string + call print_string + call flush_keyboard + +set_rtc_int_vect: # Set the interrupt vector for CMOS real-time clock + mov dx, offset rtc_interrupt + mov si, offset rtc_interrupt_org + call set_int_vect + +wait_for_keyboard: + mov ah, 0x01 + int 0x16 # KEYBOARD - CHECK BUFFER, DO NOT CLEAR + jnz boot_usb # Z is clear when characters are present in the buffer + mov ah, 0x02 + int 0x16 # KEYBOARD - GET SHIFT STATUS + and al, 0x04 # AL = shift status bits + jnz boot_usb + cmpw ds:counter_dot, 0x0000 + jg short check_timeout + +print_dot: + mov si, offset dot_string + call print_string + movw ds:counter_dot, DOT_TIMEOUT + +check_timeout: + cmpw ds:counter_timeout, 0x0000 + jnz wait_for_keyboard + +boot_fixed_disk: # Timeout occured => boot second bootable disk (non USB) + call restore_rtc_vect # Remove our RTC override + mov dx, offset dsk_interrupt # Set interrupt override to have + mov si, offset dsk_interrupt_org # disk 0x81 is seen as 0x80 + call set_int_vect + +boot_drive: + pop es + pop ds + mov dl, 0x0080 # In both case, we pretend the disk is the first bootable + jmp 0:MBR_ADDR + +# --------------------------------------------------------------------------- + +boot_usb: # Boot USB drive (0x80) + call restore_rtc_vect # Remove our RTC override + call flush_keyboard # Make sure the keyboard buffer is clear +boot_usb_no_rtc: + mov bx, offset partition_table + mov dx, es:[bx] + mov cx, es:[bx+2] + xor ax, ax + mov es, ax # make sure ES is set to seg 0 for copy + mov ax, 0x0201 + mov bx, MBR_ADDR + int 0x13 + jnb boot_drive +exit: + pop es + pop ds + retf + + +/********************************************************************************/ +/* Subroutines */ +/********************************************************************************/ + +set_int_vect: # Set the interrupt vector + cli # SI = pointer to backup vector (must contain the interrupt #) + push es # DX = pointer to interrupt + xor ax, ax + mov es, ax + mov bx, ds:[si] + mov eax, es:[bx] # Backup the original vector + mov ds:[si], eax + mov es:[bx], dx + mov es:[bx+2], cs + pop es + sti + ret + +# --------------------------------------------------------------------------- + +restore_rtc_vect: # Restore the interrupt vector for RTC + cli + push es + xor ax, ax + mov es, ax + mov bx, INT_RTC*4 + mov eax, ds:rtc_interrupt_org + mov es:[bx], eax + pop es + sti + ret + +# --------------------------------------------------------------------------- + +flush_keyboard: # Flush the keyboard buffer + mov ah, 0x01 + int 0x16 # KEYBOARD - CHECK BUFFER, DO NOT CLEAR + jz 0f # Z is set if no character in buffer + mov ah, 0x00 + int 0x16 # KEYBOARD - READ CHAR FROM BUFFER + loop flush_keyboard +0: ret + +# --------------------------------------------------------------------------- + +print_string: # Print a NUL terminated string to console + push ax + push bx + +ps_putchar: + lodsb + cmp al, 0x00 + jz ps_exit + mov ah, 0x0e + mov bx, 0x0007 # BH = display page, BL = foreground color + int 0x10 # VIDEO - WRITE CHARACTER AND ADVANCE CURSOR + jmp ps_putchar + +ps_exit: + pop bx + pop ax + ret + +# --------------------------------------------------------------------------- + +disk_swap: # Swap disks 0x80 and 0x81 + push dx + and dl, 0xfe + cmp dl, 0x80 + pop dx + jne 0f + xor dl, 0x01 +0: ret + + +/********************************************************************************/ +/* Interrupt overrides */ +/********************************************************************************/ + +# RTC (INT 8) interrupt override +rtc_interrupt: + pushf + cli + cmpw cs:counter_timeout, 0x0000 + jz rtc_exec_org + decw cs:counter_dot + decw cs:counter_timeout + +rtc_exec_org: +rtc_interrupt_org = .+1 # Same trick used by the LILO mapper + call 0:INT_RTC*4 # These CS:IP values will be changed at runtime + iret + +# --------------------------------------------------------------------------- + +# DISK (INT 13h) interrupt override +dsk_interrupt: + pushf + cli + call disk_swap + mov cs:[int13_cmd], ah # Keep a copy of the command for later referal + +dsk_interrupt_org = .+1 + call 0:INT_DSK*4 # These CS:IP values will be changed at runtime + push ax + mov ah, cs:[int13_cmd] # Check the original command for swap exceptions + cmp ah, 0x08 # LILO's mapper has these 2 exceptions + je 0f + cmp ah, 0x15 + je 0f + call disk_swap +0: pop ax + iret + + +/********************************************************************************/ +/* Data section */ +/********************************************************************************/ + +int13_cmd: .byte 0x00 +prompt_string: .string "\r\nPress any key to boot from USB." +dot_string = .-2 # Reuse the end of previous string +counter_timeout:.word DOT_NUMBER*DOT_TIMEOUT + 1 +counter_dot: .word DOT_TIMEOUT + + +/********************************************************************************/ +/* From offset 0x1b8, the MBR contains the partition table and signature data */ +/********************************************************************************/ + .org MBR_RESERVED +disk_signature: + .space 0x04 +filler: + .space 0x02 +partition_table: + .space PT_ENTRY_SIZE * PT_MAX +mbr_signature: + .word 0xAA55 diff --git a/res/mbr/mbr.bin b/res/mbr/mbr.bin new file mode 100644 index 0000000000000000000000000000000000000000..ff54ede88d9d484e410fbc2c6da2839b24c956bc GIT binary patch literal 512 zcmZ?tcJ=>bc;Hvxg*^;4zdN6GK9^(byRloCrJLQOll|ZWHr)g4eFt_jFnwMUkYP}B zy6*;?;eoybyBTVBFf#3AWZ2cna8|gOh5cnC!%h~4z58m_8tNDrYPd@!nvV)R`UDaW z1+qL#xWN)7pJUly=f|?|Ypi{-gyF>$hFwmz`@(Bq#4&7PJS$dezJ=+mmHqQ(UdbpNakVVTihttp://rufus.akeo.ie",IDC_ABOUT_RUFUS_URL, "SysLink",WS_TABSTOP,46,47,114,9 - LTEXT "Version 1.1.6 (Build 154)",IDC_STATIC,46,19,78,8 + LTEXT "Version 1.1.6 (Build 155)",IDC_STATIC,46,19,78,8 PUSHBUTTON "License...",IDC_ABOUT_LICENSE,46,175,50,14,WS_GROUP EDITTEXT IDC_ABOUT_COPYRIGHTS,46,107,235,63,ES_MULTILINE | ES_READONLY | WS_VSCROLL LTEXT "Report bugs or request enhancements at:",IDC_STATIC,46,66,187,8 @@ -223,8 +223,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,1,6,154 - PRODUCTVERSION 1,1,6,154 + FILEVERSION 1,1,6,155 + PRODUCTVERSION 1,1,6,155 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -241,13 +241,13 @@ BEGIN BEGIN VALUE "CompanyName", "akeo.ie" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.1.6.154" + VALUE "FileVersion", "1.1.6.155" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.1.6.154" + VALUE "ProductVersion", "1.1.6.155" END END BLOCK "VarFileInfo"