Paging problem. (Problem Solved)

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
StephanvanSchaik
Member
Member
Posts: 127
Joined: Sat Sep 29, 2007 5:43 pm
Location: Amsterdam, The Netherlands

Paging problem. (Problem Solved)

Post by StephanvanSchaik »

Hey all,

Sorry for this, but I have a problem with the paging I have in my kernel. When I try to get it enabled then Bochs panics which means that there is something wrong when I try to enable it but I don't know what is wrong with it tough.

I use djgpp, ld and NASM on Windows XP to compile the kernel and I use Grub 0.97 as bootsector.

Here is the code where it normally gets enabled:

Code: Select all

void switch_page_directory(mpage_directory *dir) {
   current_directory = dir;
   __asm__ __volatile__ ("mov %0, %%cr3":: "r"(&dir->tablesPhysical));
   unsigned int cr0;
   __asm__ __volatile__ ("mov %%cr0, %0": "=r"(cr0));
   cr0 |= 0x80000000; // Enable paging!
   __asm__ __volatile__ ("mov %0, %%cr0":: "r"(cr0));
}
If I comment __asm__ __volatile__ ("mov %0, %%cr0":: "r"(cr0)); then the kernel doesn't crash. The same thing if I don't initialize the paging so I can still use my kernel but not something like the paging.

Code: Select all

void initialize_paging() {
    // The size of physical memory. For the moment we 
    // assume it is 16MB big.
    unsigned int mem_end_page = 0x1000000;
    
    nframes = mem_end_page / 0x1000;
    frames = (unsigned int*)kmalloc(INDEX_FROM_BIT(nframes));
    memset(frames, 0, INDEX_FROM_BIT(nframes));
    
    // Let's make a page directory.
    kernel_directory = (mpage_directory*)kmalloc_a(sizeof(mpage_directory));
    memset(kernel_directory, 0, sizeof(mpage_directory));
    current_directory = kernel_directory;

    // Map some pages in the kernel heap area.
    // Here we call get_page but not alloc_frame. This causes page_table_t's 
    // to be created where necessary. We can't allocate frames yet because they
    // they need to be identity mapped first below, and yet we can't increase
    // placement_address between identity mapping and enabling the heap!
    int i = 0;
    for (i = KHEAP_START; i < KHEAP_START + KHEAP_INITIAL_SIZE; i += 0x1000)
        get_page(i, 1, kernel_directory);

    // We need to identity map (phys addr = virt addr) from
    // 0x0 to the end of used memory, so we can access this
    // transparently, as if paging wasn't enabled.
    // NOTE that we use a while loop here deliberately.
    // inside the loop body we actually change placement_address
    // by calling kmalloc(). A while loop causes this to be
    // computed on-the-fly rather than once at the start.
    // Allocate a lil' bit extra so the kernel heap can be
    // initialised properly.
    i = 0;
    while (i < placement_address + 0x1000) {
        // Kernel code is readable but not writeable from userspace.
        alloc_frame(get_page(i, 1, kernel_directory), 0, 0);
        i += 0x1000;
    }

    // Now allocate those pages we mapped earlier.
    for (i = KHEAP_START; i < KHEAP_START + KHEAP_INITIAL_SIZE; i += 0x1000)
        alloc_frame(get_page(i, 1, kernel_directory), 0, 0);

    // Before we enable paging, we must register our page fault handler.
    irq_install_handler(14, page_fault);

    // Now, enable paging!
    switch_page_directory(kernel_directory); // bug here!

    // Initialise the kernel heap.
    //kheap = create_heap(KHEAP_START, KHEAP_START + KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0); // commented for debug reasons.
}
I am thinking that it has to do with the kernel_directory. If you need anymore code then you can just ask.


Thank you already.
Regards, Stephan J.R. van Schaik.
Last edited by StephanvanSchaik on Sun Sep 30, 2007 6:07 am, edited 1 time in total.
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Post by jnc100 »

Does kmalloc return memory aligned on a page boundary? Your page tables and page directory need to be. Other than that, we'd really need to see the contents of get_page and alloc_page to make sense of what you're doing (which looks pretty decent, by the way). Alternatively, you can try debugging yourself by using the bochs debugger. Simply break on the start of 'switch_page_directory' (use a linker map or nm/objdump to get its address) then single step until you have just completed the mov to cr0 (it won't break on executing the command, simply on the attempt to fetch the next instruction) and issue an 'info tab' command, which will display what the processor thinks the page tables look like.

Regards,
John.
StephanvanSchaik
Member
Member
Posts: 127
Joined: Sat Sep 29, 2007 5:43 pm
Location: Amsterdam, The Netherlands

Problem fixed.

Post by StephanvanSchaik »

Hey jnc100,

Well I fixed it so far but there is quiet a problem somewhere. It can't display the addresses after page initialization. For the page aligning there is a possebility to use it or not. In the main I am currently not using it tough. The first problem was a problem with the linker script and a variable. But so far it seems to work except for the part that it the variables b, c, d don't display their address. Only a does which is for the page initialization.

This is the main:

Code: Select all

int main() {
    gdt_install();
    idt_install();
    isrs_install();
    irq_install();
    init_video();
    timer_install();
    keyboard_install();
    //initialize_paging();

    __asm__ __volatile__ ("sti");

    settextcolor(0x9, 0xF);

    puts("Welcome.\n");
    
    puts("\n");
    
    print_floppy_info();
    
    puts("\n");
    
    settextcolor(0x0, 0xF);
    
    unsigned int a = kmalloc(8);
    initialize_paging();
    unsigned int b = kmalloc(8);
    unsigned int c = kmalloc(8);
    puts("a: ");
    putd(a);
    puts(", b: ");
    putd(b);
    puts("\nc: ");
    putd(c);

    kfree(c);
    kfree(b);
    unsigned int d = kmalloc(12);
    puts(", d: ");
    putd(d);

    for (;;);
    
    return 0;
}
I use putd instead of puth because puth broke yesterday :oops:.

kmalloc:

Code: Select all

unsigned int kmalloc_int(unsigned int size, int align, unsigned int* phys) {
    if (kheap != 0) {
        void *addr = alloc(size, (unsigned char)align, kheap);
        if (phys != 0) {
            mpage *page = get_page((unsigned int)addr, 0, kernel_directory);
            *phys = page->frame*0x1000 + ((unsigned int)addr&0xFFFFF000);
        }
        return (unsigned int)addr;
    } else {
        if (align == 1 && (placement_address & 0xFFFFF000)) {
            // Align the placement address;
            placement_address &= 0xFFFFF000;
            placement_address += 0x1000;
        }
        if (phys) {
            *phys = placement_address;
        }
        unsigned int tmp = placement_address;
        placement_address += size;
        return tmp;
    }
}

unsigned int kmalloc_ap(unsigned int size, unsigned int *phys) { // page aligned and returns a physical address.
    return kmalloc_int(size, 1, phys);
}

unsigned int kmalloc_a(unsigned int size) { // page aligned.
    return kmalloc_int(size, 1, 0);
}

unsigned int kmalloc_p(unsigned int size, unsigned int *phys) { // returns a physical address.
    return kmalloc_int(size, 0, phys);
}

unsigned int kmalloc(unsigned int size) {
    return kmalloc_int(size, 0, 0);
}
The alloc function:

Code: Select all

void *alloc(unsigned int size, unsigned char page_align, mheap *heap) {

   // Make sure we take the size of header/footer into account.
   unsigned int new_size = size + sizeof(mheader) + sizeof(mfooter);
   // Find the smallest hole that will fit.
   signed int iterator = find_smallest_hole(new_size, page_align, heap);
   
   if (iterator == -1) { // If we didn't find a suitable hole
       // Save some previous data.
       unsigned int old_length = heap->end_address - heap->start_address;
       unsigned int old_end_address = heap->end_address;

       // We need to allocate some more space.
       expand(old_length + new_size, heap);
       unsigned int new_length = heap->end_address-heap->start_address;

       // Find the endmost header. (Not endmost in size, but in location).
       iterator = 0;
       // Vars to hold the index of, and value of, the endmost header found so far.
       unsigned int idx = -1;
       unsigned int value = 0x0;
       while (iterator < heap->index.size) {
           unsigned int tmp = (unsigned int)lookup_ordered_array(iterator, &heap->index);
           if (tmp > value) {
               value = tmp;
               idx = iterator;
           }
           iterator++;
       }

       // If we didn't find ANY headers, we need to add one.
       if (idx == -1) {
           mheader *header = (mheader *)old_end_address;
           header->magic = HEAP_MAGIC;
           header->size = new_length - old_length;
           header->is_hole = 1;
           mfooter *footer = (mfooter *) (old_end_address + header->size - sizeof(mfooter));
           footer->magic = HEAP_MAGIC;
           footer->header = header;
           insert_ordered_array((void*)header, &heap->index);
       } else {
           // The last header needs adjusting.
           mheader *header = lookup_ordered_array(idx, &heap->index);
           header->size += new_length - old_length;
           // Rewrite the footer.
           mfooter *footer = (mfooter *) ( (unsigned int)header + header->size - sizeof(mfooter) );
           footer->header = header;
           footer->magic = HEAP_MAGIC;
       }
       // We now have enough space. Recurse, and call the function again.
       return alloc(size, page_align, heap);
   }
   
   mheader *orig_hole_header = (mheader *)lookup_ordered_array(iterator, &heap->index);
   unsigned int orig_hole_pos = (unsigned int)orig_hole_header;
   unsigned int orig_hole_size = orig_hole_header->size;
   // Here we work out if we should split the hole we found into two parts.
   // Is the original hole size - requested hole size less than the overhead for adding a new hole?
   if (orig_hole_size-new_size < sizeof(mheader)+sizeof(mfooter)) {
       // Then just increase the requested size to the size of the hole we found.
       size += orig_hole_size-new_size;
       new_size = orig_hole_size;
   }
   
   // If we need to page-align the data, do it now and make a new hole in front of our block.
   if (page_align && orig_hole_pos&0xFFFFF000) { // 0x1000 = page size
       unsigned int new_location = orig_hole_pos + 0x1000 - (orig_hole_pos&0xFFF) - sizeof(mheader);
       mheader *hole_header = (mheader *)orig_hole_pos;
       //0x1000 = page size
       hole_header->size     = 0x1000 - (orig_hole_pos&0xFFF) - sizeof(mheader);
       hole_header->magic    = HEAP_MAGIC;
       hole_header->is_hole  = 1;
       mfooter *hole_footer = (mfooter *) ((unsigned int)new_location - sizeof(mfooter));
       hole_footer->magic    = HEAP_MAGIC;
       hole_footer->header   = hole_header;
       orig_hole_pos         = new_location;
       orig_hole_size        = orig_hole_size - hole_header->size;
   } else {
       // Else we don't need this hole any more, delete it from the index.
       remove_ordered_array(iterator, &heap->index);
   }
   
   // Overwrite the original header...
   mheader *block_header  = (mheader *)orig_hole_pos;
   block_header->magic     = HEAP_MAGIC;
   block_header->is_hole   = 0;
   block_header->size      = new_size;
   // ...And the footer
   mfooter *block_footer  = (mfooter *) (orig_hole_pos + sizeof(mheader) + size);
   block_footer->magic     = HEAP_MAGIC;
   block_footer->header    = block_header;
   
   // We may need to write a new hole after the allocated block.
   // We do this only if the new hole would have positive size...
   if (orig_hole_size - new_size > 0) {
       mheader *hole_header = (mheader *) (orig_hole_pos + sizeof(mheader) + size + sizeof(mfooter));
       hole_header->magic    = HEAP_MAGIC;
       hole_header->is_hole  = 1;
       hole_header->size     = orig_hole_size - new_size;
       mfooter *hole_footer = (mfooter *) ( (unsigned int)hole_header + orig_hole_size - new_size - sizeof(mfooter) );
       if ((unsigned int)hole_footer < heap->end_address) {
           hole_footer->magic = HEAP_MAGIC;
           hole_footer->header = hole_header;
       }
       // Put the new hole in the index;
       insert_ordered_array((void*)hole_header, &heap->index);
   }
   
   // ...And we're done!
   return (void *) ( (unsigned int)block_header+sizeof(mheader) );
}
And for in case you still need alloc_frame and get_page:

Code: Select all

mpage *get_page(unsigned int address, int make, mpage_directory *dir) {
    // Turn the address into an index.
    address /= 0x1000;
    // Find the page table containing this address.
    unsigned int table_idx = address / 1024;

    if (dir->tables[table_idx]) { // If this table is already assigned
        return &dir->tables[table_idx]->pages[address%1024];
    } else if(make) {
        unsigned int tmp;
        dir->tables[table_idx] = (mpage_table*)kmalloc_ap(sizeof(mpage_table), &tmp);
        dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US.
        return &dir->tables[table_idx]->pages[address%1024];
    } else {
        return 0;
    }
}

void alloc_frame(mpage *page, int is_kernel, int is_writeable) {
    if (page->frame != 0) {
        return;
    } else {
        unsigned int idx = first_frame();
        if (idx == (unsigned int)-1) {
            // PANIC! no free frames!!
        }
        set_frame(idx*0x1000);
        page->present = 1;
        page->rw = (is_writeable)?1:0;
        page->user = (is_kernel)?0:1;
        page->frame = idx;
    }
}
Sorry if the code is too long and thanks for helping.

Regards, Stephan J.R. van Schaik.
StephanvanSchaik
Member
Member
Posts: 127
Joined: Sat Sep 29, 2007 5:43 pm
Location: Amsterdam, The Netherlands

Problem Solved.

Post by StephanvanSchaik »

Nevermind, thanks for help. I fixed the hexadecimal print function. (Never use decimal for address :?).

The result was:

Code: Select all

a: 0x106870, b: 0x008100c
c: 0xc0081028, d: 0xc008100c
Which is what it should be.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Wooo! People using my tutorials :D Did you understand them OK, by the way, if you don't mind me asking?

James
StephanvanSchaik
Member
Member
Posts: 127
Joined: Sat Sep 29, 2007 5:43 pm
Location: Amsterdam, The Netherlands

Post by StephanvanSchaik »

Yes, I like it very much and it is well explained to. With enough C experience you understand what most or all of the code does and how it functions.


Regards, Stephan J.R. van Schaik.
Post Reply