§ Возврат в режим реальных адресов
Практически защищённый режим процессора можно использовать двумя способами:- Операционная система. Процессор входит в P-Mode при загрузке ОС и не возвращается в R-Mode. Примером таких ОС являются Windows 95 и старше либо специализированные ОС, управляющие контроллерами на базе 32-разрядных процессоров.
- DOS-программа. Программа запускается в ОС, которая работает в R-Mode (например, MS-DOS), переходит в P-Mode, выполняет свою работу, возвращается обратно в R-Mode и завершает свою работу. Примеров множество: это hymem.sys, обеспечивающий через P-Mode работу с памятью выше 1-го мегабайта (XMS-память); также это игрушки, работающие через Dos4GW - предзагружаемую ОС защищённого режима; это Windows 3.xx.
Фактически, переход в режим реальных адресов может быть только из защищённого режима и осуществляется сбросом бита PE в CR0:
mov eax, cr0 and al, 0feh mov cr0, eaxили так:
mov eax, cr0 btr eax, 0 mov cr0, eaxВы наверное уже догадались, что на самом деле не всё так просто. Действительно, выполнение трёх вышеприведенных команд переведёт процессор в R-Mode, но дальше он повиснет либо произойдёт аппаратный сброс, потому что программная среда не будет соответствовать режиму реальных адресов.
Для корректного перехода из P-Mode в R-Mode необходимо подготовить процессор следующим образом:
- Запретить прерывания (CLI)
- Передать управление в читаемый сегмент кода, имеющий предел в 64Кб (FFFFh)
- Предел = 64 Кб (FFFFh)
- Байтная гранулярность (G = 0)
- Расширяется вверх (E = 0)
- Записываемый (W = 1)
- Присутствующий (P = 1)
- Базовый адрес = любое значение
- Сбросить флаг PE в CR0
- Выполнить команду far jmp на программу режима реальных адресов
- Загрузить в регистры SS, DS, ES, FS и GS необходимые значения или 0
- Разрешить прерывания (STI)
Здесь подразумевается, что вы уже ознакомились со всеми предыдущими главами, поэтому предварительного подробного описания перехода в R-Mode не будет и мы перейдём сразу к примеру.
Этот пример является полноценной программой и в нём без комментариев повторяется переход в P-Mode. Процесс подготовки дескрипторов и GDTR зависит от предназначения каждого примера и по этой причине я не вынес его в отдельную функцию а полностью описываю каждый раз.
Пример ориентирован на использование как самостоятельная .com-программа. Это сделано по следующим причинам:
- Пользоваться обычной программной средой - библиотеки языков высокого уровня, прерывания MS-DOS и прочими особенностями этой ОС из защищённого режима вы не сможете.
- Размеры кода и данных в данном примере не большие и удобно использовать формат файла .com.
- COM-программу можно просто загрузить в память как блок данных и, передав управление на смещение 100h, использовать как оверлей.
- Вся мощь защищённого режима раскрывается при использовании 32-разрядного кода и данных. Инициализация защищённого режима должна происходить 16-разрядным кодом и 32-разрядные программы удобно использовать как отдельные модули.
Пример используется вместе с файлом "pmode.lib.asm".
Пример 3. Вход в защищённый режим и возврат в режим реальных адресов.
org 100h ;------------------------------------------------------------------------ ; Определяем селекторы как константы. У всех у них биты TI = 0 (выборка ; дескрипторов производится из GDT), RPL = 00B - уровень привилегий - ; нулевой. Code_selector equ 8 Stack_selector equ 16 Data_selector equ 24 Screen_selector equ 32 R_Mode_Code equ 40 ; Селектор дескриптора сегмента кода для возврата в режим реальных адресов R_Mode_Data equ 48 ; Селектор дескриптора сегментов стека и данных ;------------------------------------------------------------------------ ; Сохраняем сегментные регистры, используемые в R-Mode: mov [R_Mode_SS], ss mov [R_Mode_DS], ds mov [R_Mode_ES], es mov [R_Mode_FS], fs mov [R_Mode_GS], gs ; Подготавливаем адрес возврата в R-Mode: mov [R_Mode_segment], cs lea ax, [R_Mode_entry] mov [R_Mode_offset], ax ; Подготовка к переходу в защищённый режим: mov bx, GDT + 8 xor eax, eax mov edx, eax push cs pop ax shl eax, 4 mov dx, 65535 mov cl, 10011000b call set_descriptor ; Code lea dx, [Stack_seg_start] add eax, edx mov dx, 1024 mov cl, 10010110b call set_descriptor ; Stack xor eax, eax mov ax, ds shl eax, 4 mov dx,0ffffh mov cl, 10010010b call set_descriptor ; Data mov eax, 0b8000h mov edx, 4000 mov cl, 10010010b call set_descriptor ; Screen ; Готовим дополнительные дескрипторы для возврата в R-Mode: xor eax,eax push cs pop ax shl eax, 4 ; EAX = физический адрес сегмента кода ; (и всех остальных сегментов, т.к. ; это .com-программа) mov edx,0ffffh mov cl, 10011010b ; P=1, DPL=00b, S=1, Тип=101b, A=0 call set_descriptor ; R_Mode_Code mov cl, 10010010b ; P=1, DPL=00b, S=1, Тип=001b, A=0 call set_descriptor ; R_Mode_Data ; Устанавливаем GDTR: xor eax, eax mov edx, eax mov ax, ds shl eax, 4 lea dx, [GDT] add eax, edx mov [GDT_adr],eax mov dx, 55 ; Предел GDT = 8 * (1 + 6) - 1 mov [GDT_lim], dx cli lgdt [GDTR] mov [R_Mode_SP], sp ; Указатель на стек сохраняем в последний момент. ; Переходим в защищённый режим: mov eax, cr0 or al, 1 mov cr0, eax ; Процессор в защищённом режиме db 0eah ; Команда far jmp Code_selector:P_Mode_entry. dw P_Mode_entry dw Code_selector include "pmode.lib.asm" ;------------------------------------------------------------------------ P_Mode_entry: mov ax, Screen_selector mov es, ax mov ax, Data_selector mov ds, ax mov ax, Stack_selector mov ss, ax mov sp, 0 ; Сообщаем о входе в P-Mode (выводим ZS-строку): lea bx, [Start_P_Mode_ZS] mov di, 480 call putzs ; Работа программы в защищённом режиме (здесь - только вывод строки): lea bx, [P_Mode_ZS] add di, 160 call putzs ; ---------------------------------------------------------------------- ; Возвращаемся в режим реальных адресов. ; 1. Запретить прерывания (CLI). ; Прерывания уже запрещены при входе в P-Mode. ; 2. Передать управление в читаемый сегмент кода, имеющий предел в 64Кб. ; ---------------------------------------------------------------------- db 0eah ; Команда far jmp R_Mode_Code:Pre_R_Mode_entry. dw Pre_R_Mode_entry dw R_Mode_Code Pre_R_Mode_entry: ; 3. Загрузить в SS, DS, ES, FS и GS селекторы дескрипторов, имеющих ; следующие параметры: ; 1) Предел = 64 Кб (FFFFh) ; 2) Байтная гранулярность (G = 0) ; 3) Расширяется вверх (E = 0) ; 4) Записываемый (W = 1) ; 5) Присутствующий (P = 1) ; 6) Базовый адрес = любое значение mov ax, R_Mode_Data ; Селектор R_Mode_Data - "один на всех". mov ss, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax ; 4. Сбросить флаг PE в CR0. mov eax, cr0 and al, 0feh ; FEh = 1111'1110b mov cr0, eax ; 5. Выполнить команду far jump на программу режима реальных адресов. db 0eah R_Mode_offset dw ? ; Значения R_Mode_offset и R_Mode_segment R_Mode_segment dw ? ; сюда прописались перед входом в ; защищённый режим (в начале программы). ;------------------------------------------------------------------------ R_Mode_entry: ; 6. Загрузить в регистры SS, DS, ES, FS и GS необходимые значения или 0 ; (восстанавливаем сохранённые значения): mov ss, [R_Mode_SS] mov ds, [R_Mode_DS] mov es, [R_Mode_ES] mov fs, [R_Mode_FS] mov gs, [R_Mode_GS] mov sp, [R_Mode_SP] ; Восстанавливаем указатель стека ; непосредственно перед разрешением ; прерываний. ; 7. Разрешить прерывания (STI). sti ; Выводим ZS-строку "Back to real address mode..." lea bx, [R_Mode_ZS] mov ax, 0b800h mov es, ax mov di, 800 call putzs ; Функция putzs универсальна и работает ; в обоих режимах. int 20h ; Конец программы (здесь - выход в MS-DOS). ;------------------------------------------------------------------------ ; ZS-строка для вывода при входе в P-Mode: Start_P_Mode_ZS: db "Entering to protected mode...",0 ; ZS-строка для вывода при работе в P-Mode: P_Mode_ZS: db "Working in P-mode...",0 ; ZS-строка для вывода в R-Mode: R_Mode_ZS: db "Back to real address mode...",0 ;------------------------------------------------------------------------ ; Значения регистров, которые программа имела до перехода в P-Mode: R_Mode_SP dw ? R_Mode_SS dw ? R_Mode_DS dw ? R_Mode_ES dw ? R_Mode_FS dw ? R_Mode_GS dw ? ;------------------------------------------------------------------------ ; Образ регистра GDTR: GDTR: GDT_lim dw ? GDT_adr dd ? ;------------------------------------------------------------------------ GDT: dd ?,? ; 0-й дескриптор dd ?,? ; 1-й дескриптор (кода) dd ?,? ; 2-й дескриптор (стека) dd ?,? ; 3-й дескриптор (данных) dd ?,? ; 4-й дескриптор (видеопамяти) dd ?,? ; 5-й дескриптор (код для перехода в R-Mode) dd ?,? ; 6-й дескриптор (стек и данные для перехода в R-Mode) ;------------------------------------------------------------------------ db 1024 dup (?) ; Зарезервировано для стека. Stack_seg_start: ; Последняя метка программы - отсюда будет расти стек.