Почему регистры защищенного режима x86 32-бит не дают правильное значение?

Вопрос или проблема

Почему регистры защищенного режима x86 32-бит не дают правильное значение?

Я настроил 32-битный защищенный режим и пытаюсь загрузить глобальную таблицу дескрипторов (GDT) с помощью C и ассемблера и вывести значения различных регистров с помощью функции check_gdt(), где

    void check_gdt() {
    uint16_t cs, ds, es, fs, gs, ss;

    // Встроенный ассемблер для получения значений сегментных регистров
    asm volatile("mov %%cs, %0" : "=r"(cs));    
    asm volatile("mov %%ds, %0" : "=r"(ds));
    asm volatile("mov %%es, %0" : "=r"(es));
    asm volatile("mov %%fs, %0" : "=r"(fs));
    asm volatile("mov %%gs, %0" : "=r"(gs));
    asm volatile("mov %%ss, %0" : "=r"(ss));

    // Вывод значений сегментных регистров
    printf("CS: 0x%x\n", cs);
    printf("DS: 0x%x\n", ds);
    printf("ES: 0x%x\n", es);
    printf("FS: 0x%x\n", fs);
    printf("GS: 0x%x\n", gs);
    printf("SS: 0x%x\n", ss);
}

Инициализируйте GDT следующим образом

void initGdt(){
    gdt_ptr.limit = (sizeof(struct gdt_entry_struct) * 5) - 1;
    gdt_ptr.base = (uint32_t)&gdt_entries;

    setGdtGate(0,0,0,0,0);                      // Нулевой сегмент                           _________________                   _______________
                                                // Объяснение:                          |P|DPL|S|E|DC|RW|A|                 |G|DB|L|Res|    |
    setGdtGate(1,0,0xFFFFFFFF, 0x9A, 0xCF);     // Сегмент кода ядра : доступ = 0x9A =>|1|00 |1|1|0 |1 |0|, гран = 0xCF => |1|1 |0|0  |1111|
    setGdtGate(2,0,0xFFFFFFFF, 0x92, 0xCF);     // Сегмент данных ядра : доступ = 0x92 =>|1|00 |1|0|0 |1 |0|, гран = 0xCF => |1|1 |0|0  |1111|                  
                                                //                                        _________________                   _______________
    setGdtGate(3,0,0xFFFFFFFF, 0xFA, 0xCF);     // Сегмент кода пользователя : доступ = 0xFA =>|1|11 |1|1|0 |1 |0|, гран = 0xCF => |1|1 |0|0  |1111|
    setGdtGate(4,0,0xFFFFFFFF, 0xF2, 0xCF);     // Сегмент данных пользователя : доступ = 0x9A =>|1|11 |1|0|0 |1 |0|, гран = 0xCF => |1|1 |0|0  |1111|
                                                //                                        -----------------                   ---------------
    gdt_flush((uint32_t) &gdt_ptr);
}



void setGdtGate(uint32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran){

    gdt_entries[num].base_low = (base & 0xFFFF);        // Присвоить младший базовый 16 бит
    gdt_entries[num].base_middle = (base >> 16) & 0xFF; // Присвоить средний базовый 8 бит
    gdt_entries[num].base_high = (base >> 24) & 0xFF;   // Присвоить старший базовый 8 бит

    gdt_entries[num].limit_low = (limit & 0xFFFF);      // Присвоить младший лимит 16 бит
    gdt_entries[num].flags = (limit >> 16) & 0x0F;      // Присвоить флаги младшие 4 бита
    gdt_entries[num].flags |= (gran & 0xF0);            // Присвоить флаги старшие 4 бита

    gdt_entries[num].access = access;                   // Присвоить доступ 8 бит
}

и загрузить GDT в ЦП с помощью функции gdt_flush

gdt_flush:
    ; [esp] Адрес возврата (из CALL)
    ; Загрузить адрес дескриптора GDT
    MOV eax, [esp + 4]  ; Чтобы загрузить содержимое адреса [esp+4], который является первым аргументом gdt_flush((uint32_t) &gdt_ptr)
    LGDT [eax]      ; Загрузить GDT в ЦП

    MOV ax, 0x10    ; Загрузить селектор сегмента данных ядра (0x10)
    MOV ds, ax      ; Установить регистр сегмента данных в сегмент данных ядра
    MOV es, ax      ; Установить регистр дополнительного сегмента в сегмент данных ядра
    MOV fs, ax      ; Установить общий сегмент в сегмент данных ядра
    MOV gs, ax      ; Установить общий сегмент в сегмент данных ядра
    MOV ss, ax      ; Установить сегмент стека в сегмент данных ядра

    JMP 0x08:.flush ;Дальняя передача к селектору 0x08 (сегмент кода) и адресу flush
                    ; Запись 1: Сегмент кода ядра (селектор = 0x08)

.flush:
    RET

Я ожидаю от загрузки gdt выше

CS : 0x08
DS : 0x10
ES : 0X10
FS : 0X10
GS : 0X10
SS : 0X10

Но почему я получаю неправильный результат, как показано ниже?
введите описание изображения здесь

Ответ или решение

Проблема, с которой вы столкнулись, связана с неправильными значениями регистров сегмента в вашем коде для работы с таблицей глобальных дескрипторов (GDT) в защищенном режиме x86. Давайте рассмотрим возможные причины этого поведения и предложим решения.

Потенциальные причины проблемы:

  1. Неправильная инициализация GDT:
    Убедитесь, что ваша функция initGdt() правильно инициализирует сегменты GDT. У вас есть 5 записей в GDT, где первая запись является нулевой (недействительной), а последующие четыре — для кода и данных в режиме ядра и пользовательских сегментах. Убедитесь, что итоговая структура gdt_entries правильно определена и содержит нужные значения.

  2. Ошибки в загрузке и установке сегментных регистров:
    В основной функции загрузки GDT (gdt_flush) регистры сегментов устанавливаются по значениям, которые вы определили. Убедитесь, что правильные сегментные селекторы (0x08 для кода и 0x10 для данных) используются и что они соответствуют записям в GDT.

  3. Ошибки в gdt_flush:
    В вашем коде используется LGDT, чтобы загрузить адрес GDT. Убедитесь, что вы правильно передаете адрес GDT. Ваш код выглядит правильно, но проверьте, что структура gdt_ptr и ассоциированная с ней память была правильно инициализирована и не содержит ошибок.

  4. Неправильный порядок операций:
    Вы используете MOV ax, 0x10 для присвоения значения регистрам, не забывайте, что эти операции могут быть не видны сразу после выполнения, если вы не используете соответствующие механизмы синхронизации.

Возможные решения:

  1. Отладка GDT:
    Проверьте, правильно ли инициализирована каждая запись в GDT. Вы можете использовать отладчик или вывод отладочной информации, чтобы убедиться, что каждая часть GDT настроена правильно.

  2. Проверка регистров после gdt_flush:
    Программа должна корректно перейти в защищенный режим и после этого проверить значения регистров. Убедитесь, что вы проверяете значения сразу после выполнения инструкций загрузки GDT и установки регистров.

  3. Использование версий инструментов отладки:
    Используйте отладочные инструменты, такие как gdb или встроенные средства вашей среды разработки, чтобы проследить за изменениями в регистрах сегментов во время выполнения программы.

  4. Корректный выбор селекторов:
    Убедитесь, что выбранные вами селекторы действительно соответствуют тем сегментам, которые вы инициализируете в GDT. Селектор сегмента включает битовые поля, и ваши значения должны правильно учитывать уровень привилегий и тип сегмента.

Пример кода для проверки:

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

Заключение:

Убедитесь, что ваша система правильно инициализирует и использует GDT и что после всех операций по загрузке и установке регистров вы проверяете их значения. Следите за точностью при написании кода, и после выполнения этих шагов у вас не должно возникать проблем с получением нужных значений регистров сегментов.

Оцените материал
Добавить комментарий

Капча загружается...