Эксперименты по переключению в режим пользователя из режима ядра. x86 - PullRequest
0 голосов
/ 02 апреля 2020

Вот основная функция моего ядра:

void kmain(uint32_t mmapsize,uint32_t data_sect,uint32_t root_sect,uint32_t fat_sect)
{
    gdt_init();
    pmmngr_init(mmapsize); //Reads memory map and initializes page frame allocator
    vmmngr_init();  //Sets recursive map, remaps stack and vidmem

    refresh_stack();  //Remaps stack to 0xFFC00000 
    remove_identity_map(); 
    interrupt_init(); //Initializes interrupts

    init();

    ....
    ....
}

init. c

#define USERSTACK 0xC0000000
#define USERSTACK_PHY 0x40000
#define PAGE_SIZE 4096

extern void switch_to_user();

extern uint8_t __user_begin[];

__attribute__((section (".user")))void init()
{
    map_page(USERSTACK - PAGE_SIZE,USERSTACK_PHY - PAGE_SIZE,true); //Makes a virtual memory mapping for the user mode stack
    map_page((uint32_t)__user_begin,0x103000,true); //Maps the virtual address __user_begin to 0x103000, the physical address where init() begins
    switch_to_user();
    for(;;);
}

Switch_to_user:

section .user
global switch_to_user
switch_to_user:
    cli
    mov ecx,[esp]   ;Return address

        mov ax, 0x23    ;user mode data selector is 0x20 (GDT entry 3). Also sets RPL to 3
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax

        push 0x23   ; SS, notice it uses same selector as above
        push USTACK ; ESP
        pushfd      ; EFLAGS
        push 0x1b   ; CS, user mode code selector is 0x18. With RPL 3 this is 0x1b
        push ecx
        iret

Что происходит - нет тройного вина или что-нибудь. qemu просто закрывается с этим сообщением:

dd if=stage1.bin of=disk.img bs=1 count=3 seek=0 skip=0 conv=notrunc
3+0 records in
3+0 records out
3 bytes copied, 9.1847e-05 s, 32.7 kB/s
dd if=stage1.bin of=disk.img bs=1 count=451 seek=62 skip=62 conv=notrunc
450+0 records in
450+0 records out
450 bytes copied, 0.000865666 s, 520 kB/s
mcopy -i disk.img stage2.bin kernel.bin ::  -D o
qemu-system-i386 -drive format=raw,file=disk.img  -monitor stdio
QEMU 2.11.1 monitor - type 'help' for more information
(qemu) qemu: fatal: invalid tss type
EAX=00000023 EBX=000000b1 ECX=c000304b EDX=00103027
ESI=00008137 EDI=0010d200 EBP=ffbffff8 ESP=c0000000
EIP=c000304b EFL=00000086 [--S--P-] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
CS =001b 00000000 ffffffff 00cffa00 DPL=3 CS32 [-R-]
SS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
DS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
FS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
GS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     c000d840 00000027
IDT=     c000d030 000007ff
CR0=80000011 CR2=c000304b CR3=0009c000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000084 CCD=ffbffff0 CCO=EFLAGS  
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000

Точная инструкция, что это происходит для (;;); (в init) Я узнал об этом, пока шел один шаг с GDB. Это говорит о том, что мне нужно инициализировать TSS. Что ж, я думал, что это обязательно, когда вы переходите от низкого к более высокому приоритету (например, от кольца 3 до кольца 0). Здесь я делаю переключение с 0 на 3 и даже отключаю прерывания. Я просто вращаюсь, ничего не делая.

Примечание: я пытался сохранить init и switch_to_user в отдельном разделе, выровненном по границе страницы. Я поигрался со скриптом компоновщика, чтобы получить что-то такое:

OUTPUT_FORMAT(elf32-i386)
ENTRY(kmain)

SECTIONS {

. = 0xC0000000;

__begin = .;
.text : SUBALIGN(16)
    {
        *(.text.entry)       /* Ensure .text.entry appears first */
        *(.text*)
    }

.user : ALIGN(4k) 
    {
        __user_begin = . ;
        *(.user);
        __user_end = . ;
    }

.data : SUBALIGN(4k)
    {
        *(.rodata)
        *(.data)
    }

.bss : SUBALIGN(16)
    {
        __bss_start = .;
        *(COMMON)            /* all COMMON sections from all files */
        *(.bss)              /* all BSS sections from all files */
    }

. = ALIGN(4k);
__VGA_text_memory = .;

/*    __bss_end = .;
    __bss_sizeb = __bss_end - __bss_start;        BSS size in bytes 
    __bss_sizel = (__bss_end - __bss_start) / 4;  BSS size in longs/DWORDs */

    /DISCARD/ : {            /* Remove Unneeded sections */
        *(.eh_frame);
        *(.comment);
    }

     __end = .; 
}

В соответствии с моей просьбой моя подпрограмма map_page (с измененным рефакторингом, без жестко закодированного пользовательского режима) выглядит следующим образом:

bool map_page(uint32_t virtual_address,uint32_t physical_address,bool isUser)   //TODO: Need to deal with freed frames  && Make this cleaner!!
{
    if (virtual_address % BLOCK_SIZE) 
        virtual_address -= (virtual_address%BLOCK_SIZE);
    if (physical_address % BLOCK_SIZE) 
        physical_address -= (physical_address%BLOCK_SIZE);

    uint32_t pd_index = virtual_address >> 22;
    if (!(_page_directory[pd_index] & PDE_PRESENT))
    {
        if(!alloc_page(_page_directory+pd_index)) return false;
        _page_directory[pd_index] |= PDE_WRITABLE;
        if(isUser) _page_directory[pd_index]|= PDE_USER;
    }

    uint32_t* page_table = (uint32_t*)(PAGE_TABLE | (pd_index<<12)); //This is the virtual address of the page table 
    uint32_t pt_index = ((virtual_address >> 12) & 0x3FF); //Using recursive page table technique

    page_table[pt_index] = physical_address;
    page_table[pt_index] |= PTE_PRESENT;
    page_table[pt_index] |= PTE_WRITABLE;
    if(isUser) page_table[pt_index]|= PTE_USER;

    flush_tlb();

    return true;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...