<reponame>kevinyuan/pydgin<filename>arm/bootstrap.py<gh_stars>100-1000
#=======================================================================
# bootstrap.py
#=======================================================================

from machine        import State
from pydgin.utils   import r_uint
#from pydgin.storage import Memory

EMULATE_GEM5  = False
EMULATE_SIMIT = True

# Currently these constants are set to match gem5
memory_size = 2**27
page_size   = 8192
if EMULATE_SIMIT:
  memory_size = 0xc0000000 + 1
  MAX_ENVIRON = 1024 * 16

# MIPS stack starts at top of kuseg (0x7FFF.FFFF) and grows down
#stack_base = 0x7FFFFFFF
stack_base = memory_size-1   # TODO: set this correctly!

#-----------------------------------------------------------------------
# syscall_init
#-----------------------------------------------------------------------
#
# MIPS Memory Map (32-bit):
#
#   0xC000.0000 - Mapped            (kseg2) -   1GB
#   0xA000.0000 - Unmapped uncached (kseg1) - 512MB
#   0x8000.0000 - Unmapped cached   (kseg0) - 512MB
#   0x0000.0000 - 32-bit user space (kuseg) -   2GB
#
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

#=======================================================================
# bootstrap.py
#=======================================================================

from machine        import State
from pydgin.utils   import r_uint
#from pydgin.storage import Memory

EMULATE_GEM5  = False
EMULATE_SIMIT = True

# Currently these constants are set to match gem5
memory_size = 2**27
page_size   = 8192
if EMULATE_SIMIT:
  memory_size = 0xc0000000 + 1
  MAX_ENVIRON = 1024 * 16

# MIPS stack starts at top of kuseg (0x7FFF.FFFF) and grows down
#stack_base = 0x7FFFFFFF
stack_base = memory_size-1   # TODO: set this correctly!

#-----------------------------------------------------------------------
# syscall_init
#-----------------------------------------------------------------------
#
# MIPS Memory Map (32-bit):
#
#   0xC000.0000 - Mapped            (kseg2) -   1GB
#   0xA000.0000 - Unmapped uncached (kseg1) - 512MB
#   0x8000.0000 - Unmapped cached   (kseg0) - 512MB
#   0x0000.0000 - 32-bit user space (kuseg) -   2GB
#
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
#======================================================================= # bootstrap.py #======================================================================= #from pydgin.storage import Memory # Currently these constants are set to match gem5 # MIPS stack starts at top of kuseg (0x7FFF.FFFF) and grows down #stack_base = 0x7FFFFFFF # TODO: set this correctly! #----------------------------------------------------------------------- # syscall_init #----------------------------------------------------------------------- # # MIPS Memory Map (32-bit): # #   0xC000.0000 - Mapped            (kseg2) -   1GB #   0xA000.0000 - Unmapped uncached (kseg1) - 512MB #   0x8000.0000 - Unmapped cached   (kseg0) - 512MB #   0x0000.0000 - 32-bit user space (kuseg) -   2GB # #--------------------------------------------------------------------- # 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 # calculate sizes of sections # TODO: parameterize auxv/envp/argv calc for variable int size? # end mark nbytes (sentry) # envp_str nbytes # argv_str nbytes # padding  nbytes # auxv     nbytes # envp     nbytes # argv     nbytes # argc     nbytes # don't to auxv for simit # 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 # calculate stack pointer based on size of storage needed for args # TODO: round to nearest page size? # FIXME: this offset seems really wrong, but this is how gem5 does it! # FIXME: this is fails for GEM5's hacky offset... # utility functions # TODO properly handle endianess # write end marker to memory # write environment strings to memory # write argument strings to memory # write auxv vectors to memory # write envp pointers to memory # write argv pointers to memory # write argc to memory # write zeros to bottom of stack # TODO: why does gem5 do this? # initialize processor state #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] ) # TODO: where should this go? #state.pc         = entrypoint # initialize processor registers # ptr to func to run when program exits, disable # argument 1 reg = argv ptr addr # argument 2 reg = envp ptr addr # argument 1 reg = argc # argument 2 reg = argv ptr addr # stack pointer reg # program counter