ОС перезагружается при дальнем переходе после отключения подкачки

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

Моя проблема в том, что при выполнении дальнего перехода, чтобы отключить страницу, что-то идет не так, и я получаю перезагрузку.

Код, показанный ниже, работает путем создания сопоставления идентификаторов сначала с использованием таблицы страниц boot_page_table1, которая представляет собой просто таблицу страниц, идентификатор которой отображает первые 4 МиБ. Это необходимо сделать, поскольку я использую подкачку для запуска моего кода ядра из более высокой памяти, и весь код ядра адресуется, начиная с 0xC0100000, а загружается, начиная с 0x00100000. Затем я сбрасываю TLB и перехожу к ближайшей метке, но на этот раз использую адрес в нижней памяти. Теперь указатель моей инструкции должен указывать на код сопоставления идентификаторов, и отключение разбиения на страницы должно быть безопасным. Затем бит пейджинга отключается в cr3, TLB снова сбрасывается, потому что я параноик, и код для переключения режимов продолжается.

Код работает, копируя себя в 16-битную память по адресу 0x7c00, а затем перескакивая на нее, чтобы он мог работать в 16-битном реальном режиме.

Если я НЕ отключу бит подкачки и оставлю все остальное без изменений, jmpw CODE16:REBASE(p_mode16) будет работать, и бесконечный цикл после перехода введен, заставляя меня думать, что эта проблема возникает из-за того, как я отключил подкачку. Я что-то упускаю при отключении разбиения по страницам? Я видел в других сообщениях что «из-за того, что вы делаете очень необычно, вы можете столкнуться с ошибками и проблемами совместимости с вашим эмулятором», но я еще не уверен, что это просто мой код неправильный.

Код написан с использованием синтаксиса Intel с ассемблером GAS.

.intel_syntax noprefix

.code32

.global int32, _int32

#define regs16_t_size                          13*2
#define INT32_BASE                             0x00007C00
#define REBASE(x)                              (((x) - reloc) + INT32_BASE)
#define GDTENTRY(x)                            ((x) &lt;< 3)
#define CODE32                                 0x08
#define DATA32                                 0x10
#define CODE16                                 0x18
#define DATA16                                 0x20
#define STACK16                                (INT32_BASE - regs16_t_size)

.global reloc
.global int32_end

.section .text
    int32: .code32                             # by Napalm
    _int32:
        cli                                    # disable interrupts
        pusha                                  # save register state to 32bit stack

        # Enable identity mapping the first MiB, jump, then disable paging
        push [boot_page_directory] # Push first page directory entry to restore it after

        mov eax, (offset boot_page_table1) - 0xC0000000 + 0x003
        mov [boot_page_directory], eax

        mov ecx, cr3 # Reload crc3 to force a TLB flush so the changes to take effect.
        mov cr3, ecx

        mov eax, (offset napalm_switch_disable_paging) - 0xC0000000
        jmp eax
        napalm_switch_disable_paging:

        # Code is now running with the instruction pointer in lower memory,
        # but the code is still assembled as though its in higher memory. Because
        # of this, something like jmp INT32_BASE would fail since it would
        # assemble as a relative jump from an address around 0xC0100000 to 0x7C00
        # but will be running at an address around 0x00100000 causing it to jump to
        # 0x40007C00.

        # Disable paging bit
        mov eax, cr0
        and eax, ~0x80000000
        mov cr0, eax

        mov ecx, cr3 # Reload crc3 to force a TLB flush so the changes to take effect.
        mov cr3, ecx

        mov  esi, (offset reloc) - 0xC0000000  # set source to code below
        mov  edi, INT32_BASE                   # set destination to new base address
        mov  ecx, int32_end - reloc            # set copy size to our codes size
        cld                                    # clear direction flag (so we copy forward)
        rep  movsb                             # do the actual copy (relocate code to low 16bit space)
        mov eax, INT32_BASE
        jmp eax                         # jump to new code location
    reloc: .code32                             # by Napalm
        mov  [REBASE(stack32_ptr)], esp        # save 32bit stack pointer
        sidt [idt_ptr]               # save 32bit idt pointer
        sgdt [gdt_ptr]               # save 32bit gdt pointer
        lgdt [REBASE(gdt16_ptr)]               # load 16bit gdt pointer
        lea  esi, [esp+0x24]                   # set position of intnum on 32bit stack
        lodsd                                  # read intnum into eax
        mov  [REBASE(ib)], al                  # set intrrupt immediate byte from our arguments 
        mov  esi, [esi]                        # read regs pointer in esi as source
        mov  edi, STACK16                      # set destination to 16bit stack
        mov  ecx, regs16_t_size                # set copy size to our struct size
        mov  esp, edi                          # save destination to as 16bit stack offset
        rep  movsb                             # do the actual copy (32bit stack to 16bit stack)

        jmpw CODE16:REBASE(p_mode16)      # switch to 16bit selector (16bit protected mode)


    p_mode16: .code16
        jmp .-2

... 
more of the routine thats not run due to the bug 
...

    stack32_ptr:                               # address in 32bit stack after we
        .4byte 0x00000000                          #   save all general purpose registers

    idt16_ptr:                                 # IDT table pointer for 16bit access
        .2byte 0x03FF                              # table limit (size)
        .4byte 0x00000000                          # table base address

    gdt16_base:                                # GDT descriptor table
        .null:                                 # 0x00 - null segment descriptor
            .4byte 0x00000000                      # must be left zero'd
            .4byte 0x00000000                      # must be left zero'd

        .code32:                               # 0x01 - 32bit code segment descriptor 0xFFFFFFFF
            .2byte 0xFFFF                          # limit  0:15
            .2byte 0x0000                          # base   0:15
            .byte 0x00                            # base  16:23
            .byte 0x9A                            # present, iopl/0, code, execute/read
            .byte 0xCF                            # 4Kbyte granularity, 32bit selector; limit 16:19
            .byte 0x00                            # base  24:31

        .data32:                               # 0x02 - 32bit data segment descriptor 0xFFFFFFFF
            .2byte 0xFFFF                          # limit  0:15
            .2byte 0x0000                          # base   0:15
            .byte 0x00                            # base  16:23
            .byte 0x92                            # present, iopl/0, data, read/write
            .byte 0xCF                            # 4Kbyte granularity, 32bit selector; limit 16:19
            .byte 0x00                            # base  24:31

        .code16:                               # 0x03 - 16bit code segment descriptor 0x000FFFFF
            .2byte 0xFFFF                          # limit  0:15
            .2byte 0x0000                          # base   0:15
            .byte 0x00                            # base  16:23
            .byte 0x9A                            # present, iopl/0, code, execute/read
            .byte 0x0F                            # 1Byte granularity, 16bit selector; limit 16:19
            .byte 0x00                            # base  24:31

        .data16:                               # 0x04 - 16bit data segment descriptor 0x000FFFFF
            .2byte 0xFFFF                          # limit  0:15
            .2byte 0x0000                          # base   0:15
            .byte 0x00                            # base  16:23
            .byte 0x92                            # present, iopl/0, data, read/write
            .byte 0x0F                            # 1Byte granularity, 16bit selector; limit 16:19
            .byte 0x00                            # base  24:31

    gdt16_ptr:                                 # GDT table pointer for 16bit access
        .2byte gdt16_ptr - gdt16_base - 1          # table limit (size)
        .4byte gdt16_base                          # table base address

    int32_end:                                 # end marker (so we can copy the code)
        .byte 0x00

Строка с jmp .-2 на метке p_mode16 никогда не достигается, вместо этого происходит перезагрузка. Если jmp .-2 поставить прямо перед jmpw, тогда ОС войдет в бесконечный цикл, как и ожидалось. Я запускаю это на QEMU версии 2.11.1 с qemu-system-i386.


person 23scurtu    schedule 20.05.2020    source источник
comment
Куда указывает указатель стека?   -  person prl    schedule 20.05.2020
comment
@prl Значение в ESP непосредственно перед прыжком - 0x7BE6   -  person 23scurtu    schedule 20.05.2020


Ответы (1)


Проблема вот в чем:

gdt16_ptr:                             # GDT table pointer for 16bit access
    .2byte gdt16_ptr - gdt16_base - 1  # table limit (size)
    .4byte gdt16_base                  # GDT base in higher half that WILL NOT WORK WHEN PAGING IS DISABLED

Поскольку вы сказали процессору, что GDT находится в верхней половине, после того, как вы отключите разбиение на страницы, он не сможет получить доступ к записям GDT должным образом (он, вероятно, обращается к физическому адресу в 0xC000 ???? и вместо этого читает кто знает что - например, возможно регистры устройства PCI, возможно, «не RAM или устройство» и т. д.), поэтому он вылетает, когда дальний переход пытается загрузить CODE16 в CS (потому что «кто-знает-что» не является допустимым дескриптором кода).

Чтобы решить эту проблему, вам необходимо изменить значение базы GDT до выполнения sgdt [gdt_ptr] (например, возможно, просто используйте .4byte REBASE(gdt16_base) вместо .4byte gdt16_base, если gdt16_ptr не используется где-либо еще).

person Brendan    schedule 21.05.2020
comment
Ах, имеет смысл, замена .4byte gdt16_base на .4byte REBASE(gdt16_base) сработала. Спасибо! - person 23scurtu; 21.05.2020