Эмуляция жесткого диска USB вызывает сбой чтения с диска (BIOS int 13)?

Немного предыстории:

Я работаю над базовым загрузчиком, который читает вторичный загрузчик в память с BIOS INT 13h AH=02h прерывание. У меня это работает в эмуляторах (Virtualbox, Qemu и Bochs).

Впоследствии я добавил BPB (блок параметров BIOS) в мой загрузчик, сделал загрузочный USB и протестировал его на своей реальной машине с помощью USB Floppy Emulation (которую я настраивал на экране конфигурации BIOS моей реальной машины). Оно работало завораживающе.

После тестирования загрузчика на собственной машине я проверил его на другой, более новой машине. Этот новый компьютер не имел опции эмуляции гибкого диска в конфигурации BIOS и поэтому не мог загружаться с USB-накопителя. Итак, следуя этой вики-странице osdev, я добавил таблицу разделов в конце MBR, чтобы новая машина могла загружаться с USB.

Эта проблема:

С добавленным кодом таблицы разделов загрузчик не может загрузить дополнительный загрузчик в память и BIOS INT 13h выходит из строя. Я понятия не имею, почему это может произойти, так как я не изменил ни одного фактического кода загрузчика. Я только что добавил 64-битную таблицу разделов MBR, и чтение данных в память мгновенно завершается неудачей

BPB (блок параметров BIOS) и процедура доступа к диску

bits    16 
org 0x7C00

jmp start
nop
;------------------------------------------;
;  Standard BIOS Parameter Block, "BPB".   ;
;------------------------------------------;
     bpbOEM         db  'MSDOS5.0'
     bpbSectSize    dw  512
     bpbClustSize   db  1
     bpbReservedSe  dw  1
     bpbFats        db  2
     bpbRootSize    dw  224
     bpbTotalSect   dw  2880
     bpbMedia       db  240
     bpbFatSize     dw  9
     bpbTrackSect   dw  18
     bpbHeads       dw  2
     bpbHiddenSect  dd  0
     bpbLargeSect   dd  0
     ;---------------------------------;
     ;  extended BPB for FAT12/FAT16   ;
     ;---------------------------------;
     bpbDriveNo     db  0
     bpbReserved    db  0
     bpbSignature   db  41            
     bpbID          dd  1
     bpbVolumeLabel db  'BOOT FLOPPY'
     bpbFileSystem  db  'FAT12   '

drive_n: db 0
start: 
    mov [drive_n], dl

    ; setup segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; setup stack
    cli
    mov ss, ax
    mov sp, 0x7C00   ; stack will grow downward to lower adresses
    sti

    ; write start string
    mov si, start_str    ; start_str = pointer to "Bootloader Found..."
    call write_str       ; routine that prints string in si register to screen 

    ; read bootstrapper into memory
    mov dl, [drive_n]; drive number
    mov dh, 0x00    ; head (base = 0)
    mov ch, 0x00    ; track /cylinder = 0
    mov cl, 0x02    ; (1= bootloader, 2=start of bootstrapper
    mov bx, 0x7E00  ; location to load bootstrapper 
    mov si, 0x04    ; number of attempts

    ; attempt read 4 times 
  read_floppy:
    ; reset floppy disk
    xor ax, ax
    int 0x13

    ; check if attempts to read remain, if not, hlt system (jmp to fail_read)
    test    si, si
    je  fail_read   ; *** This jump happens only on real machines with 
    dec si          ; USB hard drive emulation ***

    ; attempt read 
    mov ah, 0x02    ; select read
    mov al, 0x0F    ; num sectors
    int     0x13
    jc  read_floppy

    ...             ; continue onward happily! (without any errors)

Таблица разделов MBR

; 0x1b4
db "12345678", 0x0, 0x0     ; 10 byte unique id

; 0x1be         ; Partition 1 -- create one big partition that spans the whole disk (2880 sectors, 1.44mb)
db 0x80         ; boot indicator flag = on

; start sector
db 0            ; starting head = 0
db 0b00000001   ; cyilinder = 0, sector = 1 (2 cylinder high bits, and sector. 00 000001 = high bits db 0x00)
db 0            ; 7-0 bits of cylinder (insgesamt 9 bits) 

; filesystem type
db 1            ; filesystem type = fat12

; end sector = 2880th sector (because a floppy disk is 1.44mb)
db 1            ; ending head = 1
db 18           ; cyilinder = 79, sector = 18 (2 cylinder high bits, and sector. 00 000001 = high bits db 0x00)
db 79           ; 7-0 bits of cylinder (insgesamt 9 bits) 

dd 0            ; 32 bit value of number of sectors between MBR and partition
dd 2880         ; 32 bit value of total number of sectors

; 0x1ce         ; Partition 2
times 16 db 0

; 0x1de         ; Partition 3
times 16 db 0

; 0x1ee         ; Parititon 4
times 16 db 0

; 0x1fe         ; Signature
dw  0xAA55

Вопрос

Что вызывает сбой при чтении диска, если и только если в BIOS включена эмуляция жесткого диска USB? Я попытался изменить таблицу разделов и BPB, но, похоже, ничего не работает. Могу поспорить, что это как-то связано с разницей в том, как компьютер обрабатывает информацию с дискет и жестких дисков, но сложно найти какую-либо информацию по этому поводу.

Любая помощь будет принята с благодарностью. Я не собирался задавать этот вопрос так долго; это просто накопилось. Спасибо за ваше время.

1 ответ

Решение

TL; DR: в некоторых ситуациях загрузочный диск неправильно хранится на этикетке drive_n, Это приводит к сбою процедуры чтения с диска на некотором оборудовании.


У меня есть ответ Stack ru с общим набором советов по загрузчику. Важный совет заключается в следующем:

Когда BIOS переходит к вашему коду, вы не можете полагаться на регистры CS,DS,ES,SS,SP, имеющие действительные или ожидаемые значения. Они должны быть настроены соответствующим образом при запуске вашего загрузчика. Вы можете только гарантировать, что ваш загрузчик будет загружен и запущен с физического адреса 0x00007c00 и что номер загрузочного диска загружен в регистр DL.

После того, как ваш вопрос был обновлен с использованием более подходящего кода с тем, что происходит до прочтения, проблема становится очевидной:

drive_n: db 0
start: 
    mov [drive_n], dl

    ; setup segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; setup stack
    cli
    mov ss, ax
    mov sp, 0x7C00   ; stack will grow downward to lower adresses
    sti

Проблема в том, что mov [drive_n], dl выполняется до настройки регистров сегмента. mov [drive_n], dl эквивалентно mov [ds:drive_n], dl, Сегмент в DS имеет значение. Если BIOS передает управление вашему загрузчику с сегментом DS, который не равен 0x0000, то mov [drive_n], dl запишет номер диска в ячейку памяти, которую вы не ожидаете.

Если значение DS не было равно нулю, а загрузочный диск отличался от 0x00, то имелась большая вероятность отказа. В тех случаях, когда реальный загрузочный диск был сохранен в неправильном месте памяти, начальное значение сохранялось в drive_n ярлык будет использоваться. В вашем случае это было 0x00.

В большинстве случаев вам повезло, что это сработало. Решение этой проблемы простое. Убедитесь, что вы записали значение DL в память после того, как настроили регистры сегментов (особенно DS). Код должен выглядеть так:

drive_n: db 0
start: 
    ; setup segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; setup stack
    cli
    mov ss, ax
    mov sp, 0x7C00   ; stack will grow downward to lower adresses
    sti

    mov [drive_n], dl
Другие вопросы по тегам