cfenollosa / os-tutorial

How to create an OS from scratch
BSD 3-Clause "New" or "Revised" License
27.06k stars 3.28k forks source link

Problem reading many sectors #245

Open GodToRun opened 2 years ago

GodToRun commented 2 years ago

Hi, there is a problem with increasing the number of sectors that the kernel needs to read as it grows. If the number of sectors read (dh) is greater than 62, only Loading kernel into memory appears and the kernel is not loaded. I tried to increase the memory, but it didn't work. Here's the code.
bootsect.asm

KERNEL_OFFSET equ 0x1000
mov [BOOT_DRIVE], dl
mov bp, 0x9000
mov sp, bp

call load_kernel
call switch
jmp $
%include 'boot/gdt.asm'
%include 'boot/io32.asm'
%include 'boot/io.asm'
%include 'boot/switch32.asm'
%include 'boot/disk.asm'

[bits 16]
load_kernel:
    mov bx, MSG_LOAD_KERNEL
    call print
    call print_nl
    mov bx, KERNEL_OFFSET
    mov dh, 62
    mov dl, [BOOT_DRIVE]
    call disk_load
    ret
[bits 32]
begin:
    mov esi, msg
    call print32
    call KERNEL_OFFSET
    jmp $
msg db 'Now we are actually protected mode!',0
MSG_LOAD_KERNEL db "Loading kernel into memory", 0
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0

BOOT_DRIVE db 0

times 510-($-$$) db 0
dw 0xaa55

disk.asm

[bits 16]
; load 'dh' sectors from drive 'dl' into ES:BX
disk_load:
    pusha
    ; reading from disk requires setting specific values in all registers
    ; so we will overwrite our input parameters from 'dx'. Let's save it
    ; to the stack for later use.
    push dx

    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'
    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)
    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)
                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector
    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')
    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS
    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)
    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)

    ; [es:bx] <- pointer to buffer where the data will be stored
    ; caller sets it up for us, and it is actually the standard location for int 13h
    int 0x13      ; BIOS interrupt
    ;jc disk_error ; if error (stored in the carry bit)

    pop dx
    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.
    jne sectors_error
    popa
    ret

disk_error:
    mov bx, DISK_ERROR
    call print
    call print_nl
    jmp disk_loop

sectors_error:
    mov bx, SECTORS_ERROR
    call print

disk_loop:
    jmp $

DISK_ERROR: db "Disk read error", 0
SECTORS_ERROR: db "Incorrect number of sectors read", 0
nocturn9x commented 1 year ago

AFAIK You can't read more than 62 sectors because that's the hard limit of what you can do in real mode

epic-coder-64 commented 1 year ago

You can read more sectors, the hard limit in a single interrupt is 127* sectors. However, qemu believes your disk is 512 bytes long because that is how long your boot sector is (or more if you have a kernel). You can use a quick hack to counteract this:

$ touch zero.asm
$ cat "times 65536 db 0x00" > zero.asm
$ nasm -f bin zero.asm -o zero.bin
$ rm zero.asm

You can then modify your Makefile so it runs cat bootsect.bin kernel.bin zero.bin > os-image.bin instead of just bootsect.bin and kernel.bin.

*Technically, it is 128, but I assume you don't want to reload your boot sector again