we have developed a USB composite device (CDC and MSD classes) on a custom MCU-based hardware. The disk is a RAM disk (24 kb of space) and at power up, it is formatted (FAT 12) by the MCU vendor firmware. Unfortunately, the device is seen as a bootable disk and a typical BIOS tries to boot it. However, we know that connecting various USB disks to different PCs yields different results, depending both on the disk FAT type and on the BIOS behavior (obviously each BIOS has USB boot prior than HDD). Our goal is to have an unbootable USB disk (FAT12 formatted).
We have tried many solutions but none worked. In our opinion it seems BIOSes do more than simply checking the presence of a valid boot sector, copy the first sector to address 0x7C00 and jump at that address, giving control to the x86 ASM boot code. In fact, we have formatted a commercial USB stick with Linux with the command:
mkdosfs -F 12 /dev/sda1
This USB stick has now a FAT12 Volume Boot Record and we have not found to date a BIOS that gets stuck while trying to boot from it.
Therefore, we copied its boot sector to our device and we expected it to work as the commercial USB stick. Nope: the BIOS hangs while trying to boot the disk. Here follows the disassembly of the boot sector generated by the above Linux command.
;Jump instructions
0x00007c00 eb 3c jmp 0x00007c3e
0x00007c02 90 nop
;VBR segment
0x00007c03 6d
0x00007c04 6b 66 73 2e
0x00007c08 66 61
..............
;Boot code
0x00007c3e 0e push %cs
0x00007c3f 1f pop %ds
0x00007c40 be 5b 7c mov $0x7c5b,%si
0x00007c43 ac lods %ds:(%si),%al
0x00007c44 22 c0 and %al,%al
0x00007c46 74 0b je 0x00007c53
0x00007c48 56 push %si
0x00007c49 b4 0e mov $0xe,%ah
0x00007c4b bb 07 00 mov $0x7,%bx
0x00007c4e cd 10 int $0x10
0x00007c50 5e pop %si
0x00007c51 eb f0 jmp 0x00007c43
0x00007c53 32 e4 xor %ah,%ah
0x00007c55 cd 16 int $0x16
0x00007c57 cd 19 int $0x19
0x00007c59 eb fe jmp 0x00007c59
;String to be displayed followed by zeros
0x00007c5b 54
0x00007c5c 68 69 73
..........
;Boot signature
0x00007dfe 55
0x00007dff aa
Apparently, the above code prints the string "This is not a bootable disk. Please insert a bootable floppy and press any key to try again" and then calls INT16H and INT19H.
Why does the BIOS from the PC I am writing from, for example, prints: "Attempting boot from USB disk" and then jumps to my HDD while the same assembly code written in our device does not? That is, it writes "Please insert..."? There exists a code that can substitute this and never try to boot my custom USB disk?
In addition, does the BIOS do some special communications with a USB device before reading the first sector reading, for example, something from the device descriptor in order to understand if it can be used as a boot disk?
Thank you all in advance.
Ultimately there may not anything to prevent your custom USB device from used as a boot device on any PC it might be plugged into. However there are couple of things you can do that should cover most cases, the first is not to have the 0xAA55 signature at the end of the boot sector and the second is to invoke INT 0x18 the boot sector to ask the BIOS to try the next boot device.
How BIOSes boot USB devices
The basic problem is here is that there's no standard for BIOS booting of USB mass storage devices. How exactly a BIOS might go about determining if a USB device is bootable varies between BIOS implementations. There are however two basic steps all implementations will follow.
The first is to determine whether to emulate the USB device as a "floppy", "hard drive", "cdrom", "zip" or other kind of drive. For your device it should come down to choosing between floppy or hard drive emulation. Various criteria are possible for making the this choice, like the size of the drive, whether its reports itself as being removable and the command set used. In particular there's an upper limit to the size of a drive floppy emulation can support, about 530MB I believe, and this size is often used for determining the device type. Some BIOSes can also be configured to force a certain device type in their settings.
The next step is to determine whether the device is bootable. This means at the very least reading the first sector of the drive, since if it can't be read it can't be booted. Then the BIOS may try to determine if the boot sector is valid. If it's emulating a hard drive it should always check for the presence of the 0xAA55 signature, but if it's emulating a floppy it may or may not perform this check. If it's emulating a hard drive it might also check for a valid MBR-style partition, and if's emulating a floppy it might check for a valid FAT BIOS Parameter Block (BPB).
So not having the 0xAA55 magic number at the end of the boot sector would prevent the USB device from being used a boot device in at least some cases. However it's most likely that the BIOS will choose to use floppy emulation for your device, and many BIOSes won't do any validation of the boot sector beyond verifying it's readable. However, if you can have your USB device pretend it's much larger than it actually is, beyond what floppy emulation supports, then it should make it far more likely for hard drive emulation to be used.
Invoking INT 0x18
However all is not lost even if the the BIOS does load and execute the boot sector of your device. You can invoke INT 0x18, which originally was used to start cassette BASIC on an original IBM PC, but was redefined by the BIOS Boot Specification to be the recovery vector for failed boot attempts. On a modern PC, including any that supports booting USB devices, invoking this will tell the BIOS to try the next boot device. (INT 0x18 is actually called after a boot failure in standard MBR implementations back since the MBR was invented, so it's not actually as unusual as it might sound.)
Here's the source code for a boot sector that invokes INT 0x18 I used for testing on various PCs I had available:
You should be able to modify this code for use with your device. Make sure to copy the FAT12 BPB (the bytes between offsets 0x003 and 0x03e) from your device's BPB. You'll probably want to remove the first invocation of INT 0x16 which waits for a key press before invoking INT 0x18. To assemble and link the code you can use the following commands:
To copy the BPB from your device into the boot sector created with the above commands you can use something like:
In my testing it the code above worked fairly well, but in one case the BIOS just tried the same boot device over and over again. In an another case the PC (a 15 year-old Pentium 4) crashed when trying to boot the code off a USB flash drive, but not off of an actual USB floppy disk. The crash was probably related the unusual 24k FAT12 BPB I was using to try to replicate your device as closely as possible.
The second "Remove the custom device..." message was never printed in my testing. Either the boot block was never executed in the first place or invoking 0x18 did something and never returned. You might want to move those instructions to the first message in case INT 0x18 doesn't do anything helpful.