Сегмент стека в PM

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

  1. sergh

    sergh New Member

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

    У меня тоже проблема с сабжем, и тоже какая-то непонятная.

    В качестве сегмента стека используется обычный (не expand down) сегмент данных.
    Если размер предела сегмента в дескрипторе равен 0ffffh, то всё работает.
    Иначе - либо зависание, либо исключение с перезагрузкой (я сначала хотел установить размер соответствующий количеству выделенных под стек байт, потом пробовал всякие разные круглые числа - 128, 256, 0fffeh).

    Правда, это всё проверялось не на живой машине, а в VMWare 5.0
    В Григорьеве и мануалах Intel ничего на эту тему не нашёл.

    Тестовая программа:

    Код (Text):
    1. ; stack.asm
    2. ; Программа, переключающаяся в PM и меняющая сегмент стека
    3.  
    4.         .model tiny
    5.         .code
    6.         .386p
    7.         org     100h
    8.  
    9. ;;;;;;;;;;;;;;;;;;;;;;;;;;;
    10. ;
    11. ; Структуры
    12. ;
    13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;
    14.  
    15. ; Сегментный дескриптор
    16. segment_descriptor struct
    17.     limit_low   dw      0    ; Младшие два байта поля Segment limit
    18.     base_low    dw      0    ; Младшие два байта поля Base Address
    19.     base_high0  db      0    ; Второй байт поля Base Address
    20.     type_and_permit db  0    ; Флаги
    21.     flags       db      0    ; Ещё одни флаги
    22.     base_high1  db      0    ; Старший байт поля Base Address
    23. segment_descriptor ends
    24.  
    25. ; Регистр, описывающий таблицу дескриптров
    26. table_register struct
    27.     limit       dw      0    ; Table Limit
    28.     base        dd      0    ; Linear Base Address
    29. table_register ends
    30.  
    31. ;;;;;;;;;;;;;;;;;;;;;;;;;;
    32. ;
    33. ; Код
    34. ;
    35. ;;;;;;;;;;;;;;;;;;;;;;;;;;
    36.  
    37. start:
    38.         ; Подготавливаем DS
    39.         push    cs
    40.         pop     ds
    41.  
    42.         ; Вычисляем и записываем базу стека в дескриптор
    43.         call    cs_to_eax
    44.         add     eax, offset stack0_seg
    45.  
    46.         mov     word ptr ss0_dsc.base_low, ax
    47.         shr     eax, 16
    48.         mov     byte ptr ss0_dsc.base_high0, al
    49.  
    50.         ; Инициалиируем GDT
    51.         call initialize_gdt
    52.  
    53.         call disable_interrupts
    54.         call set_PE
    55.  
    56.         ; Сохраняем старый стек
    57.         mov  old_ss, ss
    58.         mov  old_esp, esp
    59.  
    60.         ; И устанавливаем новый
    61.         mov  ax, 001000b
    62.         mov  ss, ax
    63.         mov  esp, stack0_size
    64.  
    65.         call clear_PE
    66.  
    67.         ; Восстанавливаем стек
    68.         mov  dx, old_ss
    69.         mov  ss, dx
    70.         mov  esp, old_esp
    71.  
    72.         call enable_interrupts
    73.         ret
    74.  
    75. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    76. ;;
    77. ;; Данные
    78. ;;
    79. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    80.  
    81. ; Глобальная таблица дескрипторов
    82. GDT       label   byte
    83.           ; Нулевой дескриптор
    84.           segment_descriptor <>
    85.           ; Дескриптор сегмента стека
    86.           ; Если изменить предел, машина падает или виснет
    87. ss0_dsc   segment_descriptor <0ffffh, 0, 0, 10010010b, 0, 0>
    88.  
    89.  
    90. ; Данные для загрузки в GDTR
    91. gdtr      table_register <$ - GDT - 1, 0>
    92.  
    93.  
    94. ; Собственно сегмент стека
    95. stack0_seg    db 128 dup (0)
    96. stack0_size   equ $ - stack0_seg
    97.  
    98. ; Место для сохранения указателя стека RM
    99. old_ss         dw 0
    100. old_esp       dd 0
    101.  
    102. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    103. ;;
    104. ;; Служебные функции
    105. ;;
    106. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    107.  
    108. ; Инициализирует GDT
    109. initialize_gdt:
    110.         ; Вычисляем линейный адрес начала массива дескрипторов
    111.         call    cs_to_eax
    112.         add     eax, offset GDT
    113.         ; Записываем его в структуру
    114.         mov     dword ptr gdtr.base, eax
    115.  
    116.         ; Загружаем GDTR
    117.         lgdt    fword ptr gdtr
    118.         ret
    119.  
    120. ; Запрещает маскируемые и немаскируемые прерывания
    121. disable_interrupts:
    122.         cli              ; запретить прерывания
    123.         in      al, 70h  ; индексный порт CMOS
    124.         or      al, 80h  ; установка бита 7 в нем запрещает NMI
    125.         out     70h, al
    126.         ret
    127.  
    128. ; Разрешает маскируемые и немаскируемые прерывания
    129. enable_interrupts:
    130.         in      al, 70h  ; индексный порт CMOS
    131.         and     al, 7Fh  ; сброс бита 7 отменяет блокирование NMI
    132.         out     70h, al
    133.         sti              ; разрешить прерывания
    134.         ret
    135.  
    136. ; Устанавливает флаг PE
    137. set_PE:
    138.         mov     eax, cr0 ; прочитать регистр CR0
    139.         or      al, 1    ; установить бит PE,
    140.         mov     cr0, eax ; с этого момента мы в защищенном режиме
    141.         ret
    142.  
    143. ; Сбрасывает флаг PE
    144. clear_PE:
    145.         mov     eax, cr0 ; прочитать CR0
    146.         and     al, 0FEh ; сбросить бит PE
    147.         mov     cr0, eax ; с этого момента мы в реальном режиме
    148.         ret
    149.  
    150. ; Вычисляет линейный адрес начала сгмента кода
    151. cs_to_eax:
    152.         mov     eax, 0
    153.         mov     ax, cs
    154.         shl     eax, 4
    155.         ret
    156.  
    157.         end     start
     
  2. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Код (Text):
    1. mov  ss, ax
    2. mov  esp, stack0_size
    Не уверен, что дело в этом, но, по-моему, stack_size надо тоже пересчитать в линейный адрес (база + stack_size), перед тем как класть в esp.
     
  3. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    Не, это вряд ли. Там же стандартная адресация <сегмент>:<смещение>, соответсвенно в esp - смещение. Тем более, что с пределом сегмента 0ffffh всё работает.
    Да и вообще, линейные адреса напрямую крайне редко используются (LGDT, LIDT, SGDT, SIDT, больше ничего не припоминаю)... И никогда они не используются пользователем, это слишком интимная подробность организации системы.
     
  4. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Я сейчас подумал, и мне кажется, что все же дело в этом :). Что лежит в cs я не знаю, но скорее всего нечто большее чем 0x80. Это же, большее чем 0х80 значение станет базой для сегмента стека. А в esp ляжет как раз 0x80. Получается, что esp указывает аккурат мимо сегмента стека.
     
  5. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    P.s. Как решишь проблему -- отпиши, pls, в чем дело было. Общественности (по крайней мере в моем лице :) ) будет интересно.
     
  6. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    Решил. Это оказался уникальный случай, когда тестовый пример слишком маленький :)
    На самом деле в защищённом режиме всё нормально работает. Проблемы начинаются когда мы переключаемся в реальный режим. Я как-то акцентировался на защищённом и совершенно забыл про реальный.
    Известный глюк x86 - при переключении между режимами сохраняются размеры сегментов. Естественно так происходит и со стеком. И естественно, DOS отказывается работать с "укороченным" стеком. Если наоборот, увеличить сегмент стека - всё работает.
     
  7. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    В смысле? Т.е. самому ДОСу не понравилось, что физ. адрес вершины стека стал 0x100 и лимит 0x80?
     
  8. sergh

    sergh New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    128
    Адрес:
    rsdn
    Где ты всё физические адреса находишь? Нету никаких физических адресов, вообще почти нигде. Обычная сегментная адресация.

    Тем более, что в конце можно найти такие строчки:

    ; Восстанавливаем стек
    mov dx, old_ss
    mov ss, dx
    mov esp, old_esp

    То есть и ss и esp восстанавливаются как были. Проблема в том, что размер сегмента, который адресуется сегментным регистром (в данном случае SS) при этом не восстанавливается, а остаётся таким же, каким был в PM - особенность такая у x86. На этом основан широко известный Unreal Mode и этот мой баг.
     
  9. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Физический -- он же линейный, который в RM (неявно base) + (seg_reg << 4) + effective. Я просто думал, что после перезагрузки сегментного регистра, база соответствующего ему теневого скидывается на 0, а лимит на 0xFFFFF.