Translate virtual address to physical from multi-level pagetabels - c

I'm trying to convert an virtual memory address to a physical one, but can't get it to work. I'm currently doing an operating system as an assignment, and now I have to implement a printf function for the usermode, so when you invoke the write syscall the system should print the content of the array in usermode to the serial port(for now), and to do that i have to convert the address from virtual to physical.
Here is my code from the syscall handler:
Pcb* pcb = getCR3(); // contains the page directory for usermode
setCR3(kernelPageDir); // set the CR3 register to the kernel page directory
uint32_t tableNum = (vAddr >> 22) & 0x3ffUL; // get the upper 10 bits
uint32_t pageIndex = (vAddr >> 12) & 0x3ffUL // get the middle 10 bits
uint32_t offset = vAddr & 0xfffUL; // get the 12 lower bits
uint32_t* topTable = pcb->pageDirectory[tableNum]; // Access the top level table
uint32_t lowTable = topTable[pageIndex]; // Entry to the 2nd table
uint32_t* addr = lowTable + offset; // Should be the physical address
serialPrintf("Structure: tableNum=%08x pageIndex=%08x offset=%08x\n", tableNum, pageIndex, offset);
serialPrintf("Address: topTable=%08x lowTable=%08x addr=%08x\n",topTable, lowTable, addr);
serialPrintf("Char:%c", (char*)addr[0]);
When I run the code, it gives me a page fault when trying to access the value of it:
Structure: tableNum=00000020 pageIndex=00000048 offset=00000378
Address: topTable=00000000 lowTable=0015d000 addr=0015d378
Page fault! errcode=00000000 addr=0015d378
Here is the part from the book that explains the structure of the pages:

If you fill out a pte for the last (index 1023) entry in the second level table which is pointed to by the last (index 1023) of the first level table, then:
0xfffff000 .. 0xfffffffc will by an alias of the first level table,
and
0xffc00000 .. 0xffffffff will be an alias of the entire (sparse) page table.
For example:
int vtop(unsigned vaddr, unsigned *pa) {
unsigned *pdtb = (unsigned *)0xfffff000;
unsigned *pte = (unsigned *)0xffc00000;
if (ptdb[vaddr>>20] & 1) {
if (pte[vaddr>>12] & 1) {
*pa = pte[vaddr>>12] &~0xfff;
return 0;
}
return 2;
}
return 1;
}
You can do this for any index and adjust the pointer values, but if gets more confusing, and 0xffc/20 is out of the way.

Related

Confusion about second argument to remap_page_range

While going through the remap_page_range(), confused about the usage of second argument to it, as pointed out in the following thread, it expect this argument to be a physical address.
https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch13s02.html
But in the implementation :
int simple_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
if (offset >= _ _pa(high_memory) || (filp->f_flags & O_SYNC))
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if (remap_page_range(vma->vm_start, offset,
vma->vm_end-vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
It is derived from vma->vm_pgoff that seems to the virtual address (related to current user space VMA), then how the offset value is related to physical address ?
EDIT:
I see that remap_page_range() is no more part of latest Kernel source and is converted to remap_pfn_range where 3rd argument seems to be page frame number (that is derived from physical address) but I still see vm_pgoff is used for it.
https://elixir.bootlin.com/linux/latest/source/drivers/infiniband/hw/bnxt_re/ib_verbs.c#L3936
So, how vm_pgoff is related to Physical address ?

how can I implement paging , and find physical memory address knowing virtual address

I want to implement the initialisation of paging .
Referring to some links of osdev wiki : https://wiki.osdev.org/Paging , https://wiki.osdev.org/Setting_Up_Paging , my own version is very different.
Because , when we look at the page directory , they said that 12 bits is for the flag and the rest is for the address of the page table , so I tried something like this:
void init_paging() {
unsigned int i = 0;
unsigned int __FIRST_PAGE_TABLE__[0x400] __attribute__((aligned(0x1000)));
for (i = 0; i < 0x400; i++) __PAGE_DIRECTORY__[i] = PAGE_PRESENT(0) | PAGE_READ_WRITE;
for (i = 0; i < 0x400; i++) __FIRST_PAGE_TABLE__[i] = ((i * 0x1000) << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;
__PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__ << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;
_EnablingPaging_();
}
this function help me to know the physical address knowing the virtual address :
void *get_phyaddr(void *virtualaddr) {
unsigned long pdindex = (unsigned long)virtualaddr >> 22;
unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
unsigned long *pd = (unsigned long *)__PAGE_DIRECTORY__[pdindex];
unsigned long *pt = (unsigned long *)pd[ptindex];
return (void *)(pt + ((unsigned int)virtualaddr & 0xFFF));
}
I'm in the wrong direction?
Or still the same?
Assuming you're trying to identity map the first 4 MiB of the physical address space:
a) for unsigned int __FIRST_PAGE_TABLE__[0x400] __attribute__((aligned(0x1000))); it's a local variable (e.g. likely put on the stack); and it will not survive after the function returns (e.g. the stack space it was using will be overwritten by other functions later), causing the page table to become corrupted. That isn't likely to end well.
b) For __FIRST_PAGE_TABLE__[i] = ((i * 0x1000) << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;, you're shifting i twice, once with * 0x1000 (which is the same as << 12) and again with the << 12. This is too much, and it needs to be more like __FIRST_PAGE_TABLE__[i] = (i << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;.
c) For __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__ << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;, the address is already an address (and not a "page number" that needs to be shifted), so it needs to be more like __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__) | PAGE_PRESENT(1) | PAGE_READ_WRITE;.
Beyond that; I'd very much prefer better use of types. Specifically; you should probably get in the habit of using uint32_t (or uint64_t, or a typedef of your own) for physical addresses to make sure you don't accidentally confuse a virtual address with a physical address (and make sure the compiler complains abut the wrong type when you make a mistake); because (even though it's not very important now because you're identity mapping) it will become important "soon". I'd also recommend using uint32_t for page table entries and page directory entries, because they must be 32 bits and not "whatever size the compiler felt like int should be" (note that this is a difference in how you think about the code, which is more important than what the compiler actually does or whether int happens to be 32 bits anyway).
When we ask page , but the page was not present , we have pageFault Interrupt .
SO to avoid that , we can check if the page is there , else , i choice to return 0x0:
physaddr_t *get_phyaddr(void *virtualaddr) {
uint32_t pdindex = (uint32_t)virtualaddr >> 22;
uint32_t ptindex = (uint32_t)virtualaddr >> 12 & 0x03FF;
uint32_t *pd, *pt, ptable;
if ((page_directory[pdindex] & 0x3) == 0x3) {
pd = (uint32_t *)(page_directory[pdindex] & 0xFFFFF000);
if ((pd[ptindex] & 0x3) == 0x3) {
ptable = pd[ptindex] & 0xFFFFF000;
pt = (uint32_t *)ptable;
return (physaddr_t *)(pt + ((uint32_t)(virtualaddr)&0xFFF));
} else
return 0x0;
} else
return 0x0;
}

C Virtual to physical address mapping

I am beginner in C, I am trying to convert virtual addresses to physical.
My code so far, but don't know how to the translation.
I am using 4Kb pages.
The conversion I know is: for givin address: 0x12C000, the last three numbers won't be changed 000 and the remaining 12C will be converted, and then combine the fixed numbers with the converted. correct me if wrong, and how do I do taht in code ?
#include <stdio.h>
int main() {
uint page_table[512] = { 0 };
page_table[200] = 0x1234;
page_table[300] = 0x2345 ;
page_table[511] = 0x8000 ;
uint page_table_size = (sizeof(array)/sizeof(array[0]));
uint page_size_bits 12 // // 2^12 = 4KB;
uint mask_offset ((1<<page_size_bits)-1);
// example of correct outputs:
uint log_addr = 0x12C000;
/* should be 0x2345000 */
uint correctoutput;
log_addr = 0x12CFFF;
/* should be 0x2345FFF */
correctoutput;
log_addr = 0x1FF84A;
/* should be 0x800084A */
correctoutput;
}
Unless you are running in a privileged mode of the CPU, which usually means you are in the kernel you can't. The OS + CPU team up to prevent you from accessing the page tables, and the only way you could do this is by accessing the page tables.
If you did have access to them, and you were a 32bit 386 program running without PAE, it would look something like this:
extern void *mapphys(unsigned pa);
extern unsigned getcr3(void);
unsigned vtop(unsigned va) {
unsigned *pt, t;
pt = mapphys(getcr3());
t = pdir[va>>22];
unmapphys(pt);
if (t & 1) {
pt = mapphys(t & ~ 0xfff);
t = pt[(va >> 12) & 0x3ff];
unmapphys(pt);
if (t & 1) {
return (t &~ 0xfff) | (va & 0xfff);
}
error("page table entry undefined for %#x\n", va);
return -1;
} else {
error("page directory entry undefined for %#x\n", va);
return -2;
}
}
where maphys, unmapphys provide and remove a usable pointer to the given physical address, and getcr3() returns the page table base register from the 386.
That said, there are ways of constructing these sorts of page tables so that you can indirectly address them. For example, if you made the last (index 1023) entry in the page directory table point at the page directory table, then you can using the high 4M-4K of address space as a sort of page table map; and the final 4K of address space is a map of the page directory itself. With this setup, I can instead:
unsigned vtop(unsigned va) {
unsigned *pmap = (unsigned *)0xff800000;
unsigned *pdmap = (unsigned *)0xfffff000;
if (pdmap[va>>22] & 1) {
if (pmap[va>>12] & 1) {
return (pmap[va>>12]&~0xfff) | (va & 0xfff);
}
error("page table entry undefined for %#x\n, va);
return -1;
}
error("page dir entry undefined for %#x\n", va);
return -2;
}
It is worth noting that in this recursive page table, which index you choose is not important, and the mechanism is generally applicable to any page table definition which is the interior and leaf nodes have compatible layouts.
You need to calculate two values: The index into the physical memory (page_table) and the offset to add to the page_table entries address.
page_table_index = log_address >> 12;
page_offset = log_address & 0xfff;
physical = (page_table[page_table_index] << 12) | page_offset;
The lower 12 bits are the offset. You keep that part of the address. You shift these 12 bits off and you are left with the page number in the page_table array. You can then look up the page_table to obtain the base address, make space for the 12 bits in that address and insert the offset.

Dereferncing a pointer doesn't return the real value in the memory address

I'm developing an embedded application on STM8S using STVD IDE and Cosmic C compiler. I'm trying to read FLASH memory byte by byte to calculate CRC. Following is my code snippet:
uint32_t crc32_buffer(const uint8_t *buf, uint32_t len)
{
uint32_t index = 0;
uint32_t crc = 0xFFFFFFFF;
uint32_t flashIndex = 0;
uint8_t *ptr = buf;
volatile uint8_t value = 0;
volatile uint8_t i = 0;
for (index = 0; index < len; index++)
{
value = *ptr;
flashIndex = (crc & 0xFF) ^ value;
ptr++;
crc = (crc >> 8) ^ table[flashIndex];
if(bytesCntr >= 2685)
{
i++;
}
}
return ~crc;
}
The code works fine until 2694 bytes are read from the FLASH. Viewing Memory in the debugging session, I make sure that the next byte in the FLASH has value of 0C. Checking the value of ptr, I make sure it has the address of this 0C byte in the FLASH (which is 0x8B15). However, value variable always get the value of 8B instead of 0C after ptr is dereferenced.
I also tried to exclude unnecessary variables so it be like this:
crc = (crc >> 8) ^ table[(crc & 0xFF) ^ buf[index]];
But the table index was not as it should be as the memory location was read as 8B instead of 0C.
I found that the byte before and the byte after address 0x8B15 are read correctly. Only this address is read wrongly.
UPDATE-1
The disassembly of the value = *ptr; is as following:
LDW X, (0x11,SP)
LD A, (X)
LD (0x13,SP),A
When reading the byte at address 0x8B15, if I put a breakpoint at the second assembly line and then the value in the memory location is read correctly as 0C. However, if I put the breakpoint at the third assembly line instead, I find that register X has 0x8B15 (the right address) but register A has 0x8B (the wrong value).
UPDATE-2
I added an if statement inside the for loop for debugging (to put my breakpoint). I found that the code saved in memory byte which is read wrongly is always the code inside this if statement. The disassembly of this code always have something to do with SP. Even if I changed the code, the problematic memory byte is always the first instruction in the if statement. And I also noticed that the wrong read value is always 0x8B regardless what is the right value. Here is the disassembly saved in this memory location:
0x8b15 <crc32_buffer+104> 0x0C01 INC (0x01,SP) INC (_CRC_ONGOING_s,SP)
I came across the same issue last week .. It seems to be a problem with the debugging Firmware and your code both accessing the same location. If you have an active breakpoint at that same Flash location you are trying to read with your code, then your code ends up reading 0x8B from that location. If you remove or deactivate all breakpoints, the location is read correctly..
In addition to my previous answer (see above or below ..I couldn't edit that one).. Active breakpoints substitute the existing instruction at that particular Flash memory location with a BREAK instruction (opcode 0x8B), so when that memory location is read from within the application code, 0x8B will be the result.
So this is not really a 'problem', but rather a limitation of software breakpoints as implemented within the SWIM debugging firmware on the STM8S.

computing physical addresses from virtual addresses in C

I'm struggling to write a function that translates virtual memory address to physical ones. I want the function to return the physical memory address of a virtual 16-bit address.
I'm just using 16-bit long virtual addresses that ignore permission bits (read,write, etc.). There are 256 pages in the page table so the base table register, which is BTR, just points to the table.
I want the function to either return:
Success: the physical address (a void*)
Page Fault: the virtual page number (an integer)
Protection Fault: a copy of the PTE
Here is the page table entry:
31 30..28 27..............................................4 3 2 1 0
+-----+------+-------------------------------------------------+-+-+-+-+
|Valid|unused| 24-bit Physical Page Number |P|R|W|X|
+-----+------+-------------------------------------------------+-+-+-+-+
I'm trying to learn how virtual memory works. I'm confused on how I would take a 16-bit virtual address and map it to a 32-bit page table entry, and then how to get the physical address from there. I've defined result_t and a union of the page table entry below, but I'm unsure how to use them. I've gotten a lot of help from looking online but everything is getting muddle together, I just want to learn how everything works straightforwardly.
Here are some necessary definitions:
extern void* BTR;
typedef struct result_st {
enum {SUCCESS, PAGEFAULT,
PROTFAULT, NOTIMPLEMENTED} status;
union {
void* pa;
unsigned vpn;
unsigned pte;
} value;
} result_t;
static result_t success(void* pa) {
result_t res;
res.status=SUCCESS;
res.value.pa = pa;
return res;
}
typedef union {
unsigned All;
struct {
unsigned Valid :1;
unsigned Unused :3;
unsigned PhysicalPageNumber :24;
unsigned SupervisoryMode :1;
unsigned Read :1;
unsigned Execute :1;
unsigned Write :1;
};
} PageTableEntry;
static int is_valid (unsigned pte) {
return 1 & (pte >> 31);
}
Here is the function I am writing:
result_t legacy(unsigned short va)
{
result_t result;
unsigned pte = va << 8;
result.value.pte = pte;
// This is my attempt so far.
// I want to use is_Valid() somewhere
void* pa = pte >> 8 | (va & 255);
return success(pa);
}
Thanks for any advice you can provide!
You are missing the definition of the actual page table. I assume it's like this (assuming I have understood your question correctly):
#define PAGE_TABLE_SIZE 256
PageTable page_table[PAGE_TABLE_SIZE];
Then your code would look something like this:
#define VIRT_PAGE_SIZE_BITS 8
/* Get virtual page number by dividing by the virt page size */
unsigned int virt_page_num = va >> VIRT_PAGE_SIZE_BITS;
assert(virt_page_num < PAGE_TABLE_SIZE); // Or do proper error handling
/* Use virtual page number to index into page table */
PageTableEntry pte = page_table[virt_page_num];
if (is_valid(pte)) {
if (is_access_ok(pte)) {
unsigned int phys_page_num = pte.PhysicalPageNumber;
return success(phys_page_num);
} else {
/* Protection fault code goes here */
}
} else {
/* Page fault code goes here */
}

Resources