264 lines
8.7 KiB
Python
264 lines
8.7 KiB
Python
|
#! /usr/bin/env python
|
||
|
|
||
|
import sys
|
||
|
from optparse import OptionParser
|
||
|
import random
|
||
|
import math
|
||
|
|
||
|
def convert(size):
|
||
|
length = len(size)
|
||
|
lastchar = size[length-1]
|
||
|
if (lastchar == 'k') or (lastchar == 'K'):
|
||
|
m = 1024
|
||
|
nsize = int(size[0:length-1]) * m
|
||
|
elif (lastchar == 'm') or (lastchar == 'M'):
|
||
|
m = 1024*1024
|
||
|
nsize = int(size[0:length-1]) * m
|
||
|
elif (lastchar == 'g') or (lastchar == 'G'):
|
||
|
m = 1024*1024*1024
|
||
|
nsize = int(size[0:length-1]) * m
|
||
|
else:
|
||
|
nsize = int(size)
|
||
|
return nsize
|
||
|
|
||
|
def roundup(size):
|
||
|
value = 1.0
|
||
|
while value < size:
|
||
|
value = value * 2.0
|
||
|
return value
|
||
|
|
||
|
|
||
|
class OS:
|
||
|
def __init__(self):
|
||
|
# 4k phys memory (128 pages)
|
||
|
self.pageSize = 32
|
||
|
self.physPages = 128
|
||
|
self.physMem = self.pageSize * self.physPages
|
||
|
self.vaPages = 1024
|
||
|
self.vaSize = self.pageSize * self.vaPages
|
||
|
self.pteSize = 1
|
||
|
self.pageBits = 5 # log of page size
|
||
|
|
||
|
# os tracks
|
||
|
self.usedPages = []
|
||
|
self.usedPagesCount = 0
|
||
|
self.maxPageCount = self.physMem / self.pageSize
|
||
|
|
||
|
# no pages used (yet)
|
||
|
for i in range(0, self.maxPageCount):
|
||
|
self.usedPages.append(0)
|
||
|
|
||
|
# set contents of memory to 0, too
|
||
|
self.memory = []
|
||
|
for i in range(0, self.physMem):
|
||
|
self.memory.append(0)
|
||
|
|
||
|
# associative array of pdbr's (indexed by PID)
|
||
|
self.pdbr = {}
|
||
|
|
||
|
# mask is 11111 00000 00000 --> 0111 1100 0000 0000
|
||
|
self.PDE_MASK = 0x7c00
|
||
|
self.PDE_SHIFT = 10
|
||
|
|
||
|
# 00000 11111 00000 -> 000 0011 1110 0000
|
||
|
self.PTE_MASK = 0x03e0
|
||
|
self.PTE_SHIFT = 5
|
||
|
|
||
|
self.VPN_MASK = self.PDE_MASK | self.PTE_MASK
|
||
|
self.VPN_SHIFT = self.PTE_SHIFT
|
||
|
|
||
|
# grabs the last five bits of a virtual address
|
||
|
self.OFFSET_MASK = 0x1f
|
||
|
|
||
|
def findFree(self):
|
||
|
assert(self.usedPagesCount < self.maxPageCount)
|
||
|
look = int(random.random() * self.maxPageCount)
|
||
|
while self.usedPages[look] == 1:
|
||
|
look = int(random.random() * self.maxPageCount)
|
||
|
self.usedPagesCount = self.usedPagesCount + 1
|
||
|
self.usedPages[look] = 1
|
||
|
return look
|
||
|
|
||
|
def initPageDir(self, whichPage):
|
||
|
whichByte = whichPage << self.pageBits
|
||
|
for i in range(whichByte, whichByte + self.pageSize):
|
||
|
self.memory[i] = 0x7f
|
||
|
|
||
|
def initPageTablePage(self, whichPage):
|
||
|
self.initPageDir(whichPage)
|
||
|
|
||
|
def getPageTableEntry(self, virtualAddr, ptePage, printStuff):
|
||
|
pteBits = (virtualAddr & self.PTE_MASK) >> self.PTE_SHIFT
|
||
|
pteAddr = (ptePage << self.pageBits) | pteBits
|
||
|
pte = self.memory[pteAddr]
|
||
|
valid = (pte & 0x80) >> 7
|
||
|
pfn = (pte & 0x7f)
|
||
|
if printStuff == True:
|
||
|
print ' --> pte index:0x%x pte contents:(valid %d, pfn 0x%02x)' % (pteBits, valid, pfn)
|
||
|
return (valid, pfn, pteAddr)
|
||
|
|
||
|
def getPageDirEntry(self, pid, virtualAddr, printStuff):
|
||
|
pageDir = self.pdbr[pid]
|
||
|
pdeBits = (virtualAddr & self.PDE_MASK) >> self.PDE_SHIFT
|
||
|
pdeAddr = (pageDir << self.pageBits) | pdeBits
|
||
|
pde = self.memory[pdeAddr]
|
||
|
valid = (pde & 0x80) >> 7
|
||
|
ptPtr = (pde & 0x7f)
|
||
|
if printStuff == True:
|
||
|
print ' --> pde index:0x%x pde contents:(valid %d, pfn 0x%02x)' % (pdeBits, valid, ptPtr)
|
||
|
return (valid, ptPtr, pdeAddr)
|
||
|
|
||
|
def setPageTableEntry(self, pteAddr, physicalPage):
|
||
|
self.memory[pteAddr] = 0x80 | physicalPage
|
||
|
|
||
|
def setPageDirEntry(self, pdeAddr, physicalPage):
|
||
|
self.memory[pdeAddr] = 0x80 | physicalPage
|
||
|
|
||
|
def allocVirtualPage(self, pid, virtualPage, physicalPage):
|
||
|
# make it into a virtual address, as everything uses this (and not VPN)
|
||
|
virtualAddr = virtualPage << self.pageBits
|
||
|
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, False)
|
||
|
if valid == 0:
|
||
|
# must allocate a page of the page table now, and have the PD point to it
|
||
|
assert(ptPtr == 127)
|
||
|
ptePage = self.findFree()
|
||
|
self.setPageDirEntry(pdeAddr, ptePage)
|
||
|
self.initPageTablePage(ptePage)
|
||
|
else:
|
||
|
# otherwise, just extract page number of page table page
|
||
|
ptePage = ptPtr
|
||
|
# now, look up page table entry too, and mark it valid and fill in translation
|
||
|
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, False)
|
||
|
assert(valid == 0)
|
||
|
assert(pfn == 127)
|
||
|
self.setPageTableEntry(pteAddr, physicalPage)
|
||
|
|
||
|
# -2 -> PTE fault, -1 means PDE fault
|
||
|
def translate(self, pid, virtualAddr):
|
||
|
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, True)
|
||
|
if valid == 1:
|
||
|
ptePage = ptPtr
|
||
|
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, True)
|
||
|
if valid == 1:
|
||
|
offset = (virtualAddr & self.OFFSET_MASK)
|
||
|
paddr = (pfn << self.pageBits) | offset
|
||
|
# print ' --> pfn: %02x offset: %x' % (pfn, offset)
|
||
|
return paddr
|
||
|
else:
|
||
|
return -2
|
||
|
return -1
|
||
|
|
||
|
def fillPage(self, whichPage):
|
||
|
for j in range(0, self.pageSize):
|
||
|
self.memory[(whichPage * self.pageSize) + j] = int(random.random() * 31)
|
||
|
|
||
|
def procAlloc(self, pid, numPages):
|
||
|
# need a PDBR: find one somewhere in memory
|
||
|
pageDir = self.findFree()
|
||
|
# print '**ALLOCATE** page dir', pageDir
|
||
|
self.pdbr[pid] = pageDir
|
||
|
self.initPageDir(pageDir)
|
||
|
|
||
|
used = {}
|
||
|
for vp in range(0, self.vaPages):
|
||
|
used[vp] = 0
|
||
|
allocatedVPs = []
|
||
|
|
||
|
for vp in range(0, numPages):
|
||
|
vp = int(random.random() * self.vaPages)
|
||
|
while used[vp] == 1:
|
||
|
vp = int(random.random() * self.vaPages)
|
||
|
assert(used[vp] == 0)
|
||
|
used[vp] = 1
|
||
|
allocatedVPs.append(vp)
|
||
|
pp = self.findFree()
|
||
|
# print '**ALLOCATE** page', pp
|
||
|
# print ' trying to map vp:%08x to pp:%08x' % (vp, pp)
|
||
|
self.allocVirtualPage(pid, vp, pp)
|
||
|
self.fillPage(pp)
|
||
|
return allocatedVPs
|
||
|
|
||
|
def dumpPage(self, whichPage):
|
||
|
i = whichPage
|
||
|
for j in range(0, self.pageSize):
|
||
|
print self.memory[(i * self.pageSize) + j],
|
||
|
print ''
|
||
|
|
||
|
def memoryDump(self):
|
||
|
for i in range(0, self.physMem / self.pageSize):
|
||
|
print 'page %3d:' % i,
|
||
|
for j in range(0, self.pageSize):
|
||
|
print '%02x' % self.memory[(i * self.pageSize) + j],
|
||
|
print ''
|
||
|
|
||
|
def getPDBR(self, pid):
|
||
|
return self.pdbr[pid]
|
||
|
|
||
|
def getValue(self, addr):
|
||
|
return self.memory[addr]
|
||
|
|
||
|
# allocate some processes in memory
|
||
|
# allocate some multi-level page tables in memory
|
||
|
# make a bit of a mystery:
|
||
|
# can examine PDBR (PFN of current proc's page directory)
|
||
|
# can examine contents of any page
|
||
|
# fill pages with values too
|
||
|
# ask: when given
|
||
|
# LOAD VA, R1
|
||
|
# what will final value will be loaded into R1?
|
||
|
|
||
|
#
|
||
|
# main program
|
||
|
#
|
||
|
parser = OptionParser()
|
||
|
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
|
||
|
parser.add_option('-a', '--allocated', default=64, help='number of virtual pages allocated',
|
||
|
action='store', type='int', dest='allocated')
|
||
|
parser.add_option('-n', '--addresses', default=10, help='number of virtual addresses to generate',
|
||
|
action='store', type='int', dest='num')
|
||
|
parser.add_option('-c', '--solve', help='compute answers for me', action='store_true', default=False, dest='solve')
|
||
|
|
||
|
|
||
|
(options, args) = parser.parse_args()
|
||
|
|
||
|
print 'ARG seed', options.seed
|
||
|
print 'ARG allocated', options.allocated
|
||
|
print 'ARG num', options.num
|
||
|
print ""
|
||
|
|
||
|
random.seed(options.seed)
|
||
|
|
||
|
# do the work now
|
||
|
os = OS()
|
||
|
used = os.procAlloc(1, options.allocated)
|
||
|
|
||
|
os.memoryDump()
|
||
|
|
||
|
print '\nPDBR:', os.getPDBR(1), ' (decimal) [This means the page directory is held in this page]\n'
|
||
|
|
||
|
for i in range(0, options.num):
|
||
|
if (random.random() * 100) > 50.0 or i >= len(used):
|
||
|
vaddr = int(random.random() * 1024 * 32)
|
||
|
else:
|
||
|
vaddr = (used[i] << 5) | int(random.random() * 32)
|
||
|
if options.solve == True:
|
||
|
print 'Virtual Address %04x:' % vaddr
|
||
|
r = os.translate(1, vaddr)
|
||
|
if r > -1:
|
||
|
print ' --> Translates to Physical Address 0x%03x --> Value: %02x' % (r, os.getValue(r))
|
||
|
elif r == -1:
|
||
|
print ' --> Fault (page directory entry not valid)'
|
||
|
else:
|
||
|
print ' --> Fault (page table entry not valid)'
|
||
|
else:
|
||
|
print 'Virtual Address %04x: Translates To What Physical Address (And Fetches what Value)? Or Fault?' % vaddr
|
||
|
|
||
|
print ''
|
||
|
|
||
|
exit(0)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|