def read( self, start_addr, num_bytes ): assert 0 < num_bytes <= 4 start_addr = r_uint( start_addr ) word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled( "mem" ): print ':: RD.MEM[%s] = ' % pad_hex( start_addr ), if self.debug.enabled( "memcheck" ): self.bounds_check( start_addr, 'RD' ) value = 0 if num_bytes == 4: # TODO: byte should only be 0 (only aligned) value = widen( self.data[ word ] ) elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = 0xFFFF << (byte * 8) value = ( widen( self.data[ word ] ) & mask) >> (byte * 8) elif num_bytes == 1: mask = 0xFF << (byte * 8) value = ( widen( self.data[ word ] ) & mask) >> (byte * 8) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled( "mem" ): print '%s' % pad_hex( value ), return r_uint( value )
def write(self, start_addr, num_bytes, value): assert 0 < num_bytes <= 4 start_addr = r_uint(start_addr) value = r_uint(value) word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled("memcheck") and not self.suppress_debug: self.bounds_check(start_addr, 'WR') if num_bytes == 4: # TODO: byte should only be 0 (only aligned) pass # no masking needed elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = ~(0xFFFF << (byte * 8)) & r_uint(0xFFFFFFFF) value = ( widen( self.data[ word ] ) & mask ) \ | ( (value & 0xFFFF) << (byte * 8) ) elif num_bytes == 1: mask = ~(0xFF << (byte * 8)) & r_uint(0xFFFFFFFF) value = ( widen( self.data[ word ] ) & mask ) \ | ( (value & 0xFF ) << (byte * 8) ) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled("mem") and not self.suppress_debug: print ':: WR.MEM[%s] = %s' % (pad_hex(start_addr), pad_hex(value)), self.data[word] = r_uint32(value)
def write( self, start_addr, num_bytes, value ): assert 0 < num_bytes <= 4 start_addr = r_uint( start_addr ) value = r_uint( value ) word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled( "memcheck" ): self.bounds_check( start_addr, 'WR' ) if num_bytes == 4: # TODO: byte should only be 0 (only aligned) pass # no masking needed elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = ~(0xFFFF << (byte * 8)) & r_uint( 0xFFFFFFFF ) value = ( widen( self.data[ word ] ) & mask ) \ | ( (value & 0xFFFF) << (byte * 8) ) elif num_bytes == 1: mask = ~(0xFF << (byte * 8)) & r_uint( 0xFFFFFFFF ) value = ( widen( self.data[ word ] ) & mask ) \ | ( (value & 0xFF ) << (byte * 8) ) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled( "mem" ): print ':: WR.MEM[%s] = %s' % ( pad_hex( start_addr ), pad_hex( value ) ), self.data[ word ] = r_uint32( value )
def read(self, start_addr, num_bytes): assert 0 < num_bytes <= 4 start_addr = r_uint(start_addr) word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled("mem") and not self.suppress_debug: print ':: RD.MEM[%s] = ' % pad_hex(start_addr), if self.debug.enabled("memcheck") and not self.suppress_debug: self.bounds_check(start_addr, 'RD') value = 0 if num_bytes == 4: # TODO: byte should only be 0 (only aligned) value = widen(self.data[word]) elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = 0xFFFF << (byte * 8) value = (widen(self.data[word]) & mask) >> (byte * 8) elif num_bytes == 1: mask = 0xFF << (byte * 8) value = (widen(self.data[word]) & mask) >> (byte * 8) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled("mem"): print '%s' % pad_hex(value), return r_uint(value)
def __init__(self, memory, debug, reset_addr=0x400): Machine.__init__(self, memory, ArmRegisterFile(self, num_regs=16), debug, reset_addr=reset_addr) self.rf[15] = self.pc # current program status register (CPSR) self.N = r_uint(0b0) # Negative condition self.Z = r_uint(0b0) # Zero condition self.C = r_uint(0b0) # Carry condition self.V = r_uint(0b0) # Overflow condition #self.J = 0b0 # Jazelle state flag #self.I = 0b0 # IRQ Interrupt Mask #self.F = 0b0 # FIQ Interrupt Mask #self.T = 0b0 # Thumb state flag #self.M = 0b00000 # Processor Mode # processor modes: # 0b10000 usr # 0b10001 fiq # 0b10010 irq # 0b10011 svc (supervisor) # 0b10111 abt (abort) # 0b11011 und (undefined) # 0b11111 sys self.mode = r_uint(0b10000) # syscall stuff... TODO: should this be here? self.breakpoint = 0
def __init__( self, memory, debug, reset_addr=0x400 ): Machine.__init__(self, memory, ArmRegisterFile( self, num_regs=16 ), debug, reset_addr=reset_addr ) self.rf[ 15 ] = self.pc # current program status register (CPSR) self.N = r_uint( 0b0 ) # Negative condition self.Z = r_uint( 0b0 ) # Zero condition self.C = r_uint( 0b0 ) # Carry condition self.V = r_uint( 0b0 ) # Overflow condition #self.J = 0b0 # Jazelle state flag #self.I = 0b0 # IRQ Interrupt Mask #self.F = 0b0 # FIQ Interrupt Mask #self.T = 0b0 # Thumb state flag #self.M = 0b00000 # Processor Mode # processor modes: # 0b10000 usr # 0b10001 fiq # 0b10010 irq # 0b10011 svc (supervisor) # 0b10111 abt (abort) # 0b11011 und (undefined) # 0b11111 sys self.mode = r_uint( 0b10000 ) # syscall stuff... TODO: should this be here? self.breakpoint = 0
def __init__(self, data=None, size=2**10): self.data = data if data else [r_uint32(0)] * (size >> 2) self.size = r_uint(len(self.data) << 2) self.debug = Debug() # TODO: pass data_section to memory for bounds checking self.data_section = 0x00000000
def __init__( self, data=None, size=2**10 ): self.data = data if data else [ r_uint32(0) ] * (size >> 2) self.size = r_uint(len( self.data ) << 2) self.debug = Debug() # TODO: pass data_section to memory for bounds checking self.data_section = 0x00000000
def __init__(self, constant_zero=True, num_regs=32, nbits=32): self.num_regs = num_regs self.regs = [r_uint(0)] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4 if constant_zero: self._setitemimpl = self._set_item_const_zero else: self._setitemimpl = self._set_item
def __init__( self, constant_zero=True, num_regs=32, nbits=32 ): self.num_regs = num_regs self.regs = [ r_uint(0) ] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4 if constant_zero: self._setitemimpl = self._set_item_const_zero else: self._setitemimpl = self._set_item
def __setitem__(self, idx, value): value = r_uint(value) if idx == 15: self.state.pc = value if self.debug.enabled("rf"): print ':: WR.RF[15] = %s' % (pad_hex(value)), else: self.regs[idx] = value if self.debug.enabled("rf"): print ':: WR.RF[%s] = %s' % (pad("%d" % idx, 2), pad_hex(value)),
def syscall_brk( s, arg0, arg1, arg2 ): new_brk = arg0 if s.debug.enabled( "syscalls" ): print "syscall_brk( addr=%x )" % new_brk, if new_brk != 0: s.breakpoint = r_uint( new_brk ) return intmask( s.breakpoint ), 0
def syscall_exit( s, arg0, arg1, arg2 ): exit_code = arg0 if s.debug.enabled( "syscalls" ): print "syscall_exit( status=%x )" % exit_code s.status = r_uint( exit_code ) s.running = False # ret_val errno return exit_code, 0
def __setitem__( self, idx, value ): value = r_uint( value ) if idx == 15: self.state.pc = value if self.debug.enabled( "rf" ): print ':: WR.RF[15] = %s' % ( pad_hex( value ) ), else: self.regs[idx] = value if self.debug.enabled( "rf" ): print ':: WR.RF[%s] = %s' % ( pad( "%d" % idx, 2 ), pad_hex( value ) ),
def __init__( self, memory, debug, reset_addr=0x400): Machine.__init__(self, memory, RegisterFile(), debug, reset_addr=reset_addr ) # parc special self.src_ptr = 0 self.sink_ptr = 0 # indicate if this is running a self-checking test self.testbin = False # executable name self.exe_name = "" # syscall stuff... TODO: should this be here? self.breakpoint = r_uint( 0 )
def bounds_check( self, addr, x ): # check if the accessed data is larger than the memory size if addr > self.size: print ("WARNING: %s accessing larger address than memory size. " "addr=%s size=%s") % ( x, pad_hex( addr ), pad_hex( self.size ) ) raise Exception() if addr == 0: print "WARNING: accessing null pointer!" raise Exception() # Special write checks if x == 'WR' and addr < r_uint( self.data_section ): print ("WARNING: %s writing address below .data section!!!. " "addr=%s size=%s") % ( x, pad_hex( addr ), pad_hex( self.data_section ) ) raise Exception()
def get_mcpuid(self): if self.state.xlen == 32: base = 0b00 # TODO: not currently representing RV32E elif self.state.xlen == 64: base = 0b10 elif self.state.xlen == 128: base = 0b11 else: raise FatalError("XLEN=%d not supported" % self.state.xlen) extensions = 0 for e in self.state.extensions: extensions |= 1 << (ord(e) - ord("a")) return r_uint((base << (self.state.xlen - 2)) | extensions)
def get_mcpuid( self ): if self.state.xlen == 32: base = 0b00 # TODO: not currently representing RV32E elif self.state.xlen == 64: base = 0b10 elif self.state.xlen == 128: base = 0b11 else: raise FatalError( "XLEN=%d not supported" % self.state.xlen ) extensions = 0 for e in self.state.extensions: extensions |= 1 << ( ord(e) - ord("a") ) return r_uint( ( base << (self.state.xlen - 2) ) | extensions )
def bounds_check(self, addr, x): # check if the accessed data is larger than the memory size if addr > self.size: print( "WARNING: %s accessing larger address than memory size. " "addr=%s size=%s") % (x, pad_hex(addr), pad_hex(self.size)) raise Exception() if addr == 0: print "WARNING: accessing null pointer!" raise Exception() # Special write checks if x == 'WR' and addr < r_uint(self.data_section): print( "WARNING: %s writing address below .data section!!!. " "addr=%s size=%s") % (x, pad_hex(addr), pad_hex(self.data_section)) raise Exception()
def shifter_operand_imm( s, inst ): shift_op = inst.shift Rm = s.rf[ inst.rm ] shift_imm = inst.shift_amt assert 0 <= shift_imm <= 31 if shift_op == LOGIC_SHIFT_LEFT: out = Rm if (shift_imm == 0) else Rm << shift_imm cout = s.C if (shift_imm == 0) else (Rm >> 32 - shift_imm)&1 elif shift_op == LOGIC_SHIFT_RIGHT: # NOTE: shift_imm == 0 signifies a shift by 32 out = 0 if (shift_imm == 0) else Rm >> shift_imm cout = Rm >> 31 if (shift_imm == 0) else (Rm >> shift_imm - 1)&1 elif shift_op == ARITH_SHIFT_RIGHT: # NOTE: shift_imm == 0 signifies a shift by 32 if shift_imm == 0: if (Rm >> 31) == 0: out, cout = 0, Rm >> 31 else: out, cout = 0xFFFFFFFF, Rm >> 31 else: out = arith_shift( Rm, shift_imm ) cout = (Rm >> shift_imm - 1)&1 elif shift_op == ROTATE_RIGHT: # NOTE: shift_imm == 0 signifies a rotate right with extend (RRX) if shift_imm == 0: out = ( r_uint(s.C) << 31 ) | (Rm >> 1) cout = Rm & 1 else: out = rotate_right( Rm, shift_imm ) cout = (Rm >> shift_imm - 1)&1 else: raise FatalError('Impossible shift_op!') return trim_32(out), cout
def shifter_operand_imm(s, inst): shift_op = inst.shift Rm = s.rf[inst.rm] shift_imm = inst.shift_amt assert 0 <= shift_imm <= 31 if shift_op == LOGIC_SHIFT_LEFT: out = Rm if (shift_imm == 0) else Rm << shift_imm cout = s.C if (shift_imm == 0) else (Rm >> 32 - shift_imm) & 1 elif shift_op == LOGIC_SHIFT_RIGHT: # NOTE: shift_imm == 0 signifies a shift by 32 out = 0 if (shift_imm == 0) else Rm >> shift_imm cout = Rm >> 31 if (shift_imm == 0) else (Rm >> shift_imm - 1) & 1 elif shift_op == ARITH_SHIFT_RIGHT: # NOTE: shift_imm == 0 signifies a shift by 32 if shift_imm == 0: if (Rm >> 31) == 0: out, cout = 0, Rm >> 31 else: out, cout = 0xFFFFFFFF, Rm >> 31 else: out = arith_shift(Rm, shift_imm) cout = (Rm >> shift_imm - 1) & 1 elif shift_op == ROTATE_RIGHT: # NOTE: shift_imm == 0 signifies a rotate right with extend (RRX) if shift_imm == 0: out = (r_uint(s.C) << 31) | (Rm >> 1) cout = Rm & 1 else: out = rotate_right(Rm, shift_imm) cout = (Rm >> shift_imm - 1) & 1 else: raise FatalError('Impossible shift_op!') return trim_32(out), cout
def get_mhartid(self): return r_uint(0)
def __init__(self, num_regs=32, nbits=64): self.num_regs = num_regs self.regs = [r_uint(0)] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4
def __setitem__( self, idx, value ): value = r_uint( value ) self._setitemimpl( idx, value )
def syscall_init( mem, entrypoint, breakpoint, argv, envp, debug ): #--------------------------------------------------------------------- # memory map initialization #--------------------------------------------------------------------- # TODO: for multicore allocate 8MB for each process #proc_stack_base[pid] = stack_base - pid * 8 * 1024 * 1024 # top of heap (breakpoint) # TODO: handled in load program # memory maps: 1GB above top of heap # mmap_start = mmap_end = break_point + 0x40000000 #--------------------------------------------------------------------- # stack argument initialization #--------------------------------------------------------------------- # http://articles.manugarg.com/aboutelfauxiliaryvectors.html # # contents size # # 0x7FFF.FFFF [ end marker ] 4 (NULL) # [ environment str data ] >=0 # [ arguments str data ] >=0 # [ padding ] 0-16 # [ auxv[n] data ] 8 (AT_NULL Vector) # 8*x # [ auxv[0] data ] 8 # [ envp[n] pointer ] 4 (NULL) # 4*x # [ envp[0] pointer ] 4 # [ argv[n] pointer ] 4 (NULL) # 4*x # [ argv[0] pointer ] 4 (program name) # stack ptr-> [ argc ] 4 (size of argv) # # (stack grows down!!!) # # 0x7F7F.FFFF < stack limit for pid 0 > # # auxv variables initialized by gem5, are these needed? # # - PAGESZ: system page size # - PHDR: virtual addr of program header tables # (for statically linked binaries) # - PHENT: size of program header entries in elf file # - PHNUM: number of program headers in elf file # - AT_ENRTY: program entry point # - UID: user ID # - EUID: effective user ID # - GID: group ID # - EGID: effective group ID # TODO: handle auxv, envp variables auxv = [] if EMULATE_GEM5: argv = argv[1:] argc = len( argv ) def sum_( x ): val = 0 for i in x: val += i return val # calculate sizes of sections # TODO: parameterize auxv/envp/argv calc for variable int size? stack_nbytes = [ 4, # end mark nbytes (sentry) sum_([len(x)+1 for x in envp]), # envp_str nbytes sum_([len(x)+1 for x in argv]), # argv_str nbytes 0, # padding nbytes 8*(len(auxv) + 1), # auxv nbytes 4*(len(envp) + 1), # envp nbytes 4*(len(argv) + 1), # argv nbytes 4 ] # argc nbytes if EMULATE_SIMIT: stack_nbytes[4] = 0 # don't to auxv for simit def round_up( val ): alignment = 16 return (val + alignment - 1) & ~(alignment - 1) # calculate padding to align boundary # NOTE: MIPs approach (but ignored by gem5) #stack_nbytes[3] = 16 - (sum_(stack_nbytes[:3]) % 16) # NOTE: gem5 ARM approach stack_nbytes[3] = round_up( sum_(stack_nbytes) ) - sum_(stack_nbytes) if EMULATE_SIMIT: stack_nbytes[3] = 0 def round_down( val ): alignment = 16 return val & ~(alignment - 1) # calculate stack pointer based on size of storage needed for args # TODO: round to nearest page size? stack_ptr = round_down( stack_base - sum_( stack_nbytes ) ) if EMULATE_SIMIT: stack_ptr = stack_base - MAX_ENVIRON offset = stack_ptr + sum_( stack_nbytes ) # FIXME: this offset seems really wrong, but this is how gem5 does it! if EMULATE_GEM5: offset = stack_base print "XXX", offset stack_off = [] for nbytes in stack_nbytes: offset -= nbytes stack_off.append( offset ) # FIXME: this is fails for GEM5's hacky offset... if not EMULATE_GEM5: assert offset == stack_ptr if debug.enabled( 'bootstrap' ): print 'stack base', hex( stack_base ) print 'stack min ', hex( stack_ptr ) print 'stack size', stack_base - stack_ptr print print 'sentry ', stack_nbytes[0] print 'env d ', stack_nbytes[1] print 'arg d ', stack_nbytes[2] print 'padding', stack_nbytes[3] print 'auxv ', stack_nbytes[4] print 'envp ', stack_nbytes[5] print 'argv ', stack_nbytes[6] print 'argc ', stack_nbytes[7] # utility functions def str_to_mem( mem, val, addr ): for i, char in enumerate(val+'\0'): mem.write( addr + i, 1, ord( char ) ) return addr + len(val) + 1 def int_to_mem( mem, val, addr ): # TODO properly handle endianess for i in range( 4 ): mem.write( addr+i, 1, (val >> 8*i) & 0xFF ) return addr + 4 # write end marker to memory int_to_mem( mem, 0, stack_off[0] ) # write environment strings to memory envp_ptrs = [] offset = stack_off[1] for x in envp: envp_ptrs.append( offset ) offset = str_to_mem( mem, x, offset ) assert offset == stack_off[0] # write argument strings to memory argv_ptrs = [] offset = stack_off[2] for x in argv: argv_ptrs.append( offset ) offset = str_to_mem( mem, x, offset ) assert offset == stack_off[1] # write auxv vectors to memory offset = stack_off[4] if not EMULATE_SIMIT: for type_, value in auxv + [(0,0)]: offset = int_to_mem( mem, type_, offset ) offset = int_to_mem( mem, value, offset ) assert offset == stack_off[3] # write envp pointers to memory offset = stack_off[5] for env in envp_ptrs + [0]: offset = int_to_mem( mem, env, offset ) assert offset == stack_off[4] # write argv pointers to memory offset = stack_off[6] for arg in argv_ptrs + [0]: offset = int_to_mem( mem, arg, offset ) assert offset == stack_off[5] # write argc to memory offset = stack_off[7] offset = int_to_mem( mem, argc, offset ) assert offset == stack_off[6] # write zeros to bottom of stack # TODO: why does gem5 do this? offset = stack_off[7] - 1 while offset >= stack_ptr: mem.write( offset, 1, ord( '\0' ) ) offset -= 1 # initialize processor state state = State( mem, debug, reset_addr=0x1000 ) if debug.enabled( 'bootstrap' ): print '---' #print 'argc = %d (%x)' % ( argc, stack_off[-1] ) #for i, ptr in enumerate(argv_ptrs): # print 'argv[%2d] = %x (%x)' % ( i, argv_ptrs[i], stack_off[-2]+4*i ), # print len( argv[i] ), argv[i] #print 'argd = %s (%x)' % ( argv[0], stack_off[-6] ) print '---' print 'envd-base', hex(stack_off[-7]) print 'argd-base', hex(stack_off[-6]) print 'auxv-base', hex(stack_off[-4]) print 'envp-base', hex(stack_off[-3]) print 'argv-base', hex(stack_off[-2]) print 'argc-base', hex(stack_off[-1]) print 'STACK_PTR', hex( stack_ptr ) # TODO: where should this go? #state.pc = entrypoint state.breakpoint = r_uint( breakpoint ) # initialize processor registers state.rf[ 0 ] = 0 # ptr to func to run when program exits, disable state.rf[ 1 ] = stack_off[6] # argument 1 reg = argv ptr addr state.rf[ 2 ] = stack_off[5] # argument 2 reg = envp ptr addr if EMULATE_SIMIT: state.rf[ 1 ] = argc # argument 1 reg = argc state.rf[ 2 ] = stack_off[6] # argument 2 reg = argv ptr addr state.rf[ 13 ] = stack_ptr # stack pointer reg state.rf[ 15 ] = entrypoint # program counter if debug.enabled( 'bootstrap' ): state.rf.print_regs( per_row=4 ) print '='* 20, 'end bootstrap', '='*20 return state
def iread( self, start_addr, num_bytes ): assert start_addr & 0b11 == 0 # only aligned accesses allowed return r_uint( widen( self.data[ start_addr >> 2 ] ) )
def cpsr( self ): return ( r_uint( self.N ) << 31 ) | \ ( r_uint( self.Z ) << 30 ) | \ ( r_uint( self.C ) << 29 ) | \ ( r_uint( self.V ) << 28 ) | \ ( r_uint( self.mode ) )
def syscall_init(mem, entrypoint, breakpoint, argv, envp, debug): #--------------------------------------------------------------------- # memory map initialization #--------------------------------------------------------------------- # TODO: for multicore allocate 8MB for each process #proc_stack_base[pid] = stack_base - pid * 8 * 1024 * 1024 # top of heap (breakpoint) # TODO: handled in load program # memory maps: 1GB above top of heap # mmap_start = mmap_end = break_point + 0x40000000 #--------------------------------------------------------------------- # stack argument initialization #--------------------------------------------------------------------- # http://articles.manugarg.com/aboutelfauxiliaryvectors.html # # contents size # # 0x7FFF.FFFF [ end marker ] 4 (NULL) # [ environment str data ] >=0 # [ arguments str data ] >=0 # [ padding ] 0-16 # [ auxv[n] data ] 8 (AT_NULL Vector) # 8*x # [ auxv[0] data ] 8 # [ envp[n] pointer ] 4 (NULL) # 4*x # [ envp[0] pointer ] 4 # [ argv[n] pointer ] 4 (NULL) # 4*x # [ argv[0] pointer ] 4 (program name) # stack ptr-> [ argc ] 4 (size of argv) # # (stack grows down!!!) # # 0x7F7F.FFFF < stack limit for pid 0 > # # auxv variables initialized by gem5, are these needed? # # - PAGESZ: system page size # - PHDR: virtual addr of program header tables # (for statically linked binaries) # - PHENT: size of program header entries in elf file # - PHNUM: number of program headers in elf file # - AT_ENRTY: program entry point # - UID: user ID # - EUID: effective user ID # - GID: group ID # - EGID: effective group ID # TODO: handle auxv, envp variables auxv = [] if EMULATE_GEM5: argv = argv[1:] argc = len(argv) def sum_(x): val = 0 for i in x: val += i return val # calculate sizes of sections # TODO: parameterize auxv/envp/argv calc for variable int size? stack_nbytes = [ 4, # end mark nbytes (sentry) sum_([len(x) + 1 for x in envp]), # envp_str nbytes sum_([len(x) + 1 for x in argv]), # argv_str nbytes 0, # padding nbytes 8 * (len(auxv) + 1), # auxv nbytes 4 * (len(envp) + 1), # envp nbytes 4 * (len(argv) + 1), # argv nbytes 4 ] # argc nbytes if EMULATE_SIMIT: stack_nbytes[4] = 0 # don't to auxv for simit def round_up(val): alignment = 16 return (val + alignment - 1) & ~(alignment - 1) # calculate padding to align boundary # NOTE: MIPs approach (but ignored by gem5) #stack_nbytes[3] = 16 - (sum_(stack_nbytes[:3]) % 16) # NOTE: gem5 ARM approach stack_nbytes[3] = round_up(sum_(stack_nbytes)) - sum_(stack_nbytes) if EMULATE_SIMIT: stack_nbytes[3] = 0 def round_down(val): alignment = 16 return val & ~(alignment - 1) # calculate stack pointer based on size of storage needed for args # TODO: round to nearest page size? stack_ptr = round_down(stack_base - sum_(stack_nbytes)) if EMULATE_SIMIT: stack_ptr = stack_base - MAX_ENVIRON offset = stack_ptr + sum_(stack_nbytes) # FIXME: this offset seems really wrong, but this is how gem5 does it! if EMULATE_GEM5: offset = stack_base print "XXX", offset stack_off = [] for nbytes in stack_nbytes: offset -= nbytes stack_off.append(offset) # FIXME: this is fails for GEM5's hacky offset... if not EMULATE_GEM5: assert offset == stack_ptr if debug.enabled('bootstrap'): print 'stack base', hex(stack_base) print 'stack min ', hex(stack_ptr) print 'stack size', stack_base - stack_ptr print print 'sentry ', stack_nbytes[0] print 'env d ', stack_nbytes[1] print 'arg d ', stack_nbytes[2] print 'padding', stack_nbytes[3] print 'auxv ', stack_nbytes[4] print 'envp ', stack_nbytes[5] print 'argv ', stack_nbytes[6] print 'argc ', stack_nbytes[7] # utility functions def str_to_mem(mem, val, addr): for i, char in enumerate(val + '\0'): mem.write(addr + i, 1, ord(char)) return addr + len(val) + 1 def int_to_mem(mem, val, addr): # TODO properly handle endianess for i in range(4): mem.write(addr + i, 1, (val >> 8 * i) & 0xFF) return addr + 4 # write end marker to memory int_to_mem(mem, 0, stack_off[0]) # write environment strings to memory envp_ptrs = [] offset = stack_off[1] for x in envp: envp_ptrs.append(offset) offset = str_to_mem(mem, x, offset) assert offset == stack_off[0] # write argument strings to memory argv_ptrs = [] offset = stack_off[2] for x in argv: argv_ptrs.append(offset) offset = str_to_mem(mem, x, offset) assert offset == stack_off[1] # write auxv vectors to memory offset = stack_off[4] if not EMULATE_SIMIT: for type_, value in auxv + [(0, 0)]: offset = int_to_mem(mem, type_, offset) offset = int_to_mem(mem, value, offset) assert offset == stack_off[3] # write envp pointers to memory offset = stack_off[5] for env in envp_ptrs + [0]: offset = int_to_mem(mem, env, offset) assert offset == stack_off[4] # write argv pointers to memory offset = stack_off[6] for arg in argv_ptrs + [0]: offset = int_to_mem(mem, arg, offset) assert offset == stack_off[5] # write argc to memory offset = stack_off[7] offset = int_to_mem(mem, argc, offset) assert offset == stack_off[6] # write zeros to bottom of stack # TODO: why does gem5 do this? offset = stack_off[7] - 1 while offset >= stack_ptr: mem.write(offset, 1, ord('\0')) offset -= 1 # initialize processor state state = State(mem, debug, reset_addr=0x1000) if debug.enabled('bootstrap'): print '---' #print 'argc = %d (%x)' % ( argc, stack_off[-1] ) #for i, ptr in enumerate(argv_ptrs): # print 'argv[%2d] = %x (%x)' % ( i, argv_ptrs[i], stack_off[-2]+4*i ), # print len( argv[i] ), argv[i] #print 'argd = %s (%x)' % ( argv[0], stack_off[-6] ) print '---' print 'envd-base', hex(stack_off[-7]) print 'argd-base', hex(stack_off[-6]) print 'auxv-base', hex(stack_off[-4]) print 'envp-base', hex(stack_off[-3]) print 'argv-base', hex(stack_off[-2]) print 'argc-base', hex(stack_off[-1]) print 'STACK_PTR', hex(stack_ptr) # TODO: where should this go? #state.pc = entrypoint state.breakpoint = r_uint(breakpoint) # initialize processor registers state.rf[0] = 0 # ptr to func to run when program exits, disable state.rf[1] = stack_off[6] # argument 1 reg = argv ptr addr state.rf[2] = stack_off[5] # argument 2 reg = envp ptr addr if EMULATE_SIMIT: state.rf[1] = argc # argument 1 reg = argc state.rf[2] = stack_off[6] # argument 2 reg = argv ptr addr state.rf[13] = stack_ptr # stack pointer reg state.rf[15] = entrypoint # program counter if debug.enabled('bootstrap'): state.rf.print_regs(per_row=4) print '=' * 20, 'end bootstrap', '=' * 20 return state
def execute_jal( s, inst ): s.rf[31] = s.pc + 4 s.pc = ((s.pc + 4) & r_uint( 0xF0000000 ) ) | (inst.jtarg << 2)
def __init__(self, bits, str): self.bits = r_uint(bits) self.str = str
def syscall_init( mem, breakpoint, argv, envp, debug ): #--------------------------------------------------------------------- # stack argument initialization #--------------------------------------------------------------------- # http://articles.manugarg.com/aboutelfauxiliaryvectors.html # # contents size # # 0x7FFF.FFFF [ end marker ] 8 (NULL) # [ environment str data ] >=0 # [ arguments str data ] >=0 # [ padding ] 0-16 # [ auxv[n] data ] 8 (AT_NULL Vector) # 8*x # [ auxv[0] data ] 8 # [ envp[n] pointer ] 8 (NULL) # 8*x # [ envp[0] pointer ] 8 # [ argv[n] pointer ] 8 (NULL) # 8*x # [ argv[0] pointer ] 8 (program name) # stack ptr-> [ argc ] 8 (size of argv) # # (stack grows down!!!) # # 0x7F7F.FFFF < stack limit for pid 0 > # # auxv variables initialized by gem5, are these needed? # # - PAGESZ: system page size # - PHDR: virtual addr of program header tables # (for statically linked binaries) # - PHENT: size of program header entries in elf file # - PHNUM: number of program headers in elf file # - AT_ENRTY: program entry point # - UID: user ID # - EUID: effective user ID # - GID: group ID # - EGID: effective group ID # TODO: handle auxv, envp variables auxv = [] argc = len( argv ) def sum_( x ): val = 0 for i in x: val += i return val # calculate sizes of sections # TODO: parameterize auxv/envp/argv calc for variable int size? stack_nbytes = [ 8, # end mark nbytes sum_([len(x)+1 for x in envp]), # envp_str nbytes sum_([len(x)+1 for x in argv]), # argv_str nbytes 0, # padding nbytes 16*(len(auxv) + 1), # auxv nbytes 8*(len(envp) + 1), # envp nbytes 8*(len(argv) + 1), # argv nbytes 8 ] # argc nbytes # calculate padding to align boundary # TODO: gem5 doesn't do this step, temporarily remove it. There should # really be padding to align argv to a 16 byte boundary!!! #stack_nbytes[3] = 16 - (sum_(stack_nbytes[:3]) % 16) def round_down( val ): return val & ~(page_size - 1) # calculate stack pointer based on size of storage needed for args # TODO: round to nearest page size? stack_ptr = round_down( stack_base - sum_( stack_nbytes ) ) offset = stack_ptr + sum_( stack_nbytes ) stack_off = [] for nbytes in stack_nbytes: offset -= nbytes stack_off.append( offset ) assert offset == stack_ptr #print 'stack min', hex( stack_ptr ) #print 'stack size', stack_base - stack_ptr #print 'argv', stack_nbytes[-2] #print 'envp', stack_nbytes[-3] #print 'auxv', stack_nbytes[-4] #print 'argd', stack_nbytes[-6] #print 'envd', stack_nbytes[-7] # utility functions def str_to_mem( mem, val, addr ): for i, char in enumerate(val+'\0'): mem.write( addr + i, 1, ord( char ) ) return addr + len(val) + 1 def int_to_mem( mem, val, addr ): # TODO properly handle endianess for i in range( 8 ): mem.write( addr+i, 1, (val >> 8*i) & 0xFF ) return addr + 8 # write end marker to memory int_to_mem( mem, 0, stack_off[0] ) # write environment strings to memory envp_ptrs = [] offset = stack_off[1] for x in envp: envp_ptrs.append( offset ) offset = str_to_mem( mem, x, offset ) assert offset == stack_off[0] # write argument strings to memory argv_ptrs = [] offset = stack_off[2] for x in argv: argv_ptrs.append( offset ) offset = str_to_mem( mem, x, offset ) assert offset == stack_off[1] # write auxv vectors to memory offset = stack_off[4] for type_, value in auxv + [(0,0)]: offset = int_to_mem( mem, type_, offset ) offset = int_to_mem( mem, value, offset ) assert offset == stack_off[3] # write envp pointers to memory offset = stack_off[5] for env in envp_ptrs + [0]: offset = int_to_mem( mem, env, offset ) assert offset == stack_off[4] # write argv pointers to memory offset = stack_off[6] for arg in argv_ptrs + [0]: offset = int_to_mem( mem, arg, offset ) assert offset == stack_off[5] # write argc to memory offset = stack_off[7] offset = int_to_mem( mem, argc, offset ) assert offset == stack_off[6] # initialize processor state state = State( mem, debug, reset_addr=0x10000 ) # TODO: where should this go? state.breakpoint = r_uint( breakpoint ) #print '---' #print 'argc = %d (%x)' % ( argc, stack_off[-1] ) #for i, ptr in enumerate(argv_ptrs): # print 'argv[%d] = %x (%x)' % ( i, argv_ptrs[i], stack_off[-2]+4*i ), # print len( argv[i] ), argv[i] #print 'argd = %s (%x)' % ( argv[0], stack_off[-6] ) #print '---' #print 'argv-base', hex(stack_off[-2]) #print 'envp-base', hex(stack_off[-3]) #print 'auxv-base', hex(stack_off[-4]) #print 'argd-base', hex(stack_off[-6]) #print 'envd-base', hex(stack_off[-7]) # initialize processor registers state.rf[ reg_map['a0'] ] = argc # argument 0 reg = argc state.rf[ reg_map['a1'] ] = stack_off[6] # argument 1 reg = argv ptr addr state.rf[ reg_map['sp'] ] = stack_ptr # stack pointer reg return state # instantiate architectural state with memory and reset address return state
def __setitem__(self, idx, value): value = r_uint(value) self._setitemimpl(idx, value)
def arith_shift( data, shift ): fill = r_uint( 0xFFFFFFFF if (data >> 31) else 0 ) return (fill << (32-shift)) | (data >> shift)
def get_mstatus(self): ie = r_uint(0) prv = r_uint(self.state.prv) ie1 = r_uint(0) prv1 = r_uint(0) ie2 = r_uint(0) prv2 = r_uint(0) ie3 = r_uint(0) prv3 = r_uint(0) fs = r_uint(0) xs = r_uint(0) mprv = r_uint(0) vm = r_uint(0) sd = r_uint(0) xlen = self.state.xlen return (ie | (prv << 1) | (ie1 << 3) | (prv1 << 4) | (ie2 << 6) | (prv2 << 7) | (ie3 << 9) | (prv3 << 10) | (fs << 12) | (xs << 14) | (mprv << 16) | (vm << 17) | (sd << (xlen - 1)))
def iread(self, start_addr, num_bytes): assert start_addr & 0b11 == 0 # only aligned accesses allowed return r_uint(widen(self.data[start_addr >> 2]))
def cpsr(self): return ( r_uint( self.N ) << 31 ) | \ ( r_uint( self.Z ) << 30 ) | \ ( r_uint( self.C ) << 29 ) | \ ( r_uint( self.V ) << 28 ) | \ ( r_uint( self.mode ) )
def __init__( self, bits, str ): self.bits = r_uint( bits ) self.str = str
def get_mhartid( self ): return r_uint( 0 )
def execute_j( s, inst ): s.pc = ((s.pc + 4) & r_uint( 0xF0000000 ) ) | (inst.jtarg << 2)
def get_mstatus( self ): ie = r_uint( 0 ) prv = r_uint( self.state.prv ) ie1 = r_uint( 0 ) prv1 = r_uint( 0 ) ie2 = r_uint( 0 ) prv2 = r_uint( 0 ) ie3 = r_uint( 0 ) prv3 = r_uint( 0 ) fs = r_uint( 0 ) xs = r_uint( 0 ) mprv = r_uint( 0 ) vm = r_uint( 0 ) sd = r_uint( 0 ) xlen = self.state.xlen return ( ie | ( prv << 1 ) | ( ie1 << 3 ) | ( prv1 << 4 ) | ( ie2 << 6 ) | ( prv2 << 7 ) | ( ie3 << 9 ) | ( prv3 << 10 ) | ( fs << 12 ) | ( xs << 14 ) | ( mprv << 16 ) | ( vm << 17 ) | ( sd << (xlen-1) ) )
def __init__( self, num_regs=32, nbits=64 ): self.num_regs = num_regs self.regs = [ r_uint(0) ] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4
def arith_shift(data, shift): fill = r_uint(0xFFFFFFFF if (data >> 31) else 0) return (fill << (32 - shift)) | (data >> shift)