Чтение с дискеты (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
как я и предполагал.
Я попробовал несколько вещей:
Загрузить на физический адрес
0x600
Я подумал, что, возможно, переопределение IVT или BDA может не быть хорошей идеей во время выполнения процедуры прерывания BIOS, поэтому я попытался загрузить сектор в
0x600
(es = 0x60
,bx = 0x0
) (Я знаю, что размер BDA составляет всего 256 байт). Тот же результат.Загрузите первый сектор на диске
Может быть, чтение пятого сектора каким-то образом выходит за рамки или что-то еще? Код, который использует
int 0x13
читать мои работы второго этапа, как ожидалось.int 0x13
на моем втором этапе похож, так что я бы ожидал, что это сработает. В качестве теста я изменил свой второй этап на чтение сектора 1, и он все еще не работал.Обнуление верхней части
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:
Это тот, который вы уже определили в своем вопросе. Чтение секторов поверх 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).
Ваш код переназначает 8259A, чтобы подготовиться к защищенному режиму. При этом IRQ перераспределяются в разные части IVT. Процедуры Int 13h могут полагаться на прерывания для запуска и подпрограммы прерывания BIOS для выполнения работы, необходимой для чтения с дискеты. Возможны IRQ0 (системный таймер) и IRQ6 (контроллер флоппи-дисковода). Если вы переназначаете базу 8259A в другом месте, подпрограммы прерывания, установленные BIOS, не будут выполняться. Это может привести к неожиданному поведению, включая Int 13h, которое никогда не вернется.
Чтобы решить эту проблему, я рекомендую переназначить базу PIC 8259A после того, как вы перейдете в защищенный режим. К этому времени вы, вероятно, покончили с прерываниями BIOS, так что это не должно быть проблемой.