【问题标题】:Filesystem on Loop Device not Recognized by Linux when Bootloader is Written to it当引导加载程序写入循环设备时,Linux 无法识别循环设备上的文件系统
【发布时间】:2019-11-30 22:59:25
【问题描述】:

我目前正在用 x86 NASM 程序集编写引导加载程序,旨在从 FAT16 文件系统加载内核 (R.BIN) 并跳转到它。我一直在将引导加载程序写入使用sudo losetup loop21 image.img 挂载的空白映像。我会使用sudo dd if=LOADER.BIN of=/dev/loop21 编写图像。当然,引导加载程序并没有立即工作(我基于 FAT12 引导加载程序并忘记更改一些东西)。在进行了数十次编辑后,Linux 停止识别我正在写入的循环设备,并说内容是“未知”。

当我删除引导加载程序本身的全部内容(不包括磁盘描述符等)时,Linux 将驱动器识别为 FAT12。但是,正如我所说,使用加载 R.BIN 的代码,Linux 将无法识别文件系统。

这是我的代码:

BITS 16

jmp main                            ; Jump to main bootloader
nop                                 ; Pad out remaining bytes until boot descriptor

; Disk descriptor

OEM_name            db "HOUSE   "   ; OEM label
bytes_sector        dw 0x0200       ; Bytes per sector
sectors_cluster     db 0x01         ; Sectors per cluster
sectors_record      dw 0x0001       ; Sectors reserved for boot record
fats                db 0x02         ; Number of file allocation tables
max_root_entries    dw 0x0200       ; Max number of root entries
sectors             dw 0x0FF5       ; Number of sectors
medium_type         db 0xF0         ; Type of medium (removable or fixed?)
sectors_fat         dw 0x0010       ; Sectors per file allocation table
sectors_track       dw 0x0012       ; Sectors per track
heads               dw 0x0002       ; Number of heads
hidden_sectors      dd 0x00000000   ; Number of sectors before partition
total_sectors       dd 0x00000000   ; Number of sectors in medium (zero because it was already set in the allocated word above)
drive_number        db 0x00         ; Drive number (for BIOS int 0x13)
drive_signature     db 0x00         ; NOT USED
ext_signature       db 0x29         ; Extended boot signature
volume_serial       dd 0x00000000   ; Volume's serial number
volume_label        db "HOUSEHOUSES"; Volume label
fs_type             db "FAT16   "   ; Filesystem type

main:
    mov ax, 0x07C0
    add ax, 0x0220
    mov ss, ax
    mov sp, 0x1000                  ; 4K of stack
    mov ax, 0x07C0
    mov ds, ax
    mov byte [drive_num], dl        ; Save boot drive number

    mov bx, ds
    mov es, bx                      ; Set ES to Data Segment
    mov bx, disk_buffer             ; Set BX to disk buffer
    mov ax, 0x21                    ; Start of root = sectors_record + fats * sectors_fat = 1 + 2 * 16 = logical 33
    call ls_chs                     ; Convert logical 33 to head, track and sector
    mov al, 0x20                    ; Number of sectors in root = max_root_entries * 32 / bytes_sector = 512 * 32 / 512 = 32
    mov si, a                       ; Read root dir message*
    call print_str                  ; Print!*

.read_disk:
    int 0x13                        ; BIOS disk interrupt
    jnc .search_init                ; If successful, get ready to search the disk
    call reset_disk                 ; Otherwise, reset the disk
    jmp .read_disk                  ; And retry

.search_init:
    mov si, success                 ; Success message*
    call print_str                  ; Print!*
    mov ax, ds
    mov es, ax                      ; Move data segment to extra segment
    mov di, disk_buffer             ; Location of disk buffer (ES:DI will be the location of the root entry we will be checking)
    mov si, r_name                  ; Location of filename of R (DS:SI will be the location of the string to compare to the root entry)
    mov bx, 0x00                    ; Start at root entry 0
    push si                         ; Push*
    mov si, b                       ; Search message*
    call print_str                  ; Print!
    pop si                          ; Pop*

.check_entry:
    mov cx, 0x0B                    ; Compare the first 11 bytes
    push si                         ; Push filename location to stack
    push di                         ; Push entry location to stack
    repe cmpsb                      ; Compare the two strings
    pop di                          ; Restore entry location
    pop si                          ; Restore filename location
    je .found_entry                 ; If equal, we found the root entry!
    add di, 0x20                    ; Otherwise, move to next entry
    inc bx                          ; Number of next entry
    cmp bx, [max_root_entries]      ; Have we gone through all root entries?
    jg .missing                     ; If so, R is missing
    jmp .check_entry                ; Otherwise, look at this next entry

.found_entry:
    mov si, success                 ; Success message*
    call print_str                  ; Print!*
    mov ax, word [es:di+0x1A]
    mov word [cluster], ax          ; Move starting cluster number to our spot in memory

    mov bx, disk_buffer             ; ES:BX points to disk buffer
    mov ax, 0x01                    ; 1st FAT begins at logical sector 1
    call ls_chs                     ; Convert to head, track and sector
    mov al, [sectors_fat]           ; Read all sectors in FAT
    mov si, c                       ; Read FAT message*
    call print_str                  ; Print!*

.read_fat:
    int 0x13                        ; BIOS disk interrupt
    jnc .read_cluster               ; If successful, load the first cluster of the file
    call reset_disk                 ; Otherwise, reset the disk
    jmp .read_fat                   ; And try again

.read_cluster:
    mov si, d                       ; Attempt to read cluster message*
    call print_str                  ; Print!*
    mov ax, 0x4200          ; End of disk_buffer = (sectors_record + max_root_entries * 32 / bytes_sector) * bytes_sector = 16,896
    mov es, ax                      ; Segment into which we will load R
    mov bx, word [buffer_pointer]   ; Spot into which we will load this cluster
    mov ax, word [cluster]          ; Cluster to read
    add ax, 0x1F                    ; Convert to logical sector
    call ls_chs                     ; Convert to head, track and sector
    mov al, [sectors_cluster]       ; Read the number of sectors in 1 cluster
    int 0x13                        ; BIOS disk interrupt
    jnc .find_next_cluster          ; If successful, find the next cluster
    call reset_disk                 ; Otherwise, reset the disk
    jmp .read_cluster               ; And try again

.find_next_cluster:
    mov si, success                 ; Success message*
    call print_str                  ; Print!*
    mov ax, word [cluster]          ; Location of current cluster
    mov bx, 0x02                    ; There are two bytes per entry in FAT16
    mul bx                          ; The memory location of CLUSTER should fit in AL
    mov si, disk_buffer             ; Location of start of FAT
    add si, ax                      ; Add the number of bytes until current cluster
    mov ax, word [ds:si]            ; Number of next cluster
    mov word [cluster], ax          ; Store this
    cmp ax, 0xFFF8                  ; Check whether this next cluster is an end-of-file marker
    jae .jump                       ; If it is, we have fully loaded the kernel
    add word [buffer_pointer], 0x0200 ; Otherwise, increment the buffer pointer a sector length
    jmp .read_cluster               ; And load it into memory

.jump:
    mov si, loaded                  ; Print a J
    call print_str                  ; Print!
    mov ah, 0x00                    ; Read keyboard buffer
    int 0x16                        ; BIOS keyboard interrupt
    mov dl, byte [drive_num]        ; Make the boot drive number accessible to R
    jmp 0x4200:0x0000               ; Jump to R's location!

.missing:
    mov si, m_r_missing             ; Display the missing message
    call rsod                       ; Display it in a Red Screen of Death

reset_disk:
    pusha                           ; Push register states to stack
    mov ax, 0x00                    ; RESET disk
    mov dl, byte [drive_num]        ; Boot drive number
    int 0x13                        ; BIOS disk interrupt
    jc .disk_fail                   ; If failed, fatal error and reboot
    popa                            ; Restore register states
    ret                             ; And retry

.disk_fail:
    mov si, m_disk_error            ; Display the disk error message
    call rsod                       ; Display it in a Red Screen of Death

print_str:                          ; Prints string pointed to by REGISTER SI to cursor location (si=str)
    pusha                           ; Push register states to stack
    mov ah, 0x0E                    ; BIOS will PRINT

.repeat:
    lodsb                           ; Load next character from SI
    cmp al, 0x00                    ; Is this a null character?
    je .ret                         ; If it is, return to caller
    int 0x10                        ; Otherwise, BIOS interrupt
    jmp .repeat                     ; Do this again

.ret:
    mov ah, 0x00                    ; Read keyboard buffer
    int 0x16                        ; BIOS keyboard interrupt      
    popa                            ; Restore register states
    ret                             ; Return to caller

ls_chs:                             ; Convert logical sector to head, track, and sector configuration for int 0x13 (AX = logical sector)
    mov dx, 0x00                    ; Upper word of dividend is 0
    div word [sectors_track]        ; Divide to find the number of tracks before this
    mov cl, dl                      ; The remainder is the number of the sector within the track
    add cl, 0x01                    ; Sectors start at 1, not 0
    mov dx, 0x00                    ; Upper word of dividend is 0
    div word [heads]                ; Divide by number of heads/sides
    mov dh, dl                      ; The remainder is the head number (it should only take up the lower half of DX)
    mov ch, al                      ; The quotient is the track number (it should only take up the lower half of CX)
    mov dl, byte [drive_num]        ; Boot drive number
    mov ah, 0x02                    ; READ disk sectors
    ret                             ; Return to caller

rsod:                               ; Red Screen of Death (SI = line to print)
    mov al, 0x20                    ; SPACE
    mov bh, 0x00                    ; Page 0
    mov bl, 0x40                    ; Red background
    mov cx, 0x50                    ; Enough to fit the screen width

.repeat:
    mov ah, 0x09                    ; Write character and attribute
    int 0x10                        ; BIOS VGA interrupt
    mov ah, 0x03                    ; Get cursor position
    int 0x10                        ; BIOS VGA interrupt
    cmp dh, 0x1A                    ; Have we gone all the way down the screen?
    jge .write                      ; If we have, return to caller
    inc dh                          ; Otherwise, next row down
    mov ah, 0x02                    ; Set cursor position
    int 0x10                        ; BIOS VGA interrupt
    jmp .repeat                     ; Do this again for the next line

.write:
    mov ah, 0x02                    ; Set cursor position
    mov dh, 0x01                    ; Row 1
    mov dl, 0x03                    ; Col 3
    int 0x10                        ; BIOS VGA interrupt
    push si                         ; Push line to stack
    mov si, fatal                   ; Prepare to display "FATAL" message
    call print_str                  ; Print!
    pop si                          ; Restore line and prepare to print it
    call print_str                  ; Print!
    mov si, press_a_key             ; Prepare to display prompt
    call print_str                  ; Print!
    mov ah, 0x00                    ; Wait for keyboard input
    int 0x16                        ; BIOS keyboard input
    int 0x19                        ; Reboot

data:
    r_name          db "R       BIN"        ; Filename of R
    cluster         dw 0x0000               ; Cluster that we are working with
    buffer_pointer  dw 0x0000               ; Pointer to offset of buffer
    drive_num       db 0x00                 ; Boot drive number
    fatal           db "FATAL: ", 0x00      ; Fatal error message
    press_a_key     db "! Press a key", 0x00; Instruct the user to press a key and reboot
    m_r_missing     db "R missing", 0x00    ; Missing message
    m_disk_error    db "Disk failed", 0x00  ; Disk error message
    a               db "A", 0x00            ; About to read root dir*
    b               db "B", 0x00            ; About to search root dir*
    c               db "C", 0x00            ; About to read FAT*
    d               db "D", 0x00            ; About to attempt cluster read*
    success         db "!", 0x00            ; Success!*
    loaded          db "J", 0x00            ; Loaded R message*

    times 510-($-$$) db 0x00        ; Pad remainder of boot sector
    sig             dw 0xAA55       ; Boot signature

disk_buffer:                        ; Space in memory for loading disk contents

我希望 Linux 将文件系统识别为 FAT16,并将其写入引导扇区,但它根本无法识别它,因此不允许我复制 R.BIN。

【问题讨论】:

  • 这不是您拥有的代码。请始终确保您发布准确的代码。我假设你有jmp marble_main 而不是jmp main
  • 还有,linux怎么不识别呢?当你尝试安装?在这里工作正常。
  • 写完引导加载程序后,当我在“磁盘”应用程序中查看循环卷时,它会将内容列为“未知”。
  • 您可以使用mformat -B。或者您可以使用任何方法创建一个文件系统,找出它的代码偏移量,从程序集中删除main 之前的所有内容,添加带有偏移量的org 并使用带有相同偏移量的dd
  • sectors dw 0x0FF5 不适合 FAT16 文件系统。 (它导致最大集群低于 0FF0h,被检测为 FAT12。)在安装加载程序时使用我的答案的方法来保留 BPB。

标签: assembly x86-16 bootloader mbr


【解决方案1】:

这是我刚刚将自定义引导扇区加载程序安装到映像中并在之后让它工作的方法。在我的例子中,这是一个 1440 KiB 长的 FAT12 映像,但 BPB + BPB 新字段的大小与 FAT16 相同。请报告这是否解决了您的问题。

$ dd if=bldbg12.bin of=floppy.img bs=1 count=11 conv=notrunc
11+0 records in
11+0 records out
11 bytes copied, 0.000161134 s, 68.3 kB/s
$ dd if=bldbg12.bin of=floppy.img bs=1 count=$((512 - 0x3e)) seek=$((0x3e)) skip=$((0x3e)) conv=notrunc
450+0 records in
450+0 records out
450 bytes copied, 0.00150799 s, 298 kB/s
$

一个很可能不会导致您的错误的小问题是jmp main 应该是jmp strict short main

【讨论】:

    猜你喜欢
    • 2018-05-02
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    相关资源
    最近更新 更多