#! /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)