Page 1 of 1

Problems enabling paging (added questions)

Posted: Thu Nov 24, 2011 1:52 pm
by eino
Hi. Having troubles with enabling paging. I actually got one version working taken from a tutorial, but I ofcourse want to do it differently (thou this idea is propably widely used)

Anyways. Qemu freezes and once even paniced the host machine just when i 'boot' with grub.

Here's the code (it's actually splitted in couple of files ofcourse)

Code: Select all

typedef struct page
{
	
	unsigned int	present				: 1;
	unsigned int	rw					: 1;
	unsigned int	user_super			: 1;
	unsigned int	write_through		: 1;
	unsigned int	cache_disabled		: 1;
	unsigned int	accessed			: 1;
	unsigned int	zero				: 1;
	unsigned int	page_size			: 1;
	unsigned int	ignored				: 1;
	unsigned int	available			: 3;
	unsigned int	physical			: 20;
	
} t_page;

typedef struct page_table
{
	t_page			pages[1024];
	
} t_ptable;

typedef struct page_directory
{
	t_ptable		*tables[1024];
	unsigned int	tables_physical[1024];
	unsigned int	self_physical_address;
	
} t_pd;

t_pd *page_directory;

unsigned int kernel_malloc ( unsigned int amount ) {
	
	// always preserve page alignment
	if ( current_memory_placement & 0xFFFFF000 ) {
		current_memory_placement &= 0xFFFFF000;
		current_memory_placement += 0x1000;
	}
	
	unsigned int malloced = current_memory_placement;
	current_memory_placement += amount;
	return malloced;
	
}

void installPageDirectory () {
	
	current_memory_placement = 0;
	
	while ( current_memory_placement < &kernel_end ) {
		current_memory_placement = current_memory_placement + 0x1000;
	}
	
	current_memory_placement &= 0xFFFFF000;
	current_memory_placement += 0x1000;
	
	page_directory = (t_pd*)kernel_malloc(sizeof(t_pd));
	
	int i = 0;
	
	for( i = 0; i < 1024; i++ ) {
		
		t_ptable *page_table;
		page_table = (t_ptable*)kernel_malloc(sizeof(t_ptable));
		page_directory->tables[i] = page_table;
		
	}

}

/*copied from tutorial*/
void enablePaging () {
	//moves page_directory (which is a pointer) into the cr3 register.
	asm volatile("mov %0, %%cr3":: "b"(page_directory));
	
	//reads cr0, switches the "paging enable" bit, and writes it back.
	unsigned int cr0;
	asm volatile("mov %%cr0, %0": "=b"(cr0));
	cr0 |= 0x80000000;
	asm volatile("mov %0, %%cr0":: "b"(cr0));
}
Any ideas? I really want to write this myself instead of doing some copy paste.

edit:
crash happens when:

Code: Select all

cr0 |= 0x80000000;
asm volatile("mov %0, %%cr0":: "b"(cr0));

Re: Problems enabling paging

Posted: Thu Nov 24, 2011 3:05 pm
by Nessphoro
You're passing the wrong thing into CR3. You need to the pass physical address of tables physical not the kernel structure

Re: Problems enabling paging (added questions)

Posted: Sat Nov 26, 2011 10:36 am
by eino
Hi again. I've been reading quite alot about all of this now but there are just some blank holes I don't get about Paging, framing allocating etc.

Firstly when page directory and page tables are installed is the structure like this:

page directory
page table 1
page table 2
page table n
pages for table 1
pages for table 2
pages for table n

or

page directory
page table 1
pages for table 1
page table 2
pages for table 2
page table n
pages for table n

?


Also I'm quite lost with how to assign frame to a page. Bits 31 to 12 seems to hold the very physical address of a the "start of a frame" in physical memory. For instance 0 or 4096 and the rest of the bits are for the different flags. Now I've been trying to create an array that holds the addresses for every frame available. Lets say we have 16 megs of memory

Code: Select all

unsigned int frames[4096];			//addresses to physical frames
unsigned int number_of_frames;

number_of_frames = 0x1000000 / 0x1000;
	
unsigned int used_frames_count = 0;

unsigned int l = 1;
unsigned int next_addr = 0;
unsigned int counter = 0;
char tmp33;
unsigned int *memptr;

print_string ( "Used frames: " );

for ( l = 0; l < number_of_frames; l++ ) {
	frames[l] = next_addr;
	
	for ( counter = 0; counter < 0x1000; counter++ ) { //roll through the entire frame to see if it is empty or is it being used already by the loaded kernel or other stuff in memory... bios maymbe?
		memptr = frames[l] + counter;
		if ( *memptr != 0) {  // the * in front of a pointer is a "load" (?) that gives me the value at that memory location, right?
			used_frames_count++;
			einos_itoa ( tmp33,  l );
			print_string ( tmp33 );
			print_string ( "," );
			break;
		}
	
	}
	
	next_addr = next_addr + 0x1000;  //advance to the next frame
}
This ofcourse is no way near optimized to be fast but until I get this thing I like to keep this simple stupid.

Now when I want to assign a frame for a page I do something like this:

Code: Select all

unsigned int page = physical_address_of_frame << 12;
and then set the lower bits to the correct flags with some other code?

Now this page needs to be put to a page table? Or is the correct way to get 1024 new pages when ever a new page table is created?

Do I keep track of used frames in an array of structs maybe or do I set some bit on the physical memory for a frame (a header of some sort)?

Would it be any idea to start allocating frames beginning from 16 meg (after the 15meg holeā€¦)?

Before paging is enabled should I move anything that is alreayd in memory to pages or just leave untouched?


Thank you so much in advance!

Re: Problems enabling paging (added questions)

Posted: Sat Nov 26, 2011 3:09 pm
by Nessphoro

Re: Problems enabling paging (added questions)

Posted: Sun Nov 27, 2011 7:46 pm
by eryjus
eino,

I know the tutorial. I had trouble as well. I do have a couple of comments:
  • You are allocating memory for 1024 page tables, but not initializing them. You may not need to allocate the page tables right away, depending on your needs (which is what the tutorial does).
  • I also believe there to be a bug in the malloc code (even from the tutorial). My version of the malloc code is offered below.

Code: Select all

#include "kernel.h"

extern uint32 end;

uint32 placeAddr = (uint32)(&end);

uint32 _kmalloc (uint32 sz, int16 align, uint32 * phys)
{
	if (0 != kheap) {
		void *addr = Alloc(sz, (uint8)align, kheap);
		
		if (0 != phys) {
			Page *page = GetPage((uint32)addr, 0, kernelDirectory);
			*phys = page->frame * 0x1000 + ((uint32)addr & 0x0fff);
		}
		
		return (uint32)addr;
	} else {
		if (1 == align && (placeAddr & 0x00000fff)) {
			placeAddr &= 0xfffff000;
			placeAddr += 0x1000;
		}

		if (phys) *phys = placeAddr;

		uint32 tmp = placeAddr;
		placeAddr += sz;
  
		return tmp;
	}
}
Please notice this block of code:

Code: Select all

		if (1 == align && (placeAddr & 0x00000fff)) {
			placeAddr &= 0xfffff000;
			placeAddr += 0x1000;
		}
A non page-aligned address will have a 1 in bits 0-11. Therefore, I have changed the comparison to check for these bits. Though the code from the tutorial will technically work, it "wastes" memory.

You may also want to review Intel's Software Developer's Manual.

Re: Problems enabling paging (added questions)

Posted: Sun Dec 04, 2011 12:30 pm
by eino
Hi again.

As instructed I did read a couple of manuals over again (including the intel doc) and a few new ones too and I believe my paging is now working for the parts that are implemented:

- Physical allocator (allocates 4096kb frames and handles a bitmap of free/reserved frames)
- Paging init ( creates a page directory for the kernel, a PDE and 1024 PTE's also maps the 1024th PDE to 1st PDE
(does this look anywhere near a correct way? At least without this I get a page fault)

Code: Select all

page_directory[1023] = ( page_directory[0] & 0xFFFFF000 ) | 3;
- A function to get the PDE and PTE indexes from a virtual address
- Page mapping. Takes in a physical address (given by the phys allocator) and a desired virtual address)
- Getter for a physical address. Takes in the virtual address.

I have run a few tests with this where I allocate two pages to the same physical address, output test results, change the value/contents of one of the pages and check that the value in the other page changes too. All seems to be working as expected on QEMU and on real Intel Hardware.

Most of the code I wrote myself, some parts I copied from tutorials.


But questions arise:
- I haven't made any kind of identity mapping... How come even after enabling paging all my print_string functions that deal directly with video memory can work in anyway? I'm a bit confused with this one cause if all memory access goes through the MMU then how is it possible that this works...

- I enable paging after installing GDT, IDT, IRQ & ISR routines. Is this the prefered way or should I enable paging immediately when entering my kmain() ?


Thanks again. Hopefully I'm not asking anything too obvious again. But if the RTFM is coming at me, just keep it coming :wink:

Re: Problems enabling paging (added questions)

Posted: Sun Dec 04, 2011 2:16 pm
by Nessphoro
I hate doing it that way. You lose 256 MB of addressable space.
I'd do it as it suggests in JamesM's kernel development tutorials. Which only adds 4kb overhead

Re: Problems enabling paging (added questions)

Posted: Sun Dec 04, 2011 2:29 pm
by neon
Hello,
I haven't made any kind of identity mapping... How come even after enabling paging all my print_string functions that deal directly with video memory can work in anyway?
You might need to invalidate the pages in the TLB (INVLPG instruction). Some instructions may also flush the entire buffer as well.
I enable paging after installing GDT, IDT, IRQ & ISR routines. Is this the prefered way or should I enable paging immediately when entering my kmain()
The preferred way would be to enable paging as early as possible. If your ISR routines may require memory management functionality (as is most cases) then paging should be enabled before IRQs are enabled. It can be after or before GDT and IDT, however... really depends on what you design is.