В Linux, работающем на платформе x86, где адресное пространство реального режима отображается в защищенном режиме ядра? В режиме ядра поток может напрямую обращаться к адресному пространству ядра. Ядро находится в нижних 8 МБ, таблица страниц находится в определенной позиции и т. д. (как описано здесь). Но куда уходит адресное пространство реального режима? Можно ли получить к нему доступ напрямую? Например, BIOS и надстройки BIOS (см. здесь)?
Linux x86: где отображается адресное пространство реального режима в защищенном режиме ядра?
Ответы (2)
(Мой x86-fu немного слабоват. Я добавлю несколько тегов, чтобы другие люди могли (надеюсь) поправить меня, если я где-то лгу.)
Физические адреса одинаковы в реальном и защищенном режиме. Единственная разница заключается в том, как вы переходите от адреса (смещения), указанного в инструкции, к физическому адресу:
В реальном режиме физический адрес в основном
(segment_reg << 4) + offset
.В защищенном режиме физический адрес
translate_via_page_table([segment_reg] + offset)
.
Под [segment_reg]
я подразумеваю базовый адрес сегмента, найденный в глобальной или локальной таблице дескрипторов. по смещению в segment_reg
. translate_via_page_table()
означает трансляцию адресов, выполненную посредством пейджинга (если она включена).
Глядя здесь, кажется, что ПЗУ BIOS находится по физическим адресам 0x000F0000-0x000FFFFF. Чтобы получить доступ к этой памяти в защищенном режиме с пейджингом, вам нужно будет сопоставить ее где-нибудь с виртуальным адресным пространством, настроив правильный таблица страниц записей. Если предположить, что страницы имеют размер 4 КБ (обычный случай), для сопоставления всего диапазона потребуется 16 ((0xFFFFF-0xF0000+1)/4096) записей.
Чтобы увидеть, как работает ядро Linux, вы можете посмотреть, как, например. Реализован /dev/mem
, позволяющий считывать произвольные физические адреса. Реализация находится в drivers/char/mem.c.
Следующая команда (например, из этого ответа) создаст дамп диапазона памяти 0xC0000-0xFFFFF (это означает, что она также включает видео BIOS, на карту памяти, указанную выше):
$ dd if=/dev/mem bs=1k skip=768 count=256 > bios
1024*768 = 0xC0000 и 1024*(768+256) - 1 = 0xFFFFF, что дает ожидаемый диапазон физической памяти.
Немного отслеживая ситуацию, read_mem()
в drivers/char/mem.c вызывает xlate_dev_mem_ptr()
, который имеет специфичную для x86 реализацию в arch/x86/mm/ioremap.c. Вызов ioremap_cache()
в этой функции, по-видимому, отвечает за сопоставление на странице, если это необходимо.
Обратите внимание, что подпрограммы BIOS не будут работать в защищенном режиме. Они предполагают, что процессор работает в реальном режиме.
Для 32-разрядной версии Linux x86 первый 896MB
физического ОЗУ сопоставляется с непрерывным блоком виртуальной памяти, начиная с виртуального адреса с 0xC0000000
по 0xF7FFFFFF
. Виртуальные адреса от 0xF8000000
до 0xFFFFFFFF
назначаются динамически различным частям физической памяти, поэтому ядро может отображать окно 128MB
в любую часть физической памяти за пределами 896MB
.
Само ядро загружается по физическому адресу 1 МБ и выше, оставляя первый МБ свободным. Этот первый МБ используется, например, для буферов DMA, которые необходимы ISA-устройствам, потому что они используют контроллер DMA 8237, который может быть сопоставлен только с такими адресами.
Таким образом, чтение с адреса виртуальной памяти 0xC0000000
на самом деле является чтением с физического адреса 0x00000000
(при условии, что ядро пометило эту страницу как существующую).
/dev/kmem
(который похож на /dev/mem
, но вместо этого использует виртуальную память ядра), но, к сожалению, ядро на моей машине, кажется, скомпилировано без CONFIG_DEVKMEM
, и я пошел спать. Если бы /dev/kmem
был доступен, то я думаю, что проверка работоспособности должна была бы читать, например, из. 0xC00C0000 и посмотрите, получите ли вы те же данные, что и в примере /dev/mem
(при условии 32-разрядной версии, если детали отличаются для 64-разрядной версии). Некоторый окончательный источник тоже был бы хорош. :)
- person Ulfalizer; 29.03.2015
dd
.
- person Ulfalizer; 30.03.2015