class Vamos: def __init__(self, machine, cfg): self.machine = machine self.mem = machine.get_mem() self.raw_mem = self.mem self.ram_size = self.mem.get_ram_size_bytes() self.cpu = machine.get_cpu() self.cpu_type = machine.get_cpu_type() self.traps = machine.get_traps() self.cfg = cfg # too much RAM requested? # our "custom chips" start at $BFxxxx so we allow RAM only to be below if self.ram_size >= 0xbf0000 and self.cfg.hw_access != "disable": raise VamosConfigError( "Too much RAM configured! Only up to $BF0000 allowed.") # setup custom chips if self.cfg.hw_access != "disable": self.hw_access = HardwareAccess(self.mem) self._setup_hw_access() # path manager self.path_mgr = PathManager(cfg) # create a label manager and error tracker self.label_mgr = machine.get_label_mgr() # set a label for first region if self.label_mgr: label = LabelRange("vbr", 0, 0x400) self.label_mgr.add_label(label) # shutdown range label = LabelRange("machine", 0x400, 0x800) self.label_mgr.add_label(label) # create memory access self.trace_mgr = TraceManager(self.cpu, self.label_mgr) if cfg.internal_memory_trace: self.mem = TraceMemory(self.mem, self.trace_mgr) if not log_mem_int.isEnabledFor(logging.INFO): log_mem_int.setLevel(logging.INFO) # enable mem trace? if cfg.memory_trace: self.machine.set_cpu_mem_trace_hook(self.trace_mgr.trace_mem) if not log_mem.isEnabledFor(logging.INFO): log_mem.setLevel(logging.INFO) # instr trace if cfg.instr_trace: if not log_instr.isEnabledFor(logging.INFO): log_instr.setLevel(logging.INFO) cpu = self.cpu trace_mgr = self.trace_mgr state = CPUState() def instr_hook(): # add register dump if cfg.reg_dump: state.get(cpu) res = state.dump() for r in res: log_instr.info(r) # disassemble line pc = cpu.r_reg(REG_PC) trace_mgr.trace_code_line(pc) self.machine.set_instr_hook(instr_hook) # create memory allocator self.mem_begin = 0x1000 self.mem_size = self.ram_size - self.mem_begin self.alloc = MemoryAlloc(self.mem, self.mem_begin, self.mem_size, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader(self.alloc, self.path_mgr) # setup lib manager profiler_cfg = self._get_profiler_config(cfg) self.exec_ctx = ExecLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr) self.dos_ctx = DosLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr, self.run_command, self.start_sub_process) self.lib_mgr = LibManager(self.machine, self.alloc, self.seg_loader, self.cfg, profiler_cfg=profiler_cfg) self.lib_mgr.add_ctx('exec.library', self.exec_ctx) self.lib_mgr.add_ctx('dos.library', self.dos_ctx) self.lib_mgr.bootstrap_exec() # no current process right now self.process = None self.proc_list = [] def _get_profiler_config(self, cfg): profile_libs = cfg.profile_libs if profile_libs: profile_libs = profile_libs.split(",") profiler_cfg = LibProfilerConfig(profiling=cfg.profile, all_libs=cfg.profile_all_libs, libs=profile_libs, add_samples=cfg.profile_samples, file=cfg.profile_file, append=cfg.profile_file_append, dump=cfg.profile_dump) return profiler_cfg def init(self, binary, arg_str, stack_size, shell, cwd): self.create_old_dos_guard() self.open_base_libs() return self.setup_main_proc(binary, arg_str, stack_size, shell, cwd) def cleanup(self, ok): self.cleanup_main_proc() self.close_base_libs() # shutdown of libmgr needs temp stack sp = self.machine.get_ram_begin() - 4 self.lib_mgr.shutdown(run_sp=sp) if ok: self.alloc.dump_orphans() # ----- system setup ----- def _setup_hw_access(self): # direct hw access cfg = self.cfg if cfg.hw_access == "emu": self.hw_access.set_mode(HardwareAccess.MODE_EMU) elif cfg.hw_access == "ignore": self.hw_access.set_mode(HardwareAccess.MODE_IGNORE) elif cfg.hw_access == "abort": self.hw_access.set_mode(HardwareAccess.MODE_ABORT) elif cfg.hw_access == "disable": pass else: raise VamosConfigError("Invalid HW Access mode: %s" % cfg.hw_access) # ----- process handling ----- def _set_this_task(self, proc): """tell exec about this process and all others referencing process from here""" self.process = proc self.exec_ctx.set_process(proc) self.exec_lib.set_this_task(proc) self.dos_ctx.set_process(proc) def get_current_process(self): return self.process def start_sub_process(self, proc): log_proc.info("start sub process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) # setup trampoline to enter sub process tr = Trampoline(self, "SubProcJump") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, proc.arg_len) tr.set_ax_l(0, proc.arg_base) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, proc.stack_size) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) new_stack = proc.stack_initial tr.set_ax_l(7, new_stack) # call code! (jmp - return value is on stack) tr.jmp(proc.prog_start) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # trap to clean up sub process resources def trap_stop_sub_process(): self.stop_sub_process() tr.final_rts(trap_stop_sub_process) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stack, return_addr) # place return address for new process self.mem.w32(new_stack, return_addr) def stop_sub_process(self): # get return value ret_code = self.cpu.r_reg(REG_D0) # pop process proc = self.proc_list.pop() log_proc.info("stop sub process: %s ret_code=%d", proc, ret_code) proc.free() # ----- overload a process for RunCommand ----- def run_command(self, start_pc, argsptr, arglen, stacksize): newstack = self.alloc.alloc_memory("shell command stack", stacksize) newstackbase = newstack.addr newstacktop = newstackbase + stacksize oldstackbase = self.process.this_task.access.r_s("pr_Task.tc_SPLower") oldstacktop = self.process.this_task.access.r_s("pr_Task.tc_SPUpper") old_stackptr = self.cpu.r_reg(REG_A7) # addr of sys call return # put stack size on top of stack self.mem.w32(newstacktop - 4, stacksize) # activate new stack new_stackptr = newstacktop - 8 self.process.this_task.access.w_s("pr_Task.tc_SPLower", newstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", newstacktop) # NOTE: the Manx fexec and BPCL mess is not (yet) setup here. # setup trampoline to enter sub process tr = Trampoline(self, "RunCommand") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, arglen) tr.set_ax_l(0, argsptr) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, stacksize) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) tr.set_ax_l(7, new_stackptr) # call code! (jmp - return value is on stack) tr.jmp(start_pc) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # keep the old input file handle input_fh = self.process.get_input() # trap to clean up sub process resources def trap_stop_run_command(): ret_code = self.cpu.r_reg(REG_D0) log_proc.info("return from RunCommand: ret_code=%d", ret_code) self.cpu.w_reg(REG_A7, old_stackptr) self.process.this_task.access.w_s("pr_Task.tc_SPLower", oldstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", oldstacktop) self.alloc.free_memory(newstack) input_fh.setbuf("") # The return code remains in d0 as is tr.final_rts(trap_stop_run_command) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stackptr, return_addr) # place return address for new process self.mem.w32(new_stackptr, return_addr) def run_shell(self, start_pc, packet, stacksize, trap_stop_handler): newstack = self.alloc.alloc_memory("shell command stack", stacksize) newstackbase = newstack.addr newstacktop = newstackbase + stacksize oldstackbase = self.process.this_task.access.r_s("pr_Task.tc_SPLower") oldstacktop = self.process.this_task.access.r_s("pr_Task.tc_SPUpper") old_stackptr = self.cpu.r_reg(REG_A7) # addr of sys call return # put stack size on top of stack self.mem.w32(newstacktop - 4, stacksize) # activate new stack new_stackptr = newstacktop - 8 self.process.this_task.access.w_s("pr_Task.tc_SPLower", newstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", newstacktop) # NOTE: the Manx fexec and BPCL mess is not (yet) setup here. # setup trampoline to enter sub process tr = Trampoline(self, "RunCommand") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d1=packet tr.set_dx_l(1, packet >> 2) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, stacksize) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) tr.set_ax_l(7, new_stackptr) # call code! (jmp - return value is on stack) tr.jmp(start_pc) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() def trap_stop_function(): ret_code = self.cpu.r_reg(REG_D0) log_proc.info("return from SystemTagList: ret_code=%d", ret_code) self.cpu.w_reg(REG_A7, old_stackptr) self.process.this_task.access.w_s("pr_Task.tc_SPLower", oldstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", oldstacktop) self.alloc.free_memory(newstack) trap_stop_handler(ret_code) tr.final_rts(trap_stop_function) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stackptr, return_addr) # place return address for new process self.mem.w32(new_stackptr, return_addr) # ----- init environment ----- def open_base_libs(self): log_main.info("open_base_libs") # open exec lib self.exec_addr = self.lib_mgr.open_lib('exec.library', 0) exec_vlib = self.lib_mgr.get_vlib_by_addr(self.exec_addr) self.exec_lib = exec_vlib.get_impl() # link exec to dos self.dos_ctx.set_exec_lib(self.exec_lib) # open dos lib self.dos_addr = self.lib_mgr.open_lib('dos.library', 0) dos_vlib = self.lib_mgr.get_vlib_by_addr(self.dos_addr) self.dos_lib = dos_vlib.get_impl() self.dos_ctx.set_dos_lib(self.dos_lib) # set exec base @4 self.machine.set_zero_mem(0, self.exec_addr) def close_base_libs(self): log_main.info("close_base_libs") # close dos self.lib_mgr.close_lib(self.dos_addr) # close exec self.lib_mgr.close_lib(self.exec_addr) def create_old_dos_guard(self): # create a guard memory for tracking invalid old dos access self.dos_guard_base = self.raw_mem.reserve_special_range() self.dos_guard_size = 0x010000 if self.label_mgr: label = LabelRange("old_dos guard", self.dos_guard_base, self.dos_guard_size) self.label_mgr.add_label(label) log_mem_init.info(label) # ----- main process ----- def setup_main_proc(self, binary, arg_str, stack_size, shell, cwd): proc = Process(self.dos_ctx, binary, arg_str, stack_size=stack_size, shell=shell, cwd=cwd) if not proc.ok: return False log_proc.info("set main process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) self.main_proc = proc return True def get_initial_sp(self): return self.main_proc.get_initial_sp() def get_initial_pc(self): return self.main_proc.get_initial_pc() def get_initial_regs(self): regs = self.main_proc.get_initial_regs() # to track old dos values regs[REG_A2] = self.dos_guard_base regs[REG_A5] = self.dos_guard_base regs[REG_A6] = self.dos_guard_base return regs def cleanup_main_proc(self): self.main_proc.free()
class SetupLibManager(object): def __init__(self, machine, mem_map, scheduler, path_mgr, lib_cfg=None, main_profiler=None): self.machine = machine self.mem_map = mem_map self.path_mgr = path_mgr self.scheduler = scheduler self.alloc = mem_map.get_alloc() self.lib_mgr_cfg = lib_cfg self.main_profiler = main_profiler # state self.seg_loader = None self.exec_ctx = None self.dos_ctx = None self.lib_mgr = None def parse_config(self, cfg): if not cfg: return True self.lib_mgr_cfg = LibMgrCfg.from_dict(cfg) return True def setup(self): # create def cfg if self.lib_mgr_cfg is None: self.lib_mgr_cfg = LibMgrCfg() # create segment loader self.seg_loader = SegmentLoader(self.alloc, self.path_mgr) # setup contexts odg_base = self.mem_map.get_old_dos_guard_base() self.exec_ctx = ExecLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr) self.dos_ctx = DosLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr, self.scheduler, odg_base) # create lib mgr self.lib_mgr = LibManager(self.machine, self.alloc, self.seg_loader, self.lib_mgr_cfg, main_profiler=self.main_profiler) self.lib_mgr.add_ctx('exec.library', self.exec_ctx) self.lib_mgr.add_ctx('dos.library', self.dos_ctx) # add all vamos libs for name in vamos_libs: cls = vamos_libs[name] self.lib_mgr.add_impl_cls(name, cls) # setup scheduler call back self.scheduler.set_cur_task_callback(self.cur_task_callback) # return lib_mgr return self.lib_mgr def cleanup(self): # shutdown of libmgr needs temp stack sp = self.machine.get_ram_begin() - 4 self.lib_mgr.shutdown(run_sp=sp) def open_base_libs(self): log_libmgr.info("opening base libs...") # first bootstrap exec self.lib_mgr.bootstrap_exec() # open exec lib self.exec_addr = self.lib_mgr.open_lib('exec.library', 0) self.exec_vlib = self.lib_mgr.get_vlib_by_addr(self.exec_addr) self.exec_impl = self.exec_vlib.get_impl() log_libmgr.info("open base lib: exec: @%06x", self.exec_addr) # link exec to dos self.dos_ctx.set_exec_lib(self.exec_impl) # open dos lib self.dos_addr = self.lib_mgr.open_lib('dos.library', 0) self.dos_vlib = self.lib_mgr.get_vlib_by_addr(self.dos_addr) self.dos_impl = self.dos_vlib.get_impl() self.dos_ctx.set_dos_lib(self.dos_impl) log_libmgr.info("open base lib: dos: @%06x", self.dos_addr) # set exec base @4 log_libmgr.debug("setting execbase @4") self.machine.set_zero_mem(0, self.exec_addr) def close_base_libs(self): log_libmgr.info("closing base libs...") # close dos self.lib_mgr.close_lib(self.dos_addr) log_libmgr.info("closed dos") # close exec self.lib_mgr.close_lib(self.exec_addr) log_libmgr.info("closed exec") def cur_task_callback(self, task): log_libmgr.info("current task: %s", task) if task: proc = task.process self.exec_ctx.set_process(proc) self.exec_impl.set_this_task(proc) self.dos_ctx.set_process(proc)
class SetupLibManager(object): def __init__(self, machine, mem_map, path_mgr, lib_cfg=None, profiler_cfg=None): self.machine = machine self.mem_map = mem_map self.path_mgr = path_mgr self.alloc = mem_map.get_alloc() self.lib_mgr_cfg = lib_cfg self.profiler_cfg = profiler_cfg # state self.seg_loader = None self.exec_ctx = None self.dos_ctx = None self.lib_mgr = None def parse_config(self, main_cfg): if not main_cfg: return True cfg = main_cfg.get_profile_dict().profile self.parse_profiler_cfg(cfg) cfg = main_cfg.get_libs_dict() self.parse_lib_mgr_cfg(cfg) return True def parse_lib_mgr_cfg(self, cfg): self.lib_mgr_cfg = LibMgrCfg.from_dict(cfg) def parse_profiler_cfg(self, cfg): names = cfg.libs.names if names: profiling = True all_libs = 'all' in names else: profiling = False all_libs = False self.profiler_cfg = LibProfilerConfig(profiling=profiling, all_libs=all_libs, libs=names, add_samples=cfg.libs.calls, file=cfg.output.file, append=cfg.output.append, dump=cfg.output.dump) def setup(self, vamos_legacy): # create def cfg if self.lib_mgr_cfg is None: self.lib_mgr_cfg = LibMgrCfg() # create segment loader self.seg_loader = SegmentLoader(self.alloc, self.path_mgr) # setup contexts self.exec_ctx = ExecLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr) self.dos_ctx = DosLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr, vamos_legacy) # create lib mgr self.lib_mgr = LibManager(self.machine, self.alloc, self.seg_loader, self.lib_mgr_cfg, profiler_cfg=self.profiler_cfg) self.lib_mgr.add_ctx('exec.library', self.exec_ctx) self.lib_mgr.add_ctx('dos.library', self.dos_ctx) # add all vamos libs for name in vamos_libs: cls = vamos_libs[name] self.lib_mgr.add_impl_cls(name, cls) # finally bootstrap exec self.lib_mgr.bootstrap_exec() # return lib_mgr return self.lib_mgr def cleanup(self): # shutdown of libmgr needs temp stack sp = self.machine.get_ram_begin() - 4 self.lib_mgr.shutdown(run_sp=sp) def open_base_libs(self): log_libmgr.info("opening base libs...") # open exec lib self.exec_addr = self.lib_mgr.open_lib('exec.library', 0) self.exec_vlib = self.lib_mgr.get_vlib_by_addr(self.exec_addr) self.exec_impl = self.exec_vlib.get_impl() log_libmgr.info("open base lib: exec: @%06x", self.exec_addr) # link exec to dos self.dos_ctx.set_exec_lib(self.exec_impl) # open dos lib self.dos_addr = self.lib_mgr.open_lib('dos.library', 0) self.dos_vlib = self.lib_mgr.get_vlib_by_addr(self.dos_addr) self.dos_impl = self.dos_vlib.get_impl() self.dos_ctx.set_dos_lib(self.dos_impl) log_libmgr.info("open base lib: dos: @%06x", self.dos_addr) # set exec base @4 log_libmgr.debug("setting execbase @4") self.machine.set_zero_mem(0, self.exec_addr) def close_base_libs(self): log_libmgr.info("closing base libs...") # close dos self.lib_mgr.close_lib(self.dos_addr) log_libmgr.info("closed dos") # close exec self.lib_mgr.close_lib(self.exec_addr) log_libmgr.info("closed exec")
class SetupLibManager(object): def __init__(self, machine, mem_map, scheduler, path_mgr, lib_cfg=None, main_profiler=None): self.machine = machine self.mem_map = mem_map self.path_mgr = path_mgr self.scheduler = scheduler self.alloc = mem_map.get_alloc() self.lib_mgr_cfg = lib_cfg self.main_profiler = main_profiler # state self.seg_loader = None self.exec_ctx = None self.dos_ctx = None self.lib_mgr = None def parse_config(self, cfg): if not cfg: return True self.lib_mgr_cfg = LibMgrCfg.from_dict(cfg) return True def setup(self): # create def cfg if self.lib_mgr_cfg is None: self.lib_mgr_cfg = LibMgrCfg() # create segment loader self.seg_loader = SegmentLoader(self.alloc, self.path_mgr) # setup contexts odg_base = self.mem_map.get_old_dos_guard_base() self.exec_ctx = ExecLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr) self.dos_ctx = DosLibCtx(self.machine, self.alloc, self.seg_loader, self.path_mgr, self.scheduler, odg_base) # create lib mgr self.lib_mgr = LibManager(self.machine, self.alloc, self.seg_loader, self.lib_mgr_cfg, main_profiler=self.main_profiler) self.lib_mgr.add_ctx('exec.library', self.exec_ctx) self.lib_mgr.add_ctx('dos.library', self.dos_ctx) # add all vamos libs for name in vamos_libs: cls = vamos_libs[name] self.lib_mgr.add_impl_cls(name, cls) # setup scheduler call back self.scheduler.set_cur_task_callback(self.cur_task_callback) # return lib_mgr return self.lib_mgr def cleanup(self): # shutdown of libmgr needs temp stack sp = self.machine.get_ram_begin() - 4 self.lib_mgr.shutdown(run_sp=sp) def open_base_libs(self): log_libmgr.info("opening base libs...") # first bootstrap exec self.lib_mgr.bootstrap_exec() # open exec lib self.exec_addr = self.lib_mgr.open_lib('exec.library', 0) self.exec_vlib = self.lib_mgr.get_vlib_by_addr(self.exec_addr) self.exec_impl = self.exec_vlib.get_impl() log_libmgr.info("open base lib: exec: @%06x", self.exec_addr) # link exec to dos self.dos_ctx.set_exec_lib(self.exec_impl) # open dos lib self.dos_addr = self.lib_mgr.open_lib('dos.library', 0) self.dos_vlib = self.lib_mgr.get_vlib_by_addr(self.dos_addr) self.dos_impl = self.dos_vlib.get_impl() self.dos_ctx.set_dos_lib(self.dos_impl) log_libmgr.info("open base lib: dos: @%06x", self.dos_addr) def close_base_libs(self): log_libmgr.info("closing base libs...") # close dos self.lib_mgr.close_lib(self.dos_addr) log_libmgr.info("closed dos") # close exec self.lib_mgr.close_lib(self.exec_addr) log_libmgr.info("closed exec") def cur_task_callback(self, task): log_libmgr.info("current task: %s", task) if task: proc = task.process self.exec_ctx.set_process(proc) self.exec_impl.set_this_task(proc) self.dos_ctx.set_process(proc)