Отключение пейджинга в x86 32bit

Я пытаюсь писать напрямую в физическую ячейку памяти, поэтому я использую функцию сборки, чтобы сначала отключить разбиение на страницы, записать значение, а затем повторно включить разбиение на страницы, но по какой-то причине ошибка страницы все еще запускается при попытке записать ценность.

Насколько я понимаю, в x86-32bit разбиение по страницам включается и выключается переключением бита 32 в cr0, так что вот моя функция сборки:

mov 4(%esp), %ecx //address
mov 8(%esp), %edx //value

mov %cr0, %eax
and $0x7fffffff, %eax
mov %eax, %cr0

mov %edx, (%ecx) //this line still triggers a page fault somehow

or $0x80000000, %eax
mov %eax, %cr0

ret

Это правильный способ достичь того, что я хочу делать? Если да, то почему ошибка страницы все еще запускается с перевернутым битом в cr0?


person user12341234135245    schedule 21.11.2015    source источник
comment
Вы пытаетесь отключить подкачку в ОС с уже включенной подкачкой? Это ваша собственная ОС или известная ОС (Linux, Windows, MacOS)? Ни в коем случае не следует отключать разбиение на страницы в современной ОС, если оно уже включено. Для записи на физический адрес используйте средство, предоставляемое ОС (/dev/mem или Device\PhysicalMemory), или функцию, предоставляемую ядром. BTW, кажется (я не привык к синтаксису AT&T), что вы инвертировали значение адреса в своем коде .   -  person Neitsa    schedule 21.11.2015
comment
Ага, я тоже заметил случайную инверсию, исправлю в посте. Но на самом деле это работает на ОС, которую я создавал с нуля и запускаемой на эмуляторе, так что это нормально, просто она работает не так, как я ожидал.   -  person user12341234135245    schedule 21.11.2015
comment
На самом деле это бит 31, потому что бит 0 - это первый бит.   -  person cadaniluk    schedule 21.11.2015
comment
Не вызовет ли отключение разбиения по страницам ошибку, если этот код выполняется со страницы с разными физическими и виртуальными адресами? Или, скорее, вызвать выполнение какой-либо другой инструкции, теперь, когда EIP является физическим адресом. Вы пошагово выполняли это в bochs или что-то еще, чтобы точно увидеть, что происходит, когда вы получаете ошибку?   -  person Peter Cordes    schedule 21.11.2015
comment
Кроме того, я думаю, что разумным способом было бы сопоставить физическую страницу где-нибудь в виртуальной памяти, а затем записать в нее. Но это скучно. Безумный способ, безусловно, выглядит интересно.   -  person Peter Cordes    schedule 21.11.2015
comment
Если код, стек, GDT, IDT, все обработчики прерываний и все, что они могут использовать, отображены идентичности, то было бы безопасно отключить и повторно включить разбиение на страницы; но это также будет медленным (сделайте недействительными все записи TLB), и вам нужно будет задаться вопросом, почему вы вообще беспокоились о включении разбиения по страницам, если все сопоставлено с идентификацией .. ;-)   -  person Brendan    schedule 21.11.2015
comment
Все это действительно сопоставлено с идентичностью, и я фактически отключаю прерывания непосредственно перед этим. Я понимаю, что это неразумный способ сделать это, и он неэффективен, это больше просто упражнение, чтобы увидеть, выполнимо ли это. При этом, есть ли причина, по которой то, что я делаю, не работает?   -  person user12341234135245    schedule 22.11.2015
comment
В настоящее время происходит то, что я получаю ошибку страницы по адресу, на который я пытаюсь переместить значение, но я хочу, чтобы вместо этого просто перемещалось значение туда, не вызывая ошибки страницы.   -  person user12341234135245    schedule 22.11.2015


Ответы (2)


Изменение в регистре CR0 станет активным, когда будет выполнена инструкция перехода (только дальний переход?).

Однако отключение разбиения на страницы - не лучшая идея: вы должны гарантировать, что код находится в отображаемой памяти 1: 1 и что прерывания отключены.

Если вы используете стек, вы также должны убедиться, что стек отображается 1: 1.

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

person Martin Rosenau    schedule 22.11.2015

Программное обеспечение для архитектур Intel 64 и IA-32 Руководство разработчика Руководство по системному программированию описывает, как отключить разбиение на страницы как часть процедуры переключения из защищенного режима обратно в реальный режим:

9.9.2 Возвращение в режим реального адреса

Процессор переключается из защищенного режима обратно в режим реального адреса, если программное обеспечение очищает бит PE в регистре CR0 с помощью инструкции MOV CR0. Процедура, которая повторно входит в режим реального адреса, должна выполнить следующие шаги:

  1. Отключить прерывания. Инструкция CLI отключает маскируемые аппаратные прерывания. Прерывания NMI можно отключить с помощью внешней схемы.
  2. Если подкачка включена, выполните следующие операции:

    • Transfer program control to linear addresses that are identity mapped to physical addresses (that is, linear addresses equal physical addresses).
    • Убедитесь, что GDT и IDT находятся на страницах с отображением идентификаторов.
    • Очистите бит PG в регистре CR0.
    • Переместите 0H в регистр CR3, чтобы очистить TLB.

Кажется, вы пропустили последний шаг. TLB (резервный буфер трансляции) - это место, где ЦП кэширует записи таблицы страниц и остается активным после сброса бита PG. Вам необходимо очистить TLB, иначе ЦП продолжит его использовать.

Обратите внимание, что вам придется перезагрузить CR3 перед повторной установкой бита PG. Кроме того, поскольку то, что вы делаете, очень необычно, вы можете столкнуться с ошибками и проблемами совместимости с вашим эмулятором. Он может правильно обрабатывать отключение подкачки только как часть процесса переключения обратно в реальный режим, поскольку это, вероятно, единственный сценарий, в котором он был протестирован. Даже физические процессоры могут иметь проблемы в этой области.

person Ross Ridge    schedule 23.11.2015