Page 1 of 1
How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 2:03 am
by agdever
I am trying to make my OS and trying to implement paging. This is all the paging code.
Code: Select all
#include "paging.h"
#include "kheap.h"
unsigned int* current_dir;
void init_paging() {
unsigned int* kernel_directory = (unsigned int*)kmalloc_a(sizeof(unsigned int*) * TABLES_PER_DIRECTORY);
int i, j, address = 0;
// 1024
for(i = 0; i < TABLES_PER_DIRECTORY; i++) {
kernel_directory[i] = (unsigned int) address;
unsigned int* table = (unsigned int*) kernel_directory[i];
// 1024
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
address += PAGE_SIZE; // 4096
}
kernel_directory[i] |= 5; // same
}
switch_page_directory(kernel_directory);
enable_paging();
}
void enable_paging() {
write_cr0(read_cr0() | 0x80000000);
}
void switch_page_directory(unsigned int* dir) {
current_dir = dir;
write_cr3((unsigned int)(dir));
}
My main function calls init_paging(). After calling it, the screen has some patterns on it indicating that physical 0xb8000 was not mapped to virtual 0xb8000. I am attempting identity paging.
According to the wiki, A page directory entry is essentially a pointer to an array of pages (with the zeros used for flags) and each page table entry a.k.a. page is a number which specifies the physical address of the page. My question is, what specifies the virtual address of a particular page? This is my workspace:
https://drive.google.com/open?id=10CsXU ... 1tTkhCjw7y
paging.h has a lot of unused clutter.
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 2:23 am
by Brendan
Hi,
agdever wrote:I am trying to make my OS and trying to implement paging. This is all the paging code.
Code: Select all
#include "paging.h"
#include "kheap.h"
unsigned int* current_dir;
void init_paging() {
unsigned int* kernel_directory = (unsigned int*)kmalloc_a(sizeof(unsigned int*) * TABLES_PER_DIRECTORY);
int i, j, address = 0;
// 1024
for(i = 0; i < TABLES_PER_DIRECTORY; i++) {
kernel_directory[i] = (unsigned int) address;
unsigned int* table = (unsigned int*) kernel_directory[i];
// 1024
I think you forgot to do "address += PAGE_SIZE;" here; which would cause the same physical page of RAM to be used for both a page table and a normal page, which would cause one or both of them to be overwritten/corrupted.
I'd be tempted to have a separate function, like this:
Code: Select all
uint64_t alloc_physical_page(void) {
uint64_t physical_page = address;
address += PAGE_SIZE;
return physical_page;
}
This would make it a lot easier to improve/change later (e.g. add support for "Error: ran out of memory", etc).
Cheers,
Brendan
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 3:13 am
by agdever
Thx for replying Brendan.
Code: Select all
#include "paging.h"
#include "kheap.h"
unsigned int* current_dir;
void init_paging() {
unsigned int* kernel_directory = (unsigned int*)kmalloc_a(sizeof(unsigned int*) * TABLES_PER_DIRECTORY);
int i, j, address = 0;
for(i = 0; i < TABLES_PER_DIRECTORY; i++) {
kernel_directory[i] = (unsigned int) address;
unsigned int* table = (unsigned int*) kernel_directory[i];
address += PAGE_SIZE;
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
address += PAGE_SIZE;
}
kernel_directory[i] |= 5; // same
}
switch_page_directory(kernel_directory);
enable_paging();
}
void enable_paging() {
write_cr0(read_cr0() | 0x80000001);
}
void switch_page_directory(unsigned int* dir) {
current_dir = dir;
write_cr3((unsigned int)(dir));
}
Now, the virtual machine is crashing again and again and restarting
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 4:27 am
by Brendan
Hi,
agdever wrote:Now, the virtual machine is crashing again and again and restarting
Sorry - I had to be somewhere else and rushed things earlier. I saw "kernel_directory
= (unsigned int) address;" and thought you were using "address += PAGE_SIZE;" as a simple physical memory manager during early boot (until later in boot when kernel initialises a proper memory manager after paging is enabled). Now I see that I was mistaken - you're using "kmalloc_a()" to allocate a physical page for the page directory, which means that you should probably also be using "kmalloc_a()" to allocate a physical page for each page table; like this:
Code: Select all
void init_paging() {
unsigned int* kernel_directory = (unsigned int*)kmalloc_a(sizeof(unsigned int*) * TABLES_PER_DIRECTORY);
int i, j, address = 0;
for(i = 0; i < TABLES_PER_DIRECTORY; i++) {
kernel_directory[i] = (unsigned int*)kmalloc_a(sizeof(unsigned int*) * PAGES_PER_TABLE);
unsigned int* table = (unsigned int*) kernel_directory[i];
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
address += PAGE_SIZE;
}
kernel_directory[i] |= 5; // same
}
I also notice that you're mapping the first 4 GiB of the physical address space into the virtual address space, so that it's effectively identical to not using paging at all. This is relatively silly (all it does is waste RAM for page tables, etc).
Typically you want to map the kernel from where it is in physical memory (e.g. physical address 0x00100000) to where you actually want it (e.g. the virtual address 0xC0000000); and then have a single identity mapped page for a tiny little piece of code that does "write_cr0(read_cr0() | 0x80000001);" and jumps to where the kernel actually is; then you want to unmap that single identity mapped page so that all of "user-space" (all of the virtual address space from 0x000000000 to 0xBFFFFFFFF) contains nothing at all (so that it's nice and clean, ready for when you start a process). You also have to figure out what to do with the current stack (easiest is to discard the old stack and initialise a new one in kernel space after).
Note that the initial code to setup paging (and the initial physical memory manager) probably should be written in pure assembly language; partly because it's running at the wrong address (because kernel hasn't been mapped where it should be yet) and partly because you can't change the stack while a C function is relying on it. This is why most people either have a very simple initial physical memory manager that's used before paging is enabled, or just use RAM that was reserved in the ".bss" to avoid having any initial physical memory manager; and then have a proper physical memory manager that's used after paging is enabled.
Cheers,
Brendan
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 5:00 am
by agdever
Firstly, the kmalloc_a you added was there before in my original one. I removed it because nothing was working
Also, the cast in front of that should be an integer not a pointer even though it acts like one but it doesn't matter.
Now, after making it kmalloc_a, random patterns on my screen appear. This is probably because physical 0xb8000 was not mapped to virtual 0xb8000 or random stuff appeared in the 0xb8000 randomly and I should not think much and simply reset it. Your comments? Also, how do I know that I am doing identity mapping here?
This is the new code:
Code: Select all
#include "paging.h"
#include "kheap.h"
#include "system.h"
#define BITS_IN_A_BYTE 8
unsigned int* current_dir;
unsigned int *frames;
unsigned int nframes = PAGES_PER_TABLE * TABLES_PER_DIRECTORY;
void init_paging() {
unsigned int* kernel_directory = (unsigned int*)kmalloc_a(sizeof(unsigned int*) * TABLES_PER_DIRECTORY);
int i, j, address = 0;
for(i = 0; i < TABLES_PER_DIRECTORY; i++) {
kernel_directory[i] = kmalloc_a(sizeof(unsigned int) * PAGES_PER_TABLE);
unsigned int* table = (unsigned int*) kernel_directory[i];
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
address += PAGE_SIZE;
}
kernel_directory[i] |= 5; // same
}
switch_page_directory(kernel_directory);
enable_paging();
}
void enable_paging() {
write_cr0(read_cr0() | 0x80000001);
}
void switch_page_directory(unsigned int* dir) {
current_dir = dir;
write_cr3((unsigned int)(dir));
}
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 6:02 am
by Brendan
Hi,
agdever wrote:Firstly, the kmalloc_a you added was there before in my original one. I removed it because nothing was working
Also, the cast in front of that should be an integer not a pointer even though it acts like one but it doesn't matter.
Now, after making it kmalloc_a, random patterns on my screen appear. This is probably because physical 0xb8000 was not mapped to virtual 0xb8000 or random stuff appeared in the 0xb8000 randomly and I should not think much and simply reset it. Your comments? Also, how do I know that I am doing identity mapping here?
This is the new code:
That looks like it should work to me.
The next step would be to run it inside an emulator with a nice debugger. For example, (assuming Bochs) you could put a breakpoint before the "write_cr0(read_cr0() | 0x80000001);", then single-step until the "mov cr0, .." happens and inspect the virtual address space (using "info tab") to see if it looks right; then keep single stepping to see what happens after that (and see which writes causes the random patterns to appear on the screen).
Cheers,
Brendan
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 6:08 am
by agdever
I'm using Qemu
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 6:18 am
by Brendan
Hi,
agdever wrote:I'm using Qemu
That should be fine - Qemu has a built in "monitor" (debugger) that you can use that supports most of the same features. I'm just less familiar with Qemu's built in debugger (can't remember the commands, etc).
Cheers,
Brendan
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 6:27 am
by agdever
After some old school debugging, here's what I found:
Code: Select all
#include "paging.h"
#include "kheap.h"
#include "system.h"
#define BITS_IN_A_BYTE 8
unsigned int* current_dir;
unsigned int *frames;
unsigned int nframes = PAGES_PER_TABLE * TABLES_PER_DIRECTORY;
void init_paging() {
........
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
//if(address == 0) for(;;);
address += PAGE_SIZE;
if(address == 0) for(;;);
}
On removing the comments from the first infinite loop, the screen stays okay, on commenting it out as shown in the code, it becomes crappy. Do you know what this means?
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 7:21 am
by Brendan
Hi,
agdever wrote:On removing the comments from the first infinite loop, the screen stays okay, on commenting it out as shown in the code, it becomes crappy. Do you know what this means?
Maybe it means that "kmalloc_a()" returned the physical address 0xB8000 (the address of the frame buffer), and your paging code wrote data to the address it allocated (filling the frame buffer/screen) before paging is enabled. Maybe it means that something after the "for(;;)" causes an exception and your exception handler triggers itself, so "infinite recursion" causes your stack to continue growing until it overwrites everything (including the frame buffer/screen). Maybe it means you're executing code that was compiled for 32-bit while you're in (16-bit) real mode (yes, this has happened before!). Maybe an IRQ handler corrupts a register and causes strange things to happen.
Maybe 3 years ago you wrote code to test the screen by writing a "random" pattern to the screen, but now you've forgotten that code is still there and don't realise that there isn't any bug at all!
If you posted all of your code (a link to it) I could probably carefully inspect all of it and might find the real cause of the problem that way; but that won't really help you in the long run (there's always going to be another bug sooner or later).
Cheers,
Brendan
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 7:34 am
by agdever
O shoot, I meant the other way...
Code: Select all
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
//if(address == 0) for(;;);
address += PAGE_SIZE;
if(address == 0) for(;;);
}
On commenting the first loop out, the screen becomes crappy (as shown in code), on removing the comments, it becomes normal. It stops address from incrementing. It is the incrementing of the address variable causing the goof up. My workspace there in the first post.
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 8:24 am
by Brendan
Hi,
agdever wrote:O shoot, I meant the other way...
Code: Select all
for(j = 0; j < PAGES_PER_TABLE; j++) {
table[j] = address | 5; // accessible by all, r/w, present
//if(address == 0) for(;;);
address += PAGE_SIZE;
if(address == 0) for(;;);
}
On commenting the first loop out, the screen becomes crappy (as shown in code), on removing the comments, it becomes normal. It stops address from incrementing. It is the incrementing of the address variable causing the goof up.
If the "//if(address == 0) for(;;);" is uncommented, it stops the address from incrementing and also stops the outer "for(j = 0; j < PAGES_PER_TABLE; j++)" loop (which means that "table[j] = address | 5;" is only executed once when j is zero), and it also stops everything that is after the loop.
It's extremely unlikely that "address += PAGE_SIZE;" is the problem; and very likely that the problem is in one of the many other things that commenting "//if(address == 0) for(;;);" stops.
Cheers,
Brendan
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 8:42 am
by agdever
Re: How to know the virtual and physical address of a page?
Posted: Fri Mar 16, 2018 9:52 am
by Brendan
Hi,
It'd be a lot easier to just use statically allocated memory for your initial page directory and page tables. You can do this like:
Code: Select all
#define INITIAL_PAGE_TABLES TABLES_PER_DIRECTORY
static uint32_t page_directory[TABLES_PER_DIRECTORY] __attribute__ ((aligned (4096)));
static uint32_t page_tables[INITIAL_PAGE_TABLES][PAGES_PER_TABLE] __attribute__ ((aligned (4096)));
void init_paging() {
uint32_t address;
for(int i = 0; i < INITIAL_PAGE_TABLES; i++) {
kernel_directory[i] = page_tables[i] | 5;
for(int j = 0; j < PAGES_PER_TABLE; j++) {
page_tables[i][j] = address | 5;
address += PAGE_SIZE;
}
}
switch_page_directory(page_directory);
enable_paging();
}
Of course this is still a waste of memory (the "page_tables[][]" will consume 4 MiB of RAM for no sane reason), but it'd be trivial to change it (e.g. use "#define INITIAL_PAGE_TABLES 1" so that you only use one page table - enough to identity map the first 4 MiB, which is probably still 4 times more than you actually need).
Cheers,
Brendan