Чтение с дискеты (AH=0x2, int 0x13) не завершено

На втором этапе моего загрузчика я пытаюсь загрузить несколько секторов с виртуальной дискеты в память в bochs, но после вызова int 0x13, рутина просто не возвращается.

Я считаю, что соответствующий код из моего второго этапа:

bootsys_start:
    mov %cs, %ax
    mov %ax, %ds

    /*
     * Remap IRQs. Interrupts have been disabled in the
     * bootloader already.
     */

    mov i8259A_ICW1($i8259A_IC4), %al
    out %al, i8259A_ICW1_ADDR($i8259A_MASTER)
    out %al, i8259A_ICW1_ADDR($i8259A_SLAVE)

    mov i8259A_ICW2($USER_INT_START), %al
    out %al, i8259A_ICW2_ADDR($i8259A_MASTER)
    mov i8259A_ICW2($USER_INT_START + 8), %al
    out %al, i8259A_ICW2_ADDR($i8259A_SLAVE)

    mov i8259A_ICW3($0x4), %al
    out %al, i8259A_ICW3_ADDR($i8259A_MASTER)
    mov i8259A_ICW3($0x2), %al
    out %al, i8259A_ICW3_ADDR($i8259A_SLAVE)

    mov i8259A_ICW4($i8259A_uPM & i8259A_x86), %al
    out %al, i8259A_ICW4_ADDR($i8259A_MASTER)
    out %al, i8259A_ICW4_ADDR($i8259A_SLAVE)

    call mm_detect

    /* Load the kernel now. */

    xor %bp, %bp
1:
    mov $KERNEL_ORG >> 0x4, %ax
    mov %ax, %es
    mov $KERNEL_ORG & 0xf, %bx
    mov $0x200 | KERNEL_SECTORS, %ax
    mov $(KERNEL_C << 0x8) | KERNEL_S, %cx
    mov $(KERNEL_H << 0x8) | FLOPPY_DRV, %dx
    int $0x13 /* <--- This int 0x13 doesn't seem to return */
    jnc 1f
    cmp $0x2, %bp
    je floppy_err
    inc %bp
    xor %ah, %ah
    int $0x13
    jmp 1b

Весь код можно найти в моем репозитории Github. Чтобы построить просто использовать make all а затем запустить с BOCHS с помощью команды bochs


Первым делом я проверил, правильно ли я понял все параметры. r в оболочке bochs дает:

CPU0:
rax: 00000000_534d0201 rcx: 00000000_00000005
rdx: 00000000_534d0000 rbx: 00000000_00000000
rsp: 00000000_00007700 rbp: 00000000_00000000
rsi: 00000000_000e0005 rdi: 00000000_00000316
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00000036
eflags 0x00007046: id vip vif ac vm rf NT IOPL=3 of df if tf sf ZF af PF cf

ah = 0x2 (рутина ID), al = 0x1 (количество секторов), ch = 0x0 (младший байт номера цилиндра), cl = 0x5 (номер сектора и старшие два бита цилиндра №), dh = 0x0 (номер головы), dl = 0x0 (номер диска).

sreg печатает для es:

es:0x0000

а также bx = 0x0таким образом, сектор загружен в 0x0:0x0как я и предполагал.


Я попробовал несколько вещей:

  1. Загрузить на физический адрес 0x600

    Я подумал, что, возможно, переопределение IVT или BDA может не быть хорошей идеей во время выполнения процедуры прерывания BIOS, поэтому я попытался загрузить сектор в 0x600 (es = 0x60, bx = 0x0) (Я знаю, что размер BDA составляет всего 256 байт). Тот же результат.

  2. Загрузите первый сектор на диске

    Может быть, чтение пятого сектора каким-то образом выходит за рамки или что-то еще? Код, который использует int 0x13 читать мои работы второго этапа, как ожидалось. int 0x13 на моем втором этапе похож, так что я бы ожидал, что это сработает. В качестве теста я изменил свой второй этап на чтение сектора 1, и он все еще не работал.

  3. Обнуление верхней части eax

    Я подумал, может быть, действительно есть ошибка в рутине BIOS и как-то eax используется и не ax, Я попытался обнулить верхнюю 16-битную часть eax... но безрезультатно.

Как я уже говорил ранее, я уже загрузил некоторые сектора с диска в память. Содержание GPRs прямо перед int 0x13 выглядит следующим образом (получено с использованием r в оболочке bochs):

CPU0:
rax: 00000000_00000203 rcx: 00000000_00090002
rdx: 00000000_00000000 rbx: 00000000_00000000
rsp: 00000000_00007700 rbp: 00000000_00000000
rsi: 00000000_000e7cdd rdi: 00000000_000000e2
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00007c59
eflags 0x00007046: id vip vif ac vm rf NT IOPL=3 of df if tf sf ZF af PF cf

sreg доходность es:0x8f60, который является динамически вычисляемым адресом прямо перед EBDA.

Сравнивая оба, я не вижу существенной разницы, которая могла бы повлиять на функционирование подпрограммы прерывания, поэтому проблема не может быть в параметрах, передаваемых через регистры.

У кого-нибудь есть другие предложения о том, что делать? Я как бы потерялся здесь...

1 ответ

Решение

Несколько проблем с вашим кодом чтения с дискеты Int 13h/AH=02h:

  1. Это тот, который вы уже определили в своем вопросе. Чтение секторов поверх 0x0000:0x0000 - плохая идея, когда вы работаете в реальном режиме. Это заглушит таблицу векторов прерываний (IVT). Область от 0x0000:0x0000 до 0x0040:0x0000 является IVT; область от 0x0040:0x0000 до 0x0060:0x0000 - это область данных BIOS (BDA). BDA следует рассматривать как "пустую область", которую могут использовать подпрограммы реального режима BIOS.

    Чтобы исправить это, загрузите его в безопасное место, например, 0x0060:0x0000 (физический адрес 0x00600).

    В защищенном режиме область между 0x00000000 и 0x00000600 может быть восстановлена ​​для других целей. Примечание. Не используйте область памяти расширенной области данных BIOS (EBDA) в качестве памяти общего назначения, поскольку в нее могут записываться режим управления системой (SMM) и расширенный интерфейс конфигурации и питания (ACPI).

  2. Ваш код переназначает 8259A, чтобы подготовиться к защищенному режиму. При этом IRQ перераспределяются в разные части IVT. Процедуры Int 13h могут полагаться на прерывания для запуска и подпрограммы прерывания BIOS для выполнения работы, необходимой для чтения с дискеты. Возможны IRQ0 (системный таймер) и IRQ6 (контроллер флоппи-дисковода). Если вы переназначаете базу 8259A в другом месте, подпрограммы прерывания, установленные BIOS, не будут выполняться. Это может привести к неожиданному поведению, включая Int 13h, которое никогда не вернется.

    Чтобы решить эту проблему, я рекомендую переназначить базу PIC 8259A после того, как вы перейдете в защищенный режим. К этому времени вы, вероятно, покончили с прерываниями BIOS, так что это не должно быть проблемой.

Другие вопросы по тегам