Page 1 of 1

Self-referencing page-directory

Posted: Fri Jul 24, 2015 5:54 am
by tkausl
Hello,

since the usual bitshifting and calculating with magic numbers can get really ugly and confusing like in this example:

Code: Select all

void * get_physaddr(void * virtualaddr)
{
    unsigned long pdindex = (unsigned long)virtualaddr >> 22;
    unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
 
    unsigned long * pd = (unsigned long *)0xFFFFF000;
    // Here you need to check whether the PD entry is present.
 
    unsigned long * pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
    // Here you need to check whether the PT entry is present.
 
    return (void *)((pt[ptindex] & ~0xFFF) + ((unsigned long)virtualaddr & 0xFFF));
}
i've created a little struct, which helps me with those problems:

Code: Select all

typedef struct {
    union {
        struct {
            uint32_t present        : 1;  // 1
            uint32_t write          : 1;  // 2
            uint32_t user           : 1;  // 3
            uint32_t writethrough   : 1;  // 4
            uint32_t cache_disabled : 1;  // 5
            uint32_t accessed       : 1;  // 6
            uint32_t dirty          : 1;  // 7
            uint32_t ignore0        : 1;  // 8
            uint32_t global         : 1;  // 9
            uint32_t avail          : 3;  // 12
            uint32_t frame          : 20; // 32
        };
        uint32_t val;
    };
} page_table_entry_t;

typedef struct {
    union {
        struct {
            uint32_t present        : 1;  // 1
            uint32_t write          : 1;  // 2
            uint32_t user           : 1;  // 3
            uint32_t writethrough   : 1;  // 4
            uint32_t cache_disabled : 1;  // 5
            uint32_t accessed       : 1;  // 6
            uint32_t ignore0        : 1;  // 7
            uint32_t big_pages      : 1;  // 8
            uint32_t ignore1        : 1;  // 9
            uint32_t avail          : 3;  // 12
            uint32_t frame          : 20; // 32
        };
        uint32_t val;
    };
} page_directory_entry_t;

typedef struct {
    page_table_entry_t page[1024];
} page_table_t;

typedef struct {
    union {
        page_table_t table[1023];
        page_table_entry_t page[1024 * 1023];
    };
    page_directory_entry_t directory[1023];
    uint32_t ignore;
} page_directory_t;


This will only work, if the last entry in the page-directory is mapped to itself!
After a single line of initialisation:

Code: Select all

page_directory_t* CURRENT_DIR = (page_directory_t*)0xFFC00000;


It can be used fairly easy:

Code: Select all

uint32_t virtual = 0xC0000000; //our 'demo' virtual address
uint32_t page_id = virtual / 4096; // or rightshift by 12, this is the 'id' of the page
uint32_t table_id = page_id / 1024; // Or rightshift by 10, the table in which the page is
uint32_t table_offset = page_id%1024; // Or &0x3FF, the offset our page in the table
CURRENT_DIR->directory[table_id].present; //Not doing anything, only showing how to access the directory
CURRENT_DIR->directory[table_id].frame; //and so on

//To access a table:
CURRENT_DIR->table[table_id]->page[table_offset].present;

//or even easier with the full page id
CURRENT_DIR->page[page_id].present;


Might be worth to put it in the wiki, might not.

Re: Self-referencing page-directory

Posted: Tue Jul 28, 2015 4:45 am
by Icee
Note that bit fields in C are not portable. The standard does not dictate whether they appear in little- or big-endian order. This will probably never be a real issue for this piece of (target-specific) code but still worth mentioning.