|
@@ -45,13 +45,14 @@ typedef union
|
|
bool_t no_cache : 1;
|
|
bool_t no_cache : 1;
|
|
bool_t accessed : 1;
|
|
bool_t accessed : 1;
|
|
bool_t dirty : 1;
|
|
bool_t dirty : 1;
|
|
- bool_t reserved1 : 1;
|
|
|
|
|
|
+ bool_t large : 1;
|
|
bool_t global : 1;
|
|
bool_t global : 1;
|
|
bool_t cow : 1;
|
|
bool_t cow : 1;
|
|
- size_t reserved2 : 2;
|
|
|
|
|
|
+ bool_t sticky : 1;
|
|
|
|
+ size_t reserved1 : 1;
|
|
page_num_t number : 40;
|
|
page_num_t number : 40;
|
|
- size_t reserved3 : 11;
|
|
|
|
- bool_t no_execute : 1;
|
|
|
|
|
|
+ size_t reserved2 : 11; /* PAE only */
|
|
|
|
+ bool_t no_execute : 1; /* PAE only */
|
|
} present;
|
|
} present;
|
|
|
|
|
|
struct
|
|
struct
|
|
@@ -62,9 +63,11 @@ typedef union
|
|
bool_t writable : 1;
|
|
bool_t writable : 1;
|
|
bool_t executable : 1;
|
|
bool_t executable : 1;
|
|
bool_t usermode : 1;
|
|
bool_t usermode : 1;
|
|
- size_t reserved1 : 4;
|
|
|
|
|
|
+ bool_t sticky : 1;
|
|
|
|
+ bool_t cow : 1;
|
|
|
|
+ size_t reserved1 : 2;
|
|
page_num_t number : 40;
|
|
page_num_t number : 40;
|
|
- size_t reserved2 : 12;
|
|
|
|
|
|
+ size_t reserved2 : 12; /* PAE only */
|
|
} absent;
|
|
} absent;
|
|
} pte_t;
|
|
} pte_t;
|
|
|
|
|
|
@@ -77,8 +80,10 @@ static struct
|
|
// LMA: { {39, 9}, {30, 9}, {21, 9}, {12, 9} }
|
|
// LMA: { {39, 9}, {30, 9}, {21, 9}, {12, 9} }
|
|
// VA57: { {48, 9}. {39, 9}, {30, 9}, {21, 9}, {12, 9} }
|
|
// VA57: { {48, 9}. {39, 9}, {30, 9}, {21, 9}, {12, 9} }
|
|
|
|
|
|
-page_table_t memory_default_table = (page_table_t)0xFFFFFFFC;
|
|
|
|
-byte_t paging_levels = 2;
|
|
|
|
|
|
+const page_table_t memory_default_table = (page_table_t)0xFFFFFFFC;
|
|
|
|
+const page_table_t memory_shadow_table = (page_table_t)0xFFFFFFF8;
|
|
|
|
+size_t memory_table_size = 0x400000;
|
|
|
|
+byte_t paging_levels = 2, self_entry_level = 0;
|
|
byte_t table_entry_size = 4;
|
|
byte_t table_entry_size = 4;
|
|
|
|
|
|
static inline pte_t read_pte(pte_pointer_t ppte)
|
|
static inline pte_t read_pte(pte_pointer_t ppte)
|
|
@@ -113,8 +118,7 @@ static pte_pointer_t memory_get_table_entry(page_table_t table, void *address, b
|
|
uintptr_t numeric_address = (uintptr_t)address;
|
|
uintptr_t numeric_address = (uintptr_t)address;
|
|
uintptr_t table_base = (uintptr_t)table;
|
|
uintptr_t table_base = (uintptr_t)table;
|
|
|
|
|
|
- int level;
|
|
|
|
- for (level = 0; level < paging_levels; level++)
|
|
|
|
|
|
+ for (int level = 0; level < paging_levels; level++)
|
|
{
|
|
{
|
|
uintptr_t level_mask = ((uintptr_t)1 << page_table_levels[level].bits) - 1;
|
|
uintptr_t level_mask = ((uintptr_t)1 << page_table_levels[level].bits) - 1;
|
|
uintptr_t table_mask = (level_mask + 1) * table_entry_size - 1;
|
|
uintptr_t table_mask = (level_mask + 1) * table_entry_size - 1;
|
|
@@ -157,13 +161,91 @@ static sysret_t get_or_create_table_entry(page_table_t table, void *address, pte
|
|
return ERR_SUCCESS;
|
|
return ERR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void update_sticky_pages(int level, pte_pointer_t source, pte_pointer_t destination)
|
|
|
|
+{
|
|
|
|
+ pte_t pte;
|
|
|
|
+ size_t count = 1 << (page_table_levels[level].bits - (level ? 0 : 1));
|
|
|
|
+
|
|
|
|
+ /* We must skip entries that point back into higher-level tables */
|
|
|
|
+ if (level == self_entry_level) count -= 2;
|
|
|
|
+
|
|
|
|
+#define UPDATE_PAGES_LOOP(type) \
|
|
|
|
+ do \
|
|
|
|
+ { \
|
|
|
|
+ type *src_ppte = (type*)source; \
|
|
|
|
+ type *dest_ppte = (type*)destination; \
|
|
|
|
+ \
|
|
|
|
+ for (size_t i = 0; i < count; i++) \
|
|
|
|
+ { \
|
|
|
|
+ pte.raw_entry = *src_ppte; \
|
|
|
|
+ \
|
|
|
|
+ if (pte.is_present) \
|
|
|
|
+ { \
|
|
|
|
+ if (pte.present.sticky) \
|
|
|
|
+ { \
|
|
|
|
+ if (!memory_get_page_mapping(memory_shadow_table, dest_ppte)) \
|
|
|
|
+ { \
|
|
|
|
+ page_t *table_page = memory_acquire_page(MIN_PHYS_ADDR_BITS, MAX_PHYS_ADDR_BITS, PAGE_SIZE); \
|
|
|
|
+ if (!table_page) KERNEL_CRASH("No free pages were available at a critical moment"); \
|
|
|
|
+ \
|
|
|
|
+ sysret_t ret = memory_map_page(memory_shadow_table, \
|
|
|
|
+ table_page, \
|
|
|
|
+ (void*)PAGE_ALIGN((uintptr_t)dest_ppte), \
|
|
|
|
+ MEMORY_FLAG_ACCESSIBLE | MEMORY_FLAG_WRITABLE); \
|
|
|
|
+ if (ret != ERR_SUCCESS) KERNEL_CRASH("Unexpected mapping error"); \
|
|
|
|
+ } \
|
|
|
|
+ \
|
|
|
|
+ *dest_ppte = pte.raw_entry; \
|
|
|
|
+ } \
|
|
|
|
+ else if (level + 1 < paging_levels) \
|
|
|
|
+ { \
|
|
|
|
+ uintptr_t mask = memory_table_size - 1; \
|
|
|
|
+ int shift = page_table_levels[level + 1].bits; \
|
|
|
|
+ pte_pointer_t nested_src = (pte_pointer_t)(((uintptr_t)src_ppte & ~mask) | (((uintptr_t)src_ppte << shift) & mask)); \
|
|
|
|
+ pte_pointer_t nested_dest = (pte_pointer_t)(((uintptr_t)dest_ppte & ~mask) | (((uintptr_t)dest_ppte << shift) & mask)); \
|
|
|
|
+ update_sticky_pages(level + 1, nested_src, nested_dest); \
|
|
|
|
+ } \
|
|
|
|
+ } \
|
|
|
|
+ \
|
|
|
|
+ src_ppte++; \
|
|
|
|
+ dest_ppte++; \
|
|
|
|
+ } \
|
|
|
|
+ } while(FALSE)
|
|
|
|
+
|
|
|
|
+ if (table_entry_size == 4) UPDATE_PAGES_LOOP(dword_t);
|
|
|
|
+ else if (table_entry_size == 8) UPDATE_PAGES_LOOP(qword_t);
|
|
|
|
+
|
|
|
|
+#undef UPDATE_PAGES_LOOP
|
|
|
|
+}
|
|
|
|
+
|
|
page_t *memory_get_page_mapping(page_table_t table, void *address)
|
|
page_t *memory_get_page_mapping(page_table_t table, void *address)
|
|
{
|
|
{
|
|
pte_pointer_t ppte = memory_get_table_entry(table, address, FALSE);
|
|
pte_pointer_t ppte = memory_get_table_entry(table, address, FALSE);
|
|
if (!ppte) return NULL;
|
|
if (!ppte) return NULL;
|
|
|
|
|
|
pte_t pte = read_pte(ppte);
|
|
pte_t pte = read_pte(ppte);
|
|
- return pte.is_present ? memory_find_page_by_address(pte.present.number * PAGE_SIZE) : NULL;
|
|
|
|
|
|
+
|
|
|
|
+ if (pte.is_present)
|
|
|
|
+ {
|
|
|
|
+ return memory_find_page_by_address(pte.present.number * PAGE_SIZE);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ switch (pte.absent.type)
|
|
|
|
+ {
|
|
|
|
+ case PTE_COMMITTED:
|
|
|
|
+ return memory_find_page_by_address(pte.absent.number * PAGE_SIZE);
|
|
|
|
+
|
|
|
|
+ case PTE_BLANK:
|
|
|
|
+ case PTE_RESERVED:
|
|
|
|
+ case PTE_EVICTED:
|
|
|
|
+ case PTE_TRANSITIONAL:
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ KERNEL_CRASH("Invalid page type");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
sysret_t memory_map_page(page_table_t table, page_t *page, void *address, memory_flags_t access_flags)
|
|
sysret_t memory_map_page(page_table_t table, page_t *page, void *address, memory_flags_t access_flags)
|
|
@@ -183,6 +265,7 @@ sysret_t memory_map_page(page_table_t table, page_t *page, void *address, memory
|
|
new_pte.is_present = TRUE;
|
|
new_pte.is_present = TRUE;
|
|
new_pte.present.writable = (access_flags & MEMORY_FLAG_WRITABLE) ? TRUE : FALSE;
|
|
new_pte.present.writable = (access_flags & MEMORY_FLAG_WRITABLE) ? TRUE : FALSE;
|
|
new_pte.present.usermode = (access_flags & MEMORY_FLAG_USERMODE) ? TRUE : FALSE;
|
|
new_pte.present.usermode = (access_flags & MEMORY_FLAG_USERMODE) ? TRUE : FALSE;
|
|
|
|
+ new_pte.present.sticky = (access_flags & MEMORY_FLAG_STICKY) ? TRUE : FALSE;
|
|
new_pte.present.number = page->number;
|
|
new_pte.present.number = page->number;
|
|
new_pte.present.no_execute = (access_flags & MEMORY_FLAG_EXECUTABLE) ? FALSE : TRUE;
|
|
new_pte.present.no_execute = (access_flags & MEMORY_FLAG_EXECUTABLE) ? FALSE : TRUE;
|
|
|
|
|
|
@@ -196,6 +279,7 @@ sysret_t memory_map_page(page_table_t table, page_t *page, void *address, memory
|
|
new_pte.absent.writable = (access_flags & MEMORY_FLAG_WRITABLE) ? TRUE : FALSE;
|
|
new_pte.absent.writable = (access_flags & MEMORY_FLAG_WRITABLE) ? TRUE : FALSE;
|
|
new_pte.absent.executable = (access_flags & MEMORY_FLAG_EXECUTABLE) ? TRUE : FALSE;
|
|
new_pte.absent.executable = (access_flags & MEMORY_FLAG_EXECUTABLE) ? TRUE : FALSE;
|
|
new_pte.absent.usermode = (access_flags & MEMORY_FLAG_USERMODE) ? TRUE : FALSE;
|
|
new_pte.absent.usermode = (access_flags & MEMORY_FLAG_USERMODE) ? TRUE : FALSE;
|
|
|
|
+ new_pte.absent.sticky = (access_flags & MEMORY_FLAG_STICKY) ? TRUE : FALSE;
|
|
new_pte.absent.number = page->number;
|
|
new_pte.absent.number = page->number;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -211,7 +295,32 @@ sysret_t memory_map_page(page_table_t table, page_t *page, void *address, memory
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-sysret_t memory_query_access_flags(page_table_t table, void *address, memory_flags_t *access_flags)
|
|
|
|
|
|
+sysret_t memory_map_area(page_table_t table, const area_t *area, void *address, memory_flags_t access_flags)
|
|
|
|
+{
|
|
|
|
+ sysret_t ret = ERR_SUCCESS;
|
|
|
|
+ uintptr_t numeric_address = PAGE_ALIGN((uintptr_t)address);
|
|
|
|
+ page_num_t page;
|
|
|
|
+
|
|
|
|
+ for (page = 0; page < area->count; page++)
|
|
|
|
+ {
|
|
|
|
+ if ((ret = memory_map_page(table,
|
|
|
|
+ &area->pages[page],
|
|
|
|
+ (void*)(numeric_address + (size_t)page * PAGE_SIZE),
|
|
|
|
+ access_flags)) != ERR_SUCCESS) break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ret != ERR_SUCCESS)
|
|
|
|
+ {
|
|
|
|
+ for (page_num_t i = 0; i < page; i++)
|
|
|
|
+ {
|
|
|
|
+ memory_unmap_clear_page(table, (void*)(numeric_address + (size_t)page * PAGE_SIZE));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+sysret_t memory_query_page_flags(page_table_t table, void *address, memory_flags_t *access_flags)
|
|
{
|
|
{
|
|
pte_pointer_t ppte = memory_get_table_entry(table, address, FALSE);
|
|
pte_pointer_t ppte = memory_get_table_entry(table, address, FALSE);
|
|
if (!ppte) return ERR_BADPTR;
|
|
if (!ppte) return ERR_BADPTR;
|
|
@@ -225,6 +334,8 @@ sysret_t memory_query_access_flags(page_table_t table, void *address, memory_fla
|
|
if (pte.present.writable) *access_flags |= MEMORY_FLAG_WRITABLE;
|
|
if (pte.present.writable) *access_flags |= MEMORY_FLAG_WRITABLE;
|
|
if (!(pte.present.no_execute)) *access_flags |= MEMORY_FLAG_EXECUTABLE;
|
|
if (!(pte.present.no_execute)) *access_flags |= MEMORY_FLAG_EXECUTABLE;
|
|
if (pte.present.usermode) *access_flags |= MEMORY_FLAG_USERMODE;
|
|
if (pte.present.usermode) *access_flags |= MEMORY_FLAG_USERMODE;
|
|
|
|
+ if (pte.present.sticky) *access_flags |= MEMORY_FLAG_STICKY;
|
|
|
|
+ if (pte.present.cow) *access_flags |= MEMORY_FLAG_COPY_ON_WRITE;
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
@@ -233,6 +344,8 @@ sysret_t memory_query_access_flags(page_table_t table, void *address, memory_fla
|
|
if (pte.absent.writable) *access_flags |= MEMORY_FLAG_WRITABLE;
|
|
if (pte.absent.writable) *access_flags |= MEMORY_FLAG_WRITABLE;
|
|
if (pte.absent.executable) *access_flags |= MEMORY_FLAG_EXECUTABLE;
|
|
if (pte.absent.executable) *access_flags |= MEMORY_FLAG_EXECUTABLE;
|
|
if (pte.absent.usermode) *access_flags |= MEMORY_FLAG_USERMODE;
|
|
if (pte.absent.usermode) *access_flags |= MEMORY_FLAG_USERMODE;
|
|
|
|
+ if (pte.absent.sticky) *access_flags |= MEMORY_FLAG_STICKY;
|
|
|
|
+ if (pte.absent.type == PTE_EVICTED || pte.absent.type == PTE_TRANSITIONAL) *access_flags |= MEMORY_FLAG_EVICTED;
|
|
}
|
|
}
|
|
|
|
|
|
return ERR_SUCCESS;
|
|
return ERR_SUCCESS;
|
|
@@ -312,6 +425,7 @@ sysret_t memory_unmap_keep_page(page_table_t table, void *address)
|
|
entry.absent.writable = original_entry.present.writable;
|
|
entry.absent.writable = original_entry.present.writable;
|
|
entry.absent.executable = !original_entry.present.no_execute;
|
|
entry.absent.executable = !original_entry.present.no_execute;
|
|
entry.absent.usermode = original_entry.present.usermode;
|
|
entry.absent.usermode = original_entry.present.usermode;
|
|
|
|
+ entry.absent.sticky = original_entry.present.sticky;
|
|
entry.absent.reserved1 = 0;
|
|
entry.absent.reserved1 = 0;
|
|
entry.absent.number = 0;
|
|
entry.absent.number = 0;
|
|
entry.absent.reserved2 = 0;
|
|
entry.absent.reserved2 = 0;
|
|
@@ -323,13 +437,84 @@ sysret_t memory_unmap_keep_page(page_table_t table, void *address)
|
|
return ERR_SUCCESS;
|
|
return ERR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+sysret_t memory_load_shadow_table(page_t *new_shadow_table)
|
|
|
|
+{
|
|
|
|
+ if (new_shadow_table->status < PAGE_STATUS_ALLOCATED) return ERR_INVALID;
|
|
|
|
+
|
|
|
|
+ sysret_t ret = memory_map_page(memory_default_table,
|
|
|
|
+ new_shadow_table,
|
|
|
|
+ (void*)(-2 * PAGE_SIZE),
|
|
|
|
+ MEMORY_FLAG_ACCESSIBLE | MEMORY_FLAG_WRITABLE);
|
|
|
|
+ if (ret != ERR_SUCCESS) return ret;
|
|
|
|
+
|
|
|
|
+ pte_pointer_t self_entry = (pte_pointer_t)(-PAGE_SIZE - table_entry_size * 2);
|
|
|
|
+ pte_t new_pte = { 0 };
|
|
|
|
+ pte_t old_pte = read_pte(self_entry);
|
|
|
|
+ new_pte.is_present = TRUE;
|
|
|
|
+ new_pte.present.writable = TRUE;
|
|
|
|
+ new_pte.present.number = new_shadow_table->number;
|
|
|
|
+ new_pte.present.no_execute = TRUE;
|
|
|
|
+
|
|
|
|
+ if (!cmpxchg_pte(self_entry, old_pte, new_pte))
|
|
|
|
+ {
|
|
|
|
+ memory_unmap_clear_page(memory_default_table, (void*)(-2 * PAGE_SIZE));
|
|
|
|
+ return ERR_BUSY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ERR_SUCCESS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+sysret_t memory_unload_shadow_table(void)
|
|
|
|
+{
|
|
|
|
+ return memory_unmap_clear_page(memory_default_table, (void*)(-2 * PAGE_SIZE));
|
|
|
|
+}
|
|
|
|
+
|
|
sysret_t memory_load_default_table(page_t *new_default_table)
|
|
sysret_t memory_load_default_table(page_t *new_default_table)
|
|
{
|
|
{
|
|
if (new_default_table->status < PAGE_STATUS_ALLOCATED) return ERR_INVALID;
|
|
if (new_default_table->status < PAGE_STATUS_ALLOCATED) return ERR_INVALID;
|
|
|
|
+
|
|
|
|
+ sysret_t ret = memory_load_shadow_table(new_default_table);
|
|
|
|
+ if (ret != ERR_SUCCESS) return ret;
|
|
|
|
+
|
|
|
|
+ pte_pointer_t source_table = (pte_pointer_t)((intptr_t)-PAGE_SIZE >> 1);
|
|
|
|
+ pte_pointer_t dest_table = (pte_pointer_t)(((intptr_t)-PAGE_SIZE >> 1) - (intptr_t)memory_table_size);
|
|
|
|
+ update_sticky_pages(0, source_table, dest_table);
|
|
|
|
+ ret = memory_unload_shadow_table();
|
|
|
|
+ ASSERT(ret == ERR_SUCCESS);
|
|
|
|
+
|
|
cpu_write_page_table_register(new_default_table->number * PAGE_SIZE);
|
|
cpu_write_page_table_register(new_default_table->number * PAGE_SIZE);
|
|
return ERR_SUCCESS;
|
|
return ERR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+page_t *memory_create_page_table(void)
|
|
|
|
+{
|
|
|
|
+ page_t *page = memory_acquire_page(MIN_PHYS_ADDR_BITS, MAX_PHYS_ADDR_BITS, PAGE_SIZE);
|
|
|
|
+ if (!page) return NULL;
|
|
|
|
+
|
|
|
|
+ if (memory_load_shadow_table(page) != ERR_SUCCESS)
|
|
|
|
+ {
|
|
|
|
+ memory_release_page(page);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memset((void*)(-2 * PAGE_SIZE), 0, PAGE_SIZE);
|
|
|
|
+ pte_t zero = { 0 }, pte = zero;
|
|
|
|
+ pte.is_present = TRUE;
|
|
|
|
+ pte.present.writable = TRUE;
|
|
|
|
+ pte.present.number = page->number;
|
|
|
|
+ pte.present.no_execute = TRUE;
|
|
|
|
+
|
|
|
|
+ if (!cmpxchg_pte((pte_pointer_t)(-PAGE_SIZE - table_entry_size), zero, pte))
|
|
|
|
+ {
|
|
|
|
+ memory_unload_shadow_table();
|
|
|
|
+ memory_release_page(page);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memory_unload_shadow_table();
|
|
|
|
+ return page;
|
|
|
|
+}
|
|
|
|
+
|
|
void memory_init_mapping_hack(void)
|
|
void memory_init_mapping_hack(void)
|
|
{
|
|
{
|
|
page_table_t boot_table = (page_table_t)0xC0300C00;
|
|
page_table_t boot_table = (page_table_t)0xC0300C00;
|
|
@@ -341,12 +526,11 @@ void memory_init_mapping_hack(void)
|
|
self_referencing_pte = memory_get_table_entry(boot_table, self_referencing_pte, TRUE);
|
|
self_referencing_pte = memory_get_table_entry(boot_table, self_referencing_pte, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
- pte_t zero = { 0 };
|
|
|
|
- pte_t pte = zero;
|
|
|
|
- pte.is_present = TRUE,
|
|
|
|
- pte.present.writable = TRUE,
|
|
|
|
- pte.present.number = PAGE_NUMBER(cpu_read_page_table_register()),
|
|
|
|
- pte.present.no_execute = TRUE,
|
|
|
|
|
|
+ pte_t zero = { 0 }, pte = zero;
|
|
|
|
+ pte.is_present = TRUE;
|
|
|
|
+ pte.present.writable = TRUE;
|
|
|
|
+ pte.present.number = PAGE_NUMBER(cpu_read_page_table_register());
|
|
|
|
+ pte.present.no_execute = TRUE;
|
|
|
|
|
|
cmpxchg_pte(self_referencing_pte, zero, pte);
|
|
cmpxchg_pte(self_referencing_pte, zero, pte);
|
|
}
|
|
}
|