Self-referencing page-directory

All about the OSDev Wiki. Discussions about the organization and general structure of articles and how to use the wiki. Request changes here if you don't know how to use the wiki.
Post Reply
tkausl
Posts: 17
Joined: Tue Jul 14, 2015 9:54 pm

Self-referencing page-directory

Post 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.
Icee
Member
Member
Posts: 100
Joined: Wed Jan 08, 2014 8:41 am
Location: Moscow, Russia

Re: Self-referencing page-directory

Post 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.
Post Reply