from Crypto.Cipher import AES import struct import sys import pyemu if "noprint" in sys.argv: def print(*args): pass AHMN_D8B0010 = 0xD0B0010 AHMN_MEM0_CONFIG = 0xD0B0800 AHMN_MEM1_CONFIG = 0xD0B0804 AHMN_MEM2_CONFIG = 0xD0B0808 AHMN_RDBI_MASK = 0xD0B080C AHMN_ERROR_MASK = 0xD0B0820 AHMN_ERROR = 0xD0B0824 AHMN_D8B0840 = 0xD0B0840 AHMN_D8B0844 = 0xD0B0844 AHMN_TRANSFER_STATE = 0xD0B0850 AHMN_WORKAROUND = 0xD0B0854 AHMN_MEM0_START = 0xD0B0900 AHMN_MEM1_START = 0xD0B0A00 AHMN_MEM2_START = 0xD0B0C00 AHMN_MEM0_END = 0xD0B0980 AHMN_MEM1_END = 0xD0B0C00 AHMN_MEM2_END = 0xD0B1000 AHMN_START = 0xD0B0000 AHMN_END = 0xD0B1000 class AHMNController: def __init__(self, scheduler): self.mem0_config = 0 self.mem1_config = 0 self.mem2_config = 0 self.workaround = 0 self.mem0 = [0] * ((AHMN_MEM0_END - AHMN_MEM0_START) // 4) self.mem1 = [0] * ((AHMN_MEM1_END - AHMN_MEM1_START) // 4) self.mem2 = [0] * ((AHMN_MEM2_END - AHMN_MEM2_START) // 4) self.scheduler = scheduler def read(self, addr): if addr == AHMN_MEM0_CONFIG: return self.mem0_config elif addr == AHMN_MEM1_CONFIG: return self.mem1_config elif addr == AHMN_MEM2_CONFIG: return self.mem2_config elif addr == AHMN_TRANSFER_STATE: return 0 elif addr == AHMN_WORKAROUND: return self.workaround elif AHMN_MEM0_START <= addr < AHMN_MEM0_END: return self.mem0[(addr - AHMN_MEM0_START) // 4] elif AHMN_MEM1_START <= addr < AHMN_MEM1_END: return self.mem1[(addr - AHMN_MEM1_START) // 4] elif AHMN_MEM2_START <= addr < AHMN_MEM2_END: return self.mem2[(addr - AHMN_MEM2_START) // 4] print("AHMN READ 0x%X at %08X" %(addr, self.scheduler.pc())) value = 0 def write(self, addr, value): if addr == AHMN_D8B0010: pass elif addr == AHMN_MEM0_CONFIG: self.mem0_config = value elif addr == AHMN_MEM1_CONFIG: self.mem1_config = value elif addr == AHMN_MEM2_CONFIG: self.mem2_config = value elif addr == AHMN_RDBI_MASK: pass elif addr == AHMN_ERROR_MASK: pass elif addr == AHMN_ERROR: pass elif addr == AHMN_D8B0840: pass elif addr == AHMN_D8B0844: pass elif addr == AHMN_WORKAROUND: self.workaround = value elif AHMN_MEM0_START <= addr < AHMN_MEM0_END: self.mem0[(addr - AHMN_MEM0_START) // 4] = value ^ 0x80000000 elif AHMN_MEM1_START <= addr < AHMN_MEM1_END: self.mem1[(addr - AHMN_MEM1_START) // 4] = value ^ 0x80000000 elif AHMN_MEM2_START <= addr < AHMN_MEM2_END: self.mem2[(addr - AHMN_MEM2_START) // 4] = value ^ 0x80000000 else: print("AHMN WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) MEM_D8B4200 = 0xD0B4200 MEM_FLUSH_MASK = 0xD0B4228 MEM_EDRAM_REFRESH_CTRL = 0xD0B42CC MEM_EDRAM_REFRESH_VAL = 0xD0B42CE MEM_BLOCK_MEM0_CFG = 0xD0B4400 MEM_BLOCK_MEM1_CFG = 0xD0B4402 MEM_BLOCK_MEM2_CFG = 0xD0B4404 MEM_UNK_START = 0xD0B440E MEM_UNK_END = 0xD0B44C6 MEM_D8B44E6 = 0xD0B44E6 MEM_D8B44E8 = 0xD0B44E8 MEM_D8B44EA = 0xD0B44EA MEM_START = 0xD0B4000 MEM_END = 0xD0B4800 class MEMController: def __init__(self, scheduler): self.d8b4200 = 0 self.mem0_config = 0 self.mem1_config = 0 self.mem2_config = 0 self.scheduler = scheduler def read(self, addr): if addr == MEM_D8B4200: return self.d8b4200 elif addr == MEM_FLUSH_MASK: return 0 elif addr == MEM_BLOCK_MEM0_CFG: return self.mem0_config elif addr == MEM_BLOCK_MEM1_CFG: return self.mem1_config elif addr == MEM_BLOCK_MEM2_CFG: return self.mem2_config print("MEM READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == MEM_D8B4200: self.d8b4200 = value elif addr == MEM_FLUSH_MASK: pass elif addr == MEM_EDRAM_REFRESH_CTRL: pass elif addr == MEM_EDRAM_REFRESH_VAL: pass elif addr == MEM_BLOCK_MEM0_CFG: self.mem0_config = value elif addr == MEM_BLOCK_MEM1_CFG: self.mem1_config = value elif addr == MEM_BLOCK_MEM2_CFG: self.mem2_config = value elif MEM_UNK_START <= addr < MEM_UNK_END: pass elif addr == MEM_D8B44E6: pass elif addr == MEM_D8B44E8: pass elif addr == MEM_D8B44EA: pass else: print("MEM WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) DI2SATA_CVR = 0xD006004 DI2SATA_START = 0xD006000 DI2SATA_END = 0xD00602C class DI2SATAController: def __init__(self, scheduler): self.scheduler = scheduler def read(self, addr): if addr == DI2SATA_CVR: return 1 #No disc? print("DI2SATA READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): print("DI2SATA WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) class RTCController: def __init__(self, scheduler): self.sram = [0] * 0x10 self.data = 0 self.temp_data = 0 self.write_state = 0 self.write_offset = 0 self.on_timer = 0 self.off_timer = 0 self.control_reg0 = 0 self.control_reg1 = 0 self.scheduler = scheduler def update_data(self): self.data = self.temp_data def handle(self, value): if self.write_state: self.write_state -= 1 self.handle_write(value) elif value & 0x80000000: self.write_state = 0x10 if value == 0xA0000100 else 1 self.write_offset = value & ~0x80000000 else: self.temp_data = self.handle_read(value) def handle_read(self, offset): if offset == 0x21000000: return self.on_timer elif offset == 0x21000100: return self.off_timer elif offset == 0x21000C00: return self.control_reg0 elif offset == 0x21000D00: return self.control_reg1 else: print("RTC READ %08X at %08X" %(offset, self.scheduler.pc())) return 0 def handle_write(self, value): if self.write_offset == 0x20000100: self.sram[-self.write_state] = value elif self.write_offset == 0x21000000: self.on_timer = value & 0x3FFFFFFF elif self.write_offset == 0x21000100: self.off_timer = value & 0x3FFFFFFF elif self.write_offset == 0x21000D00: if value & 0x10000: raise NotImplementedError("RTC power off") if value & 0x100: raise NotImplementedError("RTC sleep mode") self.control_reg1 = value else: print("RTC WRITE %08X %08X at %08X" %(self.write_offset, value, self.scheduler.pc())) EXI0_CSR = 0xD006800 EXI0_CR = 0xD00680C EXI0_DATA = 0xD006810 EXI_START = 0xD006800 EXI_END = 0xD00683C class EXIController: def __init__(self, scheduler, armirq): self.rtc = RTCController(scheduler) self.scheduler = scheduler self.armirq = armirq self.csr0 = 0 self.data0 = 0 def read(self, addr): if addr == EXI0_CSR: return self.csr0 elif addr == EXI0_DATA: return self.rtc.data print("EXI READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == EXI0_CSR: self.csr0 = value elif addr == EXI0_CR: if value == 0x31: #Update in data self.rtc.update_data() self.armirq.trigger_irq_all(20) elif value == 0x35: #Send data self.rtc.handle(self.data0) self.armirq.trigger_irq_all(20) self.csr0 |= 4 else: print("EXI0_CR WRITE %08X at %08X" %(value, self.scheduler.pc())) elif addr == EXI0_DATA: self.data0 = value else: print("EXI WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) EHCI_CMD = 0 EHCI_STATUS = 4 EHCI_INTR = 8 EHCI_FRAME_INDEX = 0xC EHCI_CTRL_SEGMENT = 0x10 EHCI_PER_LIST_BASE = 0x14 EHCI_ASYNC_LIST_ADDR = 0x18 EHCI_CONFIG_FLAG = 0x40 EHCI0_START = 0xD040000 EHCI0_END = 0xD050000 EHCI1_START = 0xD120000 EHCI1_END = 0xD130000 EHCI2_START = 0xD140000 EHCI2_END = 0xD150000 class EHCIController: def __init__(self, scheduler, index): self.reset() self.scheduler = scheduler self.index = index def reset(self): self.cmd = 0 self.status = 0x1000 self.intr = 0 self.frame_index = 0 self.control_segment = 0 self.per_list_addr = 0 self.async_list_addr = 0 self.config_flag = 0 def read(self, addr): if addr == EHCI_CMD: return self.cmd elif addr == EHCI_STATUS: return self.status elif addr == EHCI_INTR: return self.intr elif addr == EHCI_FRAME_INDEX: return self.frame_index elif addr == EHCI_CTRL_SEGMENT: return self.control_segment print("EHCI(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == EHCI_CMD: if value & 2: self.reset() value &= ~2 if value & 1: self.execute() value &= ~1 self.cmd = value elif addr == EHCI_STATUS: self.status &= ~value elif addr == EHCI_INTR: self.intr = value elif addr == EHCI_FRAME_INDEX: self.frame_index = value elif addr == EHCI_PER_LIST_BASE: self.per_list_addr = value elif addr == EHCI_ASYNC_LIST_ADDR: self.async_list_addr = value elif addr == EHCI_CONFIG_FLAG: self.config_flag = value else: print("EHCI(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc())) def execute(self): print("EHCI(%i) EXECUTE at %08X" %(self.index, self.scheduler.pc())) class USBDevice: device_descriptor = b"\x12\x01\x03\x10\x01\x00\x00\x10\x7E\x05\x05\x03\x09\x99\x01\x02\x03\x01" config_descriptor = b"\x09\x02\x19\x00\x01\x01\x04\x00\x32" interface_descriptor = b"\x09\x04\x00\x00\x00\x00\x00\x00\x05" endpoint_descriptor = b"\x07\x05\x81\x00\x00\x20\x04" def __init__(self): self.data = b"" self.config_value = 0 def handle(self, data): type, request, value, index, length = \ struct.unpack("> 8 if desc_type == 1: #DEVICE self.data = self.device_descriptor elif desc_type == 2: #CONFIGURATION self.data = self.config_descriptor + self.interface_descriptor + self.endpoint_descriptor elif request == 9: print("USB SET_CONFIGURATION 0x%X" %value) self.config_value = value else: print("USB REQUEST", type, request, value, index, length) def send(self, data): print("USB DATA 0x%X" %len(data)) def receive(self, size): return self.data[:size] OHCI00_START = 0xD050000 OHCI00_END = 0xD060000 OHCI01_START = 0xD060000 OHCI01_END = 0xD070000 OHCI1_START = 0xD130000 OHCI1_END = 0xD140000 OHCI2_START = 0xD150000 OHCI2_END = 0xD160000 OHCI_REVISION = 0 OHCI_CONTROL = 4 OHCI_CMD_STATUS = 8 OHCI_INT_STATUS = 0xC OHCI_INT_ENABLE = 0x10 OHCI_INT_DISABLE = 0x14 OHCI_HCCA = 0x18 OHCI_PERIOD_CURRENT_ED = 0x1C OHCI_CONTROL_HEAD_ED = 0x20 OHCI_CONTROL_CURRENT_ED = 0x24 OHCI_BULK_HEAD_ED = 0x28 OHCI_BULK_CURRENT_ED = 0x2C OHCI_DONE_HEAD = 0x30 OHCI_FM_INTERVAL = 0x34 OHCI_FM_REMAINING = 0x38 OHCI_FM_NUMBER = 0x3C OHCI_PERIODIC_START = 0x40 OHCI_LS_THRESHOLD = 0x44 OHCI_RH_DESCRIPTOR_A = 0x48 OHCI_RH_DESCRIPTOR_B = 0x4C OHCI_RH_STATUS = 0x50 OHCI_RH_PORT_STATUS = 0x54 class OHCIController: num_ports = 1 def __init__(self, scheduler, armirq, physmem, index): self.scheduler = scheduler self.armirq = armirq self.physmem = physmem self.index = index self.reset() self.device = USBDevice() def reset(self): self.control = 0 self.int_status = 0 self.int_enable = 0 self.hcca = 0 self.done_head = 0 self.frame_interval = 0 self.periodic_start = 0 self.descriptor_a = (1 << 24) | self.num_ports self.descriptor_b = 0 self.control_head_ed = 0 self.bulk_head_ed = 0 self.port_enable = [0] * self.num_ports self.port_suspend = [0] * self.num_ports self.port_power = [0] * self.num_ports self.port_reset_change = [0] * self.num_ports self.port_enable_change = [0] * self.num_ports self.port_suspend_change = [0] * self.num_ports self.port_reset_change = [0] * self.num_ports def read(self, addr): if addr == OHCI_REVISION: return 0x10 elif addr == OHCI_CONTROL: return self.control elif addr == OHCI_CMD_STATUS: return 0 elif addr == OHCI_INT_STATUS: return self.int_status elif addr == OHCI_INT_ENABLE: return self.int_enable elif addr == OHCI_CONTROL_HEAD_ED: return self.control_head_ed elif addr == OHCI_RH_DESCRIPTOR_A: return self.descriptor_a elif addr == OHCI_RH_DESCRIPTOR_B: return self.descriptor_b elif addr == OHCI_RH_STATUS: return 0 elif OHCI_RH_PORT_STATUS <= addr < OHCI_RH_PORT_STATUS + self.num_ports * 4: i = (addr - OHCI_RH_PORT_STATUS) // 4 return 1 | (self.port_enable[i] << 1) | (self.port_suspend[i] << 2) | (self.port_power[i] << 8) | \ self.port_reset_change[i] << 20 print("OHCI(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == OHCI_CONTROL: self.control = value elif addr == OHCI_CMD_STATUS: if value & 1: self.reset() if value & 2: self.process_control() if value & 4: self.process_bulk() elif addr == OHCI_INT_STATUS: self.int_status &= ~value elif addr == OHCI_INT_ENABLE: self.int_enable |= value elif addr == OHCI_INT_DISABLE: self.int_enable &= ~value elif addr == OHCI_HCCA: self.hcca = value elif addr == OHCI_CONTROL_HEAD_ED: self.control_head_ed = value elif addr == OHCI_BULK_HEAD_ED: self.bulk_head_ed = value elif addr == OHCI_FM_INTERVAL: self.frame_interval = value elif addr == OHCI_PERIODIC_START: self.periodic_start = value elif addr == OHCI_RH_DESCRIPTOR_A: self.descriptor_a = value elif addr == OHCI_RH_DESCRIPTOR_B: self.descriptor_b = value elif addr == OHCI_RH_STATUS: if value & 1: self.port_power = [0] * self.num_ports if value & 0x10000: self.port_power = [1] * self.num_ports elif OHCI_RH_PORT_STATUS <= addr < OHCI_RH_PORT_STATUS + self.num_ports * 4: i = (addr - OHCI_RH_PORT_STATUS) // 4 if value & 1: self.port_enable[i] = 0; self.port_enable_change[i] = 1 if value & 2: self.port_enable[i] = 1; self.port_enable_change[i] = 1 if value & 4: self.port_suspend[i] = 1; self.port_suspend_change[i] = 1 if value & 8: self.port_suspend[i] = 0; self.port_suspend_change[i] = 1 if value & 0x10: self.port_reset_change[i] = 1 if value & 0x100: self.port_power[i] = 1 if value & 0x200: self.port_power[i] = 0 if value & 0x20000: self.port_enable_change[i] = 0 if value & 0x40000: self.port_suspend_change[i] = 0 if value & 0x100000: self.port_reset_change[i] = 0 else: print("OHCI(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc())) def trigger_irq(self, flag): assert self.index == 1 assert self.int_enable & flag self.int_status |= flag if self.index == 1: self.armirq.trigger_irq_all(6) def process_control(self): assert self.control & 0x10 self.process_eds(self.control_head_ed) def process_bulk(self): assert self.control & 0x20 self.process_eds(self.bulk_head_ed) def process_eds(self, current_ed): while current_ed: control, tail_td, current_td, next_ed = \ struct.unpack("> 19) & 3 if direction == 0: data = self.physmem.read(current_ptr, size) self.device.handle(data) elif direction == 1: data = self.physmem.read(current_ptr, size) self.device.send(data) elif direction == 2: data = self.device.receive(size) self.physmem.write(current_ptr, data) else: raise ValueError("OHCI(%i): DIR=RESERVED" %self.index) #Set condition code control = control & ~0xF0000000 #No error self.physmem.write(current, struct.pack("> 31 self.byte_count = (dwords[3] & 0x3FFFFF) + 1 def write(self, data): if len(data) > self.byte_count: raise RuntimeError("PRDT write overflow") self.physmem.write(self.data_addr, data) class AHCICmdList: def __init__(self, physmem, addr): self.physmem = physmem self.addr = addr data = physmem.read(addr, 0x20) prdt_count = struct.unpack_from("> 16 self.block_size = value & 0xFFF elif addr == 8: self.argument = value elif addr == 0xC: self.command = value >> 16 self.transfer_mode = value & 0xFFFF self.int_status = 3 self.error_status = 0 if self.app_cmd: self.handle_app_command(value >> 24, self.argument) else: self.handle_command(value >> 24, self.argument) self.trigger_interrupt() elif addr == 0x28: self.host_control = value & 0xFF self.power_control = (value >> 8) & 0xFF self.block_gap_control = (value >> 16) & 0xFF self.wakeup_control = value >> 24 elif addr == 0x2C: self.clock_control = value & 0xFFFF if self.clock_control & 1: self.clock_control |= 2 self.timeout_control = (value >> 16) & 0xFF elif addr == 0x30: self.int_status &= ~(value & 0xFF) self.error_status &= ~(value >> 16) else: print("SDIO(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc())) def get_card_status(self): #Ready for data return (self.state << 9) | 0x100 | (self.app_cmd << 5) def handle_app_command(self, cmd, arg): if cmd == 6: #Set bus width self.bus_width = arg & 3 self.result0 = self.get_card_status() elif cmd == 41: #Voltage range if self.type == self.TYPE_SD: self.result0 = self.voltage_range if arg & self.voltage_range: self.result0 |= 0xC0000000 #Ready to operate | SDHC/SDXC else: self.int_status |= 0x8000 else: print("SDIO(%i) ACMD %i %08X at %08X" %(self.index, cmd, arg, self.scheduler.pc())) self.app_cmd = False def handle_command(self, cmd, arg): self.result0 = 0 self.result1 = 0 self.result2 = 0 self.result3 = 0 if cmd == 0: pass #Reset elif cmd == 1: #Voltage range if self.type == self.TYPE_MMC: self.result0 = self.voltage_range if arg & self.voltage_range: self.result0 |= 0x80000000 #Ready to operate else: self.int_status |= 0x8000 elif cmd == 2: #CID register pass elif cmd == 3: self.result0 = 0x400 #Relative card address elif cmd == 5: #Voltage range if self.type == self.TYPE_SDIO: self.result0 = self.voltage_range | 0x8000000 #Memory present if arg & self.voltage_range: self.result0 |= 0x80000000 #Ready to operate else: self.int_status |= 0x8000 elif cmd == 7: #Card select self.state = self.STATE_STBY self.result0 = self.get_card_status() elif cmd == 8: #Voltage and check pattern self.result0 = arg & 0xFFF elif cmd == 9: #CSD register self.result3 = ((self.csd[0] >> 8) | (self.csd[3] << 24)) & 0xFFFFFFFF self.result2 = ((self.csd[1] >> 8) | (self.csd[0] << 24)) & 0xFFFFFFFF self.result1 = ((self.csd[2] >> 8) | (self.csd[1] << 24)) & 0xFFFFFFFF self.result0 = ((self.csd[3] >> 8) | (self.csd[2] << 24)) & 0xFFFFFFFF elif cmd == 13: #Send card status self.state = self.STATE_TRAN self.result0 = self.get_card_status() elif cmd == 16: #Set block len self.block_length = arg self.result0 = self.get_card_status() elif cmd == 17: #Read single block assert self.block_count == 1 self.file.seek(self.argument << 9) self.physmem.write(self.dma_addr, self.file.read(self.block_count * self.block_size)) elif cmd == 18: #Read multiple block self.file.seek(self.argument << 9) self.physmem.write(self.dma_addr, self.file.read(self.block_count * self.block_size)) elif cmd == 25: #Write multiple block data = self.physmem.read(self.dma_addr, self.block_count * self.block_size) self.file.seek(self.argument << 9) self.file.write(data) elif cmd == 52: #Read/write direct function = (arg >> 28) & 7 if function != 0: raise NotImplementedError("SDIO(%i) CMD52 function %i at %08X" %(self.index, function, self.scheduler.pc())) reg_addr = (arg >> 9) & 0x1FFFF if arg & 0x80000000: #Write self.result0 = arg & 0xFF self.write_register(reg_addr, self.result0) if arg & 0x8000000: self.result0 = self.read_register(reg_addr) else: self.result0 = self.read_register(reg_addr) elif cmd == 55: #App cmd self.result0 = 0x20 self.app_cmd = True else: print("SDIO(%i) CMD %i %08X at %08X" %(self.index, cmd, arg, self.scheduler.pc())) def read_register(self, addr): if addr == 7: return self.bus_width | (self.cd_disable << 7) elif addr == 9: return self.cis_pointer & 0xFF elif addr == 0xA: return (self.cis_pointer >> 8) & 0xFF elif addr == 0xB: return self.cis_pointer >> 16 elif addr == 0x13: return 1 elif addr >= 0x1000: return self.card_info[addr - 0x1000] print("SDIO(%i) READ REG 0x%X at %08X" %(self.index, addr, self.scheduler.pc())) return 0 def write_register(self, addr, value): if addr == 6: pass #Reset, i/o abort elif addr == 7: self.bus_width = value & 3 self.cd_disable = value >> 7 else: print("SDIO(%i) WRITE REG 0x%X 0x%02X at %08X" %(self.index, addr, value, self.scheduler.pc())) NAND_CTRL = 0 NAND_CONFIG = 4 NAND_ADDR1 = 8 NAND_ADDR2 = 0xC NAND_DATABUF = 0x10 NAND_ECCBUF = 0x14 class NANDBank: #0xECDC: Samsung K9XXG08XXB #0xADDC: Hynix HY27UF084G2B #0x98DC: Toshiba TC58NVG2S3ETA00 #0x01DC: Spansion Undetermined chip_id = 0xECDC def __init__(self, scheduler, armirq, physmem, slc, slcspare, slccmpt, slccmptspare): self.scheduler = scheduler self.armirq = armirq self.physmem = physmem self.slc = slc self.slcspare = slcspare self.slccmpt = slccmpt self.slccmptspare = slccmptspare self.file = self.slccmpt self.filespare = self.slccmptspare self.reset() def reset(self): self.control = 0 self.config = 0 self.addr1 = 0 self.addr2 = 0 self.databuf = 0 self.eccbuf = 0 def set_bank(self, cmpt): if cmpt: self.file = self.slccmpt self.filespare = self.slccmptspare else: self.file = self.slc self.filespare = self.slcspare def read(self, addr): if addr == NAND_CTRL: return self.control elif addr == NAND_CONFIG: return self.config elif addr == NAND_ADDR1: return self.addr1 elif addr == NAND_ADDR2: return self.addr2 elif addr == NAND_DATABUF: return self.databuf elif addr == NAND_ECCBUF: return self.eccbuf def write(self, addr, value): if addr == NAND_CTRL: self.control = self.start_command(value) elif addr == NAND_CONFIG: self.config = value elif addr == NAND_ADDR1: self.addr1 = value elif addr == NAND_ADDR2: self.addr2 = value elif addr == NAND_DATABUF: self.databuf = value elif addr == NAND_ECCBUF: self.eccbuf = value def start_command(self, value): if value & 0x80000000: command = (value >> 16) & 0xFF write = (value >> 14) & 1 read = (value >> 13) & 1 length = value & 0xFFF self.handle_command(command, write, read, length) if value & 0x40000000: self.armirq.trigger_irq_all(1) return value & ~0x80000000 else: self.reset() return 0 def handle_command(self, command, write, read, length): if command == 0: #Init read assert not read and not write and not length elif command == 0x10: #Finish write assert not read and not write and not length elif command == 0x30: #Read assert read and not write and (length == 0x840 or length == 0x40) if length == 0x840: self.file.seek((self.addr2 << 11) | self.addr1) self.physmem.write(self.databuf, self.file.read(0x800)) self.filespare.seek(self.addr2 << 6) sparedata = self.filespare.read(0x40) self.physmem.write(self.eccbuf, sparedata) self.physmem.write(self.eccbuf ^ 0x40, sparedata[0x30:]) else: self.filespare.seek(self.addr2 << 6) self.physmem.write(self.databuf, self.filespare.read(0x40)) elif command == 0x60: #Erase init 1 assert not read and not write and not length elif command == 0x70: #Erase assert read and not write and length == 0x40 self.physmem.write(self.databuf, b"\0" * length) elif command == 0x80: #Write assert not read and write and length == 0x800 data = self.physmem.read(self.databuf, length) self.file.seek((self.addr2 << 11) | self.addr1) self.file.write(data) self.next_spare = self.addr2 << 6 elif command == 0x85: #Write spare assert not read and write and length == 0x40 data = self.physmem.read(self.databuf, length) self.filespare.seek(self.next_spare) self.filespare.write(data) elif command == 0x90: #Get chip id assert read and not write and length == 0x20 self.physmem.write(self.databuf, struct.pack(">H", self.chip_id)) elif command == 0xD0: #Erase init 2 assert not read and not write and not length elif command == 0xFF: #Reset assert not read and not write and not length else: print("NAND COMMAND 0x%X %i %i 0x%X at %08X" %(command, write, read, length, self.scheduler.pc())) NAND_BANK = 0xD010018 NAND_BANK_CTRL = 0xD010030 NAND_INT_MASK = 0xD010034 NAND_MAIN_START = 0xD010000 NAND_MAIN_END = 0xD010018 NAND_BANKS_START = 0xD010040 NAND_BANKS_END = 0xD010100 NAND_START = 0xD010000 NAND_END = 0xD020000 class NANDController: def __init__(self, scheduler, armirq, physmem): self.scheduler = scheduler self.slc = open("slc_work.bin", "r+b") self.slcspare = open("slcspare_work.bin", "r+b") self.slccmpt = open("slccmpt_work.bin", "r+b") self.slccmptspare = open("slccmptspare_work.bin", "r+b") self.armirq = armirq self.physmem = physmem self.main_bank = self.create_bank() self.banks = [self.create_bank() for i in range(8)] self.reset() def create_bank(self): return NANDBank(self.scheduler, self.armirq, self.physmem, self.slc, self.slcspare, self.slccmpt, self.slccmptspare) def close(self): self.slc.close() self.slcspare.close() self.slccmpt.close() self.slccmptspare.close() def reset(self): self.main_bank.reset() for bank in self.banks: bank.reset() self.bank = 0 self.bank_control = 0 self.int_mask = 0 def read(self, addr): if NAND_MAIN_START <= addr < NAND_MAIN_END: return self.main_bank.read(addr - NAND_MAIN_START) elif addr == NAND_BANK: return self.bank elif addr == NAND_BANK_CTRL: return self.bank_control elif addr == NAND_INT_MASK: return self.int_mask elif NAND_BANKS_START <= addr < NAND_BANKS_END: return self.banks[(addr - NAND_BANKS_START) // 0x18].read((addr - NAND_BANKS_START) % 0x18) print("NAND READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if NAND_MAIN_START <= addr < NAND_MAIN_END: self.main_bank.write(addr - NAND_MAIN_START, value) elif addr == NAND_BANK: self.main_bank.set_bank(not value & 2) for bank in self.banks: bank.set_bank(not value & 2) self.bank = value elif addr == NAND_BANK_CTRL: if value & 0x80000000: banks = (value >> 16) & 0xFF assert (1 << banks) - 1 == value & 0xFF for i in range(banks): bank = self.banks[i] bank.config = bank.start_command(bank.config) self.int_mask &= ~(value & 0xFF) self.bank_control = value & ~0x80000000 self.armirq.trigger_irq_all(1) elif NAND_BANKS_START <= addr < NAND_BANKS_END: self.banks[(addr - NAND_BANKS_START) // 0x18].write((addr - NAND_BANKS_END) % 0x18, value) else: print("NAND WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) AES_CTRL = 0 AES_SRC = 4 AES_DEST = 8 AES_KEY = 0xC AES_IV = 0x10 AES_START = 0xD020000 AES_END = 0xD030000 AESS_START = 0xD180000 AESS_END = 0xD190000 class AESController: def __init__(self, scheduler, armirq, physmem, index): self.scheduler = scheduler self.armirq = armirq self.physmem = physmem self.index = index self.reset() def reset(self): self.ctrl = 0 self.src = 0 self.dest = 0 self.key = b"\0" * 16 self.iv = b"\0" * 16 def read(self, addr): if addr == AES_CTRL: return self.ctrl elif addr == AES_SRC: return self.src elif addr == AES_DEST: return self.dest print("AES READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == AES_CTRL: if value & 0x80000000: self.ctrl = (value & ~0x80000000) | 0xFFF data = self.physmem.read(self.src, ((value & 0xFFF) + 1) * 16) if value & 0x10000000: if value & 0x1000: raise NotImplementedError("AES block chain continue") cipher = AES.new(self.key, AES.MODE_CBC, self.iv) if value & 0x8000000: data = cipher.decrypt(data) else: data = cipher.encrypt(data) self.physmem.write(self.dest, data) if value & 0x40000000: self.trigger_interrupt() else: self.reset() elif addr == AES_SRC: self.src = value elif addr == AES_DEST: self.dest = value elif addr == AES_KEY: self.key = self.key[4:] + struct.pack(">I", value) elif addr == AES_IV: self.iv = self.iv[4:] + struct.pack(">I", value) else: print("AES WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) def trigger_interrupt(self): if self.index == 0: self.armirq.trigger_irq_all(2) else: self.armirq.trigger_irq_lt(8) SHA_CTRL = 0 SHA_SRC = 4 SHA_H0 = 8 SHA_H1 = 0xC SHA_H2 = 0x10 SHA_H3 = 0x14 SHA_H4 = 0x18 SHA_START = 0xD030000 SHA_END = 0xD040000 SHAS_START = 0xD190000 SHAS_END = 0xD1A0000 class SHAController: def __init__(self, scheduler, armirq, physmem, index): self.scheduler = scheduler self.armirq = armirq self.physmem = physmem self.index = index self.reset() def reset(self): self.sha1 = pyemu.SHA1() self.control = 0 self.srcaddr = 0 def read(self, addr): if addr == SHA_CTRL: return self.control elif addr == SHA_SRC: return self.srcaddr elif addr == SHA_H0: return self.sha1.h0 elif addr == SHA_H1: return self.sha1.h1 elif addr == SHA_H2: return self.sha1.h2 elif addr == SHA_H3: return self.sha1.h3 elif addr == SHA_H4: return self.sha1.h4 print("SHA READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == SHA_CTRL: if value & 0x80000000: blocks = (value & 0x3FF) + 1 for i in range(blocks): data = self.physmem.read(self.srcaddr, 0x40) self.srcaddr += 0x40 self.sha1.process_block(data) if value & 0x40000000: self.trigger_interrupt() self.control = value & ~0x80000000 else: self.reset() elif addr == SHA_SRC: self.srcaddr = value elif addr == SHA_H0: self.sha1.h0 = value elif addr == SHA_H1: self.sha1.h1 = value elif addr == SHA_H2: self.sha1.h2 = value elif addr == SHA_H3: self.sha1.h3 = value elif addr == SHA_H4: self.sha1.h4 = value else: print("SHA WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) def trigger_interrupt(self): if self.index == 0: self.armirq.trigger_irq_all(3) else: self.armirq.trigger_irq_lt(9) LATTE_START = 0xD000000 LATTE_END = 0xD001000 LT_TIMER = 0xD000010 LT_ALARM = 0xD000014 LT_AHB_WDG_CONFIG = 0xD00004C LT_AHB_DMA_STATUS = 0xD000050 LT_AHB_CPU_STATUS = 0xD000054 LT_ERROR = 0xD000058 LT_ERROR_MASK = 0xD00005C LT_MEMIRR = 0xD000060 LT_AHBPROT = 0xD000064 LT_EXICTRL = 0xD000070 LT_D800074 = 0xD000074 LT_D800088 = 0xD000088 LT_D800180 = 0xD000180 LT_COMPAT_MEMCTRL_WORKAROUND = 0xD000188 LT_BOOT0 = 0xD00018C LT_CLOCKINFO = 0xD000190 LT_RESETS_COMPAT = 0xD000194 LT_CLOCKGATE_COMPAT = 0xD000198 LT_D8001D0 = 0xD0001D0 LT_IOPOWER = 0xD0001DC LT_IOSTRENGTH_CTRL0 = 0xD0001E0 LT_IOSTRENGTH_CTRL1 = 0xD0001E4 LT_ACRCLK_STRENGTH_CTRL = 0xD0001E8 LT_OTPCMD = 0xD0001EC LT_OTPDATA = 0xD0001F0 LT_D800204 = 0xD000204 LT_ASICREV_ACR = 0xD000214 LT_INTSR_AHBALL_PPC0 = 0xD000440 LT_INTSR_AHBLT_PPC0 = 0xD000444 LT_INTMR_AHBALL_PPC0 = 0xD000448 LT_INTMR_AHBLT_PPC0 = 0xD00044C LT_INTSR_AHBALL_PPC1 = 0xD000450 LT_INTSR_AHBLT_PPC1 = 0xD000454 LT_INTMR_AHBALL_PPC1 = 0xD000458 LT_INTMR_AHBLT_PPC1 = 0xD00045C LT_INTSR_AHBALL_PPC2 = 0xD000460 LT_INTSR_AHBLT_PPC2 = 0xD000464 LT_INTMR_AHBALL_PPC2 = 0xD000468 LT_INTMR_AHBLT_PPC2 = 0xD00046C LT_INTSR_AHBALL_ARM = 0xD000470 LT_INTSR_AHBLT_ARM = 0xD000474 LT_INTMR_AHBALL_ARM = 0xD000478 LT_INTMR_AHBLT_ARM = 0xD00047C LT_INTMR_AHBALL_ARM2x = 0xD000480 LT_INTMR_AHBLT_ARM2x = 0xD000484 LT_AHB2_DMA_STATUS = 0xD0004A4 LT_AHB2_CPU_STATUS = 0xD0004A8 LT_D800500 = 0xD000500 LT_D800504 = 0xD000504 LT_OTPPROT = 0xD000510 LT_ASICREV_CCR = 0xD0005A0 LT_DEBUG = 0xD0005A4 LT_COMPAT_MEMCTRL_STATE = 0xD0005B0 LT_IOP2X = 0xD0005BC LT_IOSTRENGTH_CTRL2 = 0xD0005C8 LT_D8005CC = 0xD0005CC LT_RESETS = 0xD0005E0 LT_RESETS_AHMN = 0xD0005E4 LT_SYSPLL_CFG = 0xD0005EC LT_ABIF_CPLTL_OFFSET = 0xD000620 LT_ABIF_CPLTL_DATA = 0xD000624 LT_D800628 = 0xD000628 LT_60XE_CFG = 0xD000640 LT_D800660 = 0xD000660 LT_GPIO_START = 0xD0000C0 LT_GPIO_END = 0xD000100 LT_IPC_PPC0_START = 0xD000400 LT_IPC_PPC0_END = 0xD000410 LT_IPC_PPC1_START = 0xD000410 LT_IPC_PPC1_END = 0xD000420 LT_IPC_PPC2_START = 0xD000420 LT_IPC_PPC2_END = 0xD000430 LT_IRQ_PPC0_START = 0xD000440 LT_IRQ_PPC0_END = 0xD000450 LT_IRQ_PPC1_START = 0xD000450 LT_IRQ_PPC1_END = 0xD000460 LT_IRQ_PPC2_START = 0xD000460 LT_IRQ_PPC2_END = 0xD000470 LT_IRQ_ARM_START = 0xD000470 LT_IRQ_ARM_END = 0xD000488 LT_GPIO2_START = 0xD000520 LT_GPIO2_END = 0xD000560 BUS_CLOCK_SPEED = 248625000 TIMER_SPEED = 0x1DA36E HARDWARE_VERSION_ACR = 0x21 HARDWARE_VERSION_CCR = 0xCAFE0060 class OTPController: def __init__(self, scheduler): with open("otp.bin", "rb") as f: self.data = struct.unpack(">256I", f.read()) self.scheduler = scheduler def read(self, bank, index): return self.data[bank * 0x20 + index] class SEEPROMController: LISTEN = 0 WRITE = 1 POST_WRITE = 2 POST_POST_WRITE = 3 def __init__(self, scheduler): with open("seeprom.bin", "rb") as f: self.data = list(struct.unpack(">256H", f.read())) self.state = self.LISTEN self.scheduler = scheduler def init_transfer(self): self.index = 0 self.pin_state = 0 self.bits = 11 self.offset = 0 def update_pin(self): if self.bits: self.index = (self.index << 1) | self.pin_state self.bits -= 1 if not self.bits: if self.state == self.LISTEN: self.handle_command(self.index) elif self.state == self.WRITE: self.handle_write(self.index) elif self.state == self.POST_WRITE: self.handle_write_done() else: self.state = self.LISTEN else: self.pin_state = (self.output >> (self.output_size - 1)) & 1 self.output <<= 1 def read_value(self, index): return self.data[index] def handle_command(self, value): if self.index & ~0xC0 == 0x400: pass #Control elif self.index & 0xF00 == 0x500: #Write self.bits = 16 self.index = 0 self.offset = self.index & 0xFF self.state = self.WRITE elif self.index & 0xF00 == 0x600: #Read self.output = self.read_value(self.index & 0xFF) self.output_size = 16 else: print("SEEPROM CMD 0x%X at %08X" %(self.index, self.scheduler.pc())) def handle_write(self, value): self.data[self.offset] = value self.bits = 2 self.state = self.POST_WRITE def handle_write_done(self): self.output = 1 self.output_size = 1 self.bits = 2 self.state = self.POST_POST_WRITE PIN_DWIFI_MODE = 1 PIN_ESP10_WORKAROUND = 5 PIN_9 = 9 PIN_EEPROM_CS = 10 PIN_EEPROM_SK = 11 PIN_EEPROM_DO = 12 PIN_EEPROM_DI = 13 PIN_AV0_I2C_CLOCK = 14 PIN_AV0_I2C_DATA = 15 PIN_AV1_I2C_CLOCK = 24 PIN_AV1_I2C_DATA = 25 PIN_BLUETOOTH_MODE = 27 PIN_WIFI_MODE = 29 PIN_31 = 31 class GPIOGroup1: def __init__(self, scheduler, gpio): self.seeprom = SEEPROMController(scheduler) self.scheduler = scheduler self.gpio = gpio def read(self, espresso): return self.seeprom.pin_state << PIN_EEPROM_DI def write(self, pin, state, espresso): if pin == PIN_DWIFI_MODE: pass elif pin == PIN_ESP10_WORKAROUND: pass elif pin == PIN_9: pass elif pin == PIN_EEPROM_CS: if state == 1: self.seeprom.init_transfer() elif pin == PIN_EEPROM_SK: if state == 1: self.seeprom.update_pin() elif pin == PIN_EEPROM_DO: self.seeprom.pin_state = state elif pin == PIN_AV0_I2C_CLOCK: pass elif pin == PIN_AV0_I2C_DATA: pass elif pin == PIN_AV1_I2C_CLOCK: pass elif pin == PIN_AV1_I2C_DATA: pass elif pin == PIN_BLUETOOTH_MODE: pass elif pin == PIN_WIFI_MODE: pass elif pin == PIN_31: pass else: print("GPIO PIN WRITE %i %i at %08X" %(pin, state, self.scheduler.pc())) PIN_AV_RESET = 6 class GPIOGroup2: def __init__(self, scheduler, gpio): self.scheduler = scheduler self.gpio = gpio def read(self, espresso): return 0 def write(self, pin, state, espresso): if pin == PIN_AV_RESET: pass else: print("GPIO2 PIN WRITE %i %i at %08X" %(pin, state, self.scheduler.pc())) LT_GPIOE_OUT = 0 LT_GPIOE_DIR = 4 LT_GPIOE_IN = 8 LT_GPIOE_INTLVL = 0xC LT_GPIOE_INTFLAG = 0x10 LT_GPIOE_INTMASK = 0x14 LT_GPIOE_INMIR = 0x18 LT_GPIO_ENABLE = 0x1C LT_GPIO_OUT = 0x20 LT_GPIO_DIR = 0x24 LT_GPIO_IN = 0x28 LT_GPIO_INTLVL = 0x2C LT_GPIO_INTFLAG = 0x30 LT_GPIO_INTMASK = 0x34 LT_GPIO_INMIR = 0x38 LT_GPIO_OWNER = 0x3C class GPIOController: def __init__(self, scheduler): self.scheduler = scheduler self.gpioe_dir = 0 self.gpioe_out = 0 self.gpioe_intmask = 0 self.gpioe_intflag = 0 self.gpioe_intlvl = 0 self.gpio_dir = 0 self.gpio_enabled = 0xFFFFFFFF self.gpio_out = 0 self.gpio_intmask = 0 self.gpio_owner = 0 self.gpio_intflag = 0 self.gpio_intlvl = 0 self.group = None def set_group(self, group): self.group = group def read(self, addr): if addr == LT_GPIOE_OUT: return self.gpioe_out elif addr == LT_GPIOE_DIR: return self.gpioe_dir elif addr == LT_GPIOE_INTLVL: return self.gpioe_intlvl elif addr == LT_GPIOE_INTFLAG: return self.gpioe_intflag elif addr == LT_GPIOE_INTMASK: return self.gpioe_intmask elif addr == LT_GPIO_ENABLE: return self.gpio_enabled elif addr == LT_GPIO_OUT: return self.gpio_out elif addr == LT_GPIO_DIR: return self.gpio_dir elif addr == LT_GPIO_IN: return self.group.read(False) elif addr == LT_GPIO_INTLVL: return self.gpio_intlvl elif addr == LT_GPIO_INTFLAG: return self.gpio_intflag elif addr == LT_GPIO_INTMASK: return self.gpio_intmask elif addr == LT_GPIO_OWNER: return self.gpio_owner print("GPIO READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == LT_GPIOE_OUT: for i in range(32): if self.gpio_owner & (1 << i): if value & (1 << i) != self.gpioe_out & (1 << i): self.group.write(i, (value >> i) & 1, True) self.gpioe_out = value elif addr == LT_GPIOE_DIR: self.gpioe_dir = value elif addr == LT_GPIOE_INTLVL: self.gpioe_intlvl = value elif addr == LT_GPIOE_INTFLAG: self.gpioe_intflag &= ~value elif addr == LT_GPIOE_INTMASK: self.gpioe_intmask = value elif addr == LT_GPIO_ENABLE: self.gpio_enabled = value elif addr == LT_GPIO_OUT: for i in range(32): if value & (1 << i) != self.gpio_out & (1 << i): self.group.write(i, (value >> i) & 1, False) self.gpio_out = value elif addr == LT_GPIO_DIR: self.gpio_dir = value elif addr == LT_GPIO_INTLVL: self.gpio_intlvl = value elif addr == LT_GPIO_INTFLAG: self.gpio_intflag &= ~value elif addr == LT_GPIO_INTMASK: self.gpio_intmask = value elif addr == LT_GPIO_OWNER: self.gpio_owner = value else: print("GPIO WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) def trigger_interrupt(self, type, espresso): if espresso: self.gpioe_intflag |= 1 << type else: self.gpio_intflag |= 1 << type def check_interrupts_ppc(self): return self.gpioe_intflag & self.gpioe_intmask def check_interrupts_arm(self): return self.gpio_intflag & self.gpio_intmask I2C_CLOCK = 0 I2C_WRITE_DATA = 1 I2C_WRITE_CTRL = 2 I2C_READ_DATA = 3 I2C_INT_MASK = 4 I2C_INT_STATE = 5 LT_I2C_REGS = { 0xD000570: I2C_CLOCK, 0xD000574: I2C_WRITE_DATA, 0xD000578: I2C_WRITE_CTRL, 0xD00057C: I2C_READ_DATA, 0xD000580: I2C_INT_MASK, 0xD000584: I2C_INT_STATE } LT_I2C_REGS_PPC = { 0xD000068: I2C_INT_MASK, 0xD00006C: I2C_INT_STATE, 0xD000250: I2C_CLOCK, 0xD000254: I2C_WRITE_DATA, 0xD000258: I2C_WRITE_CTRL, 0xD00025C: I2C_READ_DATA } class I2CController: def __init__(self, scheduler, gpio2, espresso): self.scheduler = scheduler self.gpio2 = gpio2 self.espresso = espresso self.readbuf = b"" self.readoffs = 0 self.readsize = False self.writebuf = b"" self.writeval = 0 self.offset = 0 self.clock = 0 self.int_mask = 0 self.int_state = 0 self.readint = 5 if espresso else 0 self.writeint = 6 if espresso else 1 self.av_int_mask = 0 self.av_int_info = [0] * 6 def read(self, addr): if addr == I2C_CLOCK: return self.clock elif addr == I2C_WRITE_DATA: return self.writeval elif addr == I2C_READ_DATA: if self.readsize: self.readsize = False return len(self.readbuf) << 16 self.readoffs += 1 return self.readbuf[self.readoffs] elif addr == I2C_WRITE_CTRL: return 0 elif addr == I2C_INT_MASK: return self.int_mask elif addr == I2C_INT_STATE: return self.int_state raise RuntimeError("Read from I2C register %i" %addr) def write(self, addr, value): if addr == I2C_CLOCK: self.clock = value elif addr == I2C_WRITE_DATA: self.writeval = value elif addr == I2C_WRITE_CTRL: if value & 1: self.writebuf += bytes([self.writeval & 0xFF]) if self.writeval & 0x100: self.handle_data(self.writebuf) self.writebuf = b"" elif addr == I2C_INT_MASK: self.int_mask = value elif addr == I2C_INT_STATE: self.int_state &= ~value else: raise RuntimeError("Write to I2C register %i" %addr) def handle_data(self, data): slave = data[0] >> 1 read = data[0] & 1 if read: self.readbuf = self.read_data(slave, self.offset, len(data) - 1) self.readoffs = -1 self.readsize = not self.espresso #This is weird self.trigger_interrupt(self.readint) else: if len(data) > 2: self.write_data(slave, data[1], data[2:]) self.offset = data[1] self.trigger_interrupt(self.writeint) def read_data(self, slave, offset, length): if slave == 0x38 and offset == 0x90: return bytes([self.av_int_mask]) elif slave == 0x38 and 0x91 <= offset <= 0x97: return bytes([self.av_int_info[offset - 0x91]]) print("I2C READ 0x%X 0x%X %i" %(slave, offset, length)) return bytes(length) def write_data(self, slave, offset, data): if slave == 0x3D and offset == 0x89: self.av_int_mask |= 0x10 self.av_int_info[4] = 0 self.gpio2.trigger_interrupt(4, True) else: print("I2C WRITE 0x%X 0x%X %s" %(slave, offset, data.hex())) def trigger_interrupt(self, type): self.int_state |= 1 << type def check_interrupts(self): return self.int_state & self.int_mask class ASICBusController: def __init__(self, scheduler): self.scheduler = scheduler self.pll_data = [0xC, 0x800, 0x1C2, 7, 0, 0, 0x100, 0x40, 0xC800] self.usbpll_data = [0x20, 3, 0x1200, 0x3F, 0xC120] self.gfxpll_data = [0x1200, 0x20, 0xA, 0x800, 0xD, 0x800, 0x81C2, 0, 0x4002, 0] self.satapll_data = [0] * 9 def set_offset(self, offset): self.offset = offset def get_data(self): if 0x3000010 <= self.offset < 0x3000022: return self.pll_data[(self.offset - 0x3000010) // 2] elif 0x4000024 <= self.offset < 0x400002E: return self.usbpll_data[(self.offset - 0x4000024) // 2] elif 0x878 <= self.offset < 0x88C: return self.gfxpll_data[(self.offset - 0x878) // 2] elif self.offset == 0x1000000: return 0x54 elif 0x4000010 <= self.offset < 0x4000022: return self.satapll_data[(self.offset - 0x4000010) // 2] elif self.offset >> 24 == 0xC0: return 0 print("ASIC BIF READ 0x%08X at %08X" %(self.offset, self.scheduler.pc())) return 0 def write(self, value): if 0x3000010 <= self.offset < 0x3000022: self.pll_data[(self.offset - 0x3000010) // 2] = value elif 0x4000024 <= self.offset < 0x400002E: self.usbpll_data[(self.offset - 0x4000024) // 2] = value elif 0x4000010 <= self.offset < 0x4000022: self.satapll_data[(self.offset - 0x4000010) // 2] = value elif self.offset >> 24 == 0xC0: pass else: print("ASIC BIF WRITE 0x%08X %08X at %08X" %(self.offset, value, self.scheduler.pc())) LT_IPC_PPCMSG = 0 LT_IPC_PPCCTRL = 4 LT_IPC_ARMMSG = 8 LT_IPC_ARMCTRL = 0xC class IPCController: def __init__(self, scheduler): self.scheduler = scheduler self.ppcmsg = 0 self.armmsg = 0 self.x1 = self.x2 = 0 self.y1 = self.y2 = 0 self.ix1 = self.ix2 = 0 self.iy1 = self.iy2 = 0 def read(self, addr): if addr == LT_IPC_PPCMSG: return self.ppcmsg elif addr == LT_IPC_ARMMSG: return self.armmsg elif addr == LT_IPC_PPCCTRL: return self.x1 | (self.y2 << 1) | (self.y1 << 2) | \ (self.x2 << 3) | (self.iy1 << 4) | (self.iy2 << 5) elif addr == LT_IPC_ARMCTRL: return self.y1 | (self.x2 << 1) | (self.x1 << 2) | \ (self.y2 << 3) | (self.ix1 << 4) | (self.ix2 << 5) def write(self, addr, value): if addr == LT_IPC_PPCMSG: self.ppcmsg = value elif addr == LT_IPC_ARMMSG: self.armmsg = value elif addr == LT_IPC_PPCCTRL: if value & 1: self.x1 = 1 if value & 2: self.y2 = 0 if value & 4: self.y1 = 0 if value & 8: self.x2 = 1 self.iy1 = (value >> 4) & 1 self.iy2 = (value >> 5) & 1 elif addr == LT_IPC_ARMCTRL: if value & 1: self.y1 = 1 if value & 2: self.x2 = 0 if value & 4: self.x1 = 0 if value & 8: self.y2 = 1 self.ix1 = (value >> 4) & 1 self.ix2 = (value >> 5) & 1 def check_interrupts_ppc(self): return (self.y1 and self.iy1) or (self.y2 and self.iy2) def check_interrupts_arm(self): return (self.x1 and self.ix1) or (self.x2 and self.ix2) LT_INTSR_AHBALL = 0 LT_INTSR_AHBLT = 4 LT_INTMR_AHBALL = 8 LT_INTMR_AHBLT = 0xC LT_INTMR_AHBALL_2X = 0x10 LT_INTMR_AHBLT_2X = 0x14 class IRQController: def __init__(self): self.intsr_ahball = 0 self.intsr_ahblt = 0 self.intmr_ahball = 0 self.intmr_ahblt = 0 def read(self, addr): if addr == LT_INTSR_AHBALL: return self.intsr_ahball elif addr == LT_INTSR_AHBLT: return self.intsr_ahblt elif addr == LT_INTMR_AHBALL: return self.intmr_ahball elif addr == LT_INTMR_AHBLT: return self.intmr_ahblt def write(self, addr, value): if addr == LT_INTSR_AHBALL: self.intsr_ahball &= ~value elif addr == LT_INTSR_AHBLT: self.intsr_ahblt &= ~value elif addr == LT_INTMR_AHBALL: self.intmr_ahball = value elif addr == LT_INTMR_AHBLT: self.intmr_ahblt = value def trigger_irq_all(self, type): self.intsr_ahball |= (1 << type) def trigger_irq_lt(self, type): self.intsr_ahblt |= (1 << type) def is_triggered_all(self, type): return self.intsr_ahball & self.intmr_ahball & (1 << type) def is_triggered_lt(self, type): return self.intsr_ahblt & self.intmr_ahblt & (1 << type) def check_interrupts(self): self.update_interrupts() return self.intsr_ahball & self.intmr_ahball or self.intsr_ahblt & self.intmr_ahblt def update_interrupts(self): pass class IRQControllerPPC(IRQController): def __init__(self, ipc, gpio, gpio2, i2c, index): super().__init__() self.ipc = ipc self.gpio = gpio self.gpio2 = gpio2 self.i2c = i2c self.index = index def update_interrupts(self): if self.ipc.check_interrupts_ppc(): self.trigger_irq_lt(30 - 2 * self.index) if self.gpio.check_interrupts_ppc() or self.gpio2.check_interrupts_ppc(): self.trigger_irq_all(10) if self.i2c.check_interrupts(): self.trigger_irq_lt(13) class IRQControllerARM(IRQController): def __init__(self, ipc, gpio, gpio2, i2c): super().__init__() self.intmr_ahball_2x = 0 self.intmr_ahblt_2x = 0 self.ipc = ipc self.gpio = gpio self.gpio2 = gpio2 self.i2c = i2c def read(self, addr): if addr == LT_INTMR_AHBALL_2X: return self.intmr_ahball_2x elif addr == LT_INTMR_AHBLT_2X: return self.intmr_ahblt_2x return super().read(addr) def write(self, addr, value): if addr == LT_INTMR_AHBALL_2X: self.intmr_ahball_2x = value elif addr == LT_INTMR_AHBLT_2X: self.intmr_ahblt_2x = value else: super().write(addr, value) def update_interrupts(self): for i in range(3): if self.ipc[i].check_interrupts_arm(): self.trigger_irq_lt(31 - 2 * i) if self.gpio.check_interrupts_arm() or self.gpio2.check_interrupts_arm(): self.trigger_irq_all(11) if self.i2c.check_interrupts(): self.trigger_irq_lt(14) class LatteController: def __init__(self, scheduler, debug=False): self.ipc = [IPCController(scheduler) for i in range(3)] self.gpio = GPIOController(scheduler) self.gpio.set_group(GPIOGroup1(scheduler, self.gpio)) self.gpio2 = GPIOController(scheduler) self.gpio2.set_group(GPIOGroup2(scheduler, self.gpio2)) self.i2c = I2CController(scheduler, self.gpio2, False) self.i2c_ppc = I2CController(scheduler, self.gpio2, True) self.irq_ppc = [IRQControllerPPC(self.ipc[i], self.gpio, self.gpio2, self.i2c_ppc, i) for i in range(3)] self.irq_arm = IRQControllerARM(self.ipc, self.gpio, self.gpio2, self.i2c) self.otp = OTPController(scheduler) self.asicbus = ASICBusController(scheduler) self.timer = 0 self.alarm = 0 self.ahb_wdg_config = 0 self.error = 0 self.error_mask = 0 self.memirr = 0 self.ahbprot = 0 self.exi_ctrl = 0 self.d800074 = 0 self.d800180 = 0 self.compat_memctrl_workaround = 0 self.boot0 = 0 self.clockinfo = 0 self.resets_compat = 0 self.clockgate_compat = 0 self.d8001d0 = 0 self.iopower = 0 self.iostrength_ctrl0 = 0 self.iostrength_ctrl1 = 0 self.acrclk_strength_ctrl = 0 self.otpcmd = 0 self.otpdata = 0 self.d800204 = 0 self.d800500 = 0 self.d800504 = 0 self.debug = 0x80000000 * debug + 0x20000000 self.compat_memctrl_state = 0 self.iop2x = 0 self.iostrength_ctrl2 = 0 self.d8005cc = 0 self.resets = 0 self.resets_ahmn = 0 self.syspll_cfg = 0 self.d800628 = 0 self.cfg_60xe = 0 self.scheduler = scheduler def read(self, addr): if addr == LT_TIMER: return self.timer elif LT_IRQ_PPC0_START <= addr < LT_IRQ_PPC0_END: return self.irq_ppc[0].read(addr - LT_IRQ_PPC0_START) elif LT_IRQ_PPC1_START <= addr < LT_IRQ_PPC1_END: return self.irq_ppc[1].read(addr - LT_IRQ_PPC1_START) elif LT_IRQ_PPC2_START <= addr < LT_IRQ_PPC2_END: return self.irq_ppc[2].read(addr - LT_IRQ_PPC2_START) elif LT_IRQ_ARM_START <= addr < LT_IRQ_ARM_END: return self.irq_arm.read(addr - LT_IRQ_ARM_START) elif LT_IPC_PPC0_START <= addr < LT_IPC_PPC0_END: return self.ipc[0].read(addr - LT_IPC_PPC0_START) elif LT_IPC_PPC1_START <= addr < LT_IPC_PPC1_END: return self.ipc[1].read(addr - LT_IPC_PPC1_START) elif LT_IPC_PPC2_START <= addr < LT_IPC_PPC2_END: return self.ipc[2].read(addr - LT_IPC_PPC2_START) elif addr == LT_AHB_WDG_CONFIG: return self.ahb_wdg_config elif addr == LT_ERROR: return self.error elif addr == LT_ERROR_MASK: return self.error_mask elif addr == LT_MEMIRR: return self.memirr elif addr == LT_AHBPROT: return self.ahbprot elif addr == LT_EXICTRL: return self.exi_ctrl elif addr == LT_D800074: return self.d800074 elif LT_GPIO_START <= addr < LT_GPIO_END: return self.gpio.read(addr - LT_GPIO_START) elif addr == LT_D800180: return self.d800180 elif addr == LT_COMPAT_MEMCTRL_WORKAROUND: return self.compat_memctrl_workaround elif addr == LT_BOOT0: return self.boot0 elif addr == LT_CLOCKINFO: return self.clockinfo elif addr == LT_RESETS_COMPAT: return self.resets_compat elif addr == LT_CLOCKGATE_COMPAT: return self.clockgate_compat elif addr == LT_D8001D0: return self.d8001d0 elif addr == LT_IOPOWER: return self.iopower elif addr == LT_IOSTRENGTH_CTRL0: return self.iostrength_ctrl0 elif addr == LT_IOSTRENGTH_CTRL1: return self.iostrength_ctrl1 elif addr == LT_ACRCLK_STRENGTH_CTRL: return self.acrclk_strength_ctrl elif addr == LT_OTPCMD: return self.otpcmd elif addr == LT_OTPDATA: return self.otpdata elif addr == LT_D800204: return self.d800204 elif addr == LT_ASICREV_ACR: return HARDWARE_VERSION_ACR elif addr == LT_D800500: return self.d800500 elif addr == LT_D800504: return self.d800504 elif LT_GPIO2_START <= addr < LT_GPIO2_END: return self.gpio2.read(addr - LT_GPIO2_START) elif addr == LT_ASICREV_CCR: return HARDWARE_VERSION_CCR elif addr == LT_DEBUG: return self.debug elif addr == LT_COMPAT_MEMCTRL_STATE: return self.compat_memctrl_state elif addr == LT_IOP2X: return self.iop2x elif addr == LT_IOSTRENGTH_CTRL2: return self.iostrength_ctrl2 elif addr == LT_D8005CC: return self.d8005cc elif addr == LT_RESETS: return self.resets elif addr == LT_RESETS_AHMN: return self.resets_ahmn elif addr == LT_SYSPLL_CFG: return self.syspll_cfg elif addr == LT_ABIF_CPLTL_DATA: return self.asicbus.get_data() elif addr == LT_D800628: return self.d800628 elif addr == LT_60XE_CFG: return self.cfg_60xe elif addr in LT_I2C_REGS: return self.i2c.read(LT_I2C_REGS[addr]) elif addr in LT_I2C_REGS_PPC: return self.i2c_ppc.read(LT_I2C_REGS_PPC[addr]) print("LATTE READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == LT_TIMER: self.timer = value elif addr == LT_ALARM: self.alarm = value elif addr == LT_AHB_WDG_CONFIG: self.ahb_wdg_config = value elif addr == LT_AHB_DMA_STATUS: pass elif addr == LT_AHB_CPU_STATUS: pass elif addr == LT_ERROR: self.error = value & self.error_mask elif addr == LT_ERROR_MASK: self.error_mask = value elif addr == LT_MEMIRR: self.memirr = value elif addr == LT_AHBPROT: self.ahbprot = value elif addr == LT_EXICTRL: self.exi_ctrl = value elif addr == LT_D800074: self.d800074 = value elif addr == LT_D800088: pass elif LT_GPIO_START <= addr < LT_GPIO_END: self.gpio.write(addr - LT_GPIO_START, value) elif addr == LT_D800180: self.d800180 = value elif addr == LT_COMPAT_MEMCTRL_WORKAROUND: self.compat_memctrl_workaround = value elif addr == LT_BOOT0: self.boot0 = value elif addr == LT_RESETS_COMPAT: self.resets_compat = value elif addr == LT_CLOCKGATE_COMPAT: self.clockgate_compat = value elif addr == LT_D8001D0: self.d8001d0 = value elif addr == LT_IOPOWER: self.iopower = value elif addr == LT_IOSTRENGTH_CTRL0: self.iostrength_ctrl0 = value elif addr == LT_IOSTRENGTH_CTRL1: self.iostrength_ctrl1 = value elif addr == LT_ACRCLK_STRENGTH_CTRL: self.acrclk_strength_ctrl = value elif addr == LT_OTPCMD: self.otpcmd = value if value & 0x80000000: self.otpdata = self.otp.read((value >> 8) & 7, value & 0x1F) elif addr == LT_D800204: self.d800204 = value elif LT_IPC_PPC0_START <= addr < LT_IPC_PPC0_END: self.ipc[0].write(addr - LT_IPC_PPC0_START, value) elif LT_IPC_PPC1_START <= addr < LT_IPC_PPC1_END: self.ipc[1].write(addr - LT_IPC_PPC1_START, value) elif LT_IPC_PPC2_START <= addr < LT_IPC_PPC2_END: self.ipc[2].write(addr - LT_IPC_PPC2_START, value) elif LT_IRQ_PPC0_START <= addr < LT_IRQ_PPC0_END: self.irq_ppc[0].write(addr - LT_IRQ_PPC0_START, value) elif LT_IRQ_PPC1_START <= addr < LT_IRQ_PPC1_END: self.irq_ppc[1].write(addr - LT_IRQ_PPC1_START, value) elif LT_IRQ_PPC2_START <= addr < LT_IRQ_PPC2_END: self.irq_ppc[2].write(addr - LT_IRQ_PPC2_START, value) elif LT_IRQ_ARM_START <= addr < LT_IRQ_ARM_END: self.irq_arm.write(addr - LT_IRQ_ARM_START, value) elif addr == LT_AHB2_DMA_STATUS: pass elif addr == LT_AHB2_CPU_STATUS: pass elif addr == LT_D800500: self.d800500 = value elif addr == LT_D800504: self.d800504 = value elif addr == LT_OTPPROT: pass elif LT_GPIO2_START <= addr < LT_GPIO2_END: self.gpio2.write(addr - LT_GPIO2_START, value) elif addr == LT_DEBUG: self.debug = value elif addr == LT_COMPAT_MEMCTRL_STATE: self.compat_memctrl_state = value elif addr == LT_IOP2X: self.iop2x = value | 4 self.irq_arm.trigger_irq_lt(12) elif addr == LT_IOSTRENGTH_CTRL2: self.iostrength_ctrl2 = value elif addr == LT_D8005CC: self.d8005cc = value elif addr == LT_RESETS: self.resets = value elif addr == LT_RESETS_AHMN: self.resets_ahmn = value elif addr == LT_SYSPLL_CFG: self.syspll_cfg = value elif addr == LT_ABIF_CPLTL_OFFSET: self.asicbus.set_offset(value) elif addr == LT_ABIF_CPLTL_DATA: self.asicbus.write(value) elif addr == LT_D800628: self.d800628 = value elif addr == LT_60XE_CFG: self.cfg_60xe = value elif addr == LT_D800660: pass elif addr in LT_I2C_REGS: self.i2c.write(LT_I2C_REGS[addr], value) elif addr in LT_I2C_REGS_PPC: self.i2c_ppc.write(LT_I2C_REGS_PPC[addr], value) else: print("LATTE WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) def update_timer(self): start = self.timer end = self.timer + 400 if end >= 0x100000000: if self.alarm > start or self.alarm <= end: self.irq_arm.trigger_irq_all(0) end &= 0xFFFFFFFF else: if start < self.alarm <= end: self.irq_arm.trigger_irq_all(0) self.timer = end PI_CPU0_START = 0xC000078 PI_CPU0_END = 0xC000080 PI_CPU1_START = 0xC000080 PI_CPU1_END = 0xC000088 PI_CPU2_START = 0xC000088 PI_CPU2_END = 0xC000090 PI_INTSR = 0 PI_INTMR = 4 class PIController: def __init__(self, scheduler, irq, gpu, dsp, index): self.scheduler = scheduler self.irq = irq self.gpu = gpu self.dsp = dsp self.index = index self.intsr = 0 self.intmr = 0 def read(self, addr): if addr == PI_INTSR: return self.intsr elif addr == PI_INTMR: return self.intmr print("PI(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == PI_INTSR: self.intsr &= ~value elif addr == PI_INTMR: self.intmr = value else: print("PI(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc())) def trigger_irq(self, type): self.intsr |= 1 << type def check_interrupts(self): self.update_interrupts() return self.intsr & self.intmr def update_interrupts(self): if self.irq.check_interrupts(): self.trigger_irq(24) if self.gpu.check_interrupts(): self.trigger_irq(23) if self.dsp.check_interrupts(): self.trigger_irq(6) PAD_START = 0xC1E0000 PAD_END = 0xC200000 class PADController: def __init__(self, scheduler): self.scheduler = scheduler def read(self, addr): print("PAD READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): print("PAD WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) GPU_START = 0xC200000 GPU_END = 0xC300000 DC0_START = 0xC206000 DC0_END = 0xC206800 DC1_START = 0xC206800 DC1_END = 0xC207000 #From dc.rpl DC_70 = 0x70 DC_9C = 0x9C DC_INT_MASK = 0xDC DC_4A0 = 0x4A0 class DCController: def __init__(self, scheduler): self.scheduler = scheduler self.int_mask = 0 def read(self, addr): if addr == DC_70: return 0x10000 elif addr == DC_9C: return 1 elif addr == DC_4A0: return 2 print("DC READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == DC_INT_MASK: self.int_mask = value else: print("DC WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) class PM4Processor: STATE_NEXT = 0 STATE_BODY = 1 def __init__(self, scheduler, physmem): self.scheduler = scheduler self.physmem = physmem self.state = self.STATE_NEXT self.count = 0 self.body = [] self.opcode_handlers = { 0x3C: self.opcode_wait_reg_mem, 0x3D: self.opcode_mem_write, 0x43: self.opcode_surface_sync, 0x68: self.opcode_set_config_reg, 0x69: self.opcode_set_context_reg } def write(self, value): if self.state == self.STATE_NEXT: type = value >> 30 if type == 1: raise RuntimeError("Invalid PM4 packet type: 1") elif type == 2: pass #Filler packet else: self.body = [value] self.count = ((value >> 16) & 0x3FFF) + 1 self.state = self.STATE_BODY elif self.state == self.STATE_BODY: self.body.append(value) self.count -= 1 if self.count == 0: self.process() self.state = self.STATE_NEXT def process(self): type = self.body[0] >> 30 if type == 0: base_index = self.body[0] & 0xFFFF print("PM4 packet type 0:%04X at %08X" %(base_index, self.scheduler.pc())) else: opcode = (self.body[0] >> 8) & 0xFF if opcode in self.opcode_handlers: self.opcode_handlers[opcode]() else: print("PM4 packet type 3:%02X at %08X" %(opcode, self.scheduler.pc())) def print_opcode(self, opcode): args = ", ".join(["%08X" %x for x in self.body[1:]]) print("PM4: %s(%s) at %08X" %(opcode, args, self.scheduler.pc())) def opcode_wait_reg_mem(self): self.print_opcode("WAIT_REG_MEM") def opcode_surface_sync(self): self.print_opcode("SURFACE_SYNC") def opcode_set_config_reg(self): self.print_opcode("SET_CONFIG_REG") def opcode_set_context_reg(self): self.print_opcode("SET_CONTEXT_REG") def opcode_mem_write(self): addr = self.body[1] & ~3 if self.body[2] & 0x40000: self.physmem.write(addr, struct.pack(">I", self.body[3])) else: self.physmem.write(addr, struct.pack(">II", self.body[4], self.body[3])) DRMDMA_RB_CNTL = 0xC20D000 DRMDMA_RB_BASE = 0xC20D004 DRMDMA_RB_RPTR = 0xC20D008 DRMDMA_RB_WPTR = 0xC20D00C DRMDMA_RB_WPTR_POLL_CNTL = 0xC20D010 DRMDMA_RB_WPTR_POLL_ADDR_HI = 0xC20D014 DRMDMA_RB_WPTR_POLL_ADDR_LO = 0xC20D018 DRMDMA_RB_RPTR_ADDR_HI = 0xC20D01C DRMDMA_RB_RPTR_ADDR_LO = 0xC20D020 DRMDMA_IB_CNTL = 0xC20D024 DRMDMA_IB_RPTR = 0xC20D028 DRMDMA_CNTL = 0xC20D02C DRMDMA_STATUS = 0xC20D034 DRMDMA_IB_OFFSET = 0xC20D04C DRMDMA_IB_BASE_LO = 0xC20D050 DRMDMA_IB_BASE_HI = 0xC20D054 DRMDMA_IB_SIZE = 0xC20D058 DRMDMA_FAULT_ADDR_LO = 0xC20D068 DRMDMA_FAULT_ADDR_HI = 0xC20D06C DRMDMA_START = 0xC20D000 DRMDMA_END = 0xC20D800 class DMAController: def __init__(self, scheduler, physmem, gpu): self.scheduler = scheduler self.physmem = physmem self.gpu = gpu self.rb_cntl = 0 self.rb_base = 0 self.rb_rptr = 0 self.rb_wptr = 0 self.rb_rptr_addr = 0 def read(self, addr): if addr == DRMDMA_RB_CNTL: return self.rb_cntl elif addr == DRMDMA_RB_BASE: return self.rb_base elif addr == DRMDMA_RB_RPTR: return self.rb_rptr elif addr == DRMDMA_RB_WPTR: return self.rb_wptr elif addr == DRMDMA_STATUS: return 1 print("DMA READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == DRMDMA_RB_CNTL: self.rb_cntl = value elif addr == DRMDMA_RB_BASE: self.rb_base = value elif addr == DRMDMA_RB_RPTR: self.rb_rptr = value elif addr == DRMDMA_RB_WPTR: self.rb_wptr = value elif addr == DRMDMA_RB_RPTR_ADDR_HI: self.rb_rptr_addr = (self.rb_rptr_addr & 0xFFFFFFFF) | (value << 32) elif addr == DRMDMA_RB_RPTR_ADDR_LO: self.rb_rptr_addr = (self.rb_rptr_addr & ~0xFFFFFFFF) | value else: print("DMA WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) return 0 def update(self): self.state = 0 self.ptr = 0 if self.rb_rptr != self.rb_wptr: self.rb_rptr = self.rb_wptr self.physmem.write(self.rb_rptr_addr, struct.pack(">I", self.rb_rptr)) self.physmem.write(self.rb_rptr_addr - 0x20, struct.pack(">I", self.rb_rptr)) self.gpu.trigger_interrupt(0xE0) #Interrupt handling GPU_IH_RB_BASE = 0xC203E04 GPU_IH_RB_RPTR = 0xC203E08 GPU_IH_RB_WPTR_ADDR_LO = 0xC203E14 GPU_IH_CONTROL = 0xC203E18 GPU_IH_STATUS = 0xC203E20 #RLC GPU_RLC_UCODE_ADDR = 0xC203F2C GPU_RLC_UCODE_DATA = 0xC203F30 GPU_CP_RESET = 0xC208020 GPU_FLUSH = 0xC208500 #Command processor GPU_CP_RB_BASE = 0xC20C100 GPU_CP_RB_RPTR_ADDR = 0xC20C10C GPU_CP_RB_WPTR = 0xC20C114 GPU_CP_PFP_UCODE_ADDR = 0xC20C150 GPU_CP_PFP_UCODE_DATA = 0xC20C154 GPU_CP_ME_RAM_WADDR = 0xC20C15C GPU_CP_ME_RAM_DATA = 0xC20C160 GPU_PM4_DATA = 0xC2C0000 class GPUController: def __init__(self, scheduler, physmem): self.scheduler = scheduler self.scheduler.add_alarm(10000, self.trigger_vsync) self.physmem = physmem self.pm4 = PM4Processor(scheduler, physmem) self.dc0 = DCController(scheduler) self.dc1 = DCController(scheduler) self.dma = DMAController(scheduler, physmem, self) self.ih_rb_base = 0 self.ih_rb_rptr = 0 self.ih_rb_wptr_addr = 0 self.ih_control = 0 self.rlc_ucode_data = [0] * 0x400 self.rlc_ucode_addr = 0 self.cp_rb_base = 0 self.cp_rb_rptr_addr = 0 self.cp_rb_wptr = 0 self.cp_pfp_ucode_data = [0] * 0x350 self.cp_pfp_ucode_addr = 0 self.cp_me_ram_data = [0] * 0x550 self.cp_me_ram_waddr = 0 def read(self, addr): if addr == GPU_RLC_UCODE_DATA: value = self.rlc_ucode_data[self.rlc_ucode_addr] self.rlc_ucode_addr += 1 return value elif DC0_START <= addr < DC0_END: return self.dc0.read(addr - DC0_START) elif DC1_START <= addr < DC1_END: return self.dc1.read(addr - DC1_START) elif DRMDMA_START <= addr < DRMDMA_END: return self.dma.read(addr) elif addr == GPU_IH_CONTROL: return self.ih_control elif addr == GPU_IH_STATUS: if self.ih_control & 1: return 0 return 5 elif addr == GPU_FLUSH: #Process command buffers here? self.physmem.write(self.cp_rb_rptr_addr, struct.pack(">H", self.cp_rb_wptr)) self.dma.update() return 0 elif addr == GPU_CP_PFP_UCODE_DATA: value = self.cp_pfp_ucode_data[self.cp_pfp_ucode_addr] self.cp_pfp_ucode_addr += 1 return value elif addr == GPU_CP_ME_RAM_DATA: value = self.cp_me_ram_data[self.cp_me_ram_waddr] self.cp_me_ram_waddr += 1 return value print("GPU READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == GPU_IH_RB_BASE: self.ih_rb_base = value << 8 elif addr == GPU_IH_RB_RPTR: self.ih_rb_rptr = value elif addr == GPU_IH_RB_WPTR_ADDR_LO: self.ih_rb_wptr_addr = value elif addr == GPU_IH_CONTROL: self.ih_control = value elif addr == GPU_RLC_UCODE_ADDR: self.rlc_ucode_addr = value elif addr == GPU_RLC_UCODE_DATA: self.rlc_ucode_data[self.rlc_ucode_addr] = value self.rlc_ucode_addr += 1 elif DC0_START <= addr < DC0_END: self.dc0.write(addr - DC0_START, value) elif DC1_START <= addr < DC1_END: self.dc1.write(addr - DC1_START, value) elif DRMDMA_START <= addr < DRMDMA_END: self.dma.write(addr, value) elif addr == GPU_CP_RESET: pass elif addr == GPU_CP_RB_BASE: self.cp_rb_base = value << 8 elif addr == GPU_CP_RB_RPTR_ADDR: self.cp_rb_rptr_addr = value elif addr == GPU_CP_RB_WPTR: self.cp_rb_wptr = value elif addr == GPU_CP_PFP_UCODE_ADDR: self.cp_pfp_ucode_addr = value elif addr == GPU_CP_PFP_UCODE_DATA: self.cp_pfp_ucode_data[self.cp_pfp_ucode_addr] = value self.cp_pfp_ucode_addr += 1 elif addr == GPU_CP_ME_RAM_WADDR: self.cp_me_ram_waddr = value elif addr == GPU_CP_ME_RAM_DATA: self.cp_me_ram_data[self.cp_me_ram_waddr] = value self.cp_me_ram_waddr += 1 elif addr == GPU_PM4_DATA: self.pm4.write(value) else: print("GPU WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) def get_ih_rb_wptr(self): return struct.unpack(">I", self.physmem.read(self.ih_rb_wptr_addr, 4))[0] def set_ih_rb_wptr(self, pos): self.physmem.write(self.ih_rb_wptr_addr, struct.pack(">I", pos)) def trigger_interrupt(self, type, data1=0, data2=0, data3=0): pos = self.get_ih_rb_wptr() self.physmem.write(self.ih_rb_base + pos, struct.pack(">IIII", type, data1, data2, data3)) self.set_ih_rb_wptr(pos + 16) def trigger_vsync(self): #avm.rpl seems to be signalling vsync on 'DVOCap' and 'TrigA' instead #of the real vsync interrupt, we're triggering 'TrigA' here if self.dc0.int_mask & 0x01000000: self.trigger_interrupt(2, 3) if self.dc1.int_mask & 0x01000000: self.trigger_interrupt(6, 3) def check_interrupts(self): return self.get_ih_rb_wptr() != self.ih_rb_rptr AI_CONTROL = 0xD006C00 AI_VOLUME = 0xD006C04 AI_AISCNT = 0xD006C08 AI_AIIT = 0xD006C0C AI_START = 0xD006C00 AI_END = 0xD006C10 class AIController: def __init__(self, scheduler): self.scheduler = scheduler self.control = 0 self.volume = 0 self.sample_count = 0 def read(self, addr): if addr == AI_CONTROL: return self.control elif addr == AI_VOLUME: return self.volume elif addr == AI_AISCNT: if self.control & 1: self.sample_count += 1000 return self.sample_count print("AI READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == AI_CONTROL: if value & 0x20: self.sample_count = 0 self.control = value elif addr == AI_VOLUME: self.volume = value else: print("AI WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) DSP_MAILBOX_IN_H = 0xC280000 DSP_MAILBOX_IN_L = 0xC280002 DSP_MAILBOX_OUT_H = 0xC280004 DSP_MAILBOX_OUT_L = 0xC280006 DSP_CONTROL_STATUS = 0xC28000A DSP_START = 0xC280000 DSP_END = 0xC2A0000 class DSPController: STATE_NEXT = 0 STATE_ARG = 1 def __init__(self, scheduler): self.scheduler = scheduler self.mailbox_in = 0 self.mailbox_out = 0x80000000 self.dsp_int_status = 0 self.dsp_int_mask = 0 self.state = self.STATE_NEXT self.message = 0 def read(self, addr): print("DSP READ 0x%X at %08X" %(addr, self.scheduler.pc())) if addr == DSP_MAILBOX_IN_H: return (self.mailbox_in >> 16) & 0x7FFF elif addr == DSP_MAILBOX_OUT_H: return self.mailbox_out >> 16 elif addr == DSP_MAILBOX_OUT_L: return self.mailbox_out & 0xFFFF elif addr == DSP_CONTROL_STATUS: return (self.dsp_int_status << 7) | (self.dsp_int_mask << 8) #print("DSP READ 0x%X at %08X" %(addr, self.scheduler.pc())) return 0 def write(self, addr, value): if addr == DSP_MAILBOX_IN_H: self.mailbox_in = (self.mailbox_in & 0xFFFF) | (value << 16) elif addr == DSP_MAILBOX_IN_L: self.mailbox_in = (self.mailbox_in & 0xFFFF0000) | value self.handle_message(self.mailbox_in) elif addr == DSP_CONTROL_STATUS: if value & 0x80: self.dsp_int_status = 0 self.dsp_int_mask = (value >> 8) & 1 #else: print("DSP WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) def handle_message(self, value): if self.state == self.STATE_NEXT: self.message = value self.state = self.STATE_ARG else: self.handle_request(self.message, value) self.state = self.STATE_NEXT def handle_request(self, message, arg): if message == 0x80F3A001: pass #Task field_C (slave data addr) elif message == 0x80F3A002: pass #Task field_10 (slave length) elif message == 0x80F3B001: pass #Task field_18 (some kind of physical addr) elif message == 0x80F3B002: pass #Task field_1C elif message == 0x80F3C001: pass #Task field_20 elif message == 0x80F3C002: pass #Task field_14 elif message == 0x80F3D001: #Task field_24 self.trigger_dsp_interrupt(0xDCD10000) else: print("DSP MESSAGE %08X %08X at %08X" %(message, arg, self.scheduler.pc())) def trigger_dsp_interrupt(self, message): self.mailbox_out = message self.dsp_int_status = 1 def check_interrupts(self): return self.dsp_int_status and self.dsp_int_mask TCL_D0000000 = 0xD0000000 class HardwareController: def __init__(self, scheduler, physmem): self.latte = LatteController(scheduler) self.pad = PADController(scheduler) self.gpu = GPUController(scheduler, physmem) self.ai = AIController(scheduler) self.dsp = DSPController(scheduler) self.pi = [PIController(scheduler, self.latte.irq_ppc[i], self.gpu, self.dsp, i) for i in range(3)] armirq = self.latte.irq_arm self.ahmn = AHMNController(scheduler) self.mem = MEMController(scheduler) self.exi = EXIController(scheduler, armirq) self.di2sata = DI2SATAController(scheduler) self.ehci0 = EHCIController(scheduler, 0) self.ehci1 = EHCIController(scheduler, 1) self.ehci2 = EHCIController(scheduler, 2) self.ohci00 = OHCIController(scheduler, armirq, physmem, 0) self.ohci01 = OHCIController(scheduler, armirq, physmem, 1) self.ohci1 = OHCIController(scheduler, armirq, physmem, 2) self.ohci2 = OHCIController(scheduler, armirq, physmem, 3) self.ahci = AHCIController(scheduler, armirq, physmem) self.sdio0 = SDIOController(scheduler, armirq, physmem, 0, SDIOController.TYPE_UNK) self.sdio1 = SDIOController(scheduler, armirq, physmem, 1, SDIOController.TYPE_SD) self.sdio2 = SDIOController(scheduler, armirq, physmem, 2, SDIOController.TYPE_UNK) self.wifi = SDIOController(scheduler, armirq, physmem, 3, SDIOController.TYPE_UNK) self.nand = NANDController(scheduler, armirq, physmem) self.aes = AESController(scheduler, armirq, physmem, 0) self.aess = AESController(scheduler, armirq, physmem, 1) self.sha = SHAController(scheduler, armirq, physmem, 0) self.shas = SHAController(scheduler, armirq, physmem, 1) self.scheduler = scheduler def read(self, addr, length): addr &= ~0x800000 if length == 2: if MEM_START <= addr < MEM_END: value = self.mem.read(addr) elif PAD_START <= addr < PAD_END: value = self.pad.read(addr) elif DSP_START <= addr < DSP_END: value = self.dsp.read(addr) else: print("HW READ(2) 0x%X at %08X" %(addr, self.scheduler.pc())) value = 0 return struct.pack(">H", value) elif length == 4: if LATTE_START <= addr < LATTE_END: value = self.latte.read(addr) elif PI_CPU0_START <= addr < PI_CPU0_END: value = self.pi[0].read(addr - PI_CPU0_START) elif PI_CPU1_START <= addr < PI_CPU1_END: value = self.pi[1].read(addr - PI_CPU1_START) elif PI_CPU2_START <= addr < PI_CPU2_END: value = self.pi[2].read(addr - PI_CPU2_START) elif GPU_START <= addr < GPU_END: value = self.gpu.read(addr) elif AI_START <= addr < AI_END: value = self.ai.read(addr) elif AHMN_START <= addr < AHMN_END: value = self.ahmn.read(addr) elif EXI_START <= addr < EXI_END: value = self.exi.read(addr) elif DI2SATA_START <= addr < DI2SATA_END: value = self.di2sata.read(addr) elif EHCI0_START <= addr < EHCI0_END: value = self.ehci0.read(addr - EHCI0_START) elif EHCI1_START <= addr < EHCI1_END: value = self.ehci1.read(addr - EHCI1_START) elif EHCI2_START <= addr < EHCI2_END: value = self.ehci2.read(addr - EHCI2_START) elif OHCI00_START <= addr < OHCI00_END: value = self.ohci00.read(addr - OHCI00_START) elif OHCI01_START <= addr < OHCI01_END: value = self.ohci01.read(addr - OHCI01_START) elif OHCI1_START <= addr < OHCI1_END: value = self.ohci1.read(addr - OHCI1_START) elif OHCI2_START <= addr < OHCI2_END: value = self.ohci2.read(addr - OHCI2_START) elif AHCI_START <= addr < AHCI_END: value = self.ahci.read(addr) elif SDIO0_START <= addr < SDIO0_END: value = self.sdio0.read(addr - SDIO0_START) elif SDIO1_START <= addr < SDIO1_END: value = self.sdio1.read(addr - SDIO1_START) elif SDIO2_START <= addr < SDIO2_END: value = self.sdio2.read(addr - SDIO2_START) elif WIFI_START <= addr < WIFI_END: value = self.wifi.read(addr - WIFI_START) elif NAND_START <= addr < NAND_END: value = self.nand.read(addr) elif AES_START <= addr < AES_END: value = self.aes.read(addr - AES_START) elif AESS_START <= addr < AESS_END: value = self.aess.read(addr - AESS_START) elif SHA_START <= addr < SHA_END: value = self.sha.read(addr - SHA_START) elif SHAS_START <= addr < SHAS_END: value = self.shas.read(addr - SHAS_START) elif addr == TCL_D0000000: value = 0 else: print("HW READ(4) 0x%X at %08X" %(addr, self.scheduler.pc())) value = 0 return struct.pack(">I", value) else: raise RuntimeError("Invalid HW read length: %i" %length) def write(self, addr, data): addr &= ~0x800000 if len(data) == 2: value = struct.unpack(">H", data)[0] if MEM_START <= addr < MEM_END: self.mem.write(addr, value) elif PAD_START <= addr < PAD_END: self.pad.write(addr, value) elif DSP_START <= addr < DSP_END: self.dsp.write(addr, value) else: print("HW WRITE 0x%X %04X at %08X" %(addr, value, self.scheduler.pc())) elif len(data) == 4: value = struct.unpack(">I", data)[0] if LATTE_START <= addr < LATTE_END: self.latte.write(addr, value) elif PI_CPU0_START <= addr < PI_CPU0_END: self.pi[0].write(addr - PI_CPU0_START, value) elif PI_CPU1_START <= addr < PI_CPU1_END: self.pi[1].write(addr - PI_CPU1_START, value) elif PI_CPU2_START <= addr < PI_CPU2_END: self.pi[2].write(addr - PI_CPU2_START, value) elif GPU_START <= addr < GPU_END: self.gpu.write(addr, value) elif AI_START <= addr < AI_END: self.ai.write(addr, value) elif AHMN_START <= addr < AHMN_END: self.ahmn.write(addr, value) elif MEM_START <= addr < MEM_END: self.mem.write(addr, value) elif EXI_START <= addr < EXI_END: self.exi.write(addr, value) elif DI2SATA_START <= addr < DI2SATA_END: self.di2sata.write(addr, value) elif EHCI0_START <= addr < EHCI0_END: self.ehci0.write(addr - EHCI0_START, value) elif EHCI1_START <= addr < EHCI1_END: self.ehci1.write(addr - EHCI1_START, value) elif EHCI2_START <= addr < EHCI2_END: self.ehci2.write(addr - EHCI2_START, value) elif OHCI00_START <= addr < OHCI00_END: self.ohci00.write(addr - OHCI00_START, value) elif OHCI01_START <= addr < OHCI01_END: self.ohci01.write(addr - OHCI01_START, value) elif OHCI1_START <= addr < OHCI1_END: self.ohci1.write(addr - OHCI1_START, value) elif OHCI2_START <= addr < OHCI2_END: self.ohci2.write(addr - OHCI2_START, value) elif AHCI_START <= addr < AHCI_END: self.ahci.write(addr, value) elif SDIO0_START <= addr < SDIO0_END: self.sdio0.write(addr - SDIO0_START, value) elif SDIO1_START <= addr < SDIO1_END: self.sdio1.write(addr - SDIO1_START, value) elif SDIO2_START <= addr < SDIO2_END: self.sdio2.write(addr - SDIO2_START, value) elif WIFI_START <= addr < WIFI_END: self.wifi.write(addr - WIFI_START, value) elif NAND_START <= addr < NAND_END: self.nand.write(addr, value) elif AES_START <= addr < AES_END: self.aes.write(addr - AES_START, value) elif AESS_START <= addr < AESS_END: self.aess.write(addr - AESS_START, value) elif SHA_START <= addr < SHA_END: self.sha.write(addr - SHA_START, value) elif SHAS_START <= addr < SHAS_END: self.shas.write(addr - SHAS_START, value) else: print("HW WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc())) else: raise RuntimeError("Invalid HW write length: %i" %len(data))