1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

[mbr] add LBA sector readout, as required by some BIOSes

* I'm looking at you, Dell Optiplex!
* also fix boot in case no secondary bootable drive exists
* also adds a readme
This commit is contained in:
Pete Batard 2012-03-20 20:31:54 +00:00
parent 0f57ed1684
commit 1b7f88eb99
4 changed files with 159 additions and 94 deletions

View file

@ -1,7 +1,7 @@
/********************************************************************************/ /********************************************************************************/
/* Rufus - The Reliable USB Formatting Utility, bootable USB MBR */ /* Rufus - The Reliable USB Formatting Utility, bootable USB MBR */
/* */ /* */
/* Copyright (c) 2011 Pete Batard <pete@akeo.ie> */ /* Copyright (c) 2012 Pete Batard <pete@akeo.ie> */
/* */ /* */
/* This program is free software; you can redistribute it and/or modify it */ /* 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 */ /* under the terms of the GNU General Public License as published by the Free */
@ -26,6 +26,7 @@
.code16 # MBR code is executed in x86 CPU real/16 bit mode .code16 # MBR code is executed in x86 CPU real/16 bit mode
/********************************************************************************/ /********************************************************************************/
/********************************************************************************/ /********************************************************************************/
/* Constants: */ /* Constants: */
/********************************************************************************/ /********************************************************************************/
@ -34,14 +35,14 @@ DOT_NUMBER = 0x04 # Number of dots to be printed before timeout
MBR_ADDR = 0x7c00 # Same address for both original MBR and copy MBR_ADDR = 0x7c00 # Same address for both original MBR and copy
MBR_SIZE = 0x200 MBR_SIZE = 0x200
MBR_RESERVED = 0x1b8 # Start of the reserved section (partition table, etc.) 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_MAX = 0x04 # Number of partition entries in the partition table
PT_ENTRY_SIZE = 0x10 # Size of a partition entry in the partition table PT_ENTRY_SIZE = 0x10 # Size of a partition entry in the partition table
INT_RTC = 0x08 INT_RTC = 0x08
INT_DSK = 0x13 INT_DSK = 0x13
/********************************************************************************/ /********************************************************************************/
/* mbr: this section must reside at 0x00007c00, and be exactly 512 bytes */ /* MBR: this section must reside at 0x00007c00, and be exactly 512 bytes */
/********************************************************************************/ /********************************************************************************/
.section main, "ax" .section main, "ax"
.globl mbr # label must be declared global for the linker .globl mbr # label must be declared global for the linker
@ -65,9 +66,9 @@ mbr:
push es push es
mov ds, ax # Original MBR is in segment 0 mov ds, ax # Original MBR is in segment 0
mov bx, 0x0413 # McAfee thinks we are a virus if we use 413 directly... mov bx, 0x0413 # McAfee thinks we are a virus if we use 413 directly...
mov ax,[bx+0] mov ax,[bx]
dec ax # Remove 1KB from the RAM for our copy dec ax # Remove 1KB from the RAM for our copy
mov [bx+0],ax mov [bx],ax
shl ax, 6 # Convert to segment address shl ax, 6 # Convert to segment address
sub ax, MBR_ADDR>>4 # For convenience of disassembly, so that irrespective 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 es, ax # of the segment, our base address remains MBR_ADDR
@ -79,60 +80,33 @@ mbr:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# From this point forward, we are running the copy at a different segment from seg 0 # From this point forward, we are running the copy at the same base but different segment
0: mov ds, ax # AX = ES = CS, only DS points back to old seg => fix this 0: mov ds, ax # AX = ES = CS, only DS points back to old seg => fix this
push es push 0
xor ax, ax pop es # ES remains set to segment 0 from here on
mov es, ax xor ebx, ebx # Sector #1 in 64 bit address mode (#0)
mov bx, MBR_ADDR # TODO optimize mov cx, 0x0001 # Sector #1 in CHS address mode (#1)
mov ax, 0x0201 mov dx, 0x0081 # drive number (DL), track 0 (DH)
mov cx, 0x0001 call read_sector
mov dx, 0x0081 # Check next bootable disk jb boot_usb # If we couldn't get data => just boot USB
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: read_success:
mov bx, offset partition_table
mov cx, PT_MAX mov cx, PT_MAX
mov bp, offset partition_table
check_table: # check the partition table for an active (bootable) partition check_table: # check the partition table for an active (bootable) partition
cmpb es:[bp+0], 0x00 cmpb es:[bx], 0x00
jl active_partition # 0x80 or greater means the partition is active jl found_active # 0x80 or greater means the partition is active
jnz invalid_table # anything between 0x01 and 0x7f is invalid jnz invalid_table # anything between 0x01 and 0x7f is invalid
add bp, PT_ENTRY_SIZE # next partition add bx, PT_ENTRY_SIZE # next partition
loop check_table 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: invalid_table:
pop es jmp boot_usb
jmp exit
valid_active: found_active:
pop bp
pop es
mov si, offset prompt_string mov si, offset prompt_string
call print_string call print_string # Prompt the user
call flush_keyboard call flush_keyboard
set_rtc_int_vect: # Set the interrupt vector for CMOS real-time clock set_rtc_int_vect: # Set the interrupt vector for CMOS real-time clock
@ -143,21 +117,21 @@ set_rtc_int_vect: # Set the interrupt vector for CMOS real-time clock
wait_for_keyboard: wait_for_keyboard:
mov ah, 0x01 mov ah, 0x01
int 0x16 # KEYBOARD - CHECK BUFFER, DO NOT CLEAR int 0x16 # KEYBOARD - CHECK BUFFER, DO NOT CLEAR
jnz boot_usb # Z is clear when characters are present in the buffer jnz boot_usb_rem_rtc # Z is clear when characters are present in the buffer
mov ah, 0x02 mov ah, 0x02
int 0x16 # KEYBOARD - GET SHIFT STATUS int 0x16 # KEYBOARD - GET SHIFT STATUS
and al, 0x04 # AL = shift status bits and al, 0x04 # AL = shift status bits
jnz boot_usb jnz boot_usb
cmpw ds:counter_dot, 0x0000 cmpb ds:counter_dot, 0x00
jg short check_timeout jg short check_timeout
print_dot: print_dot: # Every so often, we print a dot
mov si, offset dot_string mov si, offset dot_string
call print_string call print_string
movw ds:counter_dot, DOT_TIMEOUT movb ds:counter_dot, DOT_TIMEOUT
check_timeout: check_timeout:
cmpw ds:counter_timeout, 0x0000 cmpb ds:counter_timeout, 0x00
jnz wait_for_keyboard jnz wait_for_keyboard
boot_fixed_disk: # Timeout occured => boot second bootable disk (non USB) boot_fixed_disk: # Timeout occured => boot second bootable disk (non USB)
@ -169,25 +143,23 @@ boot_fixed_disk: # Timeout occured => boot second bootable disk (non USB)
boot_drive: boot_drive:
pop es pop es
pop ds pop ds
mov dl, 0x0080 # In both case, we pretend the disk is the first bootable mov dx, 0x0080 # In both case, we pretend the disk is the first bootable
jmp 0:MBR_ADDR jmp 0:MBR_ADDR
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
boot_usb: # Boot USB drive (0x80) boot_usb_rem_rtc: # Boot USB drive (0x80)
call restore_rtc_vect # Remove our RTC override call restore_rtc_vect # Remove our RTC override
boot_usb:
call flush_keyboard # Make sure the keyboard buffer is clear call flush_keyboard # Make sure the keyboard buffer is clear
boot_usb_no_rtc:
mov bx, offset partition_table mov bx, offset partition_table
mov dx, es:[bx] mov dx, ds:[bx]
mov cx, es:[bx+2] mov cx, ds:[bx+2]
xor ax, ax mov ebx, ds:[bx+8] # Must come last since it modifies BX
mov es, ax # make sure ES is set to seg 0 for copy call read_sector
mov ax, 0x0201
mov bx, MBR_ADDR
int 0x13
jnb boot_drive jnb boot_drive
exit:
exit: # failed to read PBR from USB - exit back to BIOS
pop es pop es
pop ds pop ds
retf retf
@ -197,17 +169,56 @@ exit:
/* Subroutines */ /* Subroutines */
/********************************************************************************/ /********************************************************************************/
read_sector: # Read a single sector in either CHS or LBA mode
# EBX = LBA sector address (32 bit), CH = track
# CL = sector, DL = drive number, DH = head
pusha # save all registers
mov ah, 0x41
mov bx, 0x55aa
int 0x13
jb no_ext # failure to get ext
cmp bx, 0xaa55
jnz no_ext
test cx, 1 # is packet access supported?
jz no_ext
ext: # http://en.wikipedia.org/wiki/INT_13H#INT_13h_AH.3D42h:_Extended_Read_Sectors_From_Drive
popa
push ds
xor eax, eax
mov ds, ax # We'll use the stack for DAP, which is in seg 0
push eax # bits 32-63 of sector address (set to 0)
push ebx # bits 0-31 of sector address (EBX)
push ax # destination segment
push MBR_ADDR # destination address
inc ax
push ax # number of sectors to be read (1)
push 0x0010 # size of DAP struct
mov si, sp # DAP address (= stack)
mov ah, 0x42 # Extended Read Sectors From Drive
int 0x13
lahf
add sp,0x10
sahf
pop ds
ret
no_ext: # http://en.wikipedia.org/wiki/INT_13H#INT_13h_AH.3D02h:_Read_Sectors_From_Drive
popa
mov bx, MBR_ADDR # CL, CH, DL, DH, ES are already set
mov ax, 0x0201
int 0x13
ret
# ---------------------------------------------------------------------------
set_int_vect: # Set the interrupt vector set_int_vect: # Set the interrupt vector
cli # SI = pointer to backup vector (must contain the interrupt #) 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 bx, ds:[si]
mov eax, es:[bx] # Backup the original vector mov eax, es:[bx] # Backup the original vector
mov ds:[si], eax mov ds:[si], eax
mov es:[bx], dx mov es:[bx], dx
mov es:[bx+2], cs mov es:[bx+2], cs
pop es
sti sti
ret ret
@ -215,13 +226,9 @@ set_int_vect: # Set the interrupt vector
restore_rtc_vect: # Restore the interrupt vector for RTC restore_rtc_vect: # Restore the interrupt vector for RTC
cli cli
push es
xor ax, ax
mov es, ax
mov bx, INT_RTC*4 mov bx, INT_RTC*4
mov eax, ds:rtc_interrupt_org mov eax, ds:rtc_interrupt_org
mov es:[bx], eax mov es:[bx], eax
pop es
sti sti
ret ret
@ -238,23 +245,15 @@ flush_keyboard: # Flush the keyboard buffer
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
print_string: # Print a NUL terminated string to console print_string: # Print NUL terminated string in DS:SI to console
push ax
push bx
ps_putchar:
lodsb lodsb
cmp al, 0x00 cmp al, 0x00
jz ps_exit jz 0f
mov ah, 0x0e mov ah, 0x0e
mov bx, 0x0007 # BH = display page, BL = foreground color mov bx, 0x0007 # BH = display page, BL = foreground color
int 0x10 # VIDEO - WRITE CHARACTER AND ADVANCE CURSOR int 0x10 # VIDEO - WRITE CHARACTER AND ADVANCE CURSOR
jmp ps_putchar jmp print_string
0: ret
ps_exit:
pop bx
pop ax
ret
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -267,6 +266,23 @@ disk_swap: # Swap disks 0x80 and 0x81
xor dl, 0x01 xor dl, 0x01
0: ret 0: ret
# ---------------------------------------------------------------------------
.if 0
print_hex: # Hex dump of the word at address ES:BX
mov cx, 0x04
mov dx, es:[bx]
0: rol dx, 0x04
mov ax, 0xe0f
and al, dl
daa
add al, 0xF0
adc al, 0x40
int 0x10
loop 0b
ret
.endif
/********************************************************************************/ /********************************************************************************/
/* Interrupt overrides */ /* Interrupt overrides */
@ -276,10 +292,10 @@ disk_swap: # Swap disks 0x80 and 0x81
rtc_interrupt: rtc_interrupt:
pushf pushf
cli cli
cmpw cs:counter_timeout, 0x0000 cmpb cs:counter_timeout, 0x00
jz rtc_exec_org jz rtc_exec_org
decw cs:counter_dot decb cs:counter_dot
decw cs:counter_timeout decb cs:counter_timeout
rtc_exec_org: rtc_exec_org:
rtc_interrupt_org = .+1 # Same trick used by the LILO mapper rtc_interrupt_org = .+1 # Same trick used by the LILO mapper
@ -315,8 +331,8 @@ dsk_interrupt_org = .+1
int13_cmd: .byte 0x00 int13_cmd: .byte 0x00
prompt_string: .string "\r\nPress any key to boot from USB." prompt_string: .string "\r\nPress any key to boot from USB."
dot_string = .-2 # Reuse the end of previous string dot_string = .-2 # Reuse the end of previous string
counter_timeout:.word DOT_NUMBER*DOT_TIMEOUT + 1 counter_timeout:.byte DOT_NUMBER*DOT_TIMEOUT + 1
counter_dot: .word DOT_TIMEOUT counter_dot: .byte DOT_TIMEOUT
/********************************************************************************/ /********************************************************************************/

Binary file not shown.

49
res/mbr/readme.txt Normal file
View file

@ -0,0 +1,49 @@
Rufus: The Reliable USB Formatting Utility - Custom MBR
# Description
This directory contains all the resources required to create an MBR that prompts
the user for boot selection, when a second bootable device (typically bootable
fixed HDD) is reported by the BIOS.
This aims at mimicking the Microsoft Windows optical installation media feature,
which may be necessary on for WinPE 2.x or earlier based installations.
# Compilation
Any gcc suite (except possibly the X-Code one on OS-X) should be able to compile
the MBR by invoking 'make'. A 'make dis', that produces a disassembly dump is
also provided for your convenience.
# Primer
The way this bootloader achieves the feature highlighted above is as follows:
1. An attempt to read the MBR of the second bootable drive (0x81) is made
through INT_13h (in either CHS or LBA mode depending on the extensions
detected)
2. If that attempts succeeds, then the partition table from the newly read MBR
is checked for an active/bootable entry.
3. If such a partition is found, a prompt is displayed to the user and an RTC
timer interrupt (INT_8h) override is added so that dots are displayed at
regular interval. Then the keyboard is checked for entry.
4. If the user presses a key, the first partition boot record from the USB is
read (according to the values found in the USB MBR partition table) and
executed
5. If no key is pressed, then an INT_13h (disk access interrupt) override is
added to masquerade the second bootable drive (0x81) as the first one (0x80)
so that the Windows second stage installer, or any other program relying on
BIOS disk access, behave as if there was no USB drive inserted.
6. In case there was a failure to read the second bootable drive's MBR, or no
active partition was detected there, the USB is booted without prompts.
# Limitations
* If you are using software RAID or a non-conventional setup, the second
bootable disk may not be accessible through the BIOS and therefore the USB
will always be booted
* If the bootable HDD uses LILO, a "LILO - Keytable read/checksum error" will
be displayed when trying to boot it.
* This MBR currently does not masquerade the bootable USB drive as secondary
(0x81) therefore an installation program ran from USB to install an OS on
an HDD may still configure that disk as the second drive, and prevent it to
properly boot later on.

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 206, 289 IDD_DIALOG DIALOGEX 12, 12, 206, 289
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW EXSTYLE WS_EX_APPWINDOW
CAPTION "Rufus v1.1.6.155" CAPTION "Rufus v1.1.6.156"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
DEFPUSHBUTTON "Start",IDC_START,94,248,50,14 DEFPUSHBUTTON "Start",IDC_START,94,248,50,14
@ -73,7 +73,7 @@ BEGIN
DEFPUSHBUTTON "OK",IDOK,231,175,50,14,WS_GROUP DEFPUSHBUTTON "OK",IDOK,231,175,50,14,WS_GROUP
CONTROL "<a href=""http://rufus.akeo.ie"">http://rufus.akeo.ie</a>",IDC_ABOUT_RUFUS_URL, CONTROL "<a href=""http://rufus.akeo.ie"">http://rufus.akeo.ie</a>",IDC_ABOUT_RUFUS_URL,
"SysLink",WS_TABSTOP,46,47,114,9 "SysLink",WS_TABSTOP,46,47,114,9
LTEXT "Version 1.1.6 (Build 155)",IDC_STATIC,46,19,78,8 LTEXT "Version 1.1.6 (Build 156)",IDC_STATIC,46,19,78,8
PUSHBUTTON "License...",IDC_ABOUT_LICENSE,46,175,50,14,WS_GROUP 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 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 LTEXT "Report bugs or request enhancements at:",IDC_STATIC,46,66,187,8
@ -223,8 +223,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,1,6,155 FILEVERSION 1,1,6,156
PRODUCTVERSION 1,1,6,155 PRODUCTVERSION 1,1,6,156
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -241,13 +241,13 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "akeo.ie" VALUE "CompanyName", "akeo.ie"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "1.1.6.155" VALUE "FileVersion", "1.1.6.156"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe" VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "1.1.6.155" VALUE "ProductVersion", "1.1.6.156"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"