简体   繁体   English

引导加载程序总是弄乱Int 13h调用的前两个字节来加载内核

[英]Boot loader always messes up the first two bytes of Int 13h call to load kernel

Here's my boot loader code, and here's a document showing pictures of the registers (in case that's of importance in what I'm doing wrong), what was in memory at location 0x10000 (where I told the boot loader to load the kernel), the source assembly of my kernel, and the screen output when Qemu is run. 这是我的引导加载程序代码, 这是一个文档,显示了寄存器的图片(以防我做错事情很重要),位置0x10000的内存中的内容(我告诉引导加载程序加载内核),我的内核的源程序集,以及运行Qemu时的屏幕输出。

kernelStub.bin has EB 1B (the right jump command) at the very beginning. kernelStub.bin的开头是EB 1B(右跳命令)。 hda.img has EB 1B right after 55 AA at the beginning of the second sector. hda.img在第二扇区的开头55 AA之后具有EB 1B。 The carry flag is clear in my load_mem subroutine indicating that it believes the load was good. 我的load_mem子例程中的进位标志很清楚,表明它认为负载很好。 All the bytes are right in memory except the first two are always 63 61. 除了前两个始终为63 61以外,所有字节均位于内存中。

Why would the load_mem routine always load the first two bytes of sector 2 into address 0x10000 wrong and then get the rest right? 为什么load_mem例程总是总是错误地将扇区2的前两个字节加载到地址0x10000中,然后正确处理其余部分?

The bootloader code: 引导程序代码:

Update: Changed jmp SYSADDR:0000 to jmp 0x1000:0x0000 per Matthew Slattery's correction. 更新:根据Matthew Slattery的更正,将jmp SYSADDR:0000更改为jmp 0x1000:0x0000

;Very minimal boot loader 
BITS 16                ;Tell assembler to use 16-bit mode
jmp start              ;Jump over defines
SYSADDR  dw 0x1000     ;Load system at 0x10000
DRIVENUM db 0x80       ;Variable for drive number
HEADNUM  db 0
CYLNUM   db 0          ;Low bits of cylinder number
SECTNUM  db 2          ;Bits 6 and 7 high bits of cylinder number (0),
                       ;Bits 0-5 starting sector number (2)
NUMKERNELSECTS db 0x01 ;Will Probably Change! Number of sectors
                       ;to read from disk
load_msg    db 'Loading OS', 0
msg_2       db 'carry flag not clear', 0
load_worked db 'Load worked', 0

start:
    mov ax, 0x07C0     ;Set data segment to where BIOS loaded boot loader
    mov ds, ax
    mov si, load_msg   ;Simple text string to indicate loading
    call show_message
    call load_mem      ;Subroutine to load bytes from disk to location
                       ;pointed to by es
    jmp 0x1000:0x0000

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Subroutines

;;;Show Message;;;
show_message:
    mov ah, 0x0E       ;int 0x10 print character to screen function
.repeat:
    lodsb              ;Get char pointed to by si, puts in al
    cmp al, 0          ;see if char is 0 (null)
    je .done           ;null signifies done
    int 0x10           ;If not null, print to screen
    jmp .repeat        ;Get next char

.done:
    ret

;;;Load Memory;;;
load_mem:
    xor ah, ah         ;ah=0, reset drive
    int 0x13           ;Call drive reset

    mov ax, [SYSADDR]
    mov es, ax         ;Destination- es:bx
    mov bx, 0
    mov dl, [DRIVENUM]
    mov dh, [HEADNUM]
    mov al, [NUMKERNELSECTS]
    mov ch, [CYLNUM]
    mov cl, [SECTNUM]
    mov ah, 0x02       ;ah=2, read drive
    int 0x13           ;Call read interrupt
    jnc exit           ;If carry flag is clear, exit

exit:
    ret

times 510 - ($-$$) db 0;Pad sector with 0
dw 0xAA55              ;Boot signature
 SYSADDR dw 0x1000 ;Load system at 0x10000 ... jmp SYSADDR:0000 

doesn't do what you want it to. 不执行您想要的操作。 SYSADDR is the location of the value you want ( 0x0002 here), not the value itself. SYSADDR是所需值的位置(此处为0x0002 ),而不是值本身。

I reckon that executing whatever happens to be at 0x0002:0000 (which won't even be real code; it's part of the interrupt vector table) is causing your data to be scribbled over. 我认为执行在0x0002:0000处发生的任何事情(甚至都不是真正的代码;它是中断向量表的一部分)都会导致您的数据被乱写。

(By the way, there is at least one more problem as well: the kernel stub code in your second linked doc isn't resetting ds .) (顺便说一句,至少还有另外一个问题:第二个链接文档中的内核存根代码未重置ds 。)


EDIT: 编辑:

I see that you've got this working now, but for completeness, I have a full explanation for the mysterious values in those two bytes, as a result of the jmp SYSADDR:0000 bug: 我看到您现在可以正常工作了,但是为了完整jmp SYSADDR:0000 ,由于jmp SYSADDR:0000错误,我对这两个字节中的神秘值有完整的解释:

0002:0000 , ie address 0x20 , is the vector for INT 08h . 0002:0000 (即地址0x20 )是INT 08h的向量。

The traditional entry point address for the INT 08h handler is f000:fea5 (BIOSes tend to maintain the tradition, for compatibility with code that uses the entry points directly). INT 08h处理程序的传统入口点地址为f000:fea5 (BIOS倾向于保留该传统,以便与直接使用入口点的代码兼容)。 So the bytes at this address will almost certainly be a5 fe 00 f0 ... . 因此,该地址处的字节几乎可以肯定是a5 fe 00 f0 ...

If you execute the first of those bytes, a5 , as code, it's a movsw instruction, which copies two bytes from ds:si to es:di . 如果将其中的第一个字节a5作为代码执行,则它是一条movsw指令,它将ds:si两个字节复制到es:di

  • ds is still your boot loader's data segment ds仍然是引导加载程序的数据段
  • si points to the beginning of msg_2 (where your show_message routine left it after printing load_msg ) si指向msg_2的开头(您的show_message例程在打印load_msg之后将show_message离开)
  • es is still 0x1000 es仍为0x1000
  • di presumably contains 0 di可能包含0

So the very first thing that happens after the jmp 0x0002:0000 is that the c and a from the start of msg_2 get copied to the first two bytes of the sector which was loaded. 因此,在jmp 0x0002:0000之后发生的第一件事是,将camsg_2的开头复制到已加载扇区的前两个字节。

I found the critical detail regarding my question of getting the first jump of my kernel here . 我找到了有关在这里获得内核的第一跳的问题的关键细节。 I didn't know how necessary it was (with some BIOSes, at least) to follow short jmp with nop . 我不知道用nop跟随简短的jmp有多必要(至少使用某些BIOS)。

Once I put the nop s in and set up my ds register, it finally worked. 一旦将nop放入并设置了ds寄存器,它终于可以工作了。

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

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