Page 1 of 1

Can't access VESA LFB without getting page fault

Posted: Fri May 20, 2016 2:34 pm
by codyd51
Whenever I try to write a pixel to the LFB in VESA mode, I get a page fault (page present, read). My paging implementation is from James Molloy's OS series. I've tried identity mapping the LFB as follows:

Code: Select all

for (unsigned int i = 0xFD000000; i < 0xFE000000; i += 0x1000) {
    page_t* pg = get_page(i, 1, kernel_directory);
    alloc_page(pg, 1, 1);
}
These are the prototypes for those functions:

Code: Select all

page_t* get_page(uint32_t address, int make, page_directory_t* dir);
void alloc_frame(page_t* page, int is_kernel, int is_writeable);
When paging is disabled, I'm able to write pixels to the LFB without issue.

Am I identity mapping the LFB incorrectly? Is there something else I need to do to identity map it correctly? Feel free to let me know if you need any more of the implementation. Thanks!

Re: Can't access VESA LFB without getting page fault

Posted: Fri May 20, 2016 3:25 pm
by BrightLight
Do you flush caches after modifying the page tables? Use INVLPG instruction, or simply do:

Code: Select all

mov rax, cr3
mov cr3, rax
What error code does your page fault have?

Re: Can't access VESA LFB without getting page fault

Posted: Fri May 20, 2016 5:00 pm
by max
Invalidating might help as omar said, but to tell you for sure you'd have to show us what the functions you call there do.

Re: Can't access VESA LFB without getting page fault

Posted: Fri May 20, 2016 7:10 pm
by codyd51
The page fault still happens even after flushing the cache :/ error code 2.

Here's the implementation of those two functions:

Code: Select all

page_t* get_page(uint32_t address, int make, page_directory_t* dir) {
	//turn address into an index
	address /= 0x1000;
	//find page table containing this address
	uint32_t table_idx = address / 1024;

	//if this page is already assigned
	if (dir->tables[table_idx]) {
		return &dir->tables[table_idx]->pages[address%1024];
	}
	else if (make) {
		uint32_t tmp;
		dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), &tmp);
		memset(dir->tables[table_idx], 0, 0x1000);
		//PRESENT, RW, US
		dir->tablesPhysical[table_idx] = tmp | 0x7;
		return &dir->tables[table_idx]->pages[address%1024];
	}
	return 0;
}

void alloc_frame(page_t* page, int is_kernel, int is_writeable) {
	if (page->frame != 0) {
		//frame was already allocated, return early
		return;
	}
	uint32_t idx = first_frame(); //index of first free frame
	if (idx == (uint32_t)-1) {
		PANIC("No free frames!");
	}
	set_frame(idx*0x1000); //frame is now ours
	page->present = 1; //mark as present
	page->rw = (is_writeable) ? 1 : 0; //should page be writable?
	page->user = (is_kernel) ? 0 : 1; //should page be user mode?
	page->frame = idx;
}

Re: Can't access VESA LFB without getting page fault

Posted: Fri May 20, 2016 7:18 pm
by codyd51
If I mov the regs in the order from omarrx024's comment, the page fault still occurs. If I use the order listed on http://wiki.osdev.org/TLB, it triple faults.

Re: Can't access VESA LFB without getting page fault

Posted: Fri May 20, 2016 8:54 pm
by BrightLight
codyd51 wrote:If I mov the regs in the order from omarrx024's comment, the page fault still occurs. If I use the order listed on http://wiki.osdev.org/TLB, it triple faults.
In the TLB Wiki page, it is in GAS syntax, not Intel syntax. The code there is the same as my code but in different syntax. ;)
Error code 2 says that the kernel tried to write to a non-present page. So either your page directory or your page table entry doesn't has the present bit set.

Re: Can't access VESA LFB without getting page fault

Posted: Sun May 22, 2016 9:18 am
by codyd51
omarrx024 wrote:
codyd51 wrote:If I mov the regs in the order from omarrx024's comment, the page fault still occurs. If I use the order listed on http://wiki.osdev.org/TLB, it triple faults.
In the TLB Wiki page, it is in GAS syntax, not Intel syntax. The code there is the same as my code but in different syntax. ;)
Error code 2 says that the kernel tried to write to a non-present page. So either your page directory or your page table entry doesn't has the present bit set.
Whoops! Silly me :P

I finally figured it out, I call the following function in my init_paging function. Not totally sure why it doesn't work after paging is already enabled and flushing the TLB cache, but oh well:

Code: Select all

#define VESA_WIDTH 1024
#define VESA_HEIGHT 768
void identity_map_lfb(uint32_t location) {
	uint32_t j = location;
	while (j < location + (VESA_WIDTH * VESA_HEIGHT * 4)) {
		//if frame is valid
		if (j + location + (VESA_WIDTH * VESA_HEIGHT * 4) < memsize) {
			set_frame(j); //tell frame bitset this frame is in use
		}
		//get page
		page_t* page = get_page(j, 1, kernel_directory);
		//fill it
		page->present = 1;
		page->rw = 1;
		page->user = 1;
		page->frame = j / 0x1000;
		j += 0x1000;
	}
}
And it'd be called with:
identity_map_lfb(your_vesa_mode_info->physbase);
I hope this helps someone having the same problem!