1239 lines
46 KiB
Python
Executable File
1239 lines
46 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import sys
|
|
import struct
|
|
import parser
|
|
from collections import namedtuple
|
|
import ctypes
|
|
import argparse
|
|
import re
|
|
from elftools.elf.elffile import ELFFile
|
|
from elftools.elf.sections import SymbolTableSection
|
|
|
|
# global variables
|
|
pd_complete = ''
|
|
inputfile = ''
|
|
outputfile = ''
|
|
list_of_pde = {}
|
|
num_of_regions = 0
|
|
read_buff = ''
|
|
raw_info = []
|
|
|
|
mmu_region_details = namedtuple("mmu_region_details",
|
|
"pde_index page_entries_info")
|
|
|
|
valid_pages_inside_pde = namedtuple("valid_pages_inside_pde", "start_addr size \
|
|
pte_valid_addr_start \
|
|
pte_valid_addr_end \
|
|
permissions")
|
|
|
|
mmu_region_details_pdpt = namedtuple("mmu_region_details_pdpt",
|
|
"pdpte_index pd_entries")
|
|
|
|
page_tables_list = []
|
|
pd_tables_list = []
|
|
pd_start_addr = 0
|
|
validation_issue_memory_overlap = [False, 0, -1]
|
|
output_offset = 0
|
|
print_string_pde_list = ''
|
|
pde_pte_string = {}
|
|
FourMB = (1024 * 4096) # In Bytes
|
|
|
|
# Constants
|
|
PAGE_ENTRY_PRESENT = 1
|
|
PAGE_ENTRY_READ_WRITE = 1 << 1
|
|
PAGE_ENTRY_USER_SUPERVISOR = 1 << 2
|
|
PAGE_ENTRY_PWT = 0 << 3
|
|
PAGE_ENTRY_PCD = 0 << 4
|
|
PAGE_ENTRY_ACCESSED = 0 << 5 # this is a read only field
|
|
PAGE_ENTRY_DIRTY = 0 << 6 # this is a read only field
|
|
PAGE_ENTRY_PAT = 0 << 7
|
|
PAGE_ENTRY_GLOBAL = 0 << 8
|
|
PAGE_ENTRY_ALLOC = 1 << 9
|
|
PAGE_ENTRY_CUSTOM = 0 << 10
|
|
#############
|
|
|
|
#*****************************************************************************#
|
|
# class for 4Kb Mode
|
|
|
|
|
|
class PageMode_4kb:
|
|
total_pages = 1023
|
|
write_page_entry_bin = "I"
|
|
size_addressed_per_pde = (1024 * 4096) # 4MB In Bytes
|
|
|
|
# return the page directory number for the give address
|
|
def get_pde_number(self, value):
|
|
return (value >> 22) & 0x3FF
|
|
|
|
# return the page table number for the given address
|
|
def get_pte_number(self, value):
|
|
return (value >> 12) & 0x3FF
|
|
|
|
# get the total number of pd available
|
|
def get_number_of_pd(self):
|
|
return len(list_of_pde.keys())
|
|
|
|
# the return value will have the page address and it is assumed
|
|
# to be a 4096 boundary
|
|
# hence the output of this API will be a 20bit address of the page table
|
|
def address_of_page_table(self, page_table_number):
|
|
global pd_start_addr
|
|
|
|
# location from where the Page tables will be written
|
|
PT_start_addr = pd_start_addr + 4096
|
|
return ((PT_start_addr +
|
|
(page_tables_list.index(page_table_number) * 4096) >> 12))
|
|
|
|
# union x86_mmu_pde_pt {
|
|
# u32_t value;
|
|
# struct {
|
|
# u32_t p:1;
|
|
# u32_t rw:1;
|
|
# u32_t us:1;
|
|
# u32_t pwt:1;
|
|
# u32_t pcd:1;
|
|
# u32_t a:1;
|
|
# u32_t ignored1:1;
|
|
# u32_t ps:1;
|
|
# u32_t ignored2:4;
|
|
# u32_t page_table:20;
|
|
# };
|
|
# };
|
|
def get_binary_pde_value(self, value):
|
|
perms = value.page_entries_info[0].permissions
|
|
|
|
present = PAGE_ENTRY_PRESENT
|
|
read_write = check_bits(perms, [1, 29]) << 1
|
|
user_mode = check_bits(perms, [2, 28]) << 2
|
|
|
|
pwt = PAGE_ENTRY_PWT
|
|
pcd = PAGE_ENTRY_PCD
|
|
a = PAGE_ENTRY_ACCESSED
|
|
ps = 0 << 7 # this is a read only field
|
|
page_table = self.address_of_page_table(value.pde_index) << 12
|
|
return (present |
|
|
read_write |
|
|
user_mode |
|
|
pwt |
|
|
pcd |
|
|
a |
|
|
ps |
|
|
page_table)
|
|
|
|
# union x86_mmu_pte {
|
|
# u32_t value;
|
|
# struct {
|
|
# u32_t p:1;
|
|
# u32_t rw:1;
|
|
# u32_t us:1;
|
|
# u32_t pwt:1;
|
|
# u32_t pcd:1;
|
|
# u32_t a:1;
|
|
# u32_t d:1;
|
|
# u32_t pat:1;
|
|
# u32_t g:1;
|
|
# u32_t alloc:1;
|
|
# u32_t custom:2;
|
|
# u32_t page:20;
|
|
# };
|
|
# };
|
|
def get_binary_pte_value(self, value, pte, perm_for_pte):
|
|
present = PAGE_ENTRY_PRESENT
|
|
read_write = ((perm_for_pte >> 1) & 0x1) << 1
|
|
user_mode = ((perm_for_pte >> 2) & 0x1) << 2
|
|
pwt = PAGE_ENTRY_PWT
|
|
pcd = PAGE_ENTRY_PCD
|
|
a = PAGE_ENTRY_ACCESSED
|
|
d = PAGE_ENTRY_DIRTY
|
|
pat = PAGE_ENTRY_PAT
|
|
g = PAGE_ENTRY_GLOBAL
|
|
alloc = PAGE_ENTRY_ALLOC
|
|
custom = PAGE_ENTRY_CUSTOM
|
|
|
|
# This points to the actual memory in the HW
|
|
# totally 20 bits to rep the phy address
|
|
# first 10 is the number got from pde and next 10 is pte
|
|
page_table = ((value.pde_index << 10) | pte) << 12
|
|
|
|
binary_value = (present | read_write | user_mode |
|
|
pwt | pcd | a | d | pat | g | alloc | custom |
|
|
page_table)
|
|
return binary_value
|
|
|
|
def populate_required_structs(self):
|
|
for region in raw_info:
|
|
pde_index = self.get_pde_number(region[0])
|
|
pte_valid_addr_start = self.get_pte_number(region[0])
|
|
|
|
# Get the end of the page table entries
|
|
# Since a memory region can take up only a few entries in the Page
|
|
# table, this helps us get the last valid PTE.
|
|
pte_valid_addr_end = self.get_pte_number(region[0] +
|
|
region[1] - 1)
|
|
|
|
mem_size = region[1]
|
|
|
|
# In-case the start address aligns with a page table entry other
|
|
# than zero and the mem_size is greater than (1024*4096) i.e 4MB
|
|
# in case where it overflows the currenty PDE's range then limit the
|
|
# PTE to 1024 and so make the mem_size reflect the actual size taken
|
|
# up in the current PDE
|
|
if (region[1] + (pte_valid_addr_start * 4096)) >= \
|
|
(self.size_addressed_per_pde):
|
|
|
|
pte_valid_addr_end = self.total_pages
|
|
mem_size = (((self.total_pages + 1) -
|
|
pte_valid_addr_start) * 4096)
|
|
|
|
self.set_pde_pte_values(pde_index, region[0], mem_size,
|
|
pte_valid_addr_start,
|
|
pte_valid_addr_end,
|
|
region[2])
|
|
|
|
if pde_index not in page_tables_list:
|
|
page_tables_list.append(pde_index)
|
|
|
|
# IF the current pde couldn't fit the entire requested region size
|
|
# then there is a need to create new PDEs to match the size.
|
|
# Here the overflow_size represents the size that couldn't be fit
|
|
# inside the current PDE, this is will now to used to create a
|
|
# new PDE/PDEs so the size remaining will be
|
|
# requested size - allocated size(in the current PDE)
|
|
|
|
overflow_size = region[1] - mem_size
|
|
|
|
# create all the extra PDEs needed to fit the requested size
|
|
# this loop starts from the current pde till the last pde that is
|
|
# needed the last pde is calcualted as the (start_addr + size) >>
|
|
# 22
|
|
if overflow_size != 0:
|
|
for extra_pde in range(pde_index + 1, self.get_pde_number(
|
|
region[0] + region[1]) + 1):
|
|
|
|
# new pde's start address
|
|
# each page directory entry has a addr range of (1024 *4096)
|
|
# thus the new PDE start address is a multiple of that
|
|
# number
|
|
extra_pde_start_address = (extra_pde *
|
|
(self.size_addressed_per_pde))
|
|
|
|
# the start address of and extra pde will always be 0
|
|
# and the end address is calculated with the new pde's start
|
|
# address and the overflow_size
|
|
extra_pte_valid_addr_end = self.get_pte_number(
|
|
extra_pde_start_address + overflow_size - 1)
|
|
|
|
# if the overflow_size couldn't be fit inside this new pde
|
|
# then need another pde and so we now need to limit the end
|
|
# of the PTE to 1024 and set the size of this new region to
|
|
# the max possible
|
|
extra_region_size = overflow_size
|
|
if overflow_size >= (self.size_addressed_per_pde):
|
|
extra_region_size = self.size_addressed_per_pde
|
|
extra_pte_valid_addr_end = self.total_pages
|
|
|
|
# load the new PDE's details
|
|
|
|
self.set_pde_pte_values(extra_pde,
|
|
extra_pde_start_address,
|
|
extra_region_size,
|
|
0,
|
|
extra_pte_valid_addr_end,
|
|
region[2])
|
|
|
|
# for the next iteration of the loop the size needs to
|
|
# decreased.
|
|
overflow_size -= extra_region_size
|
|
|
|
# print(hex_32(overflow_size),extra_pde)
|
|
if extra_pde not in page_tables_list:
|
|
page_tables_list.append(extra_pde)
|
|
|
|
if overflow_size == 0:
|
|
break
|
|
|
|
page_tables_list.sort()
|
|
|
|
# update the tuple values for the memory regions needed
|
|
def set_pde_pte_values(self, pde_index, address, mem_size,
|
|
pte_valid_addr_start, pte_valid_addr_end, perm):
|
|
|
|
pages_tuple = valid_pages_inside_pde(
|
|
start_addr=address,
|
|
size=mem_size,
|
|
pte_valid_addr_start=pte_valid_addr_start,
|
|
pte_valid_addr_end=pte_valid_addr_end,
|
|
permissions=perm)
|
|
|
|
mem_region_values = mmu_region_details(pde_index=pde_index,
|
|
page_entries_info=[])
|
|
|
|
mem_region_values.page_entries_info.append(pages_tuple)
|
|
|
|
if pde_index in list_of_pde.keys():
|
|
# this step adds the new page info to the exsisting pages info
|
|
list_of_pde[pde_index].page_entries_info.append(pages_tuple)
|
|
else:
|
|
list_of_pde[pde_index] = mem_region_values
|
|
|
|
def page_directory_create_binary_file(self):
|
|
global output_buffer
|
|
global output_offset
|
|
for pde in range(self.total_pages + 1):
|
|
binary_value = 0 # the page directory entry is not valid
|
|
|
|
# if i have a valid entry to populate
|
|
if pde in sorted(list_of_pde.keys()):
|
|
value = list_of_pde[pde]
|
|
binary_value = self.get_binary_pde_value(value)
|
|
self.pde_verbose_output(pde, binary_value)
|
|
|
|
struct.pack_into(self.write_page_entry_bin,
|
|
output_buffer,
|
|
output_offset,
|
|
binary_value)
|
|
|
|
output_offset += struct.calcsize(self.write_page_entry_bin)
|
|
|
|
def page_table_create_binary_file(self):
|
|
global output_buffer
|
|
global output_offset
|
|
|
|
for key, value in sorted(list_of_pde.items()):
|
|
for pte in range(self.total_pages + 1):
|
|
binary_value = 0 # the page directory entry is not valid
|
|
|
|
valid_pte = 0
|
|
for i in value.page_entries_info:
|
|
temp_value = ((pte >= i.pte_valid_addr_start) and
|
|
(pte <= i.pte_valid_addr_end))
|
|
if temp_value:
|
|
perm_for_pte = i.permissions
|
|
valid_pte |= temp_value
|
|
|
|
# if i have a valid entry to populate
|
|
if valid_pte:
|
|
binary_value = self.get_binary_pte_value(value,
|
|
pte,
|
|
perm_for_pte)
|
|
self.pte_verbose_output(key, pte, binary_value)
|
|
|
|
struct.pack_into(self.write_page_entry_bin,
|
|
output_buffer,
|
|
output_offset,
|
|
binary_value)
|
|
output_offset += struct.calcsize(self.write_page_entry_bin)
|
|
|
|
# To populate the binary file the module struct needs a buffer of the
|
|
# excat size. This returns the size needed for the given set of page
|
|
# tables.
|
|
def set_binary_file_size(self):
|
|
binary_size = ctypes.create_string_buffer((4096) +
|
|
(len(list_of_pde.keys()) *
|
|
4096))
|
|
return binary_size
|
|
|
|
# prints the details of the pde
|
|
def verbose_output(self):
|
|
|
|
print("\nTotal Page directory entries " + str(self.get_number_of_pd()))
|
|
count = 0
|
|
for key, value in list_of_pde.items():
|
|
for i in value.page_entries_info:
|
|
count += 1
|
|
print("In Page directory entry " +
|
|
format_string(value.pde_index) +
|
|
": valid start address = " +
|
|
hex_32(i.start_addr) + ", end address = " +
|
|
hex_32((i.pte_valid_addr_end + 1) * 4096 - 1 +
|
|
(value.pde_index * (FourMB))))
|
|
|
|
# print all the tables for a given page table mode
|
|
def print_all_page_table_info(self):
|
|
self.pde_print_elements()
|
|
self.pte_print_elements()
|
|
|
|
def pde_verbose_output(self, pde, binary_value):
|
|
if args.verbose < 2:
|
|
return
|
|
|
|
global print_string_pde_list
|
|
|
|
present = format_string(binary_value & 0x1)
|
|
read_write = format_string((binary_value >> 1) & 0x1)
|
|
user_mode = format_string((binary_value >> 2) & 0x1)
|
|
pwt = format_string((binary_value >> 3) & 0x1)
|
|
pcd = format_string((binary_value >> 4) & 0x1)
|
|
a = format_string((binary_value >> 5) & 0x1)
|
|
ignored1 = format_string(0)
|
|
ps = format_string((binary_value >> 7) & 0x1)
|
|
ignored2 = format_string(0000)
|
|
page_table_addr = format_string(hex((binary_value >> 12) & 0xFFFFF))
|
|
|
|
print_string_pde_list += (format_string(str(pde)) +
|
|
" | " +
|
|
(present) +
|
|
" | " +
|
|
(read_write) + " | " +
|
|
(user_mode) + " | " +
|
|
(pwt) + " | " +
|
|
(pcd) + " | " +
|
|
(a) + " | " +
|
|
(ps) + " | " +
|
|
page_table_addr + "\n"
|
|
)
|
|
|
|
def pde_print_elements(self):
|
|
global print_string_pde_list
|
|
print("PAGE DIRECTORY ")
|
|
print(format_string("PDE") + " | " +
|
|
format_string('P') + " | " +
|
|
format_string('rw') + " | " +
|
|
format_string('us') + " | " +
|
|
format_string('pwt') + " | " +
|
|
format_string('pcd') + " | " +
|
|
format_string('a') + " | " +
|
|
format_string('ps') + " | " +
|
|
format_string('Addr page table'))
|
|
print(print_string_pde_list)
|
|
print("END OF PAGE DIRECTORY")
|
|
|
|
def pte_verbose_output(self, pde, pte, binary_value):
|
|
global pde_pte_string
|
|
|
|
present = format_string((binary_value >> 0) & 0x1)
|
|
read_write = format_string((binary_value >> 1) & 0x1)
|
|
user_mode = format_string((binary_value >> 2) & 0x1)
|
|
pwt = format_string((binary_value >> 3) & 0x1)
|
|
pcd = format_string((binary_value >> 4) & 0x1)
|
|
a = format_string((binary_value >> 5) & 0x1)
|
|
d = format_string((binary_value >> 6) & 0x1)
|
|
pat = format_string((binary_value >> 7) & 0x1)
|
|
g = format_string((binary_value >> 8) & 0x1)
|
|
alloc = format_string((binary_value >> 9) & 0x1)
|
|
custom = format_string((binary_value >> 10) & 0x3)
|
|
page_table_addr = hex_20((binary_value >> 12) & 0xFFFFF)
|
|
|
|
print_string_list = (format_string(str(pte)) + " | " +
|
|
(present) + " | " +
|
|
(read_write) + " | " +
|
|
(user_mode) + " | " +
|
|
(pwt) + " | " +
|
|
(pcd) + " | " +
|
|
(a) + " | " +
|
|
(d) + " | " +
|
|
(pat) + " | " +
|
|
(g) + " | " +
|
|
(alloc) + " | " +
|
|
(custom) + " | " +
|
|
page_table_addr + "\n"
|
|
)
|
|
|
|
if pde in pde_pte_string.keys():
|
|
pde_pte_string[pde] += (print_string_list)
|
|
else:
|
|
pde_pte_string[pde] = print_string_list
|
|
|
|
def pte_print_elements(self):
|
|
global pde_pte_string
|
|
|
|
for pde, print_string in sorted(pde_pte_string.items()):
|
|
print("\nPAGE TABLE " + str(pde))
|
|
|
|
print(format_string("PTE") + " | " +
|
|
format_string('P') + " | " +
|
|
format_string('rw') + " | " +
|
|
format_string('us') + " | " +
|
|
format_string('pwt') + " | " +
|
|
format_string('pcd') + " | " +
|
|
format_string('a') + " | " +
|
|
format_string('d') + " | " +
|
|
format_string('pat') + " | " +
|
|
format_string('g') + " | " +
|
|
format_string('alloc') + " | " +
|
|
format_string('custom') + " | " +
|
|
format_string('page addr'))
|
|
print(print_string)
|
|
print("END OF PAGE TABLE " + str(pde))
|
|
|
|
|
|
#*****************************************************************************#
|
|
# class for PAE 4KB Mode
|
|
class PageMode_PAE:
|
|
total_pages = 511
|
|
write_page_entry_bin = "Q"
|
|
size_addressed_per_pde = (512 * 4096) # 2MB In Bytes
|
|
size_addressed_per_pdpte = (512 * size_addressed_per_pde) # In Bytes
|
|
list_of_pdpte = {}
|
|
pdpte_print_string = {}
|
|
print_string_pdpte_list = ''
|
|
|
|
# TODO enable all page tables on just a flag
|
|
|
|
def __init__(self):
|
|
for i in range(4):
|
|
self.list_of_pdpte[i] = mmu_region_details_pdpt(pdpte_index=i,
|
|
pd_entries={})
|
|
|
|
# return the pdpte number for the give address
|
|
def get_pdpte_number(self, value):
|
|
return (value >> 30) & 0x3
|
|
|
|
# return the page directory number for the give address
|
|
def get_pde_number(self, value):
|
|
return (value >> 21) & 0x1FF
|
|
|
|
# return the page table number for the given address
|
|
def get_pte_number(self, value):
|
|
return (value >> 12) & 0x1FF
|
|
|
|
def get_number_of_pd(self):
|
|
return len(self.get_pdpte_list())
|
|
|
|
def get_pdpte_list(self):
|
|
return list({temp[0] for temp in pd_tables_list})
|
|
|
|
# the return value will have the page address and it is assumed to be a 4096
|
|
# boundary.hence the output of this API will be a 20bit address of the page
|
|
# table
|
|
def address_of_page_table(self, pdpte, page_table_number):
|
|
global pd_start_addr
|
|
|
|
# first page given to page directory pointer
|
|
# and 2nd page till 5th page are used for storing the page directories.
|
|
|
|
# set the max pdpte used. this tells how many pd are needed after
|
|
# that we start keeping the pt
|
|
PT_start_addr = self.get_number_of_pd() * 4096 +\
|
|
pd_start_addr + 4096
|
|
return (PT_start_addr +
|
|
(pd_tables_list.index([pdpte, page_table_number]) *
|
|
4096) >> 12)
|
|
|
|
# union x86_mmu_pae_pde {
|
|
# u64_t value;
|
|
# struct {
|
|
# u64_t p:1;
|
|
# u64_t rw:1;
|
|
# u64_t us:1;
|
|
# u64_t pwt:1;
|
|
# u64_t pcd:1;
|
|
# u64_t a:1;
|
|
# u64_t ignored1:1;
|
|
# u64_t ps:1;
|
|
# u64_t ignored2:4;
|
|
# u64_t page_table:20;
|
|
# u64_t igonred3:29;
|
|
# u64_t xd:1;
|
|
# };
|
|
# };
|
|
|
|
def get_binary_pde_value(self, pdpte, value):
|
|
perms = value.page_entries_info[0].permissions
|
|
|
|
present = PAGE_ENTRY_PRESENT
|
|
read_write = check_bits(perms, [1, 29]) << 1
|
|
user_mode = check_bits(perms, [2, 28]) << 2
|
|
|
|
pwt = PAGE_ENTRY_PWT
|
|
pcd = PAGE_ENTRY_PCD
|
|
a = PAGE_ENTRY_ACCESSED
|
|
ps = 0 << 7 # set to make sure that the phy page is 4KB
|
|
page_table = self.address_of_page_table(pdpte, value.pde_index) << 12
|
|
xd = 0
|
|
return (present |
|
|
read_write |
|
|
user_mode |
|
|
pwt |
|
|
pcd |
|
|
a |
|
|
ps |
|
|
page_table |
|
|
xd)
|
|
|
|
# union x86_mmu_pae_pte {
|
|
# u64_t value;
|
|
# struct {
|
|
# u64_t p:1;
|
|
# u64_t rw:1;
|
|
# u64_t us:1;
|
|
# u64_t pwt:1;
|
|
# u64_t pcd:1;
|
|
# u64_t a:1;
|
|
# u64_t d:1;
|
|
# u64_t pat:1;
|
|
# u64_t g:1;
|
|
# u64_t ignore:3;
|
|
# u64_t page:20;
|
|
# u64_t igonred3:29;
|
|
# u64_t xd:1;
|
|
# };
|
|
# };
|
|
def get_binary_pte_value(self, value, pde, pte, perm_for_pte):
|
|
present = PAGE_ENTRY_PRESENT
|
|
read_write = perm_for_pte & PAGE_ENTRY_READ_WRITE
|
|
user_mode = perm_for_pte & PAGE_ENTRY_USER_SUPERVISOR
|
|
pwt = PAGE_ENTRY_PWT
|
|
pcd = PAGE_ENTRY_PCD
|
|
a = PAGE_ENTRY_ALLOC
|
|
d = PAGE_ENTRY_DIRTY
|
|
pat = PAGE_ENTRY_PAT
|
|
g = PAGE_ENTRY_GLOBAL
|
|
|
|
# This points to the actual memory in the HW
|
|
# totally 20 bits to rep the phy address
|
|
# first 2bits is from pdpte then 9bits is the number got from pde and
|
|
# next 9bits is pte
|
|
page_table = ((value.pdpte_index << 18) | (pde << 9) | pte) << 12
|
|
|
|
xd = ((perm_for_pte >> 63) & 0x1) << 63
|
|
|
|
binary_value = (present | read_write | user_mode |
|
|
pwt | pcd | a | d | pat | g |
|
|
page_table | xd)
|
|
return binary_value
|
|
|
|
def clean_up_unused_pdpte(self):
|
|
self.list_of_pdpte = {key: value for key, value in
|
|
self.list_of_pdpte.items()
|
|
if value.pd_entries != {}}
|
|
|
|
# update the tuple values for the memory regions needed
|
|
def set_pde_pte_values(self, pdpte, pde_index, address, mem_size,
|
|
pte_valid_addr_start, pte_valid_addr_end, perm):
|
|
|
|
pages_tuple = valid_pages_inside_pde(
|
|
start_addr=address,
|
|
size=mem_size,
|
|
pte_valid_addr_start=pte_valid_addr_start,
|
|
pte_valid_addr_end=pte_valid_addr_end,
|
|
permissions=perm)
|
|
|
|
mem_region_values = mmu_region_details(pde_index=pde_index,
|
|
page_entries_info=[])
|
|
|
|
mem_region_values.page_entries_info.append(pages_tuple)
|
|
|
|
if pde_index in self.list_of_pdpte[pdpte].pd_entries.keys():
|
|
# this step adds the new page info to the exsisting pages info
|
|
self.list_of_pdpte[pdpte].pd_entries[pde_index].\
|
|
page_entries_info.append(pages_tuple)
|
|
else:
|
|
self.list_of_pdpte[pdpte].pd_entries[pde_index] = mem_region_values
|
|
|
|
def populate_required_structs(self):
|
|
for region in raw_info:
|
|
pdpte_index = self.get_pdpte_number(region[0])
|
|
pde_index = self.get_pde_number(region[0])
|
|
pte_valid_addr_start = self.get_pte_number(region[0])
|
|
|
|
# Get the end of the page table entries
|
|
# Since a memory region can take up only a few entries in the Page
|
|
# table, this helps us get the last valid PTE.
|
|
pte_valid_addr_end = self.get_pte_number(region[0] +
|
|
region[1] - 1)
|
|
|
|
mem_size = region[1]
|
|
|
|
# In-case the start address aligns with a page table entry other
|
|
# than zero and the mem_size is greater than (1024*4096) i.e 4MB
|
|
# in case where it overflows the currenty PDE's range then limit the
|
|
# PTE to 1024 and so make the mem_size reflect the actual size
|
|
# taken up in the current PDE
|
|
if (region[1] + (pte_valid_addr_start * 4096)) >= \
|
|
(self.size_addressed_per_pde):
|
|
|
|
pte_valid_addr_end = self.total_pages
|
|
mem_size = (((self.total_pages + 1) -
|
|
pte_valid_addr_start) * 4096)
|
|
|
|
self.set_pde_pte_values(pdpte_index,
|
|
pde_index,
|
|
region[0],
|
|
mem_size,
|
|
pte_valid_addr_start,
|
|
pte_valid_addr_end,
|
|
region[2])
|
|
|
|
if [pdpte_index, pde_index] not in pd_tables_list:
|
|
pd_tables_list.append([pdpte_index, pde_index])
|
|
|
|
# IF the current pde couldn't fit the entire requested region
|
|
# size then there is a need to create new PDEs to match the size.
|
|
# Here the overflow_size represents the size that couldn't be fit
|
|
# inside the current PDE, this is will now to used to create a new
|
|
# PDE/PDEs so the size remaining will be
|
|
# requested size - allocated size(in the current PDE)
|
|
|
|
overflow_size = region[1] - mem_size
|
|
|
|
# create all the extra PDEs needed to fit the requested size
|
|
# this loop starts from the current pde till the last pde that is
|
|
# needed the last pde is calcualted as the (start_addr + size) >>
|
|
# 22
|
|
if overflow_size != 0:
|
|
for extra_pdpte in range(pdpte_index,
|
|
self.get_pdpte_number(region[0] +
|
|
region[1]) + 1):
|
|
for extra_pde in range(pde_index + 1, self.get_pde_number(
|
|
region[0] + region[1]) + 1):
|
|
|
|
# new pde's start address
|
|
# each page directory entry has a addr range of
|
|
# (1024 *4096) thus the new PDE start address is a
|
|
# multiple of that number
|
|
extra_pde_start_address = (
|
|
extra_pde * (self.size_addressed_per_pde))
|
|
|
|
# the start address of and extra pde will always be 0
|
|
# and the end address is calculated with the new
|
|
# pde's start address and the overflow_size
|
|
extra_pte_valid_addr_end = (
|
|
self.get_pte_number(extra_pde_start_address +
|
|
overflow_size - 1))
|
|
|
|
# if the overflow_size couldn't be fit inside this new
|
|
# pde then need another pde and so we now need to limit
|
|
# the end of the PTE to 1024 and set the size of this
|
|
# new region to the max possible
|
|
extra_region_size = overflow_size
|
|
if overflow_size >= (self.size_addressed_per_pde):
|
|
extra_region_size = self.size_addressed_per_pde
|
|
extra_pte_valid_addr_end = self.total_pages
|
|
|
|
# load the new PDE's details
|
|
|
|
self.set_pde_pte_values(extra_pdpte,
|
|
extra_pde,
|
|
extra_pde_start_address,
|
|
extra_region_size,
|
|
0,
|
|
extra_pte_valid_addr_end,
|
|
region[2])
|
|
|
|
# for the next iteration of the loop the size needs
|
|
# to decreased
|
|
overflow_size -= extra_region_size
|
|
|
|
if [extra_pdpte, extra_pde] not in pd_tables_list:
|
|
pd_tables_list.append([extra_pdpte, extra_pde])
|
|
|
|
if overflow_size == 0:
|
|
break
|
|
|
|
pd_tables_list.sort()
|
|
self.clean_up_unused_pdpte()
|
|
|
|
def pdpte_create_binary_file(self):
|
|
global output_buffer
|
|
global output_offset
|
|
global pd_start_addr
|
|
|
|
# pae needs a pdpte at 32byte aligned address
|
|
|
|
# Even though we have only 4 entries in the pdpte we need to move
|
|
# the output_offset variable to the next page to start pushing
|
|
# the pd contents
|
|
for pdpte in range(self.total_pages + 1):
|
|
if pdpte in self.get_pdpte_list():
|
|
present = 1 << 0
|
|
pwt = 0 << 3
|
|
pcd = 0 << 4
|
|
addr_of_pd = (((pd_start_addr + 4096) +
|
|
self.get_pdpte_list().index(pdpte) *
|
|
4096) >> 12) << 12
|
|
binary_value = (present | pwt | pcd | addr_of_pd)
|
|
self.pdpte_verbose_output(pdpte, binary_value)
|
|
else:
|
|
binary_value = 0
|
|
|
|
struct.pack_into(self.write_page_entry_bin,
|
|
output_buffer,
|
|
output_offset,
|
|
binary_value)
|
|
|
|
output_offset += struct.calcsize(self.write_page_entry_bin)
|
|
|
|
def page_directory_create_binary_file(self):
|
|
global output_buffer
|
|
global output_offset
|
|
pdpte_number_count = 0
|
|
for pdpte, pde_info in self.list_of_pdpte.items():
|
|
|
|
pde_number_count = 0
|
|
for pde in range(self.total_pages + 1):
|
|
binary_value = 0 # the page directory entry is not valid
|
|
|
|
# if i have a valid entry to populate
|
|
# if pde in sorted(list_of_pde.keys()):
|
|
if pde in sorted(pde_info.pd_entries.keys()):
|
|
value = pde_info.pd_entries[pde]
|
|
binary_value = self.get_binary_pde_value(pdpte, value)
|
|
self.pde_verbose_output(pdpte, pde, binary_value)
|
|
|
|
pde_number_count += 1
|
|
struct.pack_into(self.write_page_entry_bin,
|
|
output_buffer,
|
|
output_offset,
|
|
binary_value)
|
|
|
|
output_offset += struct.calcsize(self.write_page_entry_bin)
|
|
|
|
def page_table_create_binary_file(self):
|
|
global output_buffer
|
|
global output_offset
|
|
|
|
pdpte_number_count = 0
|
|
for pdpte, pde_info in sorted(self.list_of_pdpte.items()):
|
|
pdpte_number_count += 1
|
|
for pde, pte_info in sorted(pde_info.pd_entries.items()):
|
|
pte_number_count = 0
|
|
for pte in range(self.total_pages + 1):
|
|
binary_value = 0 # the page directory entry is not valid
|
|
|
|
valid_pte = 0
|
|
# go through all the valid pages inside the pde to
|
|
# figure out if we need to populate this pte
|
|
for i in pte_info.page_entries_info:
|
|
temp_value = ((pte >= i.pte_valid_addr_start) and
|
|
(pte <= i.pte_valid_addr_end))
|
|
if temp_value:
|
|
perm_for_pte = i.permissions
|
|
valid_pte |= temp_value
|
|
|
|
# if i have a valid entry to populate
|
|
if valid_pte:
|
|
binary_value = self.get_binary_pte_value(pde_info,
|
|
pde,
|
|
pte,
|
|
perm_for_pte)
|
|
pte_number_count += 1
|
|
self.pte_verbose_output(pdpte, pde, pte, binary_value)
|
|
|
|
# print(binary_value, (self.write_page_entry_bin))
|
|
|
|
struct.pack_into(self.write_page_entry_bin,
|
|
output_buffer,
|
|
output_offset,
|
|
binary_value)
|
|
output_offset += struct.calcsize(self.write_page_entry_bin)
|
|
|
|
# To populate the binary file the module struct needs a buffer of the
|
|
# excat size This returns the size needed for the given set of page tables.
|
|
def set_binary_file_size(self):
|
|
pages_for_pdpte = 1
|
|
pages_for_pd = self.get_number_of_pd()
|
|
pages_for_pt = len(pd_tables_list)
|
|
binary_size = ctypes.create_string_buffer((pages_for_pdpte +
|
|
pages_for_pd +
|
|
pages_for_pt) * 4096)
|
|
return binary_size
|
|
|
|
# prints the details of the pde
|
|
def verbose_output(self):
|
|
print("\nTotal Page directory Page pointer entries " +
|
|
str(self.get_number_of_pd()))
|
|
count = 0
|
|
for pdpte, pde_info in sorted(self.list_of_pdpte.items()):
|
|
print(
|
|
"In page directory page table pointer " +
|
|
format_string(pdpte))
|
|
|
|
for pde, pte_info in sorted(pde_info.pd_entries.items()):
|
|
for pte in pte_info.page_entries_info:
|
|
count += 1
|
|
print(" In Page directory entry " + format_string(pde) +
|
|
": valid start address = " +
|
|
hex_32(pte.start_addr) + ", end address = " +
|
|
hex_32((pte.pte_valid_addr_end + 1) * 4096 - 1 +
|
|
(pde * (self.size_addressed_per_pde)) +
|
|
(pdpte * self.size_addressed_per_pdpte)))
|
|
|
|
def pdpte_verbose_output(self, pdpte, binary_value):
|
|
if args.verbose < 2:
|
|
return
|
|
|
|
present = format_string(binary_value & 0x1)
|
|
pwt = format_string((binary_value >> 3) & 0x1)
|
|
pcd = format_string((binary_value >> 4) & 0x1)
|
|
page_table_addr = format_string(hex((binary_value >> 12) & 0xFFFFF))
|
|
|
|
self.print_string_pdpte_list += (format_string(str(pdpte)) +
|
|
" | " + (present) + " | " +
|
|
(pwt) + " | " +
|
|
(pcd) + " | " +
|
|
page_table_addr + "\n")
|
|
|
|
def pdpte_print_elements(self):
|
|
print("\nPAGE DIRECTORIES POINTER ")
|
|
print(format_string("PDPTE") + " | " +
|
|
format_string('P') + " | " +
|
|
format_string('pwt') + " | " +
|
|
format_string('pcd') + " | " +
|
|
format_string('Addr'))
|
|
print(self.print_string_pdpte_list)
|
|
print("END OF PAGE DIRECTORY POINTER")
|
|
|
|
def pde_verbose_output(self, pdpte, pde, binary_value):
|
|
if args.verbose < 2:
|
|
return
|
|
|
|
global print_string_pde_list
|
|
|
|
present = format_string(binary_value & 0x1)
|
|
read_write = format_string((binary_value >> 1) & 0x1)
|
|
user_mode = format_string((binary_value >> 2) & 0x1)
|
|
pwt = format_string((binary_value >> 3) & 0x1)
|
|
pcd = format_string((binary_value >> 4) & 0x1)
|
|
a = format_string((binary_value >> 5) & 0x1)
|
|
ignored1 = format_string(0)
|
|
ps = format_string((binary_value >> 7) & 0x1)
|
|
ignored2 = format_string(0000)
|
|
page_table_addr = format_string(hex((binary_value >> 12) & 0xFFFFF))
|
|
xd = format_string((binary_value >> 63) & 0x1)
|
|
|
|
print_string_pde_list = (format_string(str(pde)) + " | " +
|
|
(present) + " | " +
|
|
(read_write) + " | " +
|
|
(user_mode) + " | " +
|
|
(pwt) + " | " +
|
|
(pcd) + " | " +
|
|
(a) + " | " +
|
|
(ps) + " | " +
|
|
page_table_addr + " | " +
|
|
(xd) + "\n")
|
|
|
|
if pdpte in self.pdpte_print_string.keys():
|
|
self.pdpte_print_string[pdpte] += (print_string_pde_list)
|
|
else:
|
|
self.pdpte_print_string[pdpte] = print_string_pde_list
|
|
|
|
# print all the tables for a given page table mode
|
|
def print_all_page_table_info(self):
|
|
self.pdpte_print_elements()
|
|
self.pde_print_elements()
|
|
self.pte_print_elements()
|
|
|
|
def pde_print_elements(self):
|
|
global print_string_pde_list
|
|
|
|
for pdpte, print_string in sorted(self.pdpte_print_string.items()):
|
|
print("\n PAGE DIRECTORIES for PDPT " + str(pdpte))
|
|
print(format_string("PDE") + " | " +
|
|
format_string('P') + " | " +
|
|
format_string('rw') + " | " +
|
|
format_string('us') + " | " +
|
|
format_string('pwt') + " | " +
|
|
format_string('pcd') + " | " +
|
|
format_string('a') + " | " +
|
|
format_string('ps') + " | " +
|
|
format_string('Addr') + " | " +
|
|
format_string('xd'))
|
|
print(print_string)
|
|
print("END OF PAGE DIRECTORIES for PDPT " + str(pdpte))
|
|
|
|
def pte_verbose_output(self, pdpte, pde, pte, binary_value):
|
|
global pde_pte_string
|
|
|
|
present = format_string((binary_value >> 0) & 0x1)
|
|
read_write = format_string((binary_value >> 1) & 0x1)
|
|
user_mode = format_string((binary_value >> 2) & 0x1)
|
|
pwt = format_string((binary_value >> 3) & 0x1)
|
|
pcd = format_string((binary_value >> 4) & 0x1)
|
|
a = format_string((binary_value >> 5) & 0x1)
|
|
d = format_string((binary_value >> 6) & 0x1)
|
|
pat = format_string((binary_value >> 7) & 0x1)
|
|
g = format_string((binary_value >> 8) & 0x1)
|
|
page_table_addr = hex_20((binary_value >> 12) & 0xFFFFF)
|
|
xd = format_string((binary_value >> 63) & 0x1)
|
|
|
|
print_string_list = (format_string(str(pte)) + " | " +
|
|
(present) + " | " +
|
|
(read_write) + " | " +
|
|
(user_mode) + " | " +
|
|
(pwt) + " | " +
|
|
(pcd) + " | " +
|
|
(a) + " | " +
|
|
(d) + " | " +
|
|
(pat) + " | " +
|
|
(g) + " | " +
|
|
page_table_addr + " | " +
|
|
(xd) + "\n"
|
|
)
|
|
|
|
if (pdpte, pde) in pde_pte_string.keys():
|
|
pde_pte_string[(pdpte, pde)] += (print_string_list)
|
|
else:
|
|
pde_pte_string[(pdpte, pde)] = print_string_list
|
|
|
|
def pte_print_elements(self):
|
|
global pde_pte_string
|
|
|
|
for (pdpte, pde), print_string in sorted(pde_pte_string.items()):
|
|
print(
|
|
"\nPAGE TABLE for PDPTE = " +
|
|
str(pdpte) +
|
|
" and PDE = " +
|
|
str(pde))
|
|
|
|
print(format_string("PTE") + " | " +
|
|
format_string('P') + " | " +
|
|
format_string('rw') + " | " +
|
|
format_string('us') + " | " +
|
|
format_string('pwt') + " | " +
|
|
format_string('pcd') + " | " +
|
|
format_string('a') + " | " +
|
|
format_string('d') + " | " +
|
|
format_string('pat') + " | " +
|
|
format_string('g') + " | " +
|
|
format_string('Page Addr') + " | " +
|
|
format_string('xd'))
|
|
print(print_string)
|
|
print("END OF PAGE TABLE " + str(pde))
|
|
|
|
|
|
#*****************************************************************************#
|
|
|
|
|
|
def print_list_of_pde(list_of_pde):
|
|
for key, value in list_of_pde.items():
|
|
print(key, value)
|
|
print('\n')
|
|
|
|
|
|
# read the binary from the input file and populate a dict for
|
|
# start address of mem region
|
|
# size of the region - so page tables entries will be created with this
|
|
# read write permissions
|
|
|
|
def read_mmu_list_marshal_param(page_mode):
|
|
|
|
global read_buff
|
|
global page_tables_list
|
|
global pd_start_addr
|
|
global validation_issue_memory_overlap
|
|
read_buff = input_file.read()
|
|
input_file.close()
|
|
|
|
# read contents of the binary file first 2 values read are
|
|
# num_of_regions and page directory start address both calculated and
|
|
# populated by the linker
|
|
num_of_regions, pd_start_addr = struct.unpack_from(
|
|
header_values_format, read_buff, 0)
|
|
|
|
# a offset used to remember next location to read in the binary
|
|
size_read_from_binary = struct.calcsize(header_values_format)
|
|
|
|
# for each of the regions mentioned in the binary loop and populate all the
|
|
# required parameters
|
|
for region in range(num_of_regions):
|
|
basic_mem_region_values = struct.unpack_from(struct_mmu_regions_format,
|
|
read_buff,
|
|
size_read_from_binary)
|
|
size_read_from_binary += struct.calcsize(struct_mmu_regions_format)
|
|
|
|
# ignore zero sized memory regions
|
|
if basic_mem_region_values[1] == 0:
|
|
continue
|
|
|
|
# validate for memory overlap here
|
|
for i in raw_info:
|
|
start_location = basic_mem_region_values[0]
|
|
end_location = basic_mem_region_values[0] + \
|
|
basic_mem_region_values[1]
|
|
|
|
overlap_occurred = ((start_location >= i[0]) and
|
|
(start_location <= (i[0] + i[1]))) and \
|
|
((end_location >= i[0]) and
|
|
(end_location <= i[0] + i[1]))
|
|
|
|
if overlap_occurred:
|
|
validation_issue_memory_overlap = [
|
|
True,
|
|
start_location,
|
|
page_mode.get_pde_number(start_location)]
|
|
return
|
|
|
|
# add the retrived info another list
|
|
raw_info.append(basic_mem_region_values)
|
|
|
|
|
|
def validate_pde_regions():
|
|
# validation for correct page alignment of the regions
|
|
for key, value in list_of_pde.items():
|
|
for pages_inside_pde in value.page_entries_info:
|
|
if pages_inside_pde.start_addr & (0xFFF) != 0:
|
|
print("Memory Regions are not page aligned",
|
|
hex(pages_inside_pde.start_addr))
|
|
sys.exit(2)
|
|
|
|
# validation for correct page alignment of the regions
|
|
if pages_inside_pde.size & (0xFFF) != 0:
|
|
print("Memory Regions size is not page aligned",
|
|
hex(pages_inside_pde.size))
|
|
sys.exit(2)
|
|
|
|
# validation for spiling of the regions across various
|
|
if validation_issue_memory_overlap[0] == True:
|
|
print("Memory Regions are overlapping at memory address " +
|
|
str(hex(validation_issue_memory_overlap[1])) +
|
|
" with Page directory Entry number " +
|
|
str(validation_issue_memory_overlap[2]))
|
|
sys.exit(2)
|
|
|
|
|
|
def check_bits(val, bits):
|
|
for b in bits:
|
|
if val & (1 << b):
|
|
return 1
|
|
return 0
|
|
|
|
|
|
# Read the parameters passed to the file
|
|
def parse_args():
|
|
global args
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
parser.add_argument("-e", "--big-endian", action="store_true",
|
|
help="Target encodes data in big-endian format"
|
|
"(little endian is the default)")
|
|
|
|
parser.add_argument("-i", "--input",
|
|
help="Input file from which MMU regions are read.")
|
|
parser.add_argument("-k", "--kernel",
|
|
help="Zephyr kernel image")
|
|
parser.add_argument(
|
|
"-o", "--output",
|
|
help="Output file into which the page tables are written.")
|
|
parser.add_argument("-v", "--verbose", action="count", default=0,
|
|
help="Print debugging information. Multiple "
|
|
"invocations increase verbosity")
|
|
args = parser.parse_args()
|
|
|
|
|
|
# the format for writing in the binary file would be decided by the
|
|
# endian selected
|
|
def set_struct_endian_format(page_mode):
|
|
endian_string = "<"
|
|
if args.big_endian is True:
|
|
endian_string = ">"
|
|
global struct_mmu_regions_format
|
|
global header_values_format
|
|
|
|
struct_mmu_regions_format = endian_string + "IIQ"
|
|
header_values_format = endian_string + "II"
|
|
page_mode.write_page_entry_bin = (endian_string +
|
|
page_mode.write_page_entry_bin)
|
|
|
|
|
|
def format_string(input_str):
|
|
output_str = '{0: <5}'.format(str(input_str))
|
|
return output_str
|
|
|
|
# format for 32bit hex value
|
|
|
|
|
|
def hex_32(input_value):
|
|
output_value = "{0:#0{1}x}".format(input_value, 10)
|
|
return output_value
|
|
|
|
# format for 20bit hex value
|
|
|
|
|
|
def hex_20(input_value):
|
|
output_value = "{0:#0{1}x}".format(input_value, 7)
|
|
return output_value
|
|
|
|
|
|
def verbose_output(page_mode):
|
|
if args.verbose == 0:
|
|
return
|
|
|
|
print("\nMemory Regions as defined:")
|
|
for info in raw_info:
|
|
print("Memory region start address = " + hex_32(info[0]) +
|
|
", Memory size = " + hex_32(info[1]) +
|
|
", Permission = " + hex(info[2]))
|
|
|
|
page_mode.verbose_output()
|
|
|
|
if args.verbose > 1:
|
|
page_mode.print_all_page_table_info()
|
|
|
|
# build sym table
|
|
|
|
|
|
def get_symbols(obj):
|
|
for section in obj.iter_sections():
|
|
if isinstance(section, SymbolTableSection):
|
|
return {sym.name: sym.entry.st_value
|
|
for sym in section.iter_symbols()}
|
|
|
|
raise LookupError("Could not find symbol table")
|
|
|
|
|
|
# determine which paging mode was selected
|
|
def get_page_mode():
|
|
with open(args.kernel, "rb") as fp:
|
|
kernel = ELFFile(fp)
|
|
sym = get_symbols(kernel)
|
|
try:
|
|
return sym["CONFIG_X86_PAE_MODE"]
|
|
except BaseException:
|
|
return 0
|
|
|
|
|
|
def main():
|
|
global output_buffer
|
|
parse_args()
|
|
|
|
# select the page table needed
|
|
if get_page_mode():
|
|
page_mode = PageMode_PAE()
|
|
else:
|
|
page_mode = PageMode_4kb()
|
|
|
|
set_struct_endian_format(page_mode)
|
|
|
|
global input_file
|
|
input_file = open(args.input, 'rb')
|
|
|
|
global binary_output_file
|
|
binary_output_file = open(args.output, 'wb')
|
|
|
|
# inputfile= file_name
|
|
read_mmu_list_marshal_param(page_mode)
|
|
|
|
# populate the required structs
|
|
page_mode.populate_required_structs()
|
|
|
|
# validate the inputs
|
|
validate_pde_regions()
|
|
|
|
# The size of the output buffer has to match the number of bytes we write
|
|
# this corresponds to the number of page tables gets created.
|
|
output_buffer = page_mode.set_binary_file_size()
|
|
|
|
try:
|
|
page_mode.pdpte_create_binary_file()
|
|
except BaseException:
|
|
pass
|
|
page_mode.page_directory_create_binary_file()
|
|
page_mode.page_table_create_binary_file()
|
|
|
|
# write the binary data into the file
|
|
binary_output_file.write(output_buffer)
|
|
binary_output_file.close()
|
|
|
|
# verbose output needed by the build system
|
|
verbose_output(page_mode)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|