http://f.osdev.org/viewtopic.php?f=1&t=19681
http://f.osdev.org/viewtopic.php?f=1&t=27974&start=0
http://forum.osdev.org/viewtopic.php?f=13&t=27510
http://forum.osdev.org/viewtopic.php?f=1&t=24360
http://forum.osdev.org/viewtopic.php?f= ... 17&start=0
http://forum.osdev.org/viewtopic.php?f=1&t=27432
http://f.osdev.org/viewtopic.php?f=1&t=19366
http://forum.osdev.org/viewtopic.php?f=1&t=28781
http://f.osdev.org/viewtopic.php?f=1&t=403
http://forum.osdev.org/viewtopic.php?f=1&t=26392
http://board.flatassembler.net/topic.php?t=12389
Fortunately I managed to do it some time ago on a Dell D820 laptop.
To sum up what is in the threads above:
"USB Storage Device" needs to be the first setting in the Boot-Sequence in BIOS.
If there are "Emulate USB as Floppy" and "Emulate USB as HDD" settings in your BIOS, then set "Emulate USB as HDD".
In the BIOS of the D820 there are no such settings, so I assume that "Emulate USB as HDD" is the default setting.
This must be true because I couldn't boot from USB-drive with my floppy-image (with or without the BIOS Parameter Block (BPB)).
The code below assumes that HDD-emulation is used.
I am a Linux user, so I don't know much about Windows, but they say that diskpart can be used for
partitioning an USB-drive, and bootsect.exe for copying sectors to it.
On Linux, we will use mostly the command dd.
How to do it on Linux:
Let's assume that we have a USB-drive formatted with FAT32. Plug it in.
The mount command will print the following line (this line was missing from mount before plugging the pen-drive in):
"/dev/sdb1 on /media/68D4-F9FC type vfat (rw,nosuid,nodev,relatime,uid=1000,gid=1000,fmask=0022,dmask=0077,codepage=cp437,iocharset=utf8,
shortname=mixed,showexec,utf8,flush,errors=remount-ro,uhelper=udisks)"
So the pen-drive is /dev/sdb1. We could also get this info e.g. from dmesg.
It is important to know that sdb1 is the first partition of the pen-drive.
If there were more partitions, then there would be /dev/sdb2, /dev/sdb3, ... in the list, so we have to use /dev/sdb with dd.
1. Unmount the pen-drive
umount /dev/sdb1
2. Get the binaries (see mbr.asm and vbr.asm below)
nasm -f bin mbr.asm -o mbr.bin
nasm -f bin vbr.asm -o vbr.bin
3. Transfer the Master Boot Record (mbr.bin) to the first sector (i.e. 0th)
dd if=mbr.bin of=/dev/sdb
4. Transfer the Volume Boot Record (vbr.bin) to the second sector (i.e. 1st), seek=1 skips one sector
dd if=vbr.bin of=/dev/sdb seek=1
That's all. Just plug the pen-drive in your computer and boot. The following messages should appear:
*** USB (MBR) ***
*** Volume Boot Record (VBR) ***
There are a few things I am not certain about:
- Do we need to call GetDriveParams? It is commented out. (MBR)
- DAP buffer is not 16-byte aligned. Would it be important? Mentioned in a thread above.
Should be allocated dynamically, in order to be aligned. (it works this way too) (MBR)
- I am not sure if I correctly filled the first partition-entry (MBR)
- BPB is commented out in the vbr.asm. Would it be necessary? (VBR)
The MBR just loads the second sector (i.e. 1st, VBR) and jumps to its beginning.
It also saves the drive-index it was booted from (it is 0x80).
According to my tests 0x81 is my local winchester in the D820, and 0x82 failed, so it doesn't exist.
As it was mentioned in one of the threads above, the drive we boot from gets 0x80, and then comes the other ones in sequence.
The assembly-code is not the best, I know.
Code: Select all
;*********************************************
; mbr.asm
;
;*********************************************
bits 16 ; we are in 16 bit real mode
org 0 ; we will set regisers later
;*********************************************
; Entry Point
;*********************************************
main:
;----------------------------------------------------
; code located at 0000:7C00, adjust segment registers
;----------------------------------------------------
cli ; disable interrupts
mov ax, 0x07C0 ; setup registers to point to our segment
mov ds, ax
mov es, ax ; es: segment to put loaded data into (bx: offset)
mov fs, ax
mov gs, ax
;----------------------------------------------------
; create stack
;----------------------------------------------------
mov ax, 0x0000 ; set the stack
mov ss, ax
mov sp, 0xFFFF
sti ; restore interrupts
;----------------------------------------------------
; save drive-idx we booted from
;----------------------------------------------------
mov [drive_idx], dl
;----------------------------------------------------
; Display loading message
;----------------------------------------------------
mov si, msgTxt
call Print
;----------------------------------------------------
; Get Drive Parameters from BIOS
;----------------------------------------------------
; call GetDriveParams
;----------------------------------------------------
; Load Volume Boot Record (VBR) from sector 1 (2nd sector), to 0x0050:0000 (i.e. 0x500)
;----------------------------------------------------
mov eax, 1 ; read (from) 2nd sector
mov cx, 1 ; read 1 sector
mov bx, 0x0050 ; to 0x0050:0000
mov di, 0
call ReadSectors
mov dl, [drive_idx]
; jump to 0x500
push WORD 0x0050
push WORD 0x0000
retf
;************************************************
; Prints a string
; IN:
; DS:SI (0 terminated string)
;************************************************
Print: lodsb ; load next byte from string from SI to AL
or al, al ; Does AL=0?
jz .Back ; Yep, null terminator found-bail out
mov ah, 0x0E ; Nope-Print the character
int 0x10
jmp Print ; Repeat until null terminator found
.Back ret ; we are done, so return
;GetDriveParams: ; (DS:SI ptr to buffer)
; mov ah, 0x48
; mov dl, [drive_idx]
; push ds
; mov bx, 0x50
; mov ds, bx
; mov si, 0
; int 0x13
; ; save result
; mov ax, 0x50
; mov ds, ax
; mov si, 0
; mov eax, [ds:si+4]
; mov [phys_cylinders], eax
; mov eax, [ds:si+8]
; mov [phys_heads], eax
; mov eax, [ds:si+12]
; mov [phys_sectors], eax
; mov ax, [ds:si+24]
; mov [bytes_per_sector], ax
; pop ds
; ret
;************************************************
; Reads a series of sectors
; IN: EAX (Starting sector)
; CX (Number of sectors to read)
; BX:DI (Buffer to read to)
;************************************************
ReadSectors:
pusha
mov bp, 0x0005 ; max. 5 retries
.Again mov dl, [drive_idx]
mov BYTE [buff], 0x10 ; size of this structure (1 byte)
mov BYTE [buff+1], 0 ; always zero (1 byte)
mov WORD [buff+2], cx ; number of sectors to read (2 bytes)
mov WORD [buff+4], di ; segment:offset ptr to memory to read to (4 bytes)
mov WORD [buff+6], bx
mov DWORD [buff+8], eax ; read from sector (8 bytes)
mov DWORD [buff+12], 0
mov ah, 0x42
mov si, buff
int 0x13
jnc .Ok
dec bp
jnz .Again
mov si, msgErrTxt
call Print
jmp $
int 0x18
.Ok popa
ret
buff times 16 db 0 ; Note: DAP-buff is not 16-byte aligned. Problem!?
msgTxt db 0x0D, 0x0A, "*** USB (MBR) ***", 0x0D, 0x0A, 0x00
msgErrTxt db "VBR error", 0x0D, 0x0A, 0x00
drive_idx db 0
;phys_cylinders dd 0
;phys_heads dd 0
;phys_sectors dd 0
;bytes_per_sector dw 0
; Partition1 0x01BE (i.e. first partition-entry begins from 0x01BE)
; Partition2 0x01CE
; Partition3 0x01DE
; Partition4 0x01EE
; We only fill/use Partition1
TIMES 0x1BE-($-$$) DB 0
db 0x80 ; Boot indicator flag (0x80 means bootable)
db 0 ; Starting head
db 3 ; Starting sector (6 bits, bits 6-7 are upper 2 bits of cylinder)
db 0 ; Starting cylinder (10 bits)
db 0x8B ; System ID (0x8B means FAT32)
db 0 ; Ending head
db 100 ; Ending sector (6 bits, bits 6-7 are upper 2 bits of cylinder)
db 0 ; Ending cylinder (10 bits)
dd 2 ; Relative sector (32 bits, start of partition)
dd 97 ; Total sectors in partition (32 bits)
; it's a dummy partition-entry (sectornumbers can't be zeros,
starting CHS and LBA values should be the same if converted to each other).
TIMES 510-($-$$) DB 0
DW 0xAA55
Code: Select all
;*********************************************
; vbr.asm
;
;*********************************************
bits 16 ; we are in 16 bit real mode
org 0 ; we will set regisers later
;start: jmp main ; jump to start of bootloader
;*********************************************
; BIOS Parameter Block
;*********************************************
; BPB Begins 3 bytes from start. We do a far jump, which is 3 bytes in size.
; If you use a short jump, add a "nop" after it to offset the 3rd byte.
;bpbOEM db "My OS " ; OEM identifier (Cannot exceed 8 bytes!)
;bpbBytesPerSector: DW 512
;bpbSectorsPerCluster: DB 1 ; we want 1 sector/cluster
;bpbReservedSectors: DW 1 ; the Bootsector (it won't have a FAT)
;bpbNumberOfFATs: DB 2 ; FAT12 has 2 FATs
;bpbRootEntries: DW 224 ; Floppy has max. 224 dirs in its root dir
;bpbTotalSectors: DW 2880
;bpbMedia: DB 0xF0 ; 0xf8 ;; 0xF1 ; Info about the disk; it's a bit pattern
;bpbSectorsPerFAT: DW 9
;bpbSectorsPerTrack: DW 18
;bpbHeadsPerCylinder: DW 2
;bpbHiddenSectors: DD 0
;bpbTotalSectorsBig: DD 0
;bsDriveNumber: DB 0 ; not 1 !?
;bsUnused: DB 0
;bsExtBootSignature: DB 0x29
;bsSerialNumber: DD 0xa0a1a2a3
;bsVolumeLabel: DB "MOS FLOPPY " ; exactly 11 bytes
;bsFileSystem: DB "FAT12 " ; exactly 8 bytes
;*********************************************
; Bootloader Entry Point
;*********************************************
main:
;----------------------------------------------------
; code located at 0000:0500, adjust segment registers
;----------------------------------------------------
cli ; disable interrupts
mov ax, 0x0050 ; setup registers to point to our segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
;----------------------------------------------------
; create stack
;----------------------------------------------------
mov ax, 0x0000 ; set the stack
mov ss, ax
mov sp, 0xFFFF
sti ; restore interrupts
;----------------------------------------------------
; Ensure 80*25
;----------------------------------------------------
; mov ax, 3 ; mode 80*25, clearscreen
; int 10h
;----------------------------------------------------
; Display loading message
;----------------------------------------------------
mov si, msgTxt
call Print
hlt
;************************************************
; Prints a string
; IN:
; DS:SI (0 terminated string)
;************************************************
Print: lodsb ; load next byte from string from SI to AL
or al, al ; Does AL=0?
jz .Back ; Yep, null terminator found-bail out
mov ah, 0x0E ; Nope-Print the character
int 0x10
jmp Print ; Repeat until null terminator found
.Back ret ; we are done, so return
msgTxt db 0x0D, 0x0A, "*** Volume Boot Record ***", 0x0D, 0x0A, 0x00
TIMES 510-($-$$) DB 0
DW 0xAA55