2016-12-06 19 views
3

На втором этапе моего загрузчика я пытаюсь загрузить некоторые сектора с виртуальной дискеты в память в bochs, но после вызова int 0x13 процедура просто не возвращается.Чтение гибких дисков (AH = 0x2, 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 repository. Для того, чтобы построить просто использовать 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

Вы пытались изолировать проблему с помощью [mcve]? В частности, как вы настраиваете регистры сегментов, учитывая, что IP-адрес равен 36h в первом случае и 7c59h во втором. –

+0

", поэтому сектор загружается в 0x0: 0x0" - так, err, переписывая таблицу векторов прерываний? – davmac

+0

@MargaretBloom 'cs' является' 0x8f60' для первого, '0x0' для последнего. Чтобы очистить ситуацию: BIOS загружает загрузчик на физический адрес '0x7c00', загрузчик загружает загрузчик второго уровня прямо под EBDA, начальное значение которого получается динамически, второй загрузчик загружает ядро ​​на физический адрес' 0x0'. Я бы сделал MCVE, чтобы проверить, что загрузочные сектора работают вообще, если ** ** ** попытка была обработана. Однако загрузка вторичного загрузчика работает просто отлично. – Downvoter

ответ

6

Пара вопросов с Int 13h/AH=02h гибком кодом чтения диска:

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

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

    Однажды в защищенном режиме область между 0x00000000 и 0x00000600 может быть восстановлена ​​для других целей. Примечание: Не используйте область памяти (EBDA) в качестве памяти общего назначения, так как System Management Mode (SMM) и Advanced Configuration and Power Interface (ACPI) могут писать на нее.

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

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