简体   繁体   English

Bootloader 不能在真机上工作,但 qemu 工作

[英]Bootloader is not working at real machine but qemu works

As part of my custom kernel, I wrote a bootloader for booting the ELF kernel in BIOS.作为我自定义 kernel 的一部分,我编写了一个引导加载程序,用于在 BIOS 中引导 ELF kernel。 In the first stage of the kernel, I tried to load my kernel by pio in protected mode.在 kernel 的第一阶段,我尝试在保护模式下通过 pio 加载我的 kernel。 In QEMU, as expected, my bootloader working correctly.在 QEMU 中,正如预期的那样,我的引导加载程序工作正常。 But in bare metal, My bootloader stuck at the pio (in my assembly code, the wait_disk part is not working as I intended.).但是在裸机中,我的引导加载程序卡在了 pio 上(在我的汇编代码中,wait_disk 部分没有按我的预期工作。)。 Then I just got a blinking cursor on a black screen.然后我在黑屏上看到一个闪烁的 cursor。

My question is, is there any problem with my codes?我的问题是,我的代码有问题吗? Is there any reason why my bootloader is working at QEMU, but is blocked at the real machine?为什么我的引导加载程序在 QEMU 工作,但在真机上被阻止,有什么原因吗?

This is my bootloader code (the second stage and parsing elf section are omitted. Because I cannot succeed to enter stage two with the real machine.)这是我的bootloader代码(第二阶段和解析elf部分省略。因为我用真机无法成功进入第二阶段。)

.section .bootloader, "awx"
.global _start
.intel_syntax noprefix
.code16

_start:
  cli
  cld

# Setup the ds, es, ss
  xor ax, ax
  mov ds, ax
  mov es, ax
  mov ss, ax

  mov sp, 0x7000

# ENABLE A20
seta20_1:
  in al, 0x64
  test al, 0x2
  jnz seta20_1  # spin until not busy

  mov al, 0xd1
  out 0x64, al

seta20_2:
  in al, 0x64
  test al, 0x2
  jnz seta20_2  # spin until not busy

  mov al, 0xdf
  out 0x60, al

# Get a820 map from bios
get_e820:
  mov eax, 0xe820
  mov edi, 0x7000 + 52 + 4        # E820_map + 4
  xor ebx, ebx
  mov edx, 0x534d4150
  mov ecx, 24
  int 0x15
  jc fail
  cmp edx, eax
  jne fail
  test ebx, ebx
  je fail
  mov ebp, 24

parse_entry:
  mov [edi - 4], ecx
  add edi, 24
  mov eax, 0xe820
  mov ecx, 24
  int 0x15
  jc done
  add ebp, 24
  test ebx, ebx
  jne parse_entry

done:
  mov [edi - 4], ecx
  mov dword ptr [0x7000], 0x40
  mov dword ptr [0x7000 + 44], ebp
  mov dword ptr [0x7000 + 48], 0x7000 + 52  # E820_map
fail:


# Switch to protected mode
  lgdt gdt_desc
  mov eax, cr0
  or  eax, 1           # CR0_PE
  mov cr0, eax

# Jump to the 32bit mode
  lea eax, [_code32]
  push 0x8
  push eax
  retf

.code32
_code32:
  mov ax, 0x10         # PROT_DS
  mov ds, ax
  mov es, ax
  mov fs, ax
  mov gs, ax
  mov ss, ax


  # Load the remaining boot loaders
  mov edi, 0x7c00 # addr
  xor ecx, ecx    # sector
load_boot_loader:
  inc ecx
  add edi, 0x200
  lea esi, [boot_end]
  cmp edi, esi
  jae end
  push ecx
  push edi
  call read_sector
  pop edi
  pop ecx
  jmp load_boot_loader
end:
  jmp _head64
  hlt

boot_fail:
  mov ax, 0x8A00
  mov dx, 0x8A00
  out dx, al
  mov ax, 0x8E00
  mov dx, 0x8A00
  out dx, al
spin:
  jmp spin

read_sector: # edi: dst, ecx: offset
  call wait_disk

  mov al, 1
  mov edx, 0x1F2
  out dx, al

  mov eax, ecx
  mov edx, 0x1F3
  out dx, al

  mov eax, ecx
  shr eax, 0x8
  mov edx, 0x1F4
  out dx, al

  mov eax, ecx
  shr eax, 0x10
  mov edx, 0x1F5
  out dx, al

  mov eax, ecx
  shr eax, 0x18
  or  ax, 0xE0
  mov edx, 0x1F6
  out dx, al

  mov ax, 0x20
  mov edx, 0x1F7
  out dx, al

  call wait_disk

  mov ecx, 0x80
  mov edx, 0x1F0
  cld
  repnz ins DWORD PTR [edi], dx
  ret


wait_disk: <- kernel enter this line as expected
  mov edx, 0x1F7 
  in al, dx <- something is wrong?
  and al, 0xC0
  cmp al, 0x40
  jne wait_disk
  ret <- kernel is not returned from this line

.p2align 2
gdt:
  .quad 0;                    # NULL SEGMENT
  .quad 0xCF9A000000FFFF;     # CODE SEGMENT
  .quad 0xCF92000000FFFF;     # DATA SEGMENT
gdt_end:

gdt_desc:
  .word gdt_end - gdt # sizeof(gdt) - 1
  .word gdt  # addrof(gdt)

.org 510
.word 0xaa55

It turns out that, USB bootable disk is not recognized as an ATA port.事实证明,USB 启动盘没有被识别为 ATA 端口。 I can find out this information (ATA port number) by listing /sys/block in Linux.我可以通过在 Linux 中列出 /sys/block 来找到这些信息(ATA 端口号)。

lrwxrwxrwx 1 root root 0 Mar 29 13:49 /sys/block/sda -> ../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
lrwxrwxrwx 1 root root 0 Mar 29 13:49 /sys/block/sdb -> ../devices/pci0000:00/0000:00:1f.2/ata2/host1/target1:0:0/1:0:0:0/block/sdb
lrwxrwxrwx 1 root root 0 Mar 29 13:49 /sys/block/sdc -> ../devices/pci0000:00/0000:00:1f.2/ata3/host2/target2:0:0/2:0:0:0/block/sdc
lrwxrwxrwx 1 root root 0 Mar 29 13:49 /sys/block/sdd -> ../devices/pci0000:00/0000:00:1f.2/ata4/host3/target3:0:0/3:0:0:0/block/sdd
lrwxrwxrwx 1 root root 0 Mar 29 13:49 /sys/block/sde -> ../devices/pci0000:00/0000:00:14.0/usb3/3-8/3-8:1.0/host6/target6:0:0/6:0:0:0/block/sde

As you can see, there is no assigned ATA port to a USB disk.如您所见,没有为 USB 磁盘分配 ATA 端口。

Also, ATA supports a disk up to 4, using USB as a bootloader, and tries to access via ATA port is more likely to not work.另外,ATA最多支持4个磁盘,使用USB作为bootloader,尝试通过ATA口访问的可能性较大。 And there is another problem in my code, that I always try to access ATA bootable disk to ata 0. We have to specify the exact port and driver number to access bootable ATA.我的代码中还有另一个问题,就是我总是尝试将 ATA 可引导磁盘访问到 ata 0。我们必须指定确切的端口和驱动程序号才能访问可引导 ATA。 So the point is,所以重点是,

  1. Do not use ATA if booting disk is USB如果引导盘是 USB,则不要使用 ATA
  2. Have to find exact ATA port forwarding to booting DISK必须找到确切的 ATA 端口转发来引导磁盘
  3. ATA support disk up to 4, if you using more than 4, It probably not working ATA 最多支持 4 个磁盘,如果您使用超过 4 个,它可能无法正常工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM