Всем привет! Пытаюсь использовать сабж для передачи управления. Примерно так (это 32-х разрядный вариант, пробовал и с 16-ю разрядами, с тем же результатом): Код (Text): ; Подготавливаем стек pushfd mov eax, cs ; в cs - селектор сегмента кода push eax mov eax, offset target_code push eax ; Поехали! iretd Происходит всё это на нулевом уровне привилегий, при передаче управления уровень привилегий не меняется (начинал то я как раз со смены уровней, но оказывается даже такой простой пример не работает). После выполнения VMWare сообщает о kernel stack fault. Если заменить iret на retf (и для чистоты закомментировать pushf), всё работает. В реальном режиме - всё работает. При возврате из настоящего обработчика прерывания - всё работает. Видимо, iret делает ещё что-то, чего я не понимаю... И зависит это небось от каких-нибудь флагов. Но я чего-то пока не смог разобраться. Подскажите пожалуйста, если кто знает. Вот полный код примера: Код (Text): ; iret_pm.asm .model tiny .code .386p org 100h ;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Структуры ; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Сегментный дескриптор segment_descriptor struct limit_low dw 0 ; Младшие два байта поля Segment limit base_low dw 0 ; Младшие два байта поля Base Address base_high0 db 0 ; Второй байт поля Base Address type_and_permit db 0 ; Флаги flags db 0 ; Ещё одни флаги base_high1 db 0 ; Старший байт поля Base Address segment_descriptor ends ; Регистр, описывающий таблицу дескриптров table_register struct limit dw 0 ; Table Limit base dd 0 ; Linear Base Address table_register ends ;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Код ; ;;;;;;;;;;;;;;;;;;;;;;;;;; start: push cs pop ds push 0b800h pop es ; Устанавливаем правильный сегмент в long-jmp-to-RM mov ax, cs mov cs:rm_cs, ax ; Устанавливаем базу сегмента кода call cs_to_eax mov cs0_dsc.base_low, ax shr eax, 16 mov cs0_dsc.base_high0, al ; Инициализируем GDT call initialize_gdt call disable_interrupts call set_PE ; 16-разрядный дальний переход. Перключает содержимое cs из нормального ; для реального режима (адрес) в нормальное для защищённого (селектор). ; Базовый адрес целевого сегмента совпадает с cs, ; поэтому смещение можно прописать сразу db 0EAh ; код команды дальнего перехода dw $ + 4 ; смещение dw cs0_sel ; селектор ; подготовка стека ; pushfd mov eax, cs0_sel push eax mov eax, offset target_code push eax ; поехали! ; iretd ; - не пашет, говорит stack fault db 66h ; 32-х разрядные операнды retf back_to_rm: call clear_PE ; 16-разрядный дальний переход. Перключает содержимое cs из нормального ; для защищённог режима (селектор) в нормальное для реальног (адрес). ; Адрес сегмента вычисляется и прописывается во время выполнения. db 0EAh ; код команды дальнего перехода dw $ + 4 ; смещение rm_cs dw 0 ; сегмент call enable_interrupts ret ;; ;; Целевой код ;; target_code: push eax push ecx mov eax, 0 ; Текущий символ mov ecx, 80 * 25 ; Колическтво сиволов на экране screen_loop1: inc eax inc byte ptr es:[eax] ; Меняем атрибут inc eax loop screen_loop1 pop ecx pop eax jmp back_to_rm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Данные ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Глобальная таблица дескрипторов GDT label byte ; Нулевой дескриптор segment_descriptor <> ; Дескриптор сегмента кода cs0_dsc segment_descriptor <0ffffh, 0, 0, 10011010b, 0, 0> ; Данные для загрузки в GDTR gdtr table_register <$ - GDT - 1, 0> cs0_sel equ 00001000b ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Служебные функции ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Инициализирует GDT initialize_gdt: ; Вычисляем линейный адрес начала массива дескрипторов call cs_to_eax add eax, offset GDT ; Записываем его в структуру mov dword ptr gdtr.base, eax ; Загружаем GDTR lgdt fword ptr gdtr ret ; Запрещает маскируемые и немаскируемые прерывания disable_interrupts: cli ; запретить прерывания in al, 70h ; индексный порт CMOS or al, 80h ; установка бита 7 в нем запрещает NMI out 70h, al ret ; Разрешает маскируемые и немаскируемые прерывания enable_interrupts: in al, 70h ; индексный порт CMOS and al, 7Fh ; сброс бита 7 отменяет блокирование NMI out 70h, al sti ; разрешить прерывания ret ; Устанавливает флаг PE set_PE: mov eax, cr0 ; прочитать регистр CR0 or al, 1 ; установить бит PE, mov cr0, eax ; с этого момента мы в защищенном режиме ret ; Сбрасывает флаг PE clear_PE: mov eax, cr0 ; прочитать CR0 and al, 0FEh ; сбросить бит PE mov cr0, eax ; с этого момента мы в реальном режиме ret ; Вычисляет линейный адрес начала сгмента кода cs_to_eax: mov eax, 0 mov ax, cs shl eax, 4 ret end start
Остался от реального режима.. Ну вот, хотя бы это почитай: http://sergh.pisem.net/protected/02_segments.html#IDAL4M0D
Да, пардон, это с другим примером перепутал. Проверь, не выстален ли флаг NT в EFLAGS. М.б. IRET на другую задачу пытается переключиться? Правда, стек здесь не причем, но на всякий случай .
Ошибки две первая - надо два стека с разными уровнями привелегий, вторая - когда выполняешь RETF на менее привелигированный сегмент, процессор воспринимает это как возврат из процедуры, которая была вызвана с помощью шлюза вызова. Значит надо сделать вид, будто так оно и было. Во время CALL'а на шлюз вызова процессор делает такие манипуляции со стеком назначения: помещает старый SS, старый ESP, копирует нужное кол-во двойных слов, и только теперь помещает старые CS и EIP. Вот небольшой пример. Кстати как вернуться из процедуры, вызванной таким образом, я не уверен, разве что использовать шлюз, но тогда надо загружать TSS.
Так ведь перехода между уровнями нет в примере. Кстати, Код (Text): mov eax, cs0_sel push eax Почему eax? Код же 16 бит.
2 C0DiCK: Это специальный, максимально упрощенный пример, тут ни привилегий, ни стеков, и даже сегмент кода один. И всё равно - не работает.. Почему - не понимаю, вроде всё чисто. В примере ты используешь retf, так у меня тоже работает, интересно понять, почему здесь не выходит. 2 Mika0x65: потому что дальше - iretd. Но 16-ти разрядную версию (с ax и iret) я тоже пробовал. А про флаг NT я посмотрю. Вроде не должно быть - с чего бы ему быть установленному, но проверить стоит, спасибо за идею.
Не забудь сообщить о результатах. Код (Text): mov eax, cs0_sel push eax mov eax, offset target_code push eax Кстати, было бы очень неплохо посмотреть, что собой представляет этот код в дизассемблере -- сассемблируется ли 'mov eax, cs0_sel' в 'movzx eax, num'?
Проверил - ну в общем, ты был прав. Действительно, флаг NT почему-то стоит, если его сбросить - всё работает. Заодно выяснил, почему работает при возврате из настоящего обработчика прерывания. Инструкция int помимо прочего сбрасывает флаг NT. Потом при возврате он конечно восстанавливается, при восстановлении флагов из стека, главное, что он сброшен во время выполнения iret.