I'm getting crazy with paging!!!

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
falconfx

I'm getting crazy with paging!!!

Post by falconfx »

Hi folks, I have a big problem with paging. When I try to activate it (setting the CR3 and CR0 registers), it crashes the computer.

I have seen that, if I map physical memory exactly to virtual memory, my kernel successfully loads. And also I need to map the first 4mb of ram, otherwise my kernel crashes.

I'd want to load my kernel at 0x100000 (1mb) and map it at 0xC0000000 (3gb). Have I to use the linker script or can I do it in my kernel code?

My kernel's 'start' function (asm):

Code: Select all

[BITS 32]

[global start]
[extern _kmain]

start:
???mov esp, _sys_stack

???push ebx
???call _kmain
???jmp $
The 'main' function:

Code: Select all

void kmain(multiboot_info_t *boot_info)
{
???init_video();
???gdt_install();
???idt_install();
???isrs_install();
???irq_install();
???init_paging((boot_info->ext_mem + 1024) * 1024);
???init_multitasking();

???puts("Hello, World!\n");

???timer_install();
???keyboard_install();

???sti();
???idle();
}
The 'init_paging' function:

Code: Select all

extern char _kernel_end[];

unsigned long *page_dir, *page_tbl;
unsigned long total_mempages, free_mempages;

void init_paging(uint32 total_ram)
{
???uint32 count = 0, t = 0;

???free_mempages = total_mempages = (total_ram / 4096);

???page_dir = (uint32 *)ALIGN_PAGE(_kernel_end);
???memset(page_dir, 0, 0x1000);
???memset(page_tbl, 0, 0x1000);

???page_tbl = page_dir + 0x1000;
???install_page_table(page_dir, 0, page_tbl, PG_PRESENT | PG_WRITABLE);
???page_tbl = page_dir + 0x2000;
???install_page_table(page_dir, 0x300, page_tbl, PG_PRESENT | PG_WRITABLE);

???for (count = 0; count < 256; count++)
??????map_page(page_dir, 0, count, count * 0x1000, PG_PRESENT | PG_WRITABLE);

???for (count = 256; count < 1024; count++)
??????map_page(page_dir, 0x300, count - 256, count * 0x1000, PG_PRESENT | PG_WRITABLE);

???__asm__ __volatile__ ("movl %%eax, %%cr3" :: "a" (page_dir));

???__asm__ __volatile__ ("movl %%cr0, %%eax" : "=a" (t));
???t |= 0x80000000;
???__asm__ __volatile__ ("movl %%eax, %%cr0" :: "a" (t));
}

void install_page_table(uint32 *pgdir, uint32 tbl_index, uint32 *pgtable, uint16 flags)
{
???pgdir[tbl_index] = ((uint32)(pgtable) & ~0x3FF) | (flags & 0x3FF);
}

uint32 *get_page_table(uint32 *pgdir, uint32 tbl_index)
{
???return (uint32 *)(pgdir[tbl_index] & ~0x3FF);
}

void map_page(uint32 *pgdir, uint32 pgtable, uint32 page, uint32 phys_mem, uint16 flags)
{
???uint32 *ptr = NULL;

???ptr = get_page_table(pgdir, pgtable);

???ptr[page] = (phys_mem & ~0x3FF) | (flags & 0x3FF);
}
Sorry for the very long code, but I think it's very self-explanatory.

I've seen that, if I map the first 1024 pages in the first page table, all goes very well. But I want my kernel at 0xC0000000.

I hope you can help me...
AR

Re:I'm getting crazy with paging!!!

Post by AR »

The kernel accesses memory based on where it thinks it is, it has no way to know so has to be told. You tell it by setting the virtual and physical addresses in the link script.

Mapping it high whilst it is linked low is only going to crash the system, but the opposite is also true (linking it high whilst it is running low, ie. before paging is activated will still crash the system).

There are 2 simple ways to overcome this problem
1) Use an intermediate bootloader between GRUB and your kernel and have it initalise paging for you
2) Write an assembly function at the entrypoint of the kernel which uses entirely relative/absolute code in order to generate a page directory and table before continuing initalisation.

A third more complicated way is to divide the kernel in half using the linkscript then use the "first" half to initalise everything and set up paging then the "second" half makes up the kernel proper.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Re:I'm getting crazy with paging!!!

Post by Colonel Kernel »

AR wrote:2) Write an assembly function at the entrypoint of the kernel which uses entirely relative/absolute code in order to generate a page directory and table before continuing initalisation.
@falconfx:
The OS FAQ has an example of this called HigherHalfBareBones.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
spacedsteve

Re:I'm getting crazy with paging!!!

Post by spacedsteve »

I had a similar problem a while ago. my solution was to create a setup section which was loaded at phyical/virtual address = 0x00100000. this setup section was called by grub and it then sets up basic paging and maps the kernel into the higher half & identity maps the bottom 4mb's and then jumps into the kernel at 0xC0001000

My linker script was:

Code: Select all

OUTPUT_FORMAT("coff-go32")
ENTRY(_setup)
phys = 0x00100000;
virt = 0xC0001000;
SECTIONS
{
  .setup phys : AT( phys )
  {
    setup = .;
    *(.setup)
    . = ALIGN(4096);
  }
  .kernel virt : AT( ADDR(.setup) + SIZEOF(.setup) )
  {
    kernel = .;
    *(.kernel)
    *(.text)
    . = ALIGN(4096);
  }
  .data : AT( ADDR(.setup) + SIZEOF(.setup) + SIZEOF(.kernel) )
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT( ADDR(.setup) + SIZEOF(.setup) + SIZEOF(.kernel) + SIZEOF(.data) )
  {
     bss = .;
     *(.bss)
    . = ALIGN(4096);
    *(COMMON)
    . = ALIGN(4096);
  }
  _end = .;
}
and my setup code was:

Code: Select all

KERNEL_VMA equ 0xC0001000

PAGE_DIRCTORY equ 0x9C000
PAGE_TABLE_1 equ 0x9D000
PAGE_TABLE_2 equ 0x9E000
PRIV equ 3

_setup:
; store the pointer to the Grub multi boot header for later
push ebx
; create a page table that identity maps the first 4MB of mem
mov eax, PAGE_TABLE_1
mov ebx, 0x00000000 | PRIV
call map
; create a page table that will map the kernel into the 3GB mark
mov eax, PAGE_TABLE_2
mov ebx, 0x00100000 | PRIV
call map
; store the first page table into the page directory
mov dword [PAGE_DIRCTORY], PAGE_TABLE_1 | PRIV
; store the second page table into the page directory entry 768 (3GB mark)
mov dword [PAGE_DIRCTORY + 768*4], PAGE_TABLE_2 | PRIV
; enable paging
mov eax, PAGE_DIRCTORY
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
; restore ebx with the pointer to the multi boot header
pop ebx
; jump into the kenel at it virtual memory address
jmp 0x08:KERNEL_VMA
map:
; loop 1024 times (number of entry's in a page table)
mov ecx, 1024
lmap:
; move next entry (ebx) into the page table (eax)
mov dword [eax], ebx
; move forward to next entry in table
add eax, 4
; move address forward by a page size
add ebx, 4096
; go again untill ecx == 0
loop lmap
ret
once in your kernel you can redo paging with your C code
Post Reply