Инструкция iret/iretd в PM

Тема в разделе "WASM.ASSEMBLER", создана пользователем sergh, 27 авг 2006.

  1. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    Всем привет!

    Пытаюсь использовать сабж для передачи управления. Примерно так (это 32-х разрядный вариант, пробовал и с 16-ю разрядами, с тем же результатом):

    Код (Text):
    1. ; Подготавливаем стек
    2. pushfd
    3. mov eax, cs ; в cs - селектор сегмента кода
    4. push eax
    5. mov eax, offset target_code
    6. push eax
    7.  
    8. ; Поехали!
    9. iretd
    Происходит всё это на нулевом уровне привилегий, при передаче управления уровень привилегий не меняется (начинал то я как раз со смены уровней, но оказывается даже такой простой пример не работает).

    После выполнения VMWare сообщает о kernel stack fault.
    Если заменить iret на retf (и для чистоты закомментировать pushf), всё работает.
    В реальном режиме - всё работает.
    При возврате из настоящего обработчика прерывания - всё работает.

    Видимо, iret делает ещё что-то, чего я не понимаю... И зависит это небось от каких-нибудь флагов. Но я чего-то пока не смог разобраться. Подскажите пожалуйста, если кто знает.

    Вот полный код примера:
    Код (Text):
    1. ; iret_pm.asm
    2.  
    3.         .model tiny
    4.         .code
    5.         .386p
    6.         org     100h
    7.  
    8. ;;;;;;;;;;;;;;;;;;;;;;;;;;;
    9. ;
    10. ; Структуры
    11. ;
    12. ;;;;;;;;;;;;;;;;;;;;;;;;;;;
    13.  
    14. ; Сегментный дескриптор
    15. segment_descriptor struct
    16.     limit_low   dw      0    ; Младшие два байта поля Segment limit
    17.     base_low    dw      0    ; Младшие два байта поля Base Address
    18.     base_high0  db      0    ; Второй байт поля Base Address
    19.     type_and_permit db  0    ; Флаги
    20.     flags       db      0    ; Ещё одни флаги
    21.     base_high1  db      0    ; Старший байт поля Base Address
    22. segment_descriptor ends
    23.  
    24. ; Регистр, описывающий таблицу дескриптров
    25. table_register struct
    26.     limit       dw      0    ; Table Limit
    27.     base        dd      0    ; Linear Base Address
    28. table_register ends
    29.  
    30. ;;;;;;;;;;;;;;;;;;;;;;;;;;
    31. ;
    32. ; Код
    33. ;
    34. ;;;;;;;;;;;;;;;;;;;;;;;;;;
    35.  
    36. start:
    37.         push    cs
    38.         pop     ds
    39.  
    40.         push    0b800h
    41.         pop     es
    42.  
    43.         ; Устанавливаем правильный сегмент в long-jmp-to-RM
    44.         mov     ax, cs
    45.         mov     cs:rm_cs, ax
    46.  
    47.         ; Устанавливаем базу сегмента кода
    48.         call cs_to_eax
    49.         mov  cs0_dsc.base_low, ax
    50.         shr  eax, 16
    51.         mov  cs0_dsc.base_high0, al
    52.  
    53.         ; Инициализируем GDT
    54.         call initialize_gdt
    55.  
    56.         call disable_interrupts
    57.         call set_PE
    58.  
    59.         ; 16-разрядный дальний переход. Перключает содержимое cs из нормального
    60.         ; для реального режима (адрес) в нормальное для защищённого (селектор).
    61.         ; Базовый адрес целевого сегмента совпадает с cs,
    62.         ; поэтому смещение можно прописать сразу
    63.         db      0EAh    ; код команды дальнего перехода
    64.         dw      $ + 4   ; смещение
    65.         dw      cs0_sel ; селектор
    66.  
    67.         ; подготовка стека
    68. ;        pushfd
    69.         mov  eax, cs0_sel
    70.         push eax
    71.         mov  eax, offset target_code
    72.         push eax
    73.  
    74.         ; поехали!
    75. ;        iretd ; - не пашет, говорит stack fault
    76.         db 66h ; 32-х разрядные операнды
    77.         retf
    78.  
    79. back_to_rm:
    80.         call clear_PE
    81.  
    82.         ; 16-разрядный дальний переход. Перключает содержимое cs из нормального
    83.         ; для защищённог режима (селектор) в нормальное для реальног (адрес).
    84.         ; Адрес сегмента вычисляется и прописывается во время выполнения.
    85.         db      0EAh   ; код команды дальнего перехода
    86.         dw      $ + 4  ; смещение
    87. rm_cs   dw      0      ; сегмент
    88.  
    89.         call enable_interrupts
    90.         ret
    91.  
    92. ;;
    93. ;; Целевой код
    94. ;;
    95.  
    96. target_code:
    97.  
    98.         push   eax
    99.         push   ecx
    100.  
    101.         mov    eax, 0         ; Текущий символ
    102.         mov    ecx, 80 * 25   ; Колическтво сиволов на экране
    103.  
    104. screen_loop1:
    105.  
    106.         inc    eax
    107.         inc    byte ptr es:[eax]          ; Меняем атрибут
    108.         inc    eax
    109.         loop   screen_loop1
    110.  
    111.         pop    ecx
    112.         pop    eax
    113.  
    114.         jmp back_to_rm
    115.  
    116. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    117. ;;
    118. ;; Данные
    119. ;;
    120. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    121.  
    122. ; Глобальная таблица дескрипторов
    123. GDT       label   byte
    124.           ; Нулевой дескриптор
    125.           segment_descriptor <>
    126.           ; Дескриптор сегмента кода
    127. cs0_dsc   segment_descriptor <0ffffh, 0, 0, 10011010b, 0, 0>
    128.  
    129. ; Данные для загрузки в GDTR
    130. gdtr      table_register <$ - GDT - 1, 0>
    131.  
    132. cs0_sel   equ 00001000b
    133.  
    134. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    135. ;;
    136. ;; Служебные функции
    137. ;;
    138. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    139.  
    140. ; Инициализирует GDT
    141. initialize_gdt:
    142.         ; Вычисляем линейный адрес начала массива дескрипторов
    143.         call    cs_to_eax
    144.         add     eax, offset GDT
    145.         ; Записываем его в структуру
    146.         mov     dword ptr gdtr.base, eax
    147.  
    148.         ; Загружаем GDTR
    149.         lgdt    fword ptr gdtr
    150.         ret
    151.  
    152. ; Запрещает маскируемые и немаскируемые прерывания
    153. disable_interrupts:
    154.         cli              ; запретить прерывания
    155.         in      al, 70h  ; индексный порт CMOS
    156.         or      al, 80h  ; установка бита 7 в нем запрещает NMI
    157.         out     70h, al
    158.         ret
    159.  
    160. ; Разрешает маскируемые и немаскируемые прерывания
    161. enable_interrupts:
    162.         in      al, 70h  ; индексный порт CMOS
    163.         and     al, 7Fh  ; сброс бита 7 отменяет блокирование NMI
    164.         out     70h, al
    165.         sti              ; разрешить прерывания
    166.         ret
    167.  
    168. ; Устанавливает флаг PE
    169. set_PE:
    170.         mov     eax, cr0 ; прочитать регистр CR0
    171.         or      al, 1    ; установить бит PE,
    172.         mov     cr0, eax ; с этого момента мы в защищенном режиме
    173.         ret
    174.  
    175. ; Сбрасывает флаг PE
    176. clear_PE:
    177.         mov     eax, cr0 ; прочитать CR0
    178.         and     al, 0FEh ; сбросить бит PE
    179.         mov     cr0, eax ; с этого момента мы в реальном режиме
    180.         ret
    181.  
    182. ; Вычисляет линейный адрес начала сгмента кода
    183. cs_to_eax:
    184.         mov     eax, 0
    185.         mov     ax, cs
    186.         shl     eax, 4
    187.         ret
    188.  
    189.         end     start
     
  2. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Интересно, а как это работает -- дескриптора сегмента стека нет ведь...
     
  3. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    Остался от реального режима.. Ну вот, хотя бы это почитай: http://sergh.pisem.net/protected/02_segments.html#IDAL4M0D
     
  4. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Да, пардон, это с другим примером перепутал.

    Проверь, не выстален ли флаг NT в EFLAGS. М.б. IRET на другую задачу пытается переключиться? Правда, стек здесь не причем, но на всякий случай :).
     
  5. C0DiCK

    C0DiCK New Member

    Публикаций:
    0
    Регистрация:
    18 окт 2005
    Сообщения:
    38
    Ошибки две первая - надо два стека с разными уровнями привелегий, вторая - когда выполняешь RETF на менее привелигированный сегмент, процессор воспринимает это как возврат из процедуры, которая была вызвана с помощью шлюза вызова. Значит надо сделать вид, будто так оно и было. Во время CALL'а на шлюз вызова процессор делает такие манипуляции со стеком назначения: помещает старый SS, старый ESP, копирует нужное кол-во двойных слов, и только теперь помещает старые CS и EIP. Вот небольшой пример. Кстати как вернуться из процедуры, вызванной таким образом, я не уверен, разве что использовать шлюз, но тогда надо загружать TSS.
     
  6. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Так ведь перехода между уровнями нет в примере.

    Кстати,
    Код (Text):
    1.         mov  eax, cs0_sel
    2.         push eax
    Почему eax? Код же 16 бит.
     
  7. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    2 C0DiCK: Это специальный, максимально упрощенный пример, тут ни привилегий, ни стеков, и даже сегмент кода один. И всё равно - не работает.. Почему - не понимаю, вроде всё чисто.

    В примере ты используешь retf, так у меня тоже работает, интересно понять, почему здесь не выходит.

    2 Mika0x65: потому что дальше - iretd. Но 16-ти разрядную версию (с ax и iret) я тоже пробовал.
    А про флаг NT я посмотрю. Вроде не должно быть - с чего бы ему быть установленному, но проверить стоит, спасибо за идею.
     
  8. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Не забудь сообщить о результатах.

    Код (Text):
    1.         mov  eax, cs0_sel
    2.         push eax
    3.         mov  eax, offset target_code
    4.         push eax
    Кстати, было бы очень неплохо посмотреть, что собой представляет этот код в дизассемблере -- сассемблируется ли 'mov eax, cs0_sel' в 'movzx eax, num'?
     
  9. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    Проверил - ну в общем, ты был прав. Действительно, флаг NT почему-то стоит, если его сбросить - всё работает. Заодно выяснил, почему работает при возврате из настоящего обработчика прерывания. Инструкция int помимо прочего сбрасывает флаг NT. Потом при возврате он конечно восстанавливается, при восстановлении флагов из стека, главное, что он сброшен во время выполнения iret.