Проблема с BIOS INT 13H (чтение секторов с диска) - PullRequest
2 голосов
/ 25 октября 2019

Описание:

В своих попытках создать простую автономную программу я написал простой загрузчик в первом секторе. Его цель - загрузить программу в память. Для этого я использую INT 13h с AH = 2. Код такой:

disk_load:
  push dx           ; Store DX on stack so later we can recall how many sectors were requested to be read,
                    ; even if it is altered in the meantime.
  mov ah, 0x02        ; BIOS read sector.
  mov al, dh          ; Read DH sectors.
  mov ch, 0x00        ; Select cylinder 0.
  mov dh, 0x00        ; Select head 0.
  mov cl, 0x02        ; Start reading from second sector (i.e. after the boot sector).
  int 0x13            ; BIOS interrupt.
                      ;  <!----here
  pop dx
  ret

load_software:
  mov bx, 0x7e0
  mov es, bx
  xor bx, bx
  mov dh, 66
  mov dl, [BOOT_DRIVE]
  call disk_load

Я все тренировал в VirtualBox 5.2.8, и он работал на отлично. Перемещение всего на вторую машину, на которой установлен VirtualBox 6.0.14, проваливается. Прерывание заканчивается с установленным CF, указывая на сбой.

Чтение отличного ответа в Загрузчик не переходит на код ядра Я исправил потенциальную проблему с неопределенным значением DS, которая могла вызвать проблемы. Если я остановлю и выгрузлю состояние ЦП непосредственно перед вызовом int 0x13, я получу согласованное состояние на обоих виртуальных ящиках:

00: 00: 05.930849 eax = 00000280 ebx = 00007e00 ecx = 00000002 edx = 00000000esi = 00000000 edi = 0000fff0

00: 00: 05.930857 eip = 00007cc8 esp = 00007bf9 ebp = 00007bff iopl = 0 nv вверх ei pl nz na po nc

00: 00: 05.930864 cs ={0000 base = 0000000000000000 limit = 0000ffff flags = 0000009b} dr0 = 00000000 dr1 = 00000000

00: 00: 05.930877 ds = {0000 base = 0000000000000000 limit = 0000ffff flags = 00000093} dr2 = 00000000 dr3 = 00000000

00: 00: 05.930884 es = {0000 base = 0000000000000000 limit = 0000ffff flags = 00000093} dr4 = 00000000 dr5 = 00000000

00: 00: 05.930891 fs = {0000 base = 0000000000000000 limit =0000ffff flags = 00000093} dr6 = ffff0ff0 dr7 = 00000400

00: 00: 05.930898 gs = {0000 base = 0000000000000000 limit = 0000ffff flags = 00000093} cr0 = 00000010 cr2 = 00000000

00:00: 05.930904 сс = {0000 base = 0000000000000000 предел = 0000ffff flags = 00000093} cr3 = 00000000 cr4 = 00000000

00: 00: 05.930910 gdtr = 00000000000fe89f: 0047 idtr = 0000000000000000: ffff eflags = 00200246

При анализе всех значений я могу только заключить, что все входные параметрычтобы прерывания были настроены правильно. Состояние после дампа имеет CF и код ошибки:

00: 00: 08.984877 eax = 00000900 ebx = 00000000 ecx = 00000002 edx = 00000000 esi = 00000000 edi = 0000fff0

00: 00: 08.984887 eip = 00007cca esp = 00007bf9 ebp = 00007bff iopl = 0 nv up ei pl nz na po cy

00: 00: 08.984896 cs = {0000 base = 0000000000000000 предел = 0000ffff flags = 0000009bb} dr0 = 00000000 dr1 = 00000000

00: 00: 08.984909 ds = {0000 base = 0000000000000000 limit = 0000ffff flags = 00000093} dr2 = 00000000 dr3 = 00000000

00: 00: 08.984917 ru= {07e0 base = 0000000000007e00 limit = 0000ffff flags = 00000093} dr4 = 00000000 dr5 = 00000000

00: 00: 08.984925 fs = {0000 base = 0000000000000000 limit = 0000ffff flags = 00000093} dr6 = ffff0ff0 dr7 = 00000400

00: 00: 08.984934 gs = {0000 base = 0000000000000000 limit = 0000ffff flags = 00000093} cr0 = 00000010 cr2 = 00000000

00: 00: 08.984941 ss = {0000 base = 0000000000000000 ограничение= 0000ffff flags = 00000093} cr3 = 00000000 cr4 = 00000000

00: 00: 08.984948 gdtr = 00000000000fe89f: 0047 idtr = 0000000000000000: ffff eflags = 00200247

Принимая во внимание код ошибки AH=9 ошибка границы данных (попытка DMA через границу 64K или> 80h секторов) приводит меня кhttps://en.wikipedia.org/wiki/INT_13H, где сделано это утверждение:

Адресация буфера должна гарантировать, что полный буфер находится внутри данного сегмента, то есть (BX + size_of_buffer) <= 10000h. </p>

Это объяснит мои первоначальные проблемы, поэтому я сделал еще одно исправление, чтобы установить es=0x7e0 и bx=0. Это состояние кода, который отображается выше. Однако даже этот код не работает с состоянием, описанным выше.

Дальнейшее тестирование показывает, что я могу успешно прочитать до 65 секторов, но 66 или более не удается. Будучи странным числом, я вычислил конец 65-го сектора: 0xffff. Так что проблема становится немного более запутанной.

Вопросы:

Должны ли мои решения es=0x7e0 и bx=0 избегать пересечения сегментов (как я понимаю, это должно быть)?

Если это так, то почему возникает проблема с пересечением линейного адреса?

Или можно пересечь сегмент, но не маркер 0xffff в линейном адресе?

Спасибо за помощь.

1 Ответ

3 голосов
/ 26 октября 2019

Проблема в том, что вы "пытались выполнить DMA через границу 64 КБ". Это факт, что ваш целевой буфер охватывает физический адрес от 0x07E00 до 0x17E00, который пересекает границу 64 КБ в 0x10000. (Это не имеет ничего общего с границами сегментов, поэтому не имеет значения, какие значения сегментов и смещений вы используете для получения физического адреса 0x07E00.)

Причина этого заключается в том, как исходный IBM PCбыл разработан по дешевке. Вместо того чтобы использовать 16-битный 8086 с 16-битной шиной и 16-битными чипами поддержки, они использовали более дешевый 16-битный 8088 CPU с 8-битной шиной, который можно использовать с более дешевыми 8-битными чипами поддержки. В частности, выбранный им контроллер DMA способен адресовать только 64 КБ памяти, типичный предел адресации для 8-разрядных процессоров, для которых был разработан контроллер DMA. Они сделали это с помощью отдельного чипа, который обеспечивает старшие четыре бита адресов DMA, что позволяет адресовать полное 1024K адресное пространство 8088. (На IBM PC AT это было расширено, чтобы предоставить верхние восемь битов, чтобы можно было получить доступ ко всему адресному пространству 80286 16M.)

К сожалению, это означает, что старшие четыре бита адрес DMA фиксирован во время DMAоперация, которая эффективно делит адресное пространство 1024K на шестнадцать страниц по 64K. Любая попытка выполнить операцию DMA, которая пересекает одну страницу на другую, пересекает границу 64 КБ, вместо этого переходит в начало страницы. Хотя BIOS может обойти эту проблему, разделив чтение на две отдельные операции чтения, по одной для каждой страницы размером 64 КБ, он просто возвращает ошибку.

Обратите внимание, что это, как правило, только проблема с доступом к дискете как интерфейсами жесткого диска. обычно не используют контроллер IBM PC DMA.

Поскольку потенциальные проблемы с границами, подобные этой, также существуют при пересечении границ траектории и цилиндров, лучший способ обойти эту проблему - просто прочитать один сектор за раз,как сказал Шут и Майкл Петч в комментариях. В качестве простого обходного пути вы можете просто переместить свой буфер так, чтобы он начинался с физического адреса 0x10000, но вы можете обнаружить, что в реальных системах вы ограничены чтением только оставшихся секторов на дорожке.

...