Управление памятью.

Глава 11. Обработчик исключения страничного нарушения.

        Функции предлагаемого вашему вниманию обработчика исключения страничного нарушения просты - отобразить неприсутствующую логическую страницу памяти на свободную физическую. При необходимости, обработчик должен создать таблицу страниц и очистить в ней нулевые биты всех элементов PTE - в нашем примере очищается вся таблица целиком - это быстрее и проще. Также в этом примере подразумевается, что единственная причина исключения - неприсутствующая страница или таблица страниц; остальные причины у нас быть не могут, к ним относятся:
  •  
  • попытка записи в страницу, предназначенную только для чтения - такое может быть только, если бит R/W (1-й бит в PTE или PDE) сброшен, а у нас он везде устанавливается.
  •  
  • нарушение уровня привилегий при обращении к странице - такое может быть только при обращении программы с уровня 1, 2 или 3 к странице, бит U/S (2-й бит в PTE или PDE) в которой сброшен (страница на системном уровне), либо наоборот, в процессорах, начиная с Intel486 при установленном бите WP (Write Protect) в CR0 при попытке записи с нулевого уровня в страницу уровня пользователя. Всё это не возможно в нашем примере по причине его простоты, т.к. весь код и данные в нём находятся на уровне 0.
  •  
  • установлены зарезервированные биты в элементах PTE или PDE - в нашем примере этого не может быть, т.к. любой элемент PTE или PDE конструируется из адреса и двух установленных битов - 0-го и 1-го.

            Из всего многообразия возможных реализаций подобных обработчиков вам предлагается следующий. Просто замените макрос "exeption_0e_handler" в файле "pmode8.lib" на приводимый ниже, а лучше - скачайте полный пример (в следующей главе). Хочу вас предупредить, что писать такой обработчик очень сложно, потому что отсутсвие средств отладки совместно с чудесами в памяти делают отладку практически невозможной.
            Основная проблема при написании подобной процедуры заключается в создании новой таблицы страниц и здесь эта проблема решена следующим образом: в программе определён сегмент Page_Table, который находится по адресу 1Мб - 4Кб, т.е. занимает последнюю страницу первого мегабайта. Для того, чтобы очистить новую таблицу страниц, либо добавить в неё новый элемент PTE, обработчик отображает последнюю страницу 1-го мегабайта на ту физическую страницу, в которой расположена текущая таблица страниц и после перезагрузки регистра CR3 обращение к сегменту Page_Table обеспечивает доступ к этой странице.
            Для того, чтобы отображать последнюю страницу 1-го мегабайта на любую, нужную обработчику, используется следующий "трюк": в программе определена переменная Page_Directory_adr, которая хранит адрес каталога страниц относительно основного сегмента данных, доступного по селектору Data_selector. Первая таблица страниц, соответствующая элементу PDE 0, находится сразу же после каталога страниц, т.е. адрес её начала на 4096 больше адреса, хранящегося в Page_Directory_adr. Последняя страница 1-го мегабайта описывается 255-м элементом PTE, который находится в таблице страниц по смещению 1020 (для наглядности используется 1024 - 4). Таким образом, отображение сегмента Page_Table на новую физическую страницу будет выглядеть так:

    	mov	eax,page_address	; Какой-либо адрес страницы.
    	mov	al,3		;    Устанавливаем биты 0 и 1 (зачем - см.
    				; в тексте обработчика)
    
    	mov	bx,Page_Directory_adr
    	add	bx,4096 + 1024 - 4
    	mov	[ bx ],eax		; Записали новый PTE
    
    	mov	eax,cr3		; Перезагрузили CR3 (т.е. PDBR).
    	mov	cr3,eax
    
    	;    Всё, сегмент Page_Table отображён на физическую страницу,
    	; находящуюся по адресу page_address.
    

            Хотелось бы заметить, что использование всяких "трюков", "уловок", "фокусов" и прочих "магических" приёмов является нормальным явлением при написании программ на Ассемблере, причём, использование новых особенностей процессоров, призванных, вроде бы, повысить эффективность работы, порождает дополнительные "трюки", хотя, позволяет избавиться от некоторых старых (например, адресация памяти через любые 32-разрядные регистры общего назначения значительно упрощает доступ к памяти).

            Итак, обработчик исключения страничного нарушения:


    exeption_0e_handler macro ; Обработчик исключения страничного нарушения. cli pushad push es push gs mov tmp_ax,ax ; Сохраняем значение AX, т.к. перед ; возвратом из обработчика нужно будет ; извлечь из стека dw-код ошибки, который ; в данном примере не используется. ; Это тестовый кусок кода, предназначенный для вывода на экран числа ; отображённых страниц: mov dx,0a26h mov ax,pages_counter inc ax mov pages_counter,ax call put_dw_num ; Конец тестового кода. mov ax,Page_Directory ; Page_Directory описывает mov gs,ax ; каталог страниц. ; CR2 содержит адрес страничного нарушения, т.е. тот адрес, при обращении ; к которому процессор обнаружил, что соответствующая страница или таблица ; страниц не присутствует в памяти и сгенерировал исключение. mov ebx,cr2 mov ebp,ebx ; Сохраняем в EBP значение CR2, т.к. ; доступ к регистрам общего назначения ; быстрее, чем к регистрам управления. shr ebx,20 and bl,11111100b ; EBX = PDE (мы сбросили все биты адреса страничного нарушения, кроме 10 ; старших бит, которые содержат номер элемента каталога страниц (т.е. PDE). ; Теперь в младшей половине EBX, т.е. в BX содержится PDE, умноженный ; на 4 - это указатель на описание соответствующего элемента PDE в каталоге ; страниц. mov edx,gs:[ ebx ] ; EDX = Текущий PDE. test dl,1 ; Текущий PDE отображён или нет? jnz e0eh_1 ; Если да, то отображаем страницу. ; Иначе - устанавливаем новую таблицу страниц. call find_free_phys_page ; EAX = адрес свободной физической ; страницы. jc e0eh_err ; Если свободных страниц больше ; нет, то после возврата из процедуры ; find_free_phys_page будет ; установлен флаг CF, иначе - сброшен. mov al,3 ; Запись в AL 3 можно рассматривать как, ; установку двух бит - 0 и 1 регистра EAX. ; При записи EAX в каталог страниц, эти биты ; будут соответствовать битам P и R/W в ; элементе PDE и будут означать, что таблица ; страниц присутствует (P=1) и для всех её ; страниц разрешено считывание и ; запись (R/W=1). mov gs:[ ebx ],eax ; Записали PDE. mov edx,eax ; Временно сохранили адрес в EDX. ; Теперь мы отобразим один сегмент - Page_Table (он состоит из одной ; страницы) на этот адрес и очистим всю страницу, потому что теперь на неё ; будет отображеёна новая таблица страниц и все её элементы должны иметь ; 0 хотя бы в бите P. mov bx,Page_Directory_adr ; DS:BX - указатель на начало ; каталога страниц. add bx,4096 + 1024 - 4 ; А теперь - указатель на ; последний элемент PTE первой ; таблицы страниц. mov [ bx ],edx ; Записываем в него адрес новой физической ; страницы памяти. mov eax,cr3 ; Перегружаем содержимое CR3 (т.е. PDBR), mov cr3,eax ; это заставит процессор сбросить все TLB и ; использовать обновлённый каталог страниц и ; таблицы страниц. ; Очищаем новую физическую страницу: mov ax,Page_Table mov es,ax xor di,di mov cx,1024 cld rep stosd ; Теперь из адреса страничного нарушения выделяем номер страницы (т.е. ; элемент PTE), при обращении к которой возникло исключение. Эта страница ; может быть либо не отображена (в её PTE сброшен бит P), либо иметь запрет ; доступа по уровню привилегий, но в данном примере подразумевается, что ; страница может быть только не отображена. mov eax,ebp shl eax,10 shr eax,20 and al,11111100b mov di,ax ; DI = номер PTE, умноженный на 4 - ; получается указатель на этот PTE в ; текущей таблице страниц. call find_free_phys_page ; Ищем новую свободную физическую ; страницу, на которую уже и ; отобразим неприсутствующую ; логическую страницу. jc e0eh_err mov al,3 ; Устанавливаем биты 0 и 1. mov es:[ di ],eax ; Записываем новый элемент PTE. mov eax,cr3 ; Перезагружаем PDBR. mov cr3,eax jmp e0eh_end ; Сброс конвейера команд и переход на ; конец процедуры. e0eh_1: ; EBP = адрес страничного нарушения. ; EBX = BX - указатель на тот PDE, в котором нужно отобразить страницу. ; Если этот указатель совпадает с Current_PDE, то сегмент Page_Table уже ; отображён на эту таблицу страниц, иначе - нет и мы будем его отображать. mov ax,Current_PDE cmp ax,bx je e0eh_2 mov Current_PDE,bx and dx,0f000h mov dl,3 mov bx,Page_Directory_adr add bx,4096 + 1024 - 4 mov [ bx ],edx mov eax,cr3 mov cr3,eax jmp e0eh_2 e0eh_2: ; Сегмент Page_Table уже отображён на ту таблицу страниц, в которой нужно ; отобразить страницу. mov ax,Page_Table mov es,ax call find_free_phys_page jc e0eh_err ; Выделяем из адреса страничного нарушения номер PTE и преобразуем его ; в смещение в таблице страниц. mov ebx,ebp shl ebx,10 shr ebx,20 and bl,11111100b mov al,3 ; Устанавливаем биты 0 и 1 (P и R/W). mov es:[ bx ],eax ; Записываем новый PTE. mov eax,cr3 ; Перезагружаем PDBR. mov cr3,eax jmp e0eh_end ; Сброс конвейера команд. e0eh_end: pop gs pop es popad pop ax ; Код ошибки здесь игнорируется. mov ax,tmp_ax iret e0eh_err: ; Если больше нет свободных физических страниц, обработчик генерирует ; сообщение о нехватке памяти и аварийно завершает программу. lea bx,_no_mem_ mov dx,0200h call put_zs jmp Return_to_R_Mode endm

    Следующая глава Оглавление Вопросы? Замечания? Пишите: sasm@narod.ru

      Copyright © Александр Семенко.
    TopList

    Hosted by uCoz