Вот основная функция моего ядра:
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;
}