Hi,
BEGIN COPY PASTE OF THE REPLY I JUST SPENT 20 MINUTES CRAFTING AFTER YOU EMAILED ME PERSONALLY AND I DIDN'T KNOW YOU HAD CROSS POSTED AS WELL. <--- I hope you notice that this is in caps and bold.
--------------------------------------------------------------------------------------
Hi Tushar,
First of all I'd like to thank you for reading my tutorials, and I hope they are helping you learn.
The purpose of identity mapping is to allow the processor to still access the kernel when paging is enabled. Consider that paging is enabled, but no identity mapping has occurred.
The processor tries to read the next instruction, for example at 0x100004. This gets treated as a virtual address, and is sent to the MMU for translation to a physical address. The MMU has no
knowledge of how to convert 0x100004 to a physical address, so a page fault occurs. In this case the interrupt service routines will also be virtual addresses that the MMU will fail to translate,
and so the system will triple-fault and reset.
The answer is to map. That is - tell the MMU that the virtual address area from 0x100000 to for example 0x200000 should map to some physical address region such as 0x100000 to 0x200000. Then,
the MMU can translate virtual addresses to physical addresses and the processor can execute instructions in the kernel.
Now, why *identity* map? Well, if you compile with -fPIC (Position Independent Code), there is no need to! but if you don't, your code will contain references to *absolute* addresses, such as
mov eax, [0x100008] .
So the virtual addresses for areas in your kernel *must* map to the *same* physical addresses, or your code will crash!
I'll try to explain the uses of each function one by one.
Code: Select all
void alloc_frame(page_t* page, int is_kernel, int is_writeable);
void free_frame(page_t* page);
These functions allocate and free *frames*. Depending on how you were taught at university, you may not have used the term 'frame' before, so I will quickly explain what they are here.
A (normally 4 kilobyte) section of *virtual memory* is called a 'page'. A (normally 4 kb) section of *physical memory* is called a 'frame'. So when one maps virtual addresses to physical addresses, one
is really "putting a page in a frame". When you need to allocate one or more pages of virtual memory, you therefore need one or more frames available to map them to. In order to ensure that frames are
not allocated twice accidentally (resulting in data getting trampled), these routines are provided.
The first one takes a page that you want mapped into physical memory, and 0/1 values saying whether the
mapping should be available to all levels of code, or just kernel mode code, and whether the mapping should be read-only (only takes effect in user-mode code).
The second frees an allocated mapping of a page to a frame.
This function initialises paging. It sets up a new page directory and identity maps the area where the kernel lives (see above). *This function MUST be called for paging to be enabled*! If you
don't call this, your code will *not* be running in paged mode! (this could explain why you weren't recieving page faults - 0xA0000000 in physical memory is normally mapped to the VESA graphics framebuffer).
This function calls alloc_frame, get_page, and switch_directory.
Code: Select all
void switch_page_directory(page_directory_t* dir);
This function changes virtual memory space. It writes 'dir' into the control register 'CR3', which changes the virtual->physical mappings. It is called once from initialise_paging().
Code: Select all
page_t* get_page(u32int address, int make, page_directory_t* dir);
This function, given a page directory, will attempt to find a pointer to a Page Table Entry for the given virtual address. It is used when creating mappings for virtual to physical addresses. The
'make' parameter tells the function what to do if the Page Table the requested Page Table Entry resides in has not been created. If '1', it will create the Page Table, if '0' it will not and return NULL.
Code: Select all
void page_fault(struct regs reg1);
Handles a page fault. Should be mapped to interrupt 14.
So in conclusion,
Make sure that you are calling "initialise_paging" before attempting a page fault.
In response to:
could you please tell me why should a page fault occur by the above code ?
Simple. When initialise_paging is called, it identity maps the first, e.g. 2MB of memory. 0xA0000000 is about 3GB into addressable RAM, and has not been mapped. So the MMU shouldn't know how to handle
an access to it, and should throw a page fault.
Any more questions, do ask,
James
EDIT: JamesM: Added BBCode and swore at AJ for his "molly" comment