├── .gitignore ├── Makefile ├── README.md ├── ape_code.ld ├── ape_code_poc.c ├── ape_code_ref.c ├── ape_shell.c ├── ape_shell.ld ├── ape_shell_load.ld ├── apebyteswap.c ├── apeimg.c ├── apestamp.c ├── byteswap.c ├── cc_arm ├── cc_mips ├── ncsi-dis.lua ├── notes ├── bcm5719_talos.txt └── notes-ape.txt ├── otg.h ├── otg_common.c ├── otg_dummy.c ├── otg_stage1.c ├── otg_stage1.ld ├── otg_stage2.c ├── otg_stage2.ld ├── otgdbg.c ├── otgimg.c ├── regs.css ├── regs.yaml ├── regs2xhtml ├── rtg-spec.md ├── s1stamp.c ├── s2stamp.c ├── setdriver └── unknownregs /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | *.ll-opt 4 | *.ll-unopt 5 | *.o.s 6 | *.swp 7 | otgdbg 8 | otgimg 9 | apeimg 10 | s1stamp 11 | s2stamp 12 | apestamp 13 | byteswap 14 | apebyteswap 15 | decom 16 | regs.xhtml 17 | *.idb 18 | *.i64 19 | *.id0 20 | *.id1 21 | *.nam 22 | *.til 23 | *.bak 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HOST_CC=clang 2 | HOST_CFLAGS=-flto -O3 -g -Wno-undefined-internal -g 3 | HOST_LD=$(HOST_CC) 4 | HOST_LDFLAGS=-flto 5 | #-Wl,--orphan-handling=error 6 | 7 | USE_PROPRIETARY_APE ?= 8 | 9 | ifneq ($(USE_PROPRIETARY_APE),) 10 | APE_IMAGE_FN=$(USE_PROPRIETARY_APE) 11 | else 12 | ifneq ($(USE_REF_APE),) 13 | APE_IMAGE_FN=ape_code_ref.bs.bin 14 | else 15 | APE_IMAGE_FN=ape_code_poc.bs.bin 16 | endif 17 | endif 18 | 19 | ARM_CFLAGS= 20 | TARGET_CFLAGS=-Wno-undefined-internal -DAPE_IMAGE_FN='"$(APE_IMAGE_FN)"' 21 | 22 | .PRECIOUS: ape_code_%.bin 23 | 24 | all: otg.bin otg_dummy.bin otgdbg otgimg apeimg ape_shell.bin ape_shell_load.bin $(APE_IMAGE_FN) 25 | 26 | clean: 27 | rm -f otg*.bin *.o *.s *.ll-opt *.ll-unopt *.bin.tmp* otgdbg otgimg s1stamp s2stamp apeimg apestamp 28 | 29 | otg.bin: otg_stage1.ld otg_stage1.o otg_stage2.bin s1stamp otgimg 30 | ld.lld -o "$@.tmp" --oformat binary -T otg_stage1.ld otg_stage1.o 31 | ./s1stamp "$@.tmp" 32 | ./otgimg info "$@.tmp" 2>/dev/null | grep -E '^Defects:\s+none$$' >/dev/null 33 | mv "$@.tmp" "$@" 34 | otg_stage1.o: otg_stage1.c otg.h otg_common.c otg_stage2.bin $(APE_IMAGE_FN) 35 | ./cc_mips "$@" "$<" -DSTAGE1 $(TARGET_CFLAGS) 36 | 37 | otg_stage2.bin: otg_stage2.ld otg_stage2.o s2stamp 38 | ld.lld -o "$@.tmp" --oformat binary -T otg_stage2.ld otg_stage2.o 39 | ./s2stamp "$@.tmp" 40 | mv "$@.tmp" "$@" 41 | otg_stage2.o: otg_stage2.c otg.h otg_common.c 42 | ./cc_mips "$@" "$<" -DSTAGE2 $(TARGET_CFLAGS) 43 | 44 | otg_dummy.bin: otg_stage1.ld otg_dummy.o s1stamp 45 | ld.lld -o "$@.tmp" --oformat binary -T otg_stage1.ld otg_dummy.o 46 | ./s1stamp "$@.tmp" 47 | mv "$@.tmp" "$@" 48 | otg_dummy.o: otg_dummy.c otg.h otg_common.c ape.bs.bin 49 | ./cc_mips "$@" "$<" -DSTAGE1 $(TARGET_CFLAGS) 50 | 51 | otgdbg: otgdbg.o 52 | $(HOST_LD) -g $(HOST_LDFLAGS) -o "$@" $^ 53 | otgdbg.o: otgdbg.c otg.h otg_common.c 54 | $(HOST_CC) -g -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 55 | 56 | otgimg: otgimg.o 57 | $(HOST_LD) $(HOST_LDFLAGS) -o "$@" $^ 58 | otgimg.o: otgimg.c otg.h otg_common.c 59 | $(HOST_CC) -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 60 | 61 | apeimg: apeimg.o 62 | $(HOST_LD) $(HOST_LDFLAGS) -o "$@" $^ 63 | apeimg.o: apeimg.c otg.h otg_common.c 64 | $(HOST_CC) -c $(HOST_CFLAGS) -g -o "$@" "$<" -DOTG_HOST 65 | 66 | s1stamp: s1stamp.o 67 | $(HOST_CC) -flto -O3 -o "$@" $^ 68 | s1stamp.o: s1stamp.c otg.h otg_common.c 69 | $(HOST_CC) -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 70 | 71 | s2stamp: s2stamp.o 72 | $(HOST_CC) -flto -O3 -o "$@" $^ 73 | s2stamp.o: s2stamp.c otg.h otg_common.c 74 | $(HOST_CC) -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 75 | 76 | ape.bs.bin: ape.xbin byteswap 77 | cat "$<" | ./byteswap > "$@" 78 | 79 | byteswap: byteswap.o 80 | $(HOST_CC) -flto -O3 -o "$@" $^ 81 | byteswap.o: byteswap.c otg.h otg_common.c 82 | $(HOST_CC) -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 83 | 84 | ape_shell.bin: ape_shell.o ape_shell.ld 85 | ld.lld -o "$@" --oformat binary -T ape_shell.ld ape_shell.o 86 | ape_shell.o: ape_shell.c 87 | ./cc_arm "$@" "$<" -DOTG_APE -DAPE_SHELL $(ARM_CFLAGS) 88 | 89 | ape_shell_load.bin: ape_shell_load.o ape_shell_load.ld apestamp 90 | ld.lld -o "$@.tmp" --oformat binary -T ape_shell_load.ld ape_shell_load.o 91 | ./apestamp "$@.tmp" "$@.tmp2" 92 | mv "$@.tmp2" "$@" 93 | rm "$@.tmp" 94 | ape_shell_load.o: ape_shell.c 95 | ./cc_arm "$@" "$<" -DOTG_APE -DAPE_SHELL -DAPE_SHELL_LOAD $(ARM_CFLAGS) 96 | 97 | apestamp: apestamp.o 98 | $(HOST_CC) -flto -O3 -o "$@" $^ 99 | apestamp.o: apestamp.c otg.h otg_common.c 100 | $(HOST_CC) -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 101 | 102 | apebyteswap: apebyteswap.o 103 | $(HOST_CC) -flto -O3 -o "$@" $^ 104 | apebyteswap.o: apebyteswap.c otg.h otg_common.c 105 | $(HOST_CC) -c $(HOST_CFLAGS) -o "$@" "$<" -DOTG_HOST 106 | 107 | ape_code_%.bs.bin: ape_code_%.bin apebyteswap 108 | cp "ape_code.bin" "$@.tmp" 109 | ./apebyteswap "$@.tmp" 110 | mv "$@.tmp" "$@" 111 | ape_code_%.bin: ape_code_%.o ape_code.ld apestamp apeimg 112 | ld.lld -o "$@.tmp" --oformat binary -T ape_code.ld "$<" 113 | ./apestamp "$@.tmp" "$@.tmp2" 114 | ./apeimg info "$@.tmp2" 2>/dev/null | grep -E '^Defects:\s+none$$' >/dev/null 115 | mv "$@.tmp2" "$@" 116 | rm "$@.tmp" 117 | ape_code_%.o: ape_code_%.c 118 | ./cc_arm "$@" "$<" -DOTG_APE $(ARM_CFLAGS) 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ortega 2 | 3 | BCM5719 firmware SDK. Project files for reverse engineered reference codebase, 4 | scrubbed of any proprietary code. 5 | 6 | Background information is available here: 7 | 8 | - [Adventures in reverse engineering Broadcom NIC firmware](https://www.devever.net/~hl/ortega) 9 | - [Project Ortega (wiki page)](https://wiki.raptorcs.com/wiki/Project_Ortega) 10 | - [Meklort's reimplemented open source BCM5719 firmware](https://github.com/meklort/bcm5719-fw) 11 | 12 | See [rtg-spec.md](./rtg-spec.md) for a WIP human-readable description of what 13 | the firmware needs to do. 14 | -------------------------------------------------------------------------------- /ape_code.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | ENTRY(AEntrypoint) 3 | 4 | SECTIONS { 5 | . = 0x00100C00 - 0x78; 6 | 7 | .header . : ALIGN(4) SUBALIGN(4) { 8 | *(.header) 9 | } 10 | 11 | ASSERT(. == 0x00100C00, "header size") 12 | ASSERT(SIZEOF(.header) == 0x78, "header size") 13 | 14 | .scratchtext . : ALIGN(4) SUBALIGN(4) { 15 | *(.scratchtext) 16 | . = ALIGN(4); 17 | } 18 | 19 | _ScratchTextSize = SIZEOF(.scratchtext); 20 | _ScratchTextStart = ADDR(.scratchtext); 21 | _ScratchTextEnd = _ScratchTextStart + SIZEOF(.scratchtext); 22 | _ScratchTextOffset = _ScratchTextStart - ADDR(.header); 23 | 24 | . = 0x00108000; 25 | 26 | .text . : ALIGN(4) SUBALIGN(4) { 27 | *(.isrtable) 28 | *(.textstart) 29 | *(.text) 30 | . = ALIGN(4); 31 | } 32 | 33 | _TextSize = SIZEOF(.text); 34 | _TextStart = ADDR(.text); 35 | _TextEnd = _TextStart + SIZEOF(.text); 36 | _TextOffset = _ScratchTextOffset + SIZEOF(.scratchtext); 37 | 38 | ASSERT(_TextStart == 0x00108000, "textstart") 39 | 40 | .data . : ALIGN(4) SUBALIGN(4) { 41 | *(.data) 42 | *(.rodata) 43 | *(.rodata.*) 44 | 45 | . = ALIGN(4); 46 | /* This is the trailing CRC placeholder. We pull a trick here and stick it 47 | * at the end of .data. The idea is that .bss shouldn't take up any space 48 | * in the file; so this will come at the end of the file and apestamp will 49 | * be happy. If something changes that causes bss to start emitting data, 50 | * the build will break. So this forms an additional sanity check. 51 | */ 52 | *(.trailingcrc) 53 | } 54 | 55 | _DataSize = SIZEOF(.data); 56 | _DataStart = ADDR(.data); 57 | _DataEnd = _DataStart + SIZEOF(.data); 58 | _DataOffset = _TextOffset + SIZEOF(.text); 59 | 60 | .bss . : ALIGN(4) SUBALIGN(4) { 61 | *(.bss) 62 | . = ALIGN(4); 63 | } 64 | 65 | _BSSSize = SIZEOF(.bss); 66 | _BSSStart = ADDR(.bss); 67 | _BSSEnd = _BSSStart + SIZEOF(.bss); 68 | _BSSOffset = _DataOffset + SIZEOF(.data); 69 | 70 | _StackEnd = 0x118000; 71 | 72 | ASSERT((_StackEnd - _BSSEnd) >= 0x2000, "minimum stack room") 73 | 74 | /DISCARD/ : { 75 | *(.comment) 76 | *(.note.GNU-stack) 77 | *(.eh_frame) 78 | *(.got) 79 | *(.reginfo) 80 | *(.mdebug.abi32) 81 | *(.pdr) 82 | *(.ARM.exidx*) 83 | *(.ARM.attributes) 84 | } 85 | 86 | _ScratchTextOffsetFlags = _ScratchTextOffset | (1 << 25) | (1 << 26) | (1 << 27); /* CRC32, BIT26, BIT27 */ 87 | _TextOffsetFlags = _TextOffset | (1 << 25) | (1 << 26) | (1 << 27); /* CRC32, BIT26, BIT27 */ 88 | _DataOffsetFlags = _DataOffset | (1 << 25); /* CRC32 */ 89 | _BSSOffsetFlags = _BSSOffset | (1 << 28); /* ZERO_ON_FAST_BOOT */ 90 | 91 | _AEntrypointM = AEntrypoint & 0xFFFFFFFE; 92 | ASSERT(_AEntrypointM == (0x001080C0), "entrypoint") 93 | } 94 | -------------------------------------------------------------------------------- /ape_shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "otg.h" 3 | 4 | #define APE_IMAGE_NAME "LOAD" 5 | #define APE_VER_MAJOR 1 6 | #define APE_VER_MINOR 3 7 | #define APE_VER_PATCH 7 8 | 9 | #define CONSTP32(X) (*(uint32_t*)(X)) 10 | #define CONSTPP(X) (*(void**)(X)) 11 | 12 | #define APEDBG_STATE CONSTP32(0x60220260) 13 | #define APEDBG_CMD CONSTP32(0x60220264) 14 | #define APEDBG_ARG0 CONSTP32(0x60220268) 15 | #define APEDBG_ARG1 CONSTP32(0x6022026C) 16 | #define APEDBG_CMD_ERROR_FLAGS CONSTP32(0x60220270) 17 | #define APEDBG_CMD_ERROR_FLAGS__EXCEPTION 0x00000001 18 | #define APEDBG_EXCEPTION_COUNT CONSTP32(0x60220274) 19 | #define APEDBG_EXCEPTION_IGNORE CONSTP32(0x60220278) 20 | 21 | #define APEDBG_STATE_RUNNING 0xBEEFCAFE 22 | #define APEDBG_STATE_EXITED 0xBEEF0FFE 23 | 24 | #define APEDBG_CMD_TYPE_MASK 0x0000FFFF 25 | #define APEDBG_CMD_MAGIC_MASK 0xFFFF0000 26 | #define APEDBG_CMD_MAGIC 0x5CAF0000 27 | 28 | #define APE_EVENT_STATUS CONSTP32(0x60220300) 29 | 30 | #define APE_MODE CONSTP32(0x60200000) 31 | #define APE_MODE__EVENT_1 0x20 32 | 33 | #define NVIC_VECTOR_TABLE_OFFSET CONSTPP(0xE000ED08) 34 | #define INT_HARD_FAULT 3 35 | 36 | enum { 37 | APEDBG_CMD__NONE = 0x0000, 38 | APEDBG_CMD__MEM_GET = 0x0001, 39 | APEDBG_CMD__MEM_SET = 0x0002, 40 | APEDBG_CMD__CALL_0 = 0x0003, 41 | APEDBG_CMD__RETURN = 0x0004, 42 | }; 43 | 44 | typedef struct { 45 | uint32_t r4, r5, r6, r7, r8, r9, r10, r11; 46 | uint32_t lr, r12, r0, r1, r2, r3, xpsr, pc; 47 | } exc_frame_t; 48 | 49 | extern void APEShell_IntHandler_HardFault(void); 50 | void _IntHandler_HardFault(exc_frame_t *sp) { 51 | APEDBG_CMD_ERROR_FLAGS |= APEDBG_CMD_ERROR_FLAGS__EXCEPTION; 52 | ++APEDBG_EXCEPTION_COUNT; 53 | 54 | if (APEDBG_EXCEPTION_IGNORE) { 55 | sp->pc += 2; 56 | APEDBG_EXCEPTION_IGNORE = 0; 57 | } 58 | } 59 | 60 | static inline uint32_t _GetCommand(void) { 61 | for (;;) { 62 | APEDBG_STATE = APEDBG_STATE_RUNNING; 63 | 64 | uint32_t cmd = APEDBG_CMD; 65 | if ((cmd & APEDBG_CMD_TYPE_MASK) 66 | && (cmd & APEDBG_CMD_MAGIC_MASK) == APEDBG_CMD_MAGIC) { 67 | APEDBG_CMD &= ~APEDBG_CMD_MAGIC_MASK; 68 | return cmd & APEDBG_CMD_TYPE_MASK; 69 | } 70 | } 71 | } 72 | 73 | static inline void _CommandComplete(void) { 74 | APEDBG_CMD = 0; 75 | } 76 | 77 | // APE debugging agent, injected into APE memory via shellcode. 78 | void APEShellStart(void) { 79 | uint32_t status; 80 | asm volatile ("mrs %0, PRIMASK" : "=r" (status)); 81 | asm volatile ("cpsid i"); 82 | 83 | APE_EVENT_STATUS = 0; 84 | APEDBG_CMD_ERROR_FLAGS = 0; 85 | APEDBG_EXCEPTION_IGNORE = 0; 86 | APEDBG_CMD = 0; 87 | 88 | void **nvicTable = (void**)NVIC_VECTOR_TABLE_OFFSET; 89 | #ifdef APE_SHELL_LOAD 90 | // Boot ROM doesn't set NVIC_VECTOR_TABLE_OFFSET, so set it up here. 91 | if (!nvicTable) { 92 | nvicTable = (void*)0x108000; 93 | NVIC_VECTOR_TABLE_OFFSET = nvicTable; 94 | APE_MODE |= APE_MODE__EVENT_1; 95 | } 96 | #endif 97 | 98 | void *oldHardFaultHandler = nvicTable[INT_HARD_FAULT]; 99 | nvicTable[INT_HARD_FAULT] = APEShell_IntHandler_HardFault; 100 | 101 | // Process commands. 102 | for (;;) { 103 | uint32_t cmd = _GetCommand(); 104 | 105 | APEDBG_CMD_ERROR_FLAGS = 0; 106 | switch (cmd) { 107 | case APEDBG_CMD__MEM_GET: 108 | APEDBG_EXCEPTION_IGNORE = 1; 109 | APEDBG_ARG1 = *(uint32_t*)APEDBG_ARG0; 110 | APEDBG_EXCEPTION_IGNORE = 0; 111 | break; 112 | 113 | case APEDBG_CMD__MEM_SET: 114 | APEDBG_EXCEPTION_IGNORE = 1; 115 | *(uint32_t*)APEDBG_ARG0 = APEDBG_ARG1; 116 | APEDBG_EXCEPTION_IGNORE = 0; 117 | break; 118 | 119 | case APEDBG_CMD__CALL_0: { 120 | uint32_t addr = APEDBG_ARG0; 121 | APEDBG_ARG0 = 0; 122 | 123 | typedef uint32_t (*funcp0_t)(void); 124 | APEDBG_ARG1 = ((funcp0_t)addr)(); 125 | } break; 126 | 127 | case APEDBG_CMD__RETURN: 128 | _CommandComplete(); 129 | nvicTable[INT_HARD_FAULT] = oldHardFaultHandler; 130 | APEDBG_STATE = APEDBG_STATE_EXITED; 131 | asm ("msr PRIMASK, %0" :: "r" (status)); 132 | return; 133 | 134 | default: 135 | break; 136 | } 137 | 138 | _CommandComplete(); 139 | } 140 | } 141 | 142 | asm( 143 | ".pushsection .textstart,\"ax\",%progbits\n" 144 | ".global APEShellEntrypoint\n" 145 | ".code 16\n" 146 | ".thumb_func\n" 147 | "APEShellEntrypoint:\n" 148 | #ifdef APE_SHELL_LOAD 149 | " ldr sp, =" STRINGIFY(STACK_END) "\n" 150 | #endif 151 | " b APEShellStart\n" 152 | "\n" 153 | ".global APEShell_IntHandler_HardFault\n" 154 | ".thumb_func\n" 155 | "APEShell_IntHandler_HardFault:\n" 156 | " push {lr}\n" 157 | " push {r4-r11}\n" 158 | " mov r0, sp\n" 159 | " bl _IntHandler_HardFault\n" 160 | " pop {r4-r11}\n" 161 | " pop {pc}\n" 162 | " \n" 163 | "\n" 164 | ".popsection\n" 165 | ); 166 | 167 | #ifdef APE_SHELL_LOAD 168 | extern char _TextSize[]; 169 | 170 | const __attribute__((section(".header"))) ape_header gAPE_header = { 171 | .magic = "BCM\x1A", 172 | .unk04 = 0x03070700, // Unknown, not read by boot ROM. 173 | .imageName = APE_IMAGE_NAME " " STRINGIFY(APE_VER_MAJOR) "." STRINGIFY(APE_VER_MINOR) "." STRINGIFY(APE_VER_PATCH), 174 | .imageVersion = (APE_VER_MAJOR<<24) | (APE_VER_MINOR<<16) | (APE_VER_PATCH<<8), 175 | 176 | .entrypoint = (0x00100A00)|1, 177 | 178 | .unk020 = 0x00, // Unknown, not read by boot ROM. 179 | .headerSize = sizeof(ape_header)/4, 180 | .unk022 = 0x04, // Unknown, not read by boot ROM. 181 | .numSections = 1, 182 | .headerChecksum = 0xDEADBEEF, // Checksums are fixed later in apestamp. 183 | 184 | .sections = { 185 | [0] = { 186 | .loadAddr = 0x00100A00, 187 | .offsetFlags = 0x78|BIT(26)|BIT(27), 188 | .uncompressedSize = (uint32_t)_TextSize, 189 | .compressedSize = 0, 190 | .checksum = 0xDEADBEEF, 191 | }, 192 | }, 193 | }; 194 | #endif 195 | -------------------------------------------------------------------------------- /ape_shell.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | ENTRY(APEShellEntrypoint) 3 | 4 | SECTIONS { 5 | . = 0x00100000; 6 | 7 | .text . : ALIGN(4) SUBALIGN(4) { 8 | *(.textstart) 9 | *(.text) 10 | *(.data) 11 | *(.bss) 12 | *(.rodata) 13 | *(.rodata.*) 14 | *(.trailer) 15 | } 16 | 17 | /DISCARD/ : { 18 | *(.comment) 19 | *(.note.GNU-stack) 20 | *(.eh_frame) 21 | *(.got) 22 | *(.reginfo) 23 | *(.mdebug.abi32) 24 | *(.pdr) 25 | *(.ARM.exidx*) 26 | } 27 | 28 | ASSERT(ADDR(.text) == (0x00100000), "textstart") 29 | /*ASSERT(APEShellEntrypoint == (0x00100000), "addr 1")*/ 30 | } 31 | -------------------------------------------------------------------------------- /ape_shell_load.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | ENTRY(APEShellEntrypoint) 3 | 4 | SECTIONS { 5 | . = 0x00100A00 - 0x78; 6 | 7 | .header . : ALIGN(4) SUBALIGN(4) { 8 | *(.header) 9 | } 10 | 11 | .text . : ALIGN(4) SUBALIGN(4) { 12 | *(.textstart) 13 | *(.text) 14 | *(.data) 15 | *(.bss) 16 | *(.rodata) 17 | *(.rodata.*) 18 | *(.trailer) 19 | . = ALIGN(4); 20 | } 21 | 22 | _TextSize = SIZEOF(.text); 23 | _TextStart = ADDR(.text); 24 | _TextEnd = _TextStart + SIZEOF(.text); 25 | 26 | /DISCARD/ : { 27 | *(.comment) 28 | *(.note.GNU-stack) 29 | *(.eh_frame) 30 | *(.got) 31 | *(.reginfo) 32 | *(.mdebug.abi32) 33 | *(.pdr) 34 | *(.ARM.exidx*) 35 | } 36 | 37 | ASSERT(ADDR(.text) == (0x00100A00), "textstart") 38 | } 39 | -------------------------------------------------------------------------------- /apebyteswap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "otg_common.c" 6 | 7 | int main(int argc, char **argv) { 8 | int ec; 9 | 10 | if (argc < 2) { 11 | fprintf(stderr, "usage: \n"); 12 | return 2; 13 | } 14 | 15 | int fd = open(argv[1], O_RDWR|O_SYNC); 16 | if (fd < 0) 17 | return 1; 18 | 19 | struct stat st; 20 | ec = fstat(fd, &st); 21 | if (ec < 0) 22 | return 1; 23 | 24 | void *virt = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 25 | if (!virt) 26 | return 1; 27 | 28 | uint32_t *v32 = virt; 29 | uint32_t numWords = st.st_size/4; 30 | 31 | uint32_t oldCRC = ComputeCRC(virt, numWords-1, 0xFFFFFFFF)^0xFFFFFFFF; 32 | if (oldCRC != le32toh(v32[numWords-1])) { 33 | fprintf(stderr, "old trailing CRC is not valid\n"); 34 | return 1; 35 | } 36 | 37 | for (size_t i=0; i 2 | #include "otg_common.c" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define SPLIT(X) ((X) >> 16), ((X) & 0xFFFF) 14 | #define HEX32 "0x%04X_%04X" 15 | #define HEX32_ "%04X_%04X" 16 | 17 | static ssize_t _Write(uint8_t ch, void *arg) { 18 | *(*(uint8_t**)arg)++ = ch; 19 | return 1; 20 | } 21 | 22 | int _OpenImage(const char *fn, struct stat *st, void **pVirt) { 23 | int ec, fd = -1; 24 | void *virt = NULL; 25 | 26 | fd = open(fn, O_RDONLY|O_SYNC); 27 | if (fd < 0) 28 | return -1; 29 | 30 | ec = fstat(fd, st); 31 | if (ec < 0) 32 | goto fail; 33 | 34 | if (st->st_size < sizeof(ape_header)) 35 | goto fail; 36 | 37 | virt = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, fd, 0); 38 | if (!virt) 39 | goto fail; 40 | 41 | ape_header *hdr = virt; 42 | if (!memcmp("\x1AMCB", hdr->magic, 4) || !memcmp("\x1A" "BUB", hdr->magic, 4)) { 43 | fprintf(stderr, "error: file is word-swapped; un-word-swap the file in order to use this tool.\n"); 44 | return 1; 45 | } 46 | 47 | if (memcmp("BCM\x1A", hdr->magic, 4) && memcmp("BUB\x1A", hdr->magic, 4)) { 48 | fprintf(stderr, "error: unrecognised magic\n"); 49 | return 1; 50 | } 51 | 52 | *pVirt = virt; 53 | return fd; 54 | fail: 55 | if (virt) 56 | munmap(virt, st->st_size); 57 | if (fd >= 0) 58 | close(fd); 59 | return -1; 60 | } 61 | 62 | int _CmdInfo(int pargc, int argc, char **argv) { 63 | if (argc < 2) { 64 | fprintf(stderr, "usage: \n"); 65 | return 1; 66 | } 67 | 68 | bool errCRC = false; 69 | bool errCRCSwap = false; 70 | 71 | struct stat st; 72 | void *virt; 73 | int fd = _OpenImage(argv[1], &st, &virt); 74 | if (fd < 0) { 75 | fprintf(stderr, "error: cannot open file\n"); 76 | return 1; 77 | } 78 | 79 | ape_header *hdr = virt; 80 | printf("---------- APE CODE IMAGE ----------\n"); 81 | printf("Magic: \"%s\"\n", hdr->magic); 82 | printf("Unk 04h: " HEX32 "\n", SPLIT(le32toh(hdr->unk04))); 83 | printf("Image Name: \"%s\"\n", hdr->imageName); 84 | printf("Image Version: " HEX32 "\n", SPLIT(le32toh(hdr->imageVersion))); 85 | printf("Entrypoint: " HEX32 "\n", SPLIT(le32toh(hdr->entrypoint))); 86 | printf("Unk 20h: 0x%02X\n", hdr->unk020); 87 | printf("Header Size: 0x%X bytes\n", hdr->headerSize*4); 88 | printf("Unk 22h: 0x%02X\n", hdr->unk022); 89 | printf("Num Sections: %u\n", hdr->numSections); 90 | uint32_t hChecksumEx = le32toh(hdr->headerChecksum); 91 | uint32_t hwm = hdr->headerSize*4; 92 | 93 | uint32_t headerSize = le32toh(hdr->headerSize)*4; 94 | if (headerSize > st.st_size) { 95 | printf("error: short file\n"); 96 | return 1; 97 | } 98 | 99 | ape_header *hdr2 = malloc(headerSize); 100 | assert(hdr2); 101 | memcpy(hdr2, hdr, headerSize); 102 | 103 | const char *str; 104 | char strbuf[64]; 105 | 106 | hdr2->headerChecksum = 0; 107 | uint32_t hChecksumAc = ComputeCRC(hdr2, headerSize/4, 0); 108 | if (hChecksumEx == hChecksumAc) 109 | str = "OK"; 110 | else if (!hChecksumEx) { 111 | snprintf(strbuf, sizeof(strbuf), "SKIPPED, checksum field zero"); 112 | str = strbuf; 113 | } else { 114 | snprintf(strbuf, sizeof(strbuf), "MISMATCH, got " HEX32, SPLIT(hChecksumAc)); 115 | str = strbuf; 116 | errCRC = true; 117 | } 118 | 119 | free(hdr2); 120 | 121 | printf("Header Checksum: " HEX32 " %s\n", SPLIT(hChecksumEx), str); 122 | 123 | uint32_t trChecksumEx = le32toh(*(uint32_t*)((uint8_t*)virt + st.st_size - 4)); 124 | uint32_t trChecksumAc = ComputeCRC(virt, (st.st_size-4)/4, 0xFFFFFFFF) ^ 0xFFFFFFFF; 125 | if (trChecksumEx == trChecksumAc) 126 | str = "OK"; 127 | else { 128 | uint32_t *buf = malloc(st.st_size-4); 129 | for (size_t i=0; i<(st.st_size-4)/4; ++i) 130 | buf[i] = SwapEndian32(((uint32_t*)virt)[i]); 131 | 132 | trChecksumEx = SwapEndian32(trChecksumEx); 133 | trChecksumAc = ComputeCRC(buf, (st.st_size-4)/4, 0xFFFFFFFF) ^ 0xFFFFFFFF; 134 | if (trChecksumEx == trChecksumAc) { 135 | str = "SWAPPED"; 136 | errCRCSwap = true; 137 | } else { 138 | snprintf(strbuf, sizeof(strbuf), "MISMATCH, got " HEX32, SPLIT(trChecksumAc)); 139 | str = strbuf; 140 | errCRC = true; 141 | } 142 | 143 | free(buf); 144 | } 145 | 146 | printf("Trailing Checksum: " HEX32 " %s\n", SPLIT(trChecksumEx), str); 147 | printf("File Size: " HEX32 " bytes\n", SPLIT(st.st_size)); 148 | 149 | printf("\n"); 150 | printf(" Load Addr Offset Uncomp Sz Comp Sz Cksum \n"); 151 | printf(" --------- --------- --------- --------- ---------\n"); 152 | for (size_t i=0; inumSections; ++i) { 153 | uint32_t offsetFlags = le32toh(hdr->sections[i].offsetFlags); 154 | uint32_t offset = offsetFlags & 0xFFFFFF; 155 | uint32_t uncompressedSize = le32toh(hdr->sections[i].uncompressedSize); 156 | uint32_t compressedSize = le32toh(hdr->sections[i].compressedSize); 157 | uint32_t secChecksumEx = le32toh(hdr->sections[i].checksum); 158 | bool isZero = !!(offsetFlags & APE_SECTION_FLAG_ZERO_ON_FAST_BOOT); 159 | 160 | if (!isZero && offset + compressedSize > hwm) 161 | hwm = offset + compressedSize; 162 | 163 | str = ""; 164 | if (!isZero) { 165 | void *compBuf = NULL; 166 | uint32_t compSize = 0; 167 | void *uncompBuf = NULL; 168 | uint32_t uncompSize = 0; 169 | 170 | compBuf = (uint8_t*)virt + offset; 171 | compSize = (offsetFlags & APE_SECTION_FLAG_COMPRESSED) ? compressedSize : uncompressedSize; 172 | 173 | if (offset > st.st_size || compSize > st.st_size || (offset + compSize) > st.st_size) { 174 | compBuf = NULL; 175 | compSize = 0; 176 | str = "BAD LENGTH"; 177 | errCRC = true; 178 | } 179 | 180 | if (compBuf) { 181 | if (offsetFlags & APE_SECTION_FLAG_COMPRESSED) { 182 | uncompSize = uncompressedSize; 183 | uncompBuf = malloc(uncompSize); 184 | assert(uncompBuf); 185 | 186 | uint8_t *ptr = uncompBuf; 187 | size_t bytesRead = 0, bytesWritten = 0; 188 | Decompress(compBuf, compSize, uncompSize, _Write, &ptr, &bytesRead, &bytesWritten); 189 | } else { 190 | uncompBuf = compBuf; 191 | uncompSize = compSize; 192 | } 193 | } 194 | 195 | if (uncompBuf) { 196 | uint32_t secChecksumAc; 197 | if (offsetFlags & APE_SECTION_FLAG_CHECKSUM_IS_CRC32) { 198 | secChecksumAc = ComputeCRC(uncompBuf, uncompSize/4, 0); 199 | } else { 200 | secChecksumAc = 0; 201 | uint32_t *uncompBuf_ = uncompBuf; 202 | for (size_t i=0; isections[i].loadAddr)), 224 | SPLIT(offset), 225 | SPLIT(uncompressedSize), 226 | SPLIT(le32toh(hdr->sections[i].compressedSize)), 227 | 228 | (offsetFlags & APE_SECTION_FLAG_CHECKSUM_IS_CRC32) ? "S" : "P", 229 | (offsetFlags & APE_SECTION_FLAG_COMPRESSED) ? "C" : "U", 230 | (offsetFlags & APE_SECTION_FLAG_ZERO_ON_FAST_BOOT) ? "Z" : " ", 231 | (offsetFlags & (1U<<26)) ? "X" : " ", 232 | (offsetFlags & (1U<<27)) ? "Y" : " ", 233 | (offsetFlags & (1U<<29)) ? "Z" : " ", 234 | (offsetFlags & (1U<<30)) ? "W" : " ", 235 | (offsetFlags & (1U<<31)) ? "R" : " ", 236 | 237 | SPLIT(secChecksumEx), 238 | str 239 | ); 240 | printf("\n"); 241 | } 242 | 243 | printf("\n"); 244 | printf("Legend:\n"); 245 | printf(" S - Checksum is CRC32, P - Checksum is sum-to-zero\n"); 246 | printf(" C - Compressed, U - Uncompressed\n"); 247 | printf(" U - Zero on boot (no space allocated in file)\n"); 248 | printf(" X, Y, Z, W, R - Unknown flag bits 26, 27, 29, 30, 31\n"); 249 | printf("Typical section assignment: Sec0=Scratchcode, Sec1=Code, Sec2=Data, Sec3=BSS\n"); 250 | printf("\n"); 251 | printf("HWM: " HEX32 "\n", SPLIT(hwm)); 252 | uint32_t slack = st.st_size-4-hwm; 253 | printf("Slack room: 0x%X bytes %s\n", slack, slack == 0x100 ? "OK" : "IRREGULAR"); 254 | if (errCRC || errCRCSwap || slack != 0x100) 255 | printf("Defects: %s%s%s\n", errCRC ? " crc" : "", errCRCSwap ? " crcswap" : "", slack != 0x100 ? " slack" : ""); 256 | else 257 | printf("Defects: none\n"); 258 | return 0; 259 | } 260 | 261 | static ssize_t _WriteStdout(uint8_t ch, void *arg) { 262 | return fputc(ch, stdout) != ch ? 0 : 1; 263 | } 264 | 265 | static int _CmdExtract(int pargc, int argc, char **argv) { 266 | if (argc < 3) { 267 | fprintf(stderr, "usage: \n"); 268 | return 2; 269 | } 270 | 271 | size_t secno = strtoul(argv[2], NULL, 0); 272 | 273 | struct stat st; 274 | void *virt; 275 | int fd = _OpenImage(argv[1], &st, &virt); 276 | if (fd < 0) 277 | return 1; 278 | 279 | ape_header *hdr = virt; 280 | uint32_t hdrSize = le32toh(hdr->headerSize)*4; 281 | if (hdrSize > st.st_size || hdrSize < ((hdr->numSections-4)*sizeof(ape_section) + sizeof(ape_header))) { 282 | fprintf(stderr, "error: short file\n"); 283 | return 1; 284 | } 285 | 286 | if (secno >= hdr->numSections) { 287 | fprintf(stderr, "error: nonexistent section\n"); 288 | return 1; 289 | } 290 | 291 | ape_section *sec = &hdr->sections[secno]; 292 | uint32_t offset = le32toh(sec->offsetFlags) & 0xFFFFFF; 293 | bool isCompressed = le32toh(sec->offsetFlags) & APE_SECTION_FLAG_COMPRESSED; 294 | bool isZero = le32toh(sec->offsetFlags) & APE_SECTION_FLAG_ZERO_ON_FAST_BOOT; 295 | 296 | uint32_t compSize = le32toh(sec->compressedSize); 297 | uint32_t uncompSize = le32toh(sec->uncompressedSize); 298 | 299 | if (isZero) { 300 | while (uncompSize--) 301 | fputc(0, stdout); 302 | return 0; 303 | } 304 | 305 | if (!isCompressed) { 306 | if (offset > st.st_size || uncompSize > st.st_size || offset + uncompSize > st.st_size) { 307 | fprintf(stderr, "bad section offset/length\n"); 308 | return 1; 309 | } 310 | 311 | ssize_t wr = fwrite((uint8_t*)virt + offset, uncompSize, 1, stdout); 312 | if (wr < 1) 313 | return 1; 314 | 315 | return 0; 316 | } 317 | 318 | if (offset > st.st_size || compSize > st.st_size || offset + compSize > st.st_size) { 319 | fprintf(stderr, "bad section offset/length\n"); 320 | return 1; 321 | } 322 | 323 | size_t bytesRead, bytesWritten; 324 | Decompress((uint8_t*)virt + offset, compSize, uncompSize, _WriteStdout, NULL, &bytesRead, &bytesWritten); 325 | return 0; 326 | } 327 | 328 | static const struct argp _argp = { 329 | .args_doc = " [command-args...]", 330 | .doc = "APE image servicing tool.\vCommands:\n" 331 | " info show information about an APE image\n" 332 | " extract extract an APE section, decompressing it if necessary\n" 333 | , 334 | }; 335 | 336 | typedef struct { 337 | const char *name; 338 | int (*func)(int pargc, int argc, char **argv); 339 | } command_def_t; 340 | 341 | static const command_def_t _commands[] = { 342 | { 343 | .name = "info", 344 | .func = _CmdInfo, 345 | }, 346 | { 347 | .name = "extract", 348 | .func = _CmdExtract, 349 | }, 350 | {}, 351 | }; 352 | 353 | int main(int argc, char **argv) { 354 | int argidx; 355 | error_t argerr = argp_parse(&_argp, argc, argv, ARGP_IN_ORDER, &argidx, NULL); 356 | if (argerr) 357 | return 2; 358 | 359 | const command_def_t *cmd = _commands; 360 | for (; cmd->name; ++cmd) 361 | if (argv[argidx] && !strcmp(cmd->name, argv[argidx])) 362 | break; 363 | 364 | if (!cmd->name) { 365 | argp_help(&_argp, stderr, ARGP_HELP_STD_USAGE, argv[0]); 366 | return 2; 367 | } 368 | 369 | return cmd->func(argidx, argc-argidx, argv+argidx); 370 | } 371 | -------------------------------------------------------------------------------- /apestamp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "otg.h" 11 | #include "otg_common.c" 12 | 13 | #define BCM1A_MAGIC 0x1A4D4342 /* "BCM\x1A" */ 14 | 15 | static const void *g_virt; 16 | 17 | static uint32_t L32(uint32_t offset) { 18 | return le32toh(*(uint32_t*)(((uint8_t*)g_virt) + offset)); 19 | } 20 | 21 | #define N 2048 22 | #define F 34 23 | #define THRESHOLD 2 24 | #define NIL N 25 | 26 | typedef struct { 27 | uint8_t dict[N+F-1]; 28 | 29 | // Describes longest match. Set by _InsertNode. 30 | int matchPos, matchLen; 31 | // Left and right children and parents. Makes up a binary search tree. 32 | int lson[N+1], rson[N+257], parent[N+1]; 33 | } compressor_state; 34 | 35 | // Inserts a string of length F, text_buf[r..r+F-1] into one of the trees 36 | // (dict[r]'th tree) and returns the longest-match position and length via the 37 | // state variables matchPosition and matchLength. If matchLength == F, then 38 | // removes the old node in favour of the new one, because the old one will be 39 | // deleted sooner. Note that r plays a double role, as the tree node index and 40 | // the position in the buffer. 41 | static void _InsertNode(compressor_state *st, int r) { 42 | int p, cmp; 43 | uint8_t *key; 44 | 45 | uint8_t *dict = st->dict; 46 | int *lson = st->lson, *rson = st->rson, *parent = st->parent; 47 | 48 | cmp = 1; 49 | key = &dict[r]; 50 | p = N+1+key[0]; 51 | rson[r] = lson[r] = NIL; 52 | st->matchLen = 0; 53 | for (;;) { 54 | if (cmp >= 0) { 55 | if (rson[p] != NIL) 56 | p = rson[p]; 57 | else { 58 | rson[p] = r; 59 | parent[r] = p; 60 | return; 61 | } 62 | } else { 63 | if (lson[p] != NIL) 64 | p = lson[p]; 65 | else { 66 | lson[p] = r; 67 | parent[r] = p; 68 | return; 69 | } 70 | } 71 | 72 | // Compare. 73 | int i; 74 | for (i=1; i st->matchLen) { 80 | // We have found a longer match. 81 | st->matchPos = p; 82 | st->matchLen = i; 83 | if (i >= F) 84 | // Maximum match length, stop looking. 85 | break; 86 | } 87 | } 88 | 89 | parent[r] = parent[p]; 90 | lson[r] = lson[p]; 91 | rson[r] = rson[p]; 92 | parent[lson[p]] = r; 93 | parent[rson[p]] = r; 94 | if (rson[parent[p]] == p) 95 | rson[parent[p]] = r; 96 | else 97 | lson[parent[p]] = r; 98 | parent[p] = NIL; 99 | } 100 | 101 | // Deletes node p from the tree. 102 | static void _DeleteNode(compressor_state *st, int p) { 103 | int q; 104 | 105 | int *lson = st->lson, *rson = st->rson, *parent = st->parent; 106 | 107 | if (parent[p] == NIL) 108 | // Not in tree. 109 | return; 110 | 111 | if (rson[p] == NIL) 112 | q = lson[p]; 113 | else if (lson[p] == NIL) 114 | q = rson[p]; 115 | else { 116 | q = lson[p]; 117 | if (rson[q] != NIL) { 118 | do 119 | q = rson[q]; 120 | while (rson[q] != NIL); 121 | rson[parent[q]] = lson[q]; 122 | parent[lson[q]] = parent[q]; 123 | lson[q] = lson[p]; 124 | parent[lson[p]] = q; 125 | } 126 | 127 | rson[q] = rson[p]; 128 | parent[rson[p]] = q; 129 | } 130 | 131 | parent[q] = parent[p]; 132 | if (rson[parent[p]] == p) 133 | rson[parent[p]] = q; 134 | else 135 | lson[parent[p]] = q; 136 | parent[p] = NIL; 137 | } 138 | 139 | // Compression routine adapted from original 1989 LZSS.C by Haruhiko Okumura. 140 | // "Use, distribute, and modify this program freely." 141 | static void _Compress(const void *in, size_t inBytes, FILE *fo, size_t *bytesRead, size_t *bytesWritten) { 142 | const uint8_t *in_ = in; 143 | const uint8_t *inEnd = in_ + inBytes; 144 | size_t bytesWritten_ = 0; 145 | 146 | compressor_state st; 147 | 148 | if (!inBytes) 149 | return; 150 | 151 | // Initialize tree. 152 | for (int i=N+1; i <= N+256; ++i) 153 | st.rson[i] = NIL; 154 | for (int i=0; i= len) 181 | st.matchLen = len; 182 | 183 | if (st.matchLen <= THRESHOLD) { 184 | // Not long enough match. Send one byte. 185 | st.matchLen = 1; 186 | codeBuf[0] |= mask; // "Send one byte" flag. 187 | codeBuf[codeBufPtr++] = st.dict[r]; // Send uncoded. 188 | //printf(" LIT 0x%02x\n", st.dict[r]); 189 | } else { 190 | // Send position and length pair. Note that matchLen > THRESHOLD. 191 | //printf(" REF off=%4u len=%4u\n", st.matchPos, st.matchLen); 192 | //printf(" "); 193 | //for (size_t j=0; j> 3) & 0xE0) | (st.matchLen - (THRESHOLD+1))); 199 | } 200 | 201 | mask <<= 1; 202 | if (!mask) { 203 | // Send at most eight units of code together. 204 | for (i=0; i 0); 237 | 238 | // Send remaining code. 239 | if (codeBufPtr > 1) { 240 | for (i=0; i\n"); 259 | fprintf(stderr, "Stamps APE code image with CRC, compresses sections, performs sanity checks. Build system use only.\n"); 260 | return 2; 261 | } 262 | 263 | int fd = open(argv[1], O_RDONLY|O_SYNC); 264 | if (fd < 0) 265 | return 1; 266 | 267 | struct stat st; 268 | ec = fstat(fd, &st); 269 | if (ec < 0) { 270 | fprintf(stderr, "can't fstat\n"); 271 | return 1; 272 | } 273 | 274 | const void *virt = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 275 | if (!virt) { 276 | fprintf(stderr, "can't mmap\n"); 277 | return 1; 278 | } 279 | 280 | if (st.st_size % 4) { 281 | fprintf(stderr, "error: filesize not a multiple of 4\n"); 282 | return 1; 283 | } 284 | 285 | g_virt = virt; 286 | const ape_header *hdr = virt; 287 | 288 | if (memcmp(hdr->magic, "BCM\x1A", 4)) { 289 | fprintf(stderr, "image has bad magic\n"); 290 | return 1; 291 | } 292 | 293 | if (hdr->numSections != 4) { 294 | fprintf(stderr, "unexpected number of sections\n"); 295 | return 1; 296 | } 297 | 298 | if (hdr->headerSize*4 < (sizeof(ape_header) - sizeof(hdr->sections) + sizeof(ape_section)*hdr->numSections)) { 299 | fprintf(stderr, "short header\n"); 300 | return 1; 301 | } 302 | 303 | if (le32toh(hdr->entrypoint) != 0x001080C0) { 304 | fprintf(stderr, "bad entrypoint\n"); 305 | return 1; 306 | } 307 | 308 | if (le32toh(hdr->headerChecksum) != 0xDEADBEEF) { 309 | fprintf(stderr, "header CRC placeholder not found\n"); 310 | return 1; 311 | } 312 | 313 | if (L32(st.st_size-4) != 0xDEADBEEF) { 314 | fprintf(stderr, "trailing CRC placeholder not found, 0x%08X\n", L32(st.st_size-4)); 315 | return 1; 316 | } 317 | 318 | ape_header *hdr2 = malloc(hdr->headerSize*4); 319 | assert(hdr2); 320 | 321 | memcpy(hdr2, hdr, hdr->headerSize*4); 322 | 323 | for (size_t i=0; inumSections; ++i) 324 | if (le32toh(hdr2->sections[i].checksum) != 0xDEADBEEF 325 | && !(le32toh(hdr2->sections[i].offsetFlags & APE_SECTION_FLAG_ZERO_ON_FAST_BOOT))) { 326 | fprintf(stderr, "section CRC placeholder not found\n"); 327 | return 1; 328 | } 329 | 330 | FILE *fo = fopen(argv[2], "w+b"); 331 | if (!fo) { 332 | fprintf(stderr, "cannot open output file\n"); 333 | return 1; 334 | } 335 | 336 | size_t bodyStart = hdr2->headerSize*4; 337 | ec = fseek(fo, bodyStart, SEEK_SET); 338 | if (ec < 0) { 339 | fprintf(stderr, "seek error?\n"); 340 | return 1; 341 | } 342 | 343 | // Check sections. 344 | size_t curOffset = bodyStart; 345 | for (size_t i=0; inumSections; ++i) { 346 | bool isZero = (le32toh(hdr2->sections[i].offsetFlags) & APE_SECTION_FLAG_ZERO_ON_FAST_BOOT); 347 | bool isCompressed = (le32toh(hdr2->sections[i].offsetFlags) & APE_SECTION_FLAG_COMPRESSED); 348 | 349 | if (isZero) { 350 | if (isCompressed) { 351 | fprintf(stderr, "BSS section is compressed\n"); 352 | return 1; 353 | } 354 | 355 | if (le32toh(hdr2->sections[i].compressedSize)) { 356 | fprintf(stderr, "BSS section with nonzero compressed size\n"); 357 | return 1; 358 | } 359 | 360 | if (le32toh(hdr2->sections[i].checksum)) { 361 | fprintf(stderr, "BSS section with nonzero checksum\n"); 362 | return 1; 363 | } 364 | 365 | if (le32toh(hdr2->sections[i].offsetFlags) & APE_SECTION_FLAG_CHECKSUM_IS_CRC32) { 366 | fprintf(stderr, "BSS section with CRC32 flag\n"); 367 | return 1; 368 | } 369 | 370 | continue; 371 | } 372 | 373 | if (isCompressed) { 374 | fprintf(stderr, "section already compressed?\n"); 375 | return 1; 376 | } 377 | 378 | if (le32toh(hdr2->sections[i].compressedSize)) { 379 | fprintf(stderr, "nonzero compressed size\n"); 380 | return 1; 381 | } 382 | 383 | uint32_t offset = le32toh(hdr2->sections[i].offsetFlags) & 0xFFFFFF; 384 | if (offset != curOffset) { 385 | fprintf(stderr, "gap found\n"); 386 | return 1; 387 | } 388 | 389 | uint32_t uncompSize = le32toh(hdr2->sections[i].uncompressedSize); 390 | if (uncompSize % 4) { 391 | fprintf(stderr, "section uncompressed size is not a multiple of 4 bytes\n"); 392 | return 1; 393 | } 394 | 395 | if (uncompSize > st.st_size || (offset + uncompSize) > st.st_size) { 396 | fprintf(stderr, "section exceeds file length\n"); 397 | return 1; 398 | } 399 | 400 | curOffset += uncompSize; 401 | } 402 | 403 | void *verifyInBuf = NULL, *verifyOutBuf = NULL; 404 | size_t verifyInBufSize = 0, verifyOutBufSize = 0; 405 | 406 | // Compress and output sections. 407 | for (size_t i=0; inumSections; ++i) { 408 | if (le32toh(hdr2->sections[i].offsetFlags) & APE_SECTION_FLAG_ZERO_ON_FAST_BOOT) { 409 | // Zero the offset for BSS sections, it's not used anyway. 410 | hdr2->sections[i].offsetFlags = htole32(le32toh(hdr2->sections[i].offsetFlags) & 0xFF000000); 411 | continue; 412 | } 413 | 414 | uint32_t uncompSize = le32toh(hdr->sections[i].uncompressedSize); 415 | 416 | long compStart = ftell(fo); 417 | if (compStart < 0) { 418 | fprintf(stderr, "compStart\n"); 419 | return 1; 420 | } 421 | 422 | uint32_t offset = le32toh(hdr2->sections[i].offsetFlags) & 0xFFFFFF; 423 | hdr2->sections[i].offsetFlags = htole32(compStart 424 | | APE_SECTION_FLAG_COMPRESSED | APE_SECTION_FLAG_CHECKSUM_IS_CRC32 | (1U<<27) | (i<2 ? (1U<<26) : 0)); 425 | size_t readBytes = 0; 426 | size_t writtenBytes = 0; 427 | _Compress( 428 | (uint8_t*)hdr + offset, 429 | uncompSize, 430 | fo, &readBytes, &writtenBytes); 431 | 432 | if (readBytes < uncompSize) { 433 | fprintf(stderr, "did not read all input bytes?\n"); 434 | return 1; 435 | } 436 | 437 | size_t r = writtenBytes % 4; 438 | if (r) { 439 | static const uint8_t _zeroes[4] = {}; 440 | ec = fwrite(_zeroes, 4-r, 1, fo); 441 | if (ec < 1) { 442 | fprintf(stderr, "fwrite\n"); 443 | return 1; 444 | } 445 | writtenBytes += 4-r; 446 | } 447 | 448 | hdr2->sections[i].compressedSize = htole32(writtenBytes); 449 | 450 | long compEnd = ftell(fo); 451 | if (compEnd < 0) 452 | return 1; 453 | 454 | assert(compEnd - compStart == writtenBytes); 455 | 456 | ec = fseek(fo, compStart, SEEK_SET); 457 | if (ec < 0) { 458 | fprintf(stderr, "fseek\n"); 459 | return 1; 460 | } 461 | 462 | if (compEnd - compStart > verifyInBufSize) { 463 | verifyInBufSize = compEnd - compStart; 464 | verifyInBuf = realloc(verifyInBuf, verifyInBufSize); 465 | assert(verifyInBuf); 466 | } 467 | 468 | if (uncompSize > verifyOutBufSize) { 469 | verifyOutBufSize = uncompSize; 470 | verifyOutBuf = realloc(verifyOutBuf, verifyOutBufSize); 471 | assert(verifyOutBuf); 472 | } 473 | 474 | ssize_t rd = fread(verifyInBuf, compEnd - compStart, 1, fo); 475 | if (compEnd != compStart && rd < 1) { 476 | fprintf(stderr, "fread verify %ld\n", rd); 477 | return 1; 478 | } 479 | 480 | ec = fseek(fo, compEnd, SEEK_SET); 481 | if (ec < 0) { 482 | fprintf(stderr, "compEnd\n"); 483 | return 1; 484 | } 485 | 486 | uint8_t *p = verifyOutBuf; 487 | size_t verifyRead, verifyWritten; 488 | Decompress(verifyInBuf, compEnd - compStart, uncompSize, _WriteBuf, &p, &verifyRead, &verifyWritten); 489 | 490 | if (verifyWritten != uncompSize) { 491 | fprintf(stderr, "compression verification outputted wrong amount of data (got %u bytes, expected %u)\n", verifyWritten, uncompSize); 492 | return 1; 493 | } 494 | 495 | if (memcmp(verifyOutBuf, (uint8_t*)hdr + offset, uncompSize)) { 496 | fprintf(stderr, "compression verification failed\n"); 497 | //fwrite((uint8_t*)hdr + offset, uncompSize, 1, stdout); 498 | //fwrite(verifyOutBuf, uncompSize, 1, stdout); 499 | return 1; 500 | } 501 | 502 | hdr2->sections[i].checksum = htole32(ComputeCRC((uint8_t*)hdr + offset, uncompSize/4, 0)); 503 | hdr2->sections[i].compressedSize = compEnd - compStart; 504 | } 505 | 506 | ec = fseek(fo, 0, SEEK_SET); 507 | if (ec < 0) { 508 | fprintf(stderr, "fseek fo\n"); 509 | return 1; 510 | } 511 | 512 | hdr2->headerChecksum = 0; 513 | hdr2->headerChecksum = htole32(ComputeCRC(hdr2, hdr2->headerSize, 0)); 514 | 515 | ec = fwrite(hdr2, hdr2->headerSize*4, 1, fo); 516 | if (ec < 1) 517 | return 1; 518 | 519 | ec = fseek(fo, 0, SEEK_END); 520 | if (ec < 0) 521 | return 1; 522 | 523 | // Instead of an RSA signature, provide the user with a relaxing message 524 | char rsaBuf[256] = "OTG"; 525 | static const char padStr[] = "DON'T PANIC! "; 526 | size_t padLen = strlen(padStr); 527 | for (int i=strlen(rsaBuf)+1; i < ARRAYLEN(rsaBuf); ++i) 528 | rsaBuf[i] = padStr[i % padLen]; 529 | 530 | ssize_t wr = fwrite(rsaBuf, 256, 1, fo); 531 | if (wr < 1) 532 | return 1; 533 | 534 | ec = fflush(fo); 535 | if (ec < 0) { 536 | fprintf(stderr, "can't flush\n"); 537 | return 1; 538 | } 539 | 540 | int fofd = fileno(fo); 541 | if (fofd < 0) 542 | return 1; 543 | 544 | ec = fstat(fofd, &st); 545 | if (ec < 0) { 546 | fprintf(stderr, "can't stat after write\n"); 547 | return 1; 548 | } 549 | 550 | void *fovirt = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fofd, 0); 551 | if (!fovirt) { 552 | fprintf(stderr, "cannot mmap after write\n"); 553 | return 1; 554 | } 555 | 556 | uint32_t trailingCRC = htole32(ComputeCRC(fovirt, st.st_size/4, 0xFFFFFFFF)^0xFFFFFFFF); 557 | wr = fwrite(&trailingCRC, 4, 1, fo); 558 | if (wr < 1) 559 | return 1; 560 | 561 | ec = fclose(fo); 562 | if (ec < 0) 563 | return 1; 564 | 565 | return 0; 566 | } 567 | -------------------------------------------------------------------------------- /byteswap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "otg_common.c" 3 | 4 | uint32_t buf[8192]; 5 | 6 | int main(int argc, char **argv) { 7 | size_t rd; 8 | 9 | for (;;) { 10 | rd = fread(buf, sizeof(uint32_t), ARRAYLEN(buf), stdin); 11 | if (rd <= 0) 12 | break; 13 | 14 | for (size_t i=0; i&2 "$0: usage: out.o in.c extra-args..." 7 | exit 2 8 | } 9 | 10 | OUT="$1" 11 | IN="$2" 12 | shift 13 | shift 14 | 15 | ## Ensure that we get unwrapped clang on NixOS. 16 | [ -z "$CLANG" ] && { 17 | CLANG="$(realpath $(which clang))" 18 | q="$(dirname $(dirname "$CLANG"))/nix-support/orig-cc" 19 | [ -e "$q" ] && CLANG="$(cat $q)/bin/clang" 20 | } 21 | 22 | ## Compile. 23 | "$CLANG" -cc1 \ 24 | -triple thumbv7m-v7m-unknown \ 25 | -target-cpu cortex-m3 \ 26 | -nostdsysteminc \ 27 | -emit-llvm \ 28 | -Oz "$@" \ 29 | -o "${OUT}.ll-opt" "$IN" 30 | 31 | ## Assemble. 32 | llc -o="${OUT}.s" "${OUT}.ll-opt" 33 | llc -o="${OUT}" -filetype=obj "${OUT}.ll-opt" 34 | -------------------------------------------------------------------------------- /cc_mips: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Alright, here's the deal: The "RX CPU" we're targeting is a big-endian MIPS 4 | # CPU. There are a number of things to consider here: 5 | # 6 | # 1. Cross-compilation will be necessary. clang doesn't need to be recompiled 7 | # to crosscompile, so it's more convenient to use clang. Thus this build 8 | # toolchain is designed to use clang and clang alone. 9 | # 10 | # 2. We're targeting custom embedded hardware and are invariably going to 11 | # know better than the OS environment when it comes to knowing what 12 | # options to pass to clang. So we invoke the clang -cc1 frontend directly, 13 | # rather than using the driver interface. 14 | # 15 | # 3. The RX CPU does not appear to have a hardware multiplier; that is, it 16 | # does not support the MULT instruction. However, neither GCC nor clang 17 | # support disabling output of MULT instructions when targeting MIPS, since 18 | # MULT support is a requirement of the MIPS ISA, so _theoretically_ such 19 | # hardware doesn't actually exist, right? 20 | # 21 | # This is a bit trickier to deal with. Rather than tracking down some 22 | # ancient version of gcc 2.x which still supports targeting MIPS CPUs 23 | # without MULT support, I decided to cheese it: 24 | # 25 | # The C source is careful to avoid using the multiplication operator. 26 | # Careful examination of the assembly output of clang reveals that, 27 | # thankfully, it is very rare for clang/LLVM to introduce multiplication 28 | # instructions itself. In fact, the only instance of its emitting the MULT 29 | # instruction is for a function which performs multiplication manually 30 | # using a loop - i.e., the optimizer is too smart. 31 | # 32 | # So the following things are done: 33 | # 34 | # a. The optimization passes to be enabled are manually specified, 35 | # and the optimization pass which was identified as causing 36 | # optimization of the above loop to a MULT instruction (indvars) is 37 | # removed from the list. 38 | # 39 | # Unfortunately, clang doesn't expose a command line option for 40 | # disabling optimization passes. This is apparently deliberate due to 41 | # the instability of the optimizer's command line interface. 42 | # 43 | # So we tell clang -cc1 to just emit LLVM code and pass it manually 44 | # to opt (the LLVM optimization tool), and then pass the output of 45 | # that manually to llvm-as, etc. 46 | # 47 | # b. At the last minute, the final MIPS ISA assembly is crudely grepped 48 | # for any MULT instructions. If any are found, the build fails. This 49 | # ensures that any changes to code/the build environment/clang/LLVM 50 | # which cause MULT instructions to be emitted despite the above 51 | # measures result in the build failing noisily rather than the 52 | # building of bad code. 53 | # 54 | # This shell script is used to compile a single C source file to an object 55 | # file. It invokes clang -cc1, opt and llvm-as and checks the assembly for 56 | # errant MULT (or even DIV) instructions. 57 | 58 | set -e 59 | 60 | OPTIMIZATIONS=" 61 | -globalopt 62 | -demanded-bits 63 | -branch-prob 64 | -inferattrs 65 | -ipsccp 66 | -dse 67 | -loop-simplify 68 | -scoped-noalias 69 | -barrier 70 | -adce 71 | -deadargelim 72 | -memdep 73 | -licm 74 | -globals-aa 75 | -rpo-functionattrs 76 | -basiccg 77 | -loop-idiom 78 | -forceattrs 79 | -mem2reg 80 | -simplifycfg 81 | -early-cse 82 | " 83 | # -instcombine 84 | OPTIMIZATIONS="$OPTIMIZATIONS 85 | -sccp 86 | -loop-unswitch 87 | -loop-vectorize 88 | -tailcallelim 89 | -functionattrs 90 | -loop-accesses 91 | -memcpyopt 92 | -loop-deletion" 93 | # -reassociate 94 | OPTIMIZATIONS="$OPTIMIZATIONS 95 | -strip-dead-prototypes 96 | -loops 97 | -basicaa 98 | -correlated-propagation 99 | -lcssa 100 | -domtree 101 | -always-inline 102 | -aa 103 | -block-freq 104 | -float2int 105 | -lower-expect 106 | -sroa 107 | -loop-unroll 108 | -alignment-from-assumptions 109 | -lazy-value-info 110 | -prune-eh -jump-threading 111 | -loop-rotate 112 | " 113 | #-indvars 114 | OPTIMIZATIONS="$OPTIMIZATIONS 115 | -bdce 116 | -scalar-evolution 117 | -tbaa 118 | -assumption-cache-tracker 119 | 120 | -elim-avail-extern 121 | -mldst-motion 122 | -gvn 123 | -inline 124 | -globaldce 125 | -constmerge 126 | 127 | -argpromotion 128 | "; 129 | 130 | # Usage: ./cc out.o in.c extra-args... 131 | [ -z "$2" ] && { 132 | echo >&2 "$0: usage: out.o in.c extra-args..." 133 | exit 2 134 | } 135 | 136 | OUT="$1" 137 | IN="$2" 138 | shift 139 | shift 140 | 141 | ## Ensure that we get unwrapped clang on NixOS. 142 | [ -z "$CLANG" ] && { 143 | CLANG="$(realpath $(which clang))" 144 | q="$(dirname $(dirname "$CLANG"))/nix-support/orig-cc" 145 | [ -e "$q" ] && CLANG="$(cat $q)/bin/clang" 146 | } 147 | 148 | ## Compile. 149 | "$CLANG" -cc1 \ 150 | -triple mips-unknown-none -target-cpu mips2 -nostdsysteminc \ 151 | -disable-llvm-passes -emit-llvm \ 152 | -Oz "$@" \ 153 | -o "${OUT}.ll-unopt" "$IN" 154 | 155 | ## Optimize. 156 | opt -S $OPTIMIZATIONS \ 157 | \ 158 | "${OUT}.ll-unopt" > "${OUT}.ll-opt" 159 | 160 | ## Ensure no MUL/DIV instructions have gotten through. 161 | if cat "${OUT}.ll-opt" | grep -Eiq '(mul|divu?\s|mov[nz]\s)'; then 162 | echo >&2 "Unsupported instruction detected" 163 | exit 1 164 | fi 165 | 166 | llc -o="${OUT}.s" "${OUT}.ll-opt" 167 | if cat "${OUT}.s" | grep -Eiq '(mul|divu?\s|mov[nz]\s)'; then 168 | echo >&2 "Unsupported instruction detected in assembly" 169 | exit 1 170 | fi 171 | 172 | ## Assemble 173 | llc -o="${OUT}" -filetype=obj "${OUT}.ll-opt" 174 | -------------------------------------------------------------------------------- /ncsi-dis.lua: -------------------------------------------------------------------------------- 1 | -- usage: wireshark -X lua_script:.../ncsi-dis.lua foo.cap 2 | 3 | local ncsi = Proto("ncsi", "NCSI control protocol") 4 | 5 | local types = { 6 | [0x00] = "Clear Initial State Request", 7 | [0x80] = "Clear Initial State Response", 8 | 9 | [0x01] = "Select Package Request", 10 | [0x81] = "Select Package Response", 11 | 12 | [0x02] = "Deselect Package Request", 13 | [0x82] = "Deselect Package Response", 14 | 15 | [0x03] = "Enable Channel Request", 16 | [0x83] = "Enable Channel Response", 17 | 18 | [0x04] = "Disable Channel Request", 19 | [0x84] = "Disable Channel Response", 20 | 21 | [0x05] = "Reset Channel Request", 22 | [0x85] = "Reset Channel Response", 23 | 24 | [0x06] = "Enable Channel Network Tx Request", 25 | [0x86] = "Enable Channel Network Tx Response", 26 | 27 | [0x07] = "Disable Channel Network Tx Request", 28 | [0x87] = "Disable Channel Network Tx Response", 29 | 30 | [0x08] = "AEN Enable Request", 31 | [0x88] = "AEN Enable Response", 32 | 33 | [0x09] = "Set Link Request", 34 | [0x89] = "Set Link Response", 35 | 36 | [0x0A] = "Get Link Status Request", 37 | [0x8A] = "Get Link Status Response", 38 | 39 | [0x0B] = "Set VLAN Filter Request", 40 | [0x8B] = "Set VLAN Filter Response", 41 | 42 | [0x0C] = "Enable VLAN Request", 43 | [0x8C] = "Enable VLAN Response", 44 | 45 | [0x0D] = "Disable VLAN Request", 46 | [0x8D] = "Disable VLAN Response", 47 | 48 | [0x0E] = "Set MAC Address Request", 49 | [0x8E] = "Set MAC Address Response", 50 | 51 | [0x10] = "Enable Broadcast Filter Request", 52 | [0x90] = "Enable Broadcast Filter Response", 53 | 54 | [0x11] = "Disable Broadcast Filter Request", 55 | [0x91] = "Disable Broadcast Filter Response", 56 | 57 | [0x12] = "Enable Global Multicast Filter Request", 58 | [0x92] = "Enable Global Multicast Filter Response", 59 | 60 | [0x13] = "Disable Global Multicast Filter Request", 61 | [0x93] = "Disable Global Multicast Filter Response", 62 | 63 | [0x14] = "Set NC-SI Flow Control Request", 64 | [0x94] = "Set NC-SI Flow Control Response", 65 | 66 | [0x15] = "Get Version ID Request", 67 | [0x95] = "Get Version ID Response", 68 | 69 | [0x16] = "Get Capabilities Request", 70 | [0x96] = "Get Capabilities Response", 71 | 72 | [0x17] = "Get Parameters Request", 73 | [0x97] = "Get Parameters Response", 74 | 75 | [0x18] = "Get Controller Packet Statistics Request", 76 | [0x98] = "Get Controller Packet Statistics Response", 77 | 78 | [0x19] = "Get NC-SI Statistics Request", 79 | [0x99] = "Get NC-SI Statistics Response", 80 | 81 | [0x1A] = "Get NC-SI Passthrough Statistics Request", 82 | [0x9A] = "Get NC-SI Passthrough Statistics Response", 83 | 84 | [0x1B] = "Get Package Status Request", 85 | [0x9B] = "Get Package Status Response", 86 | 87 | [0x50] = "OEM Request", 88 | [0xD0] = "OEM Response", 89 | 90 | [0x51] = "PLDM Request", 91 | [0xD1] = "PLDM Response", 92 | 93 | [0x52] = "Get Package UUID Request", 94 | [0xD2] = "Get Package UUID Response", 95 | } 96 | setmetatable(types, { 97 | __index = function(t, k) 98 | return "UNKNOWN" 99 | end 100 | }) 101 | 102 | local resCode = { 103 | [0x00] = "Command Completed", 104 | [0x01] = "Command Failed", 105 | [0x02] = "Command Unavailable", 106 | [0x03] = "Command Unsupported", 107 | } 108 | setmetatable(resCode, { 109 | __index = function(t, k) 110 | return "UNKNOWN" 111 | end 112 | }) 113 | 114 | local reaCode = { 115 | [0x0000] = "No Error", 116 | [0x0001] = "Interface Initialization Required", 117 | [0x0002] = "Parameter Invalid", 118 | [0x0003] = "Channel Not Ready", 119 | [0x0004] = "Package Not Ready", 120 | [0x0005] = "Invalid Payload Length", 121 | [0x7FFF] = "Unsupported Command", 122 | } 123 | setmetatable(reaCode, { 124 | __index = function(t, k) 125 | return "UNKNOWN" 126 | end 127 | }) 128 | 129 | function ncsi.dissector(buf, pkt, tree) 130 | pkt.cols.protocol = "NCSI-CTL" 131 | if buf:len() < 16 then 132 | return 133 | end 134 | 135 | local subtree = tree:add(ncsi, buf(), "NCSI Control Protocol Data") 136 | 137 | subtree:add(buf(0,1), "MC ID: " .. buf(0,1)) 138 | subtree:add(buf(1,1), "HdrRev: " .. buf(1,1)) 139 | subtree:add(buf(2,1), "Rsvd: " .. buf(2,1)) 140 | subtree:add(buf(3,1), "IID: " .. buf(3,1)) 141 | 142 | local typ = buf(4,1):uint() 143 | subtree:add(buf(4,1), "Control Packet Type: " .. buf(4,1) .. " (" .. typ .. ") " .. types[typ]) 144 | subtree:add(buf(5,1), "Ch. ID: " .. buf(5,1)) 145 | subtree:add(buf(6,2), "Payload Len: " .. buf(6,2)) 146 | 147 | subtree:add(buf(8,4), "Rsvd: " .. buf(8,4)) 148 | subtree:add(buf(12,4), "Rsvd: " .. buf(12,4)) 149 | 150 | local info = "[" .. buf(3,1):uint() .. "] " .. types[typ] 151 | 152 | if typ >= 0x80 then 153 | subtree:add(buf(16,2), "Response Code: " .. buf(16,2) .. " (" .. buf(16,2):uint() .. ") " .. resCode[buf(16,2):uint()]) 154 | subtree:add(buf(18,2), "Reason Code: " .. buf(18,2) .. " (" .. buf(18,2):uint() .. ") " .. reaCode[buf(18,2):uint()]) 155 | info = info .. " - " .. resCode[buf(16,2):uint()] .. ", " .. reaCode[buf(18,2):uint()] 156 | end 157 | 158 | if typ == 0x8A then 159 | subtree:add(buf(20,4), "Link Status (up=" .. buf(20,4):bitfield(31-0,1) .. ", speed=" .. buf(20,4):bitfield(31-4,4) .. ", anEn=" .. buf(20,4):bitfield(31-5,1) .. ", anDone=" .. buf(20,4):bitfield(31-6,1) .. ")") 160 | subtree:add(buf(24,4), "Other Indications") 161 | subtree:add(buf(28,4), "OEM Link Status") 162 | end 163 | 164 | pkt.cols.info:set(info) 165 | end 166 | 167 | local eth_table = DissectorTable.get("ethertype") 168 | eth_table:add(0x88f8, ncsi) 169 | -------------------------------------------------------------------------------- /notes/notes-ape.txt: -------------------------------------------------------------------------------- 1 | Boot ROM Notes 2 | Modifies REG_APE__STATUS. 3 | 4 | Erases area [0x11_5E48, 0x11_6B6C) (~3.3 KiB). 5 | Copies rest of boot ROM to 0x11_5800 and jumps to it. 6 | 7 | APE Memory Map 8 | 9 | --Code------------------------------0000_0000 10 | 0x0000_0000 \ Invalid 11 | 0x000F_FFFF / 12 | 0x0010_0000 \ Code 13 | 0x0011_7FFF / End of stack/code 14 | 0x0011_8000 \ 15 | 0x1FFF_FFFF / Invalid 16 | --SRAM------------------------------2000_0000 17 | (TODO) 18 | --Peripheral------------------------4000_0000 19 | (TODO) 20 | --External RAM----------------------6000_0000 21 | 0x6000_0000 \ Appears to be boot ROM. 22 | 0x6000_072F / 23 | ... 24 | 0x6020_0000 \ Maps to APE registers (APE+0). 25 | 0x6021_FFFF / End guessed. 26 | 0x6022_0000 \ APE Function 0 SHM (APE+0x4000). 27 | 0x6022_0FFF / 28 | 0x6022_1000 \ APE Function 1 SHM. 29 | 0x6022_1FFF / 30 | 0x6022_2000 \ APE Function 2 SHM. 31 | 0x6022_2FFF / 32 | 0x6022_3000 \ APE Function 3 SHM. 33 | 0x6022_3FFF / 34 | ... 35 | 0x6024_0000 \ NVM Registers (same layout as device register block 0x7000) 36 | 0x6024_.... / 37 | 0x6024_1000 \ APE Peripheral: SMBus 1 38 | / 39 | --External Device-------------------A000_0000 40 | 0xA000_0000 \ Unknown thing A (port 0). 41 | 0xA000_3FFF / 42 | Provides access to some sort of from-network buffer. 43 | This buffer is indexed by Series APE+0x14's head field 44 | multiplied by 128. 45 | 46 | 0xA000_4000 \ Unknown thing A (port 1). 47 | 0xA000_7FFF / 48 | 0xA000_8000 \ Unknown thing A (port 2). 49 | 0xA000_BFFF / 50 | 0xA000_C000 \ Unknown thing A (port 3). 51 | 0xA000_FFFF / 52 | 53 | 0xA002_0000 \ Unknown thing B, transmits to network (port 0). 54 | 0xA002_1FFF / 55 | 0xA002_0000 (dw0) 56 | bit 0: 6: unknown, block length? 57 | bit 7:12: receives offset of outgoing packet relative to 0xA002_0000 58 | bit 23:31: set to length of data just written 59 | XXX, the above can be written anywhere in 0xA002, not a register, 60 | just some sort of header 61 | 0xA002_0004 (dw1) 62 | 0xA002_0008 (dw2) <-- non-first block of a frame copied 63 | 0xA002_000C (dw3) to buffer starting here <-- low16 receives some sort of frame len 64 | 0xA002_0010 (dw4) 65 | 0xA002_0014 (dw5) 66 | 0xA002_0018 (dw6) 67 | 0xA002_001C (dw7) 68 | 0xA002_0020 (dw8) 69 | 0xA002_0024 (dw9) <-- low16 receives some sort of frame len, num blocks? 70 | 0xA002_0028 (dw10) 71 | 0xA002_002C (dw11) 72 | 0xA002_0030 (dw12) <-- first block of a frame copied 73 | to buffer starting here 74 | Size of this buffer appears to be ~508 bytes (copy block size up to 508 bytes). 75 | 76 | Memory probing has determined the size of this region to be 0x8000. 77 | But it might be subdivided equally between the four ports? 78 | 79 | 0xA002_2000 \ Unknown thing B (port 1). 80 | 0xA002_3FFF / 81 | 0xA002_4000 \ Unknown thing B (port 2). 82 | 0xA002_5FFF / 83 | 0xA002_6000 \ Unknown thing B (port 3). 84 | 0xA002_7FFF / 85 | 86 | 0xA004_0000 \ Device Registers (function 0, e.g. 0xA004362C -> REG_STATUS). 87 | 0xA004_FFFF / 88 | 0xA005_0000 \ Device Registers (function 1). 89 | 0xA005_FFFF / 90 | 0xA006_0000 \ Device Registers (function 2). 91 | 0xA006_FFFF / 92 | 0xA007_0000 \ Device Registers (function 3). 93 | 0xA007_FFFF / 94 | 95 | 0xA008_0000 \ H2B \___ Read in repeated sequence (read +0x4, read +0x0, read +0x4, read +0x0, etc.) 96 | 0xA008_0008 / / to consume frame. Check H2B register for frame length. Works vaguely like RMU RX. 97 | 0xA008_0400 \ B2H Func0 \ 98 | 0xA008_0404 / | 99 | 0xA008_0800 \ B2H Func1 | 100 | 0xA008_0804 / |-- B2H: Write frame to these in 64-bit blocks 101 | 0xA008_0C00 \ B2H Func2 | each 64-bit write done as two 32-bit writes, 102 | 0xA008_0C04 / | the first 4 bytes of a frame going to +0x4, second 4 going to +0x0, third four going to +0x4, etc. 103 | 0xA008_1000 \ B2H Func3 | then write to B2H Enqueue doorbell for the function 104 | 0xA008_1004 / / 105 | 106 | (TODO) 107 | --PPB Internal----------------------E000_0000 108 | (TODO) 109 | --PPB External----------------------E004_0000 110 | (TODO) 111 | --Vendor Specific-------------------E010_0000 112 | (TODO) 113 | --------------------------------------------- 114 | 115 | 116 | 117 | 0x6000_0000 \ Looks like the boot ROM. 118 | 0x6000_072F / 119 | 120 | 0x6020_0000 Maps to APE registers (APE+0) 121 | 122 | 0x6022_0000 Maps to APE shared memory region (APE+0x4000). Appears to 123 | correspond to function 0. 124 | 0x6022_0000: test for 'APE!' magic 125 | 126 | APE 0x4100 0x6022_0100: RCPU magic ('RCPU'), written by RX CPU 127 | APE 0x4104 0x6022_0104: RCPU segment length 128 | APE 0x4108 0x6022_0108: RCPU init count 129 | APE 0x410C 0x6022_010C: RX firmware version 130 | APE 0x4110 0x6022_0110: GEN_CFG_FEATURE (GEN 1E4) 131 | APE 0x4114 0x6022_0114: PCI Vendor/Device ID 132 | APE 0x4118 0x6022_0118: PCI Subsystem ID 133 | APE 0x4128 0x6022_0128: GEN_CFG_HW (GEN 1E8) 134 | APE 0x412C 0x6022_012C: GEN_CFG_HW_2 (GEN 2A8) 135 | APE 0x4130 0x6022_0130: g_cpmuStatus (REG_STATUS[31:16]||0x362C) 136 | 137 | APE 0x481C 0x6022_081C: build date string 138 | APE 0x4810 0x6022_0810: build time string 139 | 0x6022_0890: Set to REG 0x3658 140 | 141 | 0x6022_1000 Seems to be Function 1 APE SHM 142 | 0x6022_2000 Seems to be Function 2 APE SHM 143 | 0x6022_3000 Seems to be Function 3 APE SHM 144 | 145 | 0x6024_0000 Probably APE Peripheral Space 146 | 0x6024_0000 NVM 147 | 0x6024_0100 SMBus 1 148 | 149 | 0x6026 not seen 150 | 151 | 0xA000_0000 unknown, split by port: 152 | 0xA000_0000 Port 0 153 | 0xA000_4000 Port 1 154 | 0xA000_8000 Port 2 155 | 0xA000_C000 Port 3 156 | 157 | 0xA002_0000 unknown, split by port: 158 | 0xA002_0000 Port 0 159 | 0xA002_2000 Port 1 160 | 0xA002_4000 Port 2 161 | 0xA002_6000 Port 3 162 | 163 | 0xA004_0000 Device Registers (confirmed func 0, e.g. 0xA004362C -> REG_STATUS) 164 | 0xA005_0000 Device Registers (confirmed func 1) 165 | 0xA006_0000 Device Registers (confirmed func 2) 166 | 0xA007_0000 Device Registers (confirmed func 3) 167 | 168 | 0xA008_0000 ??? 169 | 0xA008_0400 ??? Func0 170 | 0xA008_0800 ??? Func1 171 | Note: these don't seem to exist for Func2/3. 172 | 173 | Control register space: 174 | [0xC001_0000] = 0x00CC_CA20 Mode 175 | [0xC001_0004] = 0x0600_0200 Status 176 | [0xC001_0008] = 0x0010_0001 GPIO_MSG 177 | [0xC001_000C] = 0x0000_0000 Event 178 | [0xC001_0010] = 0x0000_0000 179 | [0xC001_0014] = 0x0000_0000 Series APE+0x14 Unk Func0 180 | [0xC001_0018] = 0x0000_0000 Series APE+0x18 Unk Func0 181 | [0xC001_001C] = 0x0000_0000 Series APE+0x1C Unk Func0 182 | [0xC001_0020] = 0x0000_0000 Series APE+0x20 Unk Func0 183 | [0xC001_0024] = 0x0000_0000 Series APE+0x24 Unk Func1 184 | [0xC001_0028] = 0x6022_0000 *** Pointer to Func0 APE SHM. ?? 185 | [0xC001_002C] = 0x00C0_C000 Mode 2 186 | [0xC001_0030] = 0x0000_0210 Status 2 187 | [0xC001_0034] = 0x0000_0000 188 | [0xC001_0038] = 0x00FF_0000 <-- ? 189 | [0xC001_003C] = 0x00FF_0000 <-- ? 190 | [0xC001_0040] = 0x0000_0000 191 | [0xC001_0044] = 0x0000_0000 192 | [0xC001_0048] = 0x0000_0000 193 | [0xC001_004C] = 0x0000_0000 194 | [0xC001_0050] = 0x0000_0000 195 | [0xC001_0054] = 0x0000_0000 196 | [0xC001_0058] = 0x0000_0000 197 | [0xC001_005C] = 0x0000_0000 198 | [0xC001_0060] = 0x0000_0000 199 | [0xC001_0064] = 0x0000_0000 200 | [0xC001_0068] = 0x0000_0000 201 | [0xC001_006C] = 0x6022_1000 *** Pointer to Func1 APE SHM. ?? 202 | Only bits 12-16 can be modified. 203 | => range [0x6022_0000, 0x6023_F000] 204 | [0xC001_0070] = 0xA000_0000 *** Func0 unk addr 205 | [0xC001_0074] = 0xA002_0000 *** Func0 unk addr 206 | [0xC001_0078] = 0x0000_0004 < 207 | [0xC001_007C] = 0x0000_0004 < 208 | [0xC001_0080] = 0x1A06_1063 209 | [0xC001_0084] = 0x7E06_5063 210 | [0xC001_0088] = 0x0000_0000 211 | [0xC001_008C] = 0x0000_0004 < 212 | [0xC001_0090] = 0x0000_0000 213 | [0xC001_0094] = 0x0000_0000 214 | [0xC001_0098] = 0x3F00_003F <-- Func0 unk, possibly control status, 215 | slightly different to F1/F2/F3 216 | [0xC001_009C] = 0x7E00_107F <-- Func0/1 unk, possibly control/status 217 | [0xC001_00A0] = 0x0000_0000 218 | [0xC001_00A4] = 0x0000_0000 219 | [0xC001_00A8] = 0x75F9_2D27 some timer 220 | [0xC001_00AC] = 0x0601_8778 some timer 221 | [0xC001_00B0] = 0x000F_6013 some timer: Ticks 222 | [0xC001_00B4] = 0xA004_0000 Func0 devregs addr (RO) 223 | [0xC001_00B8] = 0x0000_0003 GPIO (pin0,1 UNKIN) 224 | [0xC001_00BC] = 0x0000_0000 GINT (TODO: see DIAG) 225 | [0xC001_00C0] = 0x0000_0000 226 | [0xC001_00C4] = 0x0000_0000 227 | [0xC001_00C8] = 0x0000_0000 228 | [0xC001_00CC] = 0x0000_0000 229 | [0xC001_00D0] = 0x0000_0000 230 | [0xC001_00D4] = 0x0000_0000 231 | [0xC001_00D8] = 0x0000_003C 232 | [0xC001_00DC] = 0x0000_0000 233 | [0xC001_00E0] = 0x8000_0000 "XOFF0" 234 | [0xC001_00E4] = 0x0000_0000 235 | [0xC001_00E8] = 0x0000_0000 236 | [0xC001_00EC] = 0x0000_0080 237 | [0xC001_00F0] = 0x0000_0000 238 | [0xC001_00F4] = 0x0000_0000 239 | [0xC001_00F8] = 0x0000_0000 240 | [0xC001_00FC] = 0xA000_4000 Func1 unk addr 241 | [0xC001_0100] = 0xA005_0000 Func1 devregs addr (RO) 242 | [0xC001_0104] = 0x8000_0000 "XOFF1" 243 | [0xC001_0108] = 0x0000_0000 "CM3" 244 | [0xC001_010C] = 0xA002_2000 Func1 unk addr 245 | [0xC001_0110] = 0x0000_0004 < 246 | [0xC001_0114] = 0x0000_0000 247 | [0xC001_0118] = 0x0000_0000 248 | [0xC001_011C] = 0x3F00_003F <-- Func1 unk, possibly control/status 249 | [0xC001_0120] = 0x0000_0000 Series APE+1C Unk Func1 250 | [0xC001_0124] = 0x00FF_0000 <-- ? 251 | 252 | [0xC001_0128] = 0x0000_0000 253 | [0xC001_012C] = 0x0000_0000 254 | [0xC001_0130] = 0x0000_0000 255 | [0xC001_0134] = 0x0000_0000 256 | [0xC001_0138] = 0x0000_0000 257 | [0xC001_013C] = 0x0000_0000 258 | [0xC001_0140] = 0x0000_0000 259 | [0xC001_0144] = 0x0000_0000 260 | [0xC001_0148] = 0x0000_0000 261 | [0xC001_014C] = 0x0000_0000 262 | [0xC001_0150] = 0x0000_0000 263 | [0xC001_0154] = 0x0000_0000 264 | [0xC001_0158] = 0x0000_0000 265 | [0xC001_015C] = 0x0000_0000 266 | [0xC001_0160] = 0x0000_0000 267 | [0xC001_0164] = 0x0000_0000 268 | [0xC001_0168] = 0x0000_0000 269 | [0xC001_016C] = 0x0000_0000 270 | [0xC001_0170] = 0x0000_0000 271 | [0xC001_0174] = 0x0000_0000 272 | [0xC001_0178] = 0x0000_0000 273 | [0xC001_017C] = 0x0000_0000 274 | [0xC001_0180] = 0x0000_0000 275 | [0xC001_0184] = 0x0000_0000 276 | [0xC001_0188] = 0x0000_0000 277 | [0xC001_018C] = 0x0000_0000 278 | [0xC001_0190] = 0x0000_0000 279 | [0xC001_0194] = 0x0000_0000 280 | [0xC001_0198] = 0x0000_0000 281 | [0xC001_019C] = 0x0000_0000 282 | [0xC001_01A0] = 0x0000_0000 283 | [0xC001_01A4] = 0x0000_0000 284 | [0xC001_01A8] = 0x0000_0000 285 | [0xC001_01AC] = 0x0000_0000 286 | [0xC001_01B0] = 0x0000_0000 287 | [0xC001_01B4] = 0x0000_0000 288 | [0xC001_01B8] = 0x0000_0000 289 | [0xC001_01BC] = 0x0000_0000 290 | [0xC001_01C0] = 0x0000_0000 291 | [0xC001_01C4] = 0x0000_0000 292 | [0xC001_01C8] = 0x0000_0000 293 | [0xC001_01CC] = 0x0000_0000 294 | [0xC001_01D0] = 0x0000_0000 295 | [0xC001_01D4] = 0x0000_0000 296 | [0xC001_01D8] = 0x0000_0000 297 | [0xC001_01DC] = 0x0000_0000 298 | [0xC001_01E0] = 0x0000_0000 299 | [0xC001_01E4] = 0x0000_0000 300 | [0xC001_01E8] = 0x0000_0000 301 | [0xC001_01EC] = 0x0000_0000 302 | [0xC001_01F0] = 0x0000_0000 303 | [0xC001_01F4] = 0x0000_0000 304 | [0xC001_01F8] = 0x0000_0000 305 | [0xC001_01FC] = 0x0000_0000 306 | 307 | ---------------------------- 308 | [0xC001_0200] = 0x0000_0000 (TX/RX stuff) -- Func2-related stuff probably starts here -- 309 | [0xC001_0204] = 0x0000_0000 310 | [0xC001_0208] = 0x0000_0000 311 | [0xC001_020C] = 0xA000_8000 Func2 unk addr 312 | [0xC001_0210] = 0xA002_4000 Func2 unk addr 313 | [0xC001_0214] = 0x0000_0004 (TX/RX stuff) 314 | [0xC001_0218] = 0x0000_0000 (TX/RX stuff) 315 | [0xC001_021C] = 0x7E00_107F (TX/RX stuff) 316 | [0xC001_0220] = 0x0000_0004 <-- possible status/control stuff 317 | [0xC001_0224] = 0x0000_0000 318 | [0xC001_0228] = 0x0000_0000 319 | [0xC001_022C] = 0x3F00_003F <-- possible status/control stuff 320 | [0xC001_0230] = 0x6022_2000 Func2 APE SHM... 321 | [0xC001_0234] = 0xA006_0000 Func2 devreg... 322 | [0xC001_0238] = 0x8000_0000 Func2 thing always 0x8000_0000? 323 | [0xC001_023C] = 0x0000_0000 \ 324 | [0xC001_0240] = 0x0000_0000 \ 325 | [0xC001_0244] = 0x0000_0000 \ 326 | [0xC001_0248] = 0x0000_0000 \ 327 | [0xC001_024C] = 0x0000_0000 | could be RFU 328 | [0xC001_0250] = 0x0000_0000 | 329 | [0xC001_0254] = 0x0000_0000 330 | [0xC001_0258] = 0x0000_0000 331 | [0xC001_025C] = 0x0000_0000 332 | [0xC001_0260] = 0x0000_0000 333 | [0xC001_0264] = 0x0000_0000 334 | [0xC001_0268] = 0x0000_0000 335 | [0xC001_026C] = 0x0000_0000 336 | [0xC001_0270] = 0x0000_0000 337 | [0xC001_0274] = 0x0000_0000 338 | [0xC001_0278] = 0x0000_0000 339 | [0xC001_027C] = 0x0000_0000 340 | [0xC001_0280] = 0x0000_0000 341 | [0xC001_0284] = 0x0000_0000 342 | [0xC001_0288] = 0x0000_0000 343 | [0xC001_028C] = 0x0000_0000 344 | [0xC001_0290] = 0x0000_0000 345 | [0xC001_0294] = 0x0000_0000 346 | [0xC001_0298] = 0x0000_0000 347 | [0xC001_029C] = 0x0000_0000 348 | [0xC001_02A0] = 0x0000_0000 349 | [0xC001_02A4] = 0x0000_0000 350 | [0xC001_02A8] = 0x0000_0000 351 | [0xC001_02AC] = 0x0000_0000 352 | [0xC001_02B0] = 0x0000_0000 353 | [0xC001_02B4] = 0x0000_0000 354 | [0xC001_02B8] = 0x0000_0000 355 | [0xC001_02BC] = 0x0000_0000 356 | [0xC001_02C0] = 0x0000_0000 357 | [0xC001_02C4] = 0x0000_0000 358 | [0xC001_02C8] = 0x0000_0000 359 | [0xC001_02CC] = 0x0000_0000 360 | [0xC001_02D0] = 0x0000_0000 361 | [0xC001_02D4] = 0x0000_0000 362 | [0xC001_02D8] = 0x0000_0000 363 | [0xC001_02DC] = 0x0000_0000 364 | [0xC001_02E0] = 0x0000_0000 365 | [0xC001_02E4] = 0x0000_0000 366 | [0xC001_02E8] = 0x0000_0000 | 367 | [0xC001_02EC] = 0x0000_0000 | 368 | [0xC001_02F0] = 0x0000_0000 / 369 | [0xC001_02F4] = 0x0000_0000 / 370 | [0xC001_02F8] = 0x0000_0000 / 371 | [0xC001_02FC] = 0x0000_0000 / 372 | [0xC001_0300] = 0x0000_0000 (TX/RX stuff) -- Func3-related stuff probably starts here -- 373 | [0xC001_0304] = 0x0000_0000 374 | [0xC001_0308] = 0x0000_0000 375 | [0xC001_030C] = 0xA000_C000 Func3 unk addr 376 | [0xC001_0310] = 0xA002_6000 Func3 unk addr 377 | [0xC001_0314] = 0x0000_0004 (TX/RX stuff) 378 | [0xC001_0318] = 0x0000_0000 (TX/RX stuff) 379 | [0xC001_031C] = 0x7E00_107F (TX/RX stuff) 380 | [0xC001_0320] = 0x0000_0004 <-- same 381 | [0xC001_0324] = 0x0000_0000 382 | [0xC001_0328] = 0x0000_0000 383 | [0xC001_032C] = 0x3F00_003F <-- same 384 | [0xC001_0330] = 0x6022_3000 Func3 APE SHM 385 | [0xC001_0334] = 0xA007_0000 Func3 devreg 386 | [0xC001_0338] = 0x8000_0000 Func3 thing always 0x8000_0000? 387 | [0xC001_033C] = 0x0000_0000 388 | [0xC001_0340] = 0x0000_0000 389 | -------------------------------------------------------------------------------- /otg_common.c: -------------------------------------------------------------------------------- 1 | #include "otg.h" 2 | #ifdef OTG_HOST 3 | # include 4 | #endif 5 | 6 | static const uint8_t g_apePortTable[] = {APE_PORT_PHY0, APE_PORT_PHY1, APE_PORT_PHY2, APE_PORT_PHY3}; //0,2,3,5 7 | 8 | static void Sleep(uint32_t delay) { 9 | #ifdef PROPRIETARY 10 | /* scrubbed */ 11 | /* scrubbed */ 12 | #endif 13 | } 14 | 15 | // Wait for the MII communication interface to not be busy. 16 | static void MIIWait(void) { 17 | #ifdef PROPRIETARY 18 | /* scrubbed */ 19 | #endif 20 | } 21 | 22 | // Read an MDI value via the MII communication interface. 23 | // phyNo and regNo are 5 bits. 24 | static uint16_t MIIRead(uint8_t phyNo, uint8_t regNo) { 25 | #ifdef PROPRIETARY 26 | /* scrubbed */ 27 | /* scrubbed */ 28 | /* scrubbed */ 29 | /* scrubbed */ 30 | /* scrubbed */ 31 | #endif 32 | } 33 | 34 | // Begin writing an MDI value via the MII communication interface. Does not 35 | // wait for the write to finish, but does wait for any previous writes to 36 | // finish first. Use MIIWait afterwards if synchronous operation is required. 37 | static void MIIWrite(uint8_t phyNo, uint8_t regNo, uint16_t data) { 38 | #ifdef PROPRIETARY 39 | /* scrubbed */ 40 | /* scrubbed */ 41 | /* scrubbed */ 42 | #endif 43 | } 44 | 45 | static void MIIOr(uint8_t phyNo, uint8_t regNo, uint16_t bitsToOr) { 46 | #ifdef PROPRIETARY 47 | /* scrubbed */ 48 | /* scrubbed */ 49 | #endif 50 | } 51 | 52 | // Mask out a bit of a MII register and rewrite it. 53 | static void MIIMask(uint8_t phyNo, uint8_t regNo, uint16_t bitsToDisable) { 54 | #ifdef PROPRIETARY 55 | /* scrubbed */ 56 | /* scrubbed */ 57 | #endif 58 | } 59 | 60 | // Sets or clears a bit of a MII register and rewrites it. 61 | static void MIISetClear(uint8_t phyNo, uint8_t regNo, uint16_t bit, bool yes) { 62 | #ifdef PROPRIETARY 63 | /* scrubbed */ 64 | /* scrubbed */ 65 | /* scrubbed */ 66 | /* scrubbed */ 67 | /* scrubbed */ 68 | /* scrubbed */ 69 | #endif 70 | } 71 | 72 | // This is only used with the SERDES PHYs - the GPHYs have a different register 73 | // map with a different method of overloading the limited MII register space, 74 | // so never try and use it on a GPHY. 75 | static void MIISelectBlockSERDES(uint8_t phyNo, uint16_t blockNo) { 76 | #ifdef PROPRIETARY 77 | /* scrubbed */ 78 | /* scrubbed */ 79 | #endif 80 | } 81 | 82 | static void NVMAcquire(void) { 83 | #ifdef PROPRIETARY 84 | /* scrubbed */ 85 | /* scrubbed */ 86 | /* scrubbed */ 87 | /* scrubbed */ 88 | /* scrubbed */ 89 | /* scrubbed */ 90 | /* scrubbed */ 91 | #endif 92 | } 93 | 94 | static uint32_t NVMAcquireAndRelease(void) { 95 | #ifdef PROPRIETARY 96 | /* scrubbed */ 97 | /* scrubbed */ 98 | /* scrubbed */ 99 | /* scrubbed */ 100 | /* scrubbed */ 101 | /* scrubbed */ 102 | /* scrubbed */ 103 | /* scrubbed */ 104 | /* scrubbed */ 105 | #endif 106 | } 107 | 108 | static void NVMWaitDone(void) { 109 | #ifdef PROPRIETARY 110 | /* scrubbed */ 111 | #endif 112 | } 113 | 114 | // TODO: refactor 115 | static uint32_t NVMGetPageSize(void) { 116 | #ifdef PROPRIETARY 117 | /* scrubbed */ 118 | /* scrubbed */ 119 | /* scrubbed */ 120 | /* scrubbed */ 121 | /* scrubbed */ 122 | /* scrubbed */ 123 | /* scrubbed */ 124 | /* scrubbed */ 125 | /* scrubbed */ 126 | /* scrubbed */ 127 | /* scrubbed */ 128 | /* scrubbed */ 129 | /* scrubbed */ 130 | /* scrubbed */ 131 | /* scrubbed */ 132 | #endif 133 | } 134 | 135 | #if defined(STAGE1) 136 | extern uint32_t g_using264BytePagesForNVM; 137 | 138 | static uint32_t NVMInflateByteCountIfUsing264BytePages(uint32_t byteOffset) { 139 | #ifdef PROPRIETARY 140 | /* scrubbed */ 141 | /* scrubbed */ 142 | /* scrubbed */ 143 | /* scrubbed */ 144 | /* scrubbed */ 145 | /* scrubbed */ 146 | /* scrubbed */ 147 | /* scrubbed */ 148 | /* scrubbed */ 149 | /* scrubbed */ 150 | /* scrubbed */ 151 | /* scrubbed */ 152 | /* scrubbed */ 153 | /* scrubbed */ 154 | #endif 155 | } 156 | 157 | #elif defined(STAGE2) || defined(OTG_HOST) 158 | static uint32_t NVMInflateByteCountIfUsing264BytePages(uint32_t byteOffset) { 159 | #ifdef PROPRIETARY 160 | /* scrubbed */ 161 | /* scrubbed */ 162 | /* scrubbed */ 163 | /* scrubbed */ 164 | /* scrubbed */ 165 | /* scrubbed */ 166 | /* scrubbed */ 167 | /* scrubbed */ 168 | /* scrubbed */ 169 | /* scrubbed */ 170 | /* scrubbed */ 171 | /* scrubbed */ 172 | /* scrubbed */ 173 | /* scrubbed */ 174 | /* scrubbed */ 175 | /* scrubbed */ 176 | /* scrubbed */ 177 | /* scrubbed */ 178 | /* scrubbed */ 179 | /* scrubbed */ 180 | /* scrubbed */ 181 | /* scrubbed */ 182 | /* scrubbed */ 183 | /* scrubbed */ 184 | /* scrubbed */ 185 | /* scrubbed */ 186 | /* scrubbed */ 187 | #endif 188 | } 189 | #else 190 | # error STAGE1/STAGE2 not defined 191 | #endif 192 | 193 | // Takes flags ARB_ACQUIRE, ARB_RELEASE. 194 | static uint32_t NVMRead32(uint32_t byteOffset, uint32_t flags) { 195 | #ifdef PROPRIETARY 196 | /* scrubbed */ 197 | /* scrubbed */ 198 | /* scrubbed */ 199 | /* scrubbed */ 200 | /* scrubbed */ 201 | /* scrubbed */ 202 | /* scrubbed */ 203 | /* scrubbed */ 204 | /* scrubbed */ 205 | /* scrubbed */ 206 | /* scrubbed */ 207 | /* scrubbed */ 208 | /* scrubbed */ 209 | /* scrubbed */ 210 | /* scrubbed */ 211 | /* scrubbed */ 212 | /* scrubbed */ 213 | /* scrubbed */ 214 | /* scrubbed */ 215 | /* scrubbed */ 216 | /* scrubbed */ 217 | /* scrubbed */ 218 | /* scrubbed */ 219 | /* scrubbed */ 220 | #endif 221 | } 222 | 223 | #ifdef OTG_HOST 224 | static void NVMReadBulk(uint32_t byteOffset, void *buf, size_t numWords, uint32_t flags) { 225 | #ifdef PROPRIETARY 226 | /* scrubbed */ 227 | /* scrubbed */ 228 | /* scrubbed */ 229 | /* scrubbed */ 230 | /* scrubbed */ 231 | /* scrubbed */ 232 | /* scrubbed */ 233 | /* scrubbed */ 234 | /* scrubbed */ 235 | /* scrubbed */ 236 | /* scrubbed */ 237 | /* scrubbed */ 238 | /* scrubbed */ 239 | /* scrubbed */ 240 | /* scrubbed */ 241 | /* scrubbed */ 242 | /* scrubbed */ 243 | /* scrubbed */ 244 | /* scrubbed */ 245 | /* scrubbed */ 246 | /* scrubbed */ 247 | /* scrubbed */ 248 | /* scrubbed */ 249 | /* scrubbed */ 250 | /* scrubbed */ 251 | /* scrubbed */ 252 | /* scrubbed */ 253 | /* scrubbed */ 254 | /* scrubbed */ 255 | /* scrubbed */ 256 | #endif 257 | } 258 | 259 | static void NVMWrite32(uint32_t byteOffset, uint32_t value, uint32_t flags) { 260 | #ifdef PROPRIETARY 261 | /* scrubbed */ 262 | /* scrubbed */ 263 | /* scrubbed */ 264 | /* scrubbed */ 265 | /* scrubbed */ 266 | /* scrubbed */ 267 | /* scrubbed */ 268 | /* scrubbed */ 269 | /* scrubbed */ 270 | /* scrubbed */ 271 | /* scrubbed */ 272 | /* scrubbed */ 273 | /* scrubbed */ 274 | /* scrubbed */ 275 | /* scrubbed */ 276 | /* scrubbed */ 277 | /* scrubbed */ 278 | /* scrubbed */ 279 | /* scrubbed */ 280 | /* scrubbed */ 281 | /* scrubbed */ 282 | /* scrubbed */ 283 | /* scrubbed */ 284 | /* scrubbed */ 285 | /* scrubbed */ 286 | /* scrubbed */ 287 | /* scrubbed */ 288 | /* scrubbed */ 289 | #endif 290 | } 291 | 292 | // untested 293 | static void NVMWriteBulk(uint32_t byteOffset, const void *buf, uint32_t numWords, uint32_t flags) { 294 | #ifdef PROPRIETARY 295 | /* scrubbed */ 296 | /* scrubbed */ 297 | /* scrubbed */ 298 | /* scrubbed */ 299 | /* scrubbed */ 300 | /* scrubbed */ 301 | /* scrubbed */ 302 | /* scrubbed */ 303 | /* scrubbed */ 304 | /* scrubbed */ 305 | /* scrubbed */ 306 | /* scrubbed */ 307 | /* scrubbed */ 308 | /* scrubbed */ 309 | /* scrubbed */ 310 | /* scrubbed */ 311 | /* scrubbed */ 312 | /* scrubbed */ 313 | /* scrubbed */ 314 | /* scrubbed */ 315 | /* scrubbed */ 316 | /* scrubbed */ 317 | /* scrubbed */ 318 | /* scrubbed */ 319 | /* scrubbed */ 320 | /* scrubbed */ 321 | /* scrubbed */ 322 | /* scrubbed */ 323 | /* scrubbed */ 324 | /* scrubbed */ 325 | /* scrubbed */ 326 | /* scrubbed */ 327 | /* scrubbed */ 328 | /* scrubbed */ 329 | /* scrubbed */ 330 | /* scrubbed */ 331 | /* scrubbed */ 332 | /* scrubbed */ 333 | /* scrubbed */ 334 | /* scrubbed */ 335 | /* scrubbed */ 336 | /* scrubbed */ 337 | /* scrubbed */ 338 | /* scrubbed */ 339 | /* scrubbed */ 340 | /* scrubbed */ 341 | /* scrubbed */ 342 | /* scrubbed */ 343 | /* scrubbed */ 344 | /* scrubbed */ 345 | /* scrubbed */ 346 | /* scrubbed */ 347 | /* scrubbed */ 348 | /* scrubbed */ 349 | /* scrubbed */ 350 | /* scrubbed */ 351 | /* scrubbed */ 352 | /* scrubbed */ 353 | #endif 354 | } 355 | #endif 356 | 357 | static uint32_t NVMFindEntry(uint8_t desiredTag) { 358 | #ifdef PROPRIETARY 359 | /* scrubbed */ 360 | /* scrubbed */ 361 | /* scrubbed */ 362 | /* scrubbed */ 363 | /* scrubbed */ 364 | /* scrubbed */ 365 | /* scrubbed */ 366 | /* scrubbed */ 367 | /* scrubbed */ 368 | /* scrubbed */ 369 | /* scrubbed */ 370 | #endif 371 | } 372 | 373 | // len is in words. 374 | static uint32_t ComputeCRC(const void *base, uint32_t len, uint32_t initialValue) { 375 | #ifdef PROPRIETARY 376 | /* scrubbed */ 377 | /* scrubbed */ 378 | /* scrubbed */ 379 | /* scrubbed */ 380 | /* scrubbed */ 381 | /* scrubbed */ 382 | /* scrubbed */ 383 | /* scrubbed */ 384 | /* scrubbed */ 385 | /* scrubbed */ 386 | /* scrubbed */ 387 | /* scrubbed */ 388 | /* scrubbed */ 389 | /* scrubbed */ 390 | /* scrubbed */ 391 | /* scrubbed */ 392 | /* scrubbed */ 393 | /* scrubbed */ 394 | /* scrubbed */ 395 | /* scrubbed */ 396 | #endif 397 | } 398 | 399 | static void DebugPrint(const char *msg) { 400 | #ifdef OTG_HOST 401 | puts(msg); 402 | #else 403 | for (;*msg;) { 404 | uint32_t x = 0; 405 | uint8_t c; 406 | for (size_t i=0; i<4; ++i) { 407 | c = *msg; 408 | if (c) { 409 | x <<= 8; 410 | x |= c; 411 | ++msg; 412 | } 413 | } 414 | 415 | SetGencom32(0x360, x); 416 | SetGencom32(0x364, 1); 417 | while (GetGencom32(0x364)) 418 | if (GetGencom32(0x368) != 0xDECAFBAD) 419 | return; 420 | } 421 | #endif 422 | } 423 | 424 | 425 | #ifdef OTG_HOST 426 | static bool g_readFail = false; 427 | 428 | static uint32_t _SetReadFailure(const char *msg) { 429 | if (!g_readFail) { 430 | fprintf(stderr, "WARNING: Read failure occurred. Further failures will not be indicated, and failed reads will return 0xFFFF_FFFF. Cause of first failure: %s\n", msg); 431 | g_readFail = true; 432 | } 433 | 434 | return 0xFFFFFFFF; 435 | } 436 | 437 | // General APE Locking 438 | // --------------------------------------------------------------------------- 439 | 440 | // lockType: APE_PORT_* 441 | static uint32_t GetAPELockBit(uint32_t lockType) { 442 | #ifdef PROPRIETARY 443 | /* scrubbed */ 444 | /* scrubbed */ 445 | /* scrubbed */ 446 | /* scrubbed */ 447 | /* scrubbed */ 448 | #endif 449 | } 450 | 451 | // lockType: APE_PORT_* 452 | static int LockAPE(uint32_t lockType) { 453 | #ifdef PROPRIETARY 454 | /* scrubbed */ 455 | /* scrubbed */ 456 | /* scrubbed */ 457 | /* scrubbed */ 458 | /* scrubbed */ 459 | /* scrubbed */ 460 | /* scrubbed */ 461 | /* scrubbed */ 462 | /* scrubbed */ 463 | /* scrubbed */ 464 | /* scrubbed */ 465 | /* scrubbed */ 466 | /* scrubbed */ 467 | #endif 468 | } 469 | 470 | // lockType: APE_PORT_* 471 | static void UnlockAPE(uint32_t lockType) { 472 | #ifdef PROPRIETARY 473 | /* scrubbed */ 474 | #endif 475 | } 476 | 477 | // APE Event System 478 | // --------------------------------------------------------------------------- 479 | 480 | static int WaitAPEEvent(void) { 481 | #ifdef PROPRIETARY 482 | /* scrubbed */ 483 | /* scrubbed */ 484 | /* scrubbed */ 485 | /* scrubbed */ 486 | /* scrubbed */ 487 | /* scrubbed */ 488 | /* scrubbed */ 489 | /* scrubbed */ 490 | /* scrubbed */ 491 | /* scrubbed */ 492 | #endif 493 | } 494 | 495 | static int LockAPEEvent(void) { 496 | #ifdef PROPRIETARY 497 | /* scrubbed */ 498 | /* scrubbed */ 499 | /* scrubbed */ 500 | /* scrubbed */ 501 | /* scrubbed */ 502 | /* scrubbed */ 503 | /* scrubbed */ 504 | /* scrubbed */ 505 | /* scrubbed */ 506 | /* scrubbed */ 507 | /* scrubbed */ 508 | /* scrubbed */ 509 | /* scrubbed */ 510 | /* scrubbed */ 511 | /* scrubbed */ 512 | /* scrubbed */ 513 | #endif 514 | } 515 | 516 | static void UnlockAPEEvent(void) { 517 | #ifdef PROPRIETARY 518 | /* scrubbed */ 519 | #endif 520 | } 521 | 522 | static void _WarnAboutAPEState(const char *msg) { 523 | static bool _done = false; 524 | if (_done) 525 | return; 526 | 527 | fprintf(stderr, "warning: APE is not in correct state for scratchpad read/write to work; reads/writes will fail (%s)\n", msg); 528 | _done = true; 529 | } 530 | 531 | static uint32_t GetAPEEventScratchpadWord(uint32_t offset) { 532 | #ifdef PROPRIETARY 533 | /* scrubbed */ 534 | /* scrubbed */ 535 | /* scrubbed */ 536 | /* scrubbed */ 537 | /* scrubbed */ 538 | /* scrubbed */ 539 | /* scrubbed */ 540 | /* scrubbed */ 541 | /* scrubbed */ 542 | /* scrubbed */ 543 | /* scrubbed */ 544 | /* scrubbed */ 545 | /* scrubbed */ 546 | /* scrubbed */ 547 | /* scrubbed */ 548 | /* scrubbed */ 549 | /* scrubbed */ 550 | /* scrubbed */ 551 | /* scrubbed */ 552 | /* scrubbed */ 553 | /* scrubbed */ 554 | /* scrubbed */ 555 | /* scrubbed */ 556 | /* scrubbed */ 557 | /* scrubbed */ 558 | /* scrubbed */ 559 | /* scrubbed */ 560 | /* scrubbed */ 561 | /* scrubbed */ 562 | /* scrubbed */ 563 | /* scrubbed */ 564 | /* scrubbed */ 565 | /* scrubbed */ 566 | /* scrubbed */ 567 | /* scrubbed */ 568 | /* scrubbed */ 569 | /* scrubbed */ 570 | /* scrubbed */ 571 | /* scrubbed */ 572 | /* scrubbed */ 573 | /* scrubbed */ 574 | /* scrubbed */ 575 | /* scrubbed */ 576 | /* scrubbed */ 577 | /* scrubbed */ 578 | /* scrubbed */ 579 | /* scrubbed */ 580 | /* scrubbed */ 581 | #endif 582 | } 583 | 584 | void SetAPEEventScratchpadWord(uint32_t offset, uint32_t value) { 585 | #ifdef PROPRIETARY 586 | /* scrubbed */ 587 | /* scrubbed */ 588 | /* scrubbed */ 589 | /* scrubbed */ 590 | /* scrubbed */ 591 | /* scrubbed */ 592 | /* scrubbed */ 593 | /* scrubbed */ 594 | /* scrubbed */ 595 | /* scrubbed */ 596 | /* scrubbed */ 597 | /* scrubbed */ 598 | /* scrubbed */ 599 | /* scrubbed */ 600 | /* scrubbed */ 601 | /* scrubbed */ 602 | /* scrubbed */ 603 | /* scrubbed */ 604 | /* scrubbed */ 605 | /* scrubbed */ 606 | /* scrubbed */ 607 | /* scrubbed */ 608 | /* scrubbed */ 609 | /* scrubbed */ 610 | /* scrubbed */ 611 | /* scrubbed */ 612 | /* scrubbed */ 613 | /* scrubbed */ 614 | /* scrubbed */ 615 | /* scrubbed */ 616 | /* scrubbed */ 617 | /* scrubbed */ 618 | /* scrubbed */ 619 | /* scrubbed */ 620 | /* scrubbed */ 621 | /* scrubbed */ 622 | /* scrubbed */ 623 | /* scrubbed */ 624 | /* scrubbed */ 625 | /* scrubbed */ 626 | /* scrubbed */ 627 | #endif 628 | } 629 | 630 | static void _WarnAboutAPEStateShell(const char *msg) { 631 | static bool _done = false; 632 | if (_done) 633 | return; 634 | 635 | fprintf(stderr, "warning: apedbg is not running, shell reads/writes will not work (%s)\n", msg); 636 | _done = true; 637 | } 638 | 639 | static int _WaitAPEShellCmd(void) { 640 | uint32_t timeout = 10000/10; 641 | while (GetReg(REG_APE__APEDBG_CMD)) { 642 | if (!timeout--) { 643 | _WarnAboutAPEState("command timed out"); 644 | return -1; 645 | } 646 | 647 | usleep(10); 648 | } 649 | 650 | return 0; 651 | } 652 | 653 | uint32_t GetAPEMemShell(uint32_t offset) { 654 | if (GetReg(REG_APE__APEDBG_STATE) != REG_APE__APEDBG_STATE__RUNNING) { 655 | _WarnAboutAPEState("apedbg not running"); 656 | return 0xFFFFFFFF; 657 | } 658 | 659 | if (_WaitAPEShellCmd()) 660 | return 0xFFFFFFFF; 661 | 662 | SetReg(REG_APE__APEDBG_ARG0, offset); 663 | SetReg(REG_APE__APEDBG_CMD, 664 | REG_APE__APEDBG_CMD__MAGIC 665 | |REG_APE__APEDBG_CMD__TYPE__MEM_GET); 666 | 667 | if (_WaitAPEShellCmd()) 668 | return 0xFFFFFFFF; 669 | 670 | uint32_t exc = GetReg(REG_APE__APEDBG_CMD_ERROR_FLAGS); 671 | if (exc) { 672 | _WarnAboutAPEState("exception caught"); 673 | return 0xFFFFFFFF; 674 | } 675 | 676 | return GetReg(REG_APE__APEDBG_ARG1); 677 | } 678 | 679 | void SetAPEMemShell(uint32_t offset, uint32_t v) { 680 | if (GetReg(REG_APE__APEDBG_STATE) != REG_APE__APEDBG_STATE__RUNNING) { 681 | _WarnAboutAPEState("apedbg not running"); 682 | return; 683 | } 684 | 685 | if (_WaitAPEShellCmd()) 686 | return; 687 | 688 | SetReg(REG_APE__APEDBG_ARG0, offset); 689 | SetReg(REG_APE__APEDBG_ARG1, v); 690 | SetReg(REG_APE__APEDBG_CMD, 691 | REG_APE__APEDBG_CMD__MAGIC 692 | |REG_APE__APEDBG_CMD__TYPE__MEM_SET); 693 | 694 | _WaitAPEShellCmd(); 695 | 696 | uint32_t exc = GetReg(REG_APE__APEDBG_CMD_ERROR_FLAGS); 697 | if (exc) 698 | _WarnAboutAPEState("exception caught"); 699 | } 700 | 701 | // OTP Access 702 | // --------------------------------------------------------------------------- 703 | static uint32_t GetOTP(uint32_t offset) { 704 | #ifdef PROPRIETARY 705 | /* scrubbed */ 706 | /* scrubbed */ 707 | /* scrubbed */ 708 | /* scrubbed */ 709 | /* scrubbed */ 710 | /* scrubbed */ 711 | /* scrubbed */ 712 | /* scrubbed */ 713 | /* scrubbed */ 714 | /* scrubbed */ 715 | /* scrubbed */ 716 | /* scrubbed */ 717 | /* scrubbed */ 718 | /* scrubbed */ 719 | /* scrubbed */ 720 | /* scrubbed */ 721 | /* scrubbed */ 722 | /* scrubbed */ 723 | /* scrubbed */ 724 | /* scrubbed */ 725 | /* scrubbed */ 726 | /* scrubbed */ 727 | /* scrubbed */ 728 | /* scrubbed */ 729 | /* scrubbed */ 730 | /* scrubbed */ 731 | /* scrubbed */ 732 | /* scrubbed */ 733 | /* scrubbed */ 734 | /* scrubbed */ 735 | /* scrubbed */ 736 | #endif 737 | } 738 | 739 | // Compression 740 | // --------------------------------------------------------------------------- 741 | #define CMPS_MAGIC 0x53504D43 742 | 743 | typedef struct __attribute__((packed)) { 744 | uint32_t magic; // "CMPS" (CMPS_MAGIC) 745 | uint32_t compressedSize; // 746 | uint32_t uncompressedSize; 747 | uint32_t unkC; 748 | } cmps_header; 749 | static_assert(sizeof(cmps_header) == 16, "cmps_header"); 750 | 751 | typedef struct { 752 | uint8_t dict[2048 +64]; 753 | size_t dictIdx; 754 | } cmps_state; 755 | 756 | static cmps_state g_state; 757 | 758 | // uncompSize may be SIZE_MAX 759 | typedef ssize_t (write_t)(uint8_t ch, void *arg); 760 | static void Decompress(const void *inStart, size_t inBytes, size_t uncompSize, write_t *writef, void *arg, 761 | size_t *bytesRead_, size_t *bytesWritten_) { 762 | #ifdef PROPRIETARY 763 | /* scrubbed */ 764 | /* scrubbed */ 765 | /* scrubbed */ 766 | /* scrubbed */ 767 | /* scrubbed */ 768 | /* scrubbed */ 769 | /* scrubbed */ 770 | /* scrubbed */ 771 | /* scrubbed */ 772 | /* scrubbed */ 773 | /* scrubbed */ 774 | /* scrubbed */ 775 | /* scrubbed */ 776 | /* scrubbed */ 777 | /* scrubbed */ 778 | /* scrubbed */ 779 | /* scrubbed */ 780 | /* scrubbed */ 781 | /* scrubbed */ 782 | /* scrubbed */ 783 | /* scrubbed */ 784 | /* scrubbed */ 785 | /* scrubbed */ 786 | /* scrubbed */ 787 | /* scrubbed */ 788 | /* scrubbed */ 789 | /* scrubbed */ 790 | /* scrubbed */ 791 | /* scrubbed */ 792 | /* scrubbed */ 793 | /* scrubbed */ 794 | /* scrubbed */ 795 | /* scrubbed */ 796 | /* scrubbed */ 797 | /* scrubbed */ 798 | /* scrubbed */ 799 | /* scrubbed */ 800 | /* scrubbed */ 801 | /* scrubbed */ 802 | /* scrubbed */ 803 | /* scrubbed */ 804 | /* scrubbed */ 805 | /* scrubbed */ 806 | /* scrubbed */ 807 | /* scrubbed */ 808 | /* scrubbed */ 809 | /* scrubbed */ 810 | /* scrubbed */ 811 | /* scrubbed */ 812 | /* scrubbed */ 813 | /* scrubbed */ 814 | /* scrubbed */ 815 | /* scrubbed */ 816 | /* scrubbed */ 817 | /* scrubbed */ 818 | /* scrubbed */ 819 | /* scrubbed */ 820 | /* scrubbed */ 821 | /* scrubbed */ 822 | /* scrubbed */ 823 | /* scrubbed */ 824 | /* scrubbed */ 825 | /* scrubbed */ 826 | #endif 827 | } 828 | 829 | #endif 830 | -------------------------------------------------------------------------------- /otg_dummy.c: -------------------------------------------------------------------------------- 1 | #include "otg.h" 2 | #include "otg_common.c" 3 | 4 | // This is a dummy stage1 which just spins forever. We use it to ensure that 5 | // the original firmware doesn't run when developing, but instead of writing 6 | // every new iteration to the NVM during development, we can load into memory 7 | // as this program spins endlessly, then halt the CPU and move it to the 8 | // entrypoint. This avoids unnecessary wear on the NVM. 9 | void noreturn S1Start(void) { 10 | // Enable memory arbiter so host can access memory. 11 | OrReg(REG_MEMORY_ARBITER_MODE, REG_MEMORY_ARBITER_MODE__ENABLE); 12 | 13 | // Disable data cache. 14 | MaskReg(REG_RX_RISC_MODE, REG_RX_RISC_MODE__ENABLE_DATA_CACHE); 15 | 16 | // Enable APE program/SHM/register writes. 17 | OrReg(REG_PCI_STATE, 18 | REG_PCI_STATE__APE_PROGRAM_SPACE_WRITE_ENABLE 19 | |REG_PCI_STATE__APE_SHARED_MEMORY_WRITE_ENABLE 20 | |REG_PCI_STATE__APE_CONTROL_REGISTER_WRITE_ENABLE); 21 | 22 | // Loop forever. 23 | for (;;) 24 | SetGencom32(0, 0xBADF00D); 25 | } 26 | 27 | const char gS1_versionString[] = "DummyOTG"; 28 | 29 | asm( 30 | ".pushsection .textstart,\"ax\",@progbits\n" 31 | ".global S1Entrypoint\n" 32 | ".set noreorder\n" 33 | "S1Entrypoint:\n" 34 | "j S1Entrypoint2\n" 35 | "nop\n" 36 | ".4byte gS1_versionString\n" 37 | ".popsection\n" 38 | 39 | ".pushsection .textasm,\"ax\",@progbits\n" 40 | ".global S1Entrypoint2\n" 41 | "S1Entrypoint2:\n" 42 | "la $sp, S1StackTop\n" 43 | "move $fp, $sp\n" 44 | "lui $k1, 0xC000\n" 45 | "jal S1Start\n" 46 | "nop\n" 47 | ".popsection\n" 48 | ); 49 | 50 | asm( 51 | ".pushsection .stage2,\"a\",@progbits\n" 52 | ".global S1CRC\n" 53 | "S1CRC:\n" 54 | ".4byte 0xDEADBEEF\n" 55 | "\n" 56 | ".popsection\n" 57 | 58 | ".pushsection .ape,\"a\",@progbits\n" 59 | ".global APEImageStart\n" 60 | ".global APEImageEnd\n" 61 | "APEImageStart:\n" 62 | // This is just a dummy APE for the linker script, for the dummy we reuse the 63 | // APE binary already on the device, since it's fairly large and we don't 64 | // need to rewrite it. 65 | ".4byte 0\n" 66 | "APEImageEnd:\n" 67 | ".popsection\n" 68 | ); 69 | 70 | extern int S1Entrypoint; 71 | extern int S1SizeInWords; 72 | extern int S1TextOffset; 73 | extern int APEImageStartPhys; 74 | extern int APEImageSizeInWordsWithTypeByte; 75 | 76 | const __attribute__((section(".header"))) otg_header gS1_otgHeader = { 77 | .magic = HEADER_MAGIC, 78 | .s1Entrypoint = (uint32_t)&S1Entrypoint, 79 | .s1Size = (uint32_t)&S1SizeInWords, 80 | .s1Offset = (uint32_t)&S1TextOffset, //sizeof(otg_header), 81 | .bootHdrCRC = 0xDEADBEEF, 82 | .dir = { 83 | [0] = { // Extended directory 84 | .loadAddr = 0, 85 | .typeSize = (0x10<<24)|0x0031, 86 | .offset = 0x34604, 87 | }, 88 | [1] = { // APE code 89 | .loadAddr = 0x00000007, 90 | .typeSize = (0x0D<<24)|0x2D5A, //(uint32_t)&APEImageSizeInWordsWithTypeByte, 91 | /* OTG_HEADER_TAG_TYPE__APE_CODE | APEImageSizeInWordsWithTypeByte,*/ 92 | .offset = 0x3ED34, //(uint32_t)&APEImageStartPhys, 93 | }, 94 | [2] = { // iSCSI boot ROM 95 | .loadAddr = 0x00010000, 96 | .typeSize = (0x09<<24)|0x3E1C, 97 | .offset = 0x4A29C, 98 | }, 99 | [3] = { // iSCSI configuration 100 | .loadAddr = 0x00000000, 101 | .typeSize = (0x05<<24)|0x0200, 102 | .offset = 0x59B0C, 103 | }, 104 | [4] = { // iSCSI configuration (1) 105 | .loadAddr = 0x00000000, 106 | .typeSize = (0x0B<<24)|0x0200, 107 | .offset = 0x5A30C, 108 | }, 109 | [5] = { // Extended VPD 110 | .loadAddr = 0x00000000, 111 | .typeSize = (0x14<<24)|0x0065, 112 | .offset = 0x5BB0C, 113 | }, 114 | }, 115 | .mfrFormatRev = 0x44, 116 | .dirCRC = 0x00, 117 | .mfrLen = 0x008C, 118 | .mfr2Len = 0x008C, 119 | .mac0 = { 0x000A, 0xF7A44C84 }, // TODO CHANGE THIS 120 | .partNo = "DummyOTG", //"BCM95719", 121 | .partRev = "A0", 122 | .fwRev = 0x012D, 123 | .mfrDate = 0x00000000, 124 | .pciDevice = PCI_DEVICE_ID, 125 | .pciVendor = PCI_VENDOR_ID, 126 | .pciSubsystem = PCI_DEVICE_ID, 127 | .pciSubsystemVendor = PCI_VENDOR_ID, 128 | .cpuClock = 0x0042, 129 | .ncSMBUSAddr = 0x64, 130 | .bmcSMBUSAddr = 0x00, 131 | .powerDissipated = {0x0A,0x00,0x00,0x64}, // @BC 132 | .powerConsumed = {0x0A,0x00,0x00,0x64}, // @C0 133 | .func0CfgFeature = 0xCDB50282, //Talos=0xC5C00080, 134 | .func0CfgHW = 0x00006014, //Talos=0x00004014, 135 | .func0CfgHW2 = 0x00000042, //Talos=0x00000040, 136 | .mac1 = { 0x000A, 0xF7A44C85 }, 137 | .func1CfgFeature = 0xCDB50202, //Talos=0xC5C00000, 138 | .func1CfgHW = 0x00006014, //Talos=0x00004014, 139 | .func1CfgHW2 = 0x00000042, //Talos=0x00000040, 140 | .func2CfgFeature = 0xCDB50202, //Talos=0xC5C00000, 141 | .func2CfgHW = 0x00006014, //Talos=0x00004014, 142 | .func2CfgHW2 = 0x00000042, //Talos=0x00000040, 143 | .func3CfgFeature = 0xCDB50202, //Talos=0xC5C00000, 144 | .func3CfgHW = 0x00006014, //Talos=0x00004014, 145 | .func3CfgHW2 = 0x00000042, //Talos=0x00000040, 146 | .cfgShared = 0x0012AA38, //Talos=0x00C2AA38, 147 | .powerBudget0= 0x2C163C2C, 148 | .powerBudget1= 0x0000230A, 149 | .powerBudget2= 0x00000000, 150 | .powerBudget3= 0x00000000, 151 | .macNVMSize = 0x0002, 152 | .cfg5 = 0, 153 | .pciSubsystemF0GPHY = PCI_SUBSYSTEM_ID, 154 | .pciSubsystemF1GPHY = PCI_SUBSYSTEM_ID, 155 | .pciSubsystemF2GPHY = PCI_SUBSYSTEM_ID, 156 | .pciSubsystemF3GPHY = PCI_SUBSYSTEM_ID, 157 | .pciSubsystemF0SERDES = PCI_DEVICE_ID, 158 | .pciSubsystemF1SERDES = PCI_DEVICE_ID, 159 | .pciSubsystemF2SERDES = PCI_DEVICE_ID, 160 | .pciSubsystemF3SERDES = PCI_DEVICE_ID, 161 | .mac2 = { 0x000A, 0xF7A44C86 }, 162 | .mac3 = { 0x000A, 0xF7A44C87 }, 163 | .vpd = { 164 | 0x82,0x0F,0x00,0x55,0x73,0x65,0x20,0x27,0x76,0x70,0x64,0x73,0x65,0x63,0x66,0x67, 165 | 0x27,0x00,0x90,0x6B,0x00,0x50,0x4E,0x0E,0x55,0x73,0x65,0x20,0x27,0x76,0x70,0x64, 166 | 0x73,0x65,0x63,0x66,0x67,0x27,0x45,0x43,0x09,0x31,0x30,0x36,0x36,0x37,0x39,0x2D, 167 | 0x31,0x35,0x53,0x4E,0x0E,0x55,0x73,0x65,0x20,0x27,0x76,0x70,0x64,0x73,0x65,0x63, 168 | 0x66,0x67,0x27,0x4D,0x4E,0x04,0x31,0x34,0x65,0x34,0x56,0x30,0x0E,0x55,0x73,0x65, 169 | 0x20,0x27,0x76,0x70,0x64,0x73,0x65,0x63,0x66,0x67,0x27,0x52,0x56,0x22,0x09,0x00, 170 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 171 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 172 | 0x91,0x7C,0x00,0x59,0x41,0x0E,0x55,0x73,0x65,0x20,0x27,0x76,0x70,0x64,0x73,0x65, 173 | 0x63,0x66,0x67,0x27,0x56,0x31,0x0E,0x55,0x73,0x65,0x20,0x27,0x76,0x70,0x64,0x73, 174 | 0x65,0x63,0x66,0x67,0x27,0x52,0x57,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 175 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 176 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 177 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 178 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 179 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 180 | 0x78}, 181 | }; 182 | -------------------------------------------------------------------------------- /otg_stage1.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | ENTRY(S1Entrypoint) 3 | 4 | SECTIONS { 5 | . = 0x08003800 - 0x28c; 6 | 7 | .header . : ALIGN(4) SUBALIGN(4) { 8 | *(.header) 9 | S1TextStartBegin = .; 10 | *(.textstart) 11 | S1TextStartEnd = .; 12 | } 13 | 14 | ASSERT(S1TextStartBegin == (0x08003800), "textstart") 15 | ASSERT(S1TextStartEnd == (0x08003800 + 12), "textstart end") 16 | ASSERT(ADDR(.header) == (0x08003800 - 0x28c), "header VMA") 17 | ASSERT(SIZEOF(.header) == (0x28C + 12), "header size") 18 | ASSERT(. == (0x08003800 + 12), "cur") 19 | 20 | .text . : ALIGN(4) SUBALIGN(4) { 21 | *(.textasm) 22 | *(.text) 23 | *(.data) 24 | *(.bss) 25 | *(.rodata) 26 | *(.rodata.*) 27 | } 28 | 29 | /*S1SizeInWords_ = ((ADDR(.text) + SIZEOF(.text) - ADDR(.text)) + 4 + 3) / 4;*/ 30 | 31 | S1StackTop = 0x08007000; 32 | 33 | S1TextOffset = S1TextStartBegin - gS1_otgHeader; 34 | 35 | .stage2 . : ALIGN(4) SUBALIGN(4) { 36 | *(.stage2) 37 | } 38 | 39 | /* Add 4 here because we put the S1 CRC word at the start of the S2 section. */ 40 | S1SizeInWords = ((ADDR(.stage2) - S1TextStartBegin + 4) + 3) / 4; 41 | _qS1SizeInWords = (ADDR(.stage2) + 4 /* CRC word */ - ADDR(.text) + 3) / 4; 42 | 43 | .ape . : ALIGN(4) SUBALIGN(4) { 44 | *(.ape) 45 | } 46 | 47 | APEImageStartPhys = APEImageStart - 0x08003570; 48 | APEImageSizeInWords = (((APEImageEnd - APEImageStart) / 4)); 49 | APEImageSizeInWordsWithTypeByte = APEImageSizeInWords | 0x0D000000; 50 | 51 | /DISCARD/ : { 52 | *(.comment) 53 | *(.note.GNU-stack) 54 | *(.eh_frame) 55 | *(.got) 56 | *(.MIPS.abiflags) 57 | *(.reginfo) 58 | *(.mdebug.abi32) 59 | *(.pdr) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /otg_stage2.c: -------------------------------------------------------------------------------- 1 | #include "otg.h" 2 | #include "otg_common.c" 3 | 4 | #ifdef PROPRIETARY 5 | /* scrubbed */ 6 | /* scrubbed */ 7 | /* scrubbed */ 8 | /* scrubbed */ 9 | /* scrubbed */ 10 | /* scrubbed */ 11 | /* scrubbed */ 12 | /* scrubbed */ 13 | /* scrubbed */ 14 | /* scrubbed */ 15 | /* scrubbed */ 16 | /* scrubbed */ 17 | /* scrubbed */ 18 | /* scrubbed */ 19 | /* scrubbed */ 20 | /* scrubbed */ 21 | /* scrubbed */ 22 | /* scrubbed */ 23 | /* scrubbed */ 24 | /* scrubbed */ 25 | /* scrubbed */ 26 | /* scrubbed */ 27 | /* scrubbed */ 28 | /* scrubbed */ 29 | /* scrubbed */ 30 | /* scrubbed */ 31 | /* scrubbed */ 32 | /* scrubbed */ 33 | /* scrubbed */ 34 | /* scrubbed */ 35 | /* scrubbed */ 36 | /* scrubbed */ 37 | /* scrubbed */ 38 | /* scrubbed */ 39 | /* scrubbed */ 40 | /* scrubbed */ 41 | /* scrubbed */ 42 | /* scrubbed */ 43 | /* scrubbed */ 44 | /* scrubbed */ 45 | /* scrubbed */ 46 | /* scrubbed */ 47 | /* scrubbed */ 48 | /* scrubbed */ 49 | /* scrubbed */ 50 | /* scrubbed */ 51 | /* scrubbed */ 52 | /* scrubbed */ 53 | /* scrubbed */ 54 | /* scrubbed */ 55 | /* scrubbed */ 56 | /* scrubbed */ 57 | /* scrubbed */ 58 | /* scrubbed */ 59 | /* scrubbed */ 60 | /* scrubbed */ 61 | /* scrubbed */ 62 | /* scrubbed */ 63 | /* scrubbed */ 64 | /* scrubbed */ 65 | /* scrubbed */ 66 | /* scrubbed */ 67 | /* scrubbed */ 68 | /* scrubbed */ 69 | /* scrubbed */ 70 | /* scrubbed */ 71 | /* scrubbed */ 72 | /* scrubbed */ 73 | /* scrubbed */ 74 | /* scrubbed */ 75 | /* scrubbed */ 76 | /* scrubbed */ 77 | /* scrubbed */ 78 | /* scrubbed */ 79 | /* scrubbed */ 80 | /* scrubbed */ 81 | /* scrubbed */ 82 | /* scrubbed */ 83 | /* scrubbed */ 84 | /* scrubbed */ 85 | /* scrubbed */ 86 | /* scrubbed */ 87 | /* scrubbed */ 88 | /* scrubbed */ 89 | /* scrubbed */ 90 | /* scrubbed */ 91 | /* scrubbed */ 92 | /* scrubbed */ 93 | /* scrubbed */ 94 | /* scrubbed */ 95 | /* scrubbed */ 96 | /* scrubbed */ 97 | /* scrubbed */ 98 | /* scrubbed */ 99 | /* scrubbed */ 100 | /* scrubbed */ 101 | /* scrubbed */ 102 | /* scrubbed */ 103 | /* scrubbed */ 104 | /* scrubbed */ 105 | /* scrubbed */ 106 | /* scrubbed */ 107 | /* scrubbed */ 108 | /* scrubbed */ 109 | /* scrubbed */ 110 | /* scrubbed */ 111 | /* scrubbed */ 112 | /* scrubbed */ 113 | /* scrubbed */ 114 | /* scrubbed */ 115 | /* scrubbed */ 116 | /* scrubbed */ 117 | /* scrubbed */ 118 | /* scrubbed */ 119 | /* scrubbed */ 120 | /* scrubbed */ 121 | /* scrubbed */ 122 | /* scrubbed */ 123 | /* scrubbed */ 124 | /* scrubbed */ 125 | /* scrubbed */ 126 | /* scrubbed */ 127 | /* scrubbed */ 128 | /* scrubbed */ 129 | /* scrubbed */ 130 | /* scrubbed */ 131 | /* scrubbed */ 132 | /* scrubbed */ 133 | /* scrubbed */ 134 | /* scrubbed */ 135 | /* scrubbed */ 136 | /* scrubbed */ 137 | /* scrubbed */ 138 | /* scrubbed */ 139 | /* scrubbed */ 140 | /* scrubbed */ 141 | /* scrubbed */ 142 | /* scrubbed */ 143 | /* scrubbed */ 144 | /* scrubbed */ 145 | /* scrubbed */ 146 | /* scrubbed */ 147 | /* scrubbed */ 148 | /* scrubbed */ 149 | /* scrubbed */ 150 | /* scrubbed */ 151 | /* scrubbed */ 152 | /* scrubbed */ 153 | /* scrubbed */ 154 | /* scrubbed */ 155 | /* scrubbed */ 156 | /* scrubbed */ 157 | /* scrubbed */ 158 | /* scrubbed */ 159 | /* scrubbed */ 160 | /* scrubbed */ 161 | /* scrubbed */ 162 | /* scrubbed */ 163 | /* scrubbed */ 164 | /* scrubbed */ 165 | /* scrubbed */ 166 | /* scrubbed */ 167 | /* scrubbed */ 168 | /* scrubbed */ 169 | /* scrubbed */ 170 | /* scrubbed */ 171 | /* scrubbed */ 172 | /* scrubbed */ 173 | /* scrubbed */ 174 | /* scrubbed */ 175 | /* scrubbed */ 176 | /* scrubbed */ 177 | /* scrubbed */ 178 | /* scrubbed */ 179 | /* scrubbed */ 180 | /* scrubbed */ 181 | /* scrubbed */ 182 | /* scrubbed */ 183 | /* scrubbed */ 184 | /* scrubbed */ 185 | /* scrubbed */ 186 | /* scrubbed */ 187 | /* scrubbed */ 188 | /* scrubbed */ 189 | /* scrubbed */ 190 | /* scrubbed */ 191 | /* scrubbed */ 192 | /* scrubbed */ 193 | /* scrubbed */ 194 | /* scrubbed */ 195 | /* scrubbed */ 196 | /* scrubbed */ 197 | /* scrubbed */ 198 | /* scrubbed */ 199 | /* scrubbed */ 200 | /* scrubbed */ 201 | /* scrubbed */ 202 | /* scrubbed */ 203 | /* scrubbed */ 204 | /* scrubbed */ 205 | /* scrubbed */ 206 | /* scrubbed */ 207 | /* scrubbed */ 208 | /* scrubbed */ 209 | /* scrubbed */ 210 | /* scrubbed */ 211 | /* scrubbed */ 212 | /* scrubbed */ 213 | /* scrubbed */ 214 | /* scrubbed */ 215 | /* scrubbed */ 216 | /* scrubbed */ 217 | /* scrubbed */ 218 | /* scrubbed */ 219 | /* scrubbed */ 220 | /* scrubbed */ 221 | /* scrubbed */ 222 | /* scrubbed */ 223 | /* scrubbed */ 224 | /* scrubbed */ 225 | /* scrubbed */ 226 | /* scrubbed */ 227 | /* scrubbed */ 228 | /* scrubbed */ 229 | /* scrubbed */ 230 | /* scrubbed */ 231 | /* scrubbed */ 232 | /* scrubbed */ 233 | /* scrubbed */ 234 | /* scrubbed */ 235 | /* scrubbed */ 236 | /* scrubbed */ 237 | /* scrubbed */ 238 | /* scrubbed */ 239 | /* scrubbed */ 240 | /* scrubbed */ 241 | /* scrubbed */ 242 | /* scrubbed */ 243 | /* scrubbed */ 244 | /* scrubbed */ 245 | /* scrubbed */ 246 | /* scrubbed */ 247 | /* scrubbed */ 248 | /* scrubbed */ 249 | /* scrubbed */ 250 | /* scrubbed */ 251 | /* scrubbed */ 252 | /* scrubbed */ 253 | /* scrubbed */ 254 | /* scrubbed */ 255 | /* scrubbed */ 256 | /* scrubbed */ 257 | /* scrubbed */ 258 | /* scrubbed */ 259 | /* scrubbed */ 260 | /* scrubbed */ 261 | /* scrubbed */ 262 | /* scrubbed */ 263 | /* scrubbed */ 264 | /* scrubbed */ 265 | /* scrubbed */ 266 | /* scrubbed */ 267 | /* scrubbed */ 268 | /* scrubbed */ 269 | /* scrubbed */ 270 | /* scrubbed */ 271 | /* scrubbed */ 272 | /* scrubbed */ 273 | /* scrubbed */ 274 | /* scrubbed */ 275 | /* scrubbed */ 276 | /* scrubbed */ 277 | /* scrubbed */ 278 | /* scrubbed */ 279 | /* scrubbed */ 280 | /* scrubbed */ 281 | /* scrubbed */ 282 | /* scrubbed */ 283 | /* scrubbed */ 284 | /* scrubbed */ 285 | /* scrubbed */ 286 | /* scrubbed */ 287 | /* scrubbed */ 288 | /* scrubbed */ 289 | /* scrubbed */ 290 | /* scrubbed */ 291 | /* scrubbed */ 292 | /* scrubbed */ 293 | /* scrubbed */ 294 | /* scrubbed */ 295 | /* scrubbed */ 296 | /* scrubbed */ 297 | /* scrubbed */ 298 | /* scrubbed */ 299 | /* scrubbed */ 300 | /* scrubbed */ 301 | /* scrubbed */ 302 | /* scrubbed */ 303 | /* scrubbed */ 304 | /* scrubbed */ 305 | /* scrubbed */ 306 | /* scrubbed */ 307 | /* scrubbed */ 308 | /* scrubbed */ 309 | /* scrubbed */ 310 | /* scrubbed */ 311 | /* scrubbed */ 312 | /* scrubbed */ 313 | /* scrubbed */ 314 | /* scrubbed */ 315 | /* scrubbed */ 316 | /* scrubbed */ 317 | /* scrubbed */ 318 | /* scrubbed */ 319 | /* scrubbed */ 320 | /* scrubbed */ 321 | /* scrubbed */ 322 | /* scrubbed */ 323 | /* scrubbed */ 324 | /* scrubbed */ 325 | /* scrubbed */ 326 | /* scrubbed */ 327 | /* scrubbed */ 328 | /* scrubbed */ 329 | /* scrubbed */ 330 | /* scrubbed */ 331 | /* scrubbed */ 332 | /* scrubbed */ 333 | /* scrubbed */ 334 | /* scrubbed */ 335 | /* scrubbed */ 336 | /* scrubbed */ 337 | /* scrubbed */ 338 | /* scrubbed */ 339 | /* scrubbed */ 340 | /* scrubbed */ 341 | /* scrubbed */ 342 | /* scrubbed */ 343 | /* scrubbed */ 344 | /* scrubbed */ 345 | /* scrubbed */ 346 | /* scrubbed */ 347 | /* scrubbed */ 348 | /* scrubbed */ 349 | /* scrubbed */ 350 | /* scrubbed */ 351 | /* scrubbed */ 352 | /* scrubbed */ 353 | /* scrubbed */ 354 | /* scrubbed */ 355 | /* scrubbed */ 356 | /* scrubbed */ 357 | /* scrubbed */ 358 | /* scrubbed */ 359 | /* scrubbed */ 360 | /* scrubbed */ 361 | /* scrubbed */ 362 | /* scrubbed */ 363 | /* scrubbed */ 364 | /* scrubbed */ 365 | /* scrubbed */ 366 | /* scrubbed */ 367 | /* scrubbed */ 368 | /* scrubbed */ 369 | /* scrubbed */ 370 | /* scrubbed */ 371 | /* scrubbed */ 372 | /* scrubbed */ 373 | /* scrubbed */ 374 | /* scrubbed */ 375 | /* scrubbed */ 376 | /* scrubbed */ 377 | /* scrubbed */ 378 | /* scrubbed */ 379 | /* scrubbed */ 380 | /* scrubbed */ 381 | /* scrubbed */ 382 | /* scrubbed */ 383 | /* scrubbed */ 384 | /* scrubbed */ 385 | /* scrubbed */ 386 | /* scrubbed */ 387 | /* scrubbed */ 388 | /* scrubbed */ 389 | /* scrubbed */ 390 | /* scrubbed */ 391 | /* scrubbed */ 392 | /* scrubbed */ 393 | /* scrubbed */ 394 | /* scrubbed */ 395 | /* scrubbed */ 396 | /* scrubbed */ 397 | /* scrubbed */ 398 | /* scrubbed */ 399 | /* scrubbed */ 400 | /* scrubbed */ 401 | /* scrubbed */ 402 | /* scrubbed */ 403 | /* scrubbed */ 404 | /* scrubbed */ 405 | /* scrubbed */ 406 | /* scrubbed */ 407 | /* scrubbed */ 408 | /* scrubbed */ 409 | /* scrubbed */ 410 | /* scrubbed */ 411 | /* scrubbed */ 412 | /* scrubbed */ 413 | /* scrubbed */ 414 | /* scrubbed */ 415 | /* scrubbed */ 416 | /* scrubbed */ 417 | /* scrubbed */ 418 | /* scrubbed */ 419 | /* scrubbed */ 420 | /* scrubbed */ 421 | /* scrubbed */ 422 | /* scrubbed */ 423 | /* scrubbed */ 424 | /* scrubbed */ 425 | /* scrubbed */ 426 | /* scrubbed */ 427 | /* scrubbed */ 428 | /* scrubbed */ 429 | /* scrubbed */ 430 | /* scrubbed */ 431 | /* scrubbed */ 432 | /* scrubbed */ 433 | /* scrubbed */ 434 | /* scrubbed */ 435 | /* scrubbed */ 436 | /* scrubbed */ 437 | /* scrubbed */ 438 | /* scrubbed */ 439 | /* scrubbed */ 440 | /* scrubbed */ 441 | /* scrubbed */ 442 | /* scrubbed */ 443 | /* scrubbed */ 444 | /* scrubbed */ 445 | /* scrubbed */ 446 | /* scrubbed */ 447 | /* scrubbed */ 448 | /* scrubbed */ 449 | /* scrubbed */ 450 | /* scrubbed */ 451 | /* scrubbed */ 452 | /* scrubbed */ 453 | /* scrubbed */ 454 | /* scrubbed */ 455 | /* scrubbed */ 456 | /* scrubbed */ 457 | /* scrubbed */ 458 | /* scrubbed */ 459 | /* scrubbed */ 460 | /* scrubbed */ 461 | /* scrubbed */ 462 | /* scrubbed */ 463 | /* scrubbed */ 464 | /* scrubbed */ 465 | /* scrubbed */ 466 | /* scrubbed */ 467 | /* scrubbed */ 468 | /* scrubbed */ 469 | /* scrubbed */ 470 | /* scrubbed */ 471 | /* scrubbed */ 472 | /* scrubbed */ 473 | /* scrubbed */ 474 | /* scrubbed */ 475 | /* scrubbed */ 476 | /* scrubbed */ 477 | /* scrubbed */ 478 | /* scrubbed */ 479 | /* scrubbed */ 480 | /* scrubbed */ 481 | /* scrubbed */ 482 | /* scrubbed */ 483 | /* scrubbed */ 484 | /* scrubbed */ 485 | /* scrubbed */ 486 | /* scrubbed */ 487 | /* scrubbed */ 488 | /* scrubbed */ 489 | /* scrubbed */ 490 | /* scrubbed */ 491 | /* scrubbed */ 492 | /* scrubbed */ 493 | /* scrubbed */ 494 | /* scrubbed */ 495 | /* scrubbed */ 496 | /* scrubbed */ 497 | /* scrubbed */ 498 | /* scrubbed */ 499 | /* scrubbed */ 500 | /* scrubbed */ 501 | /* scrubbed */ 502 | /* scrubbed */ 503 | /* scrubbed */ 504 | /* scrubbed */ 505 | /* scrubbed */ 506 | /* scrubbed */ 507 | /* scrubbed */ 508 | /* scrubbed */ 509 | /* scrubbed */ 510 | /* scrubbed */ 511 | /* scrubbed */ 512 | /* scrubbed */ 513 | /* scrubbed */ 514 | /* scrubbed */ 515 | /* scrubbed */ 516 | /* scrubbed */ 517 | /* scrubbed */ 518 | /* scrubbed */ 519 | /* scrubbed */ 520 | /* scrubbed */ 521 | /* scrubbed */ 522 | /* scrubbed */ 523 | /* scrubbed */ 524 | /* scrubbed */ 525 | /* scrubbed */ 526 | /* scrubbed */ 527 | /* scrubbed */ 528 | /* scrubbed */ 529 | /* scrubbed */ 530 | /* scrubbed */ 531 | /* scrubbed */ 532 | /* scrubbed */ 533 | /* scrubbed */ 534 | /* scrubbed */ 535 | /* scrubbed */ 536 | /* scrubbed */ 537 | /* scrubbed */ 538 | /* scrubbed */ 539 | /* scrubbed */ 540 | /* scrubbed */ 541 | /* scrubbed */ 542 | /* scrubbed */ 543 | /* scrubbed */ 544 | /* scrubbed */ 545 | /* scrubbed */ 546 | /* scrubbed */ 547 | /* scrubbed */ 548 | /* scrubbed */ 549 | /* scrubbed */ 550 | /* scrubbed */ 551 | /* scrubbed */ 552 | /* scrubbed */ 553 | /* scrubbed */ 554 | /* scrubbed */ 555 | /* scrubbed */ 556 | /* scrubbed */ 557 | /* scrubbed */ 558 | /* scrubbed */ 559 | /* scrubbed */ 560 | /* scrubbed */ 561 | /* scrubbed */ 562 | /* scrubbed */ 563 | /* scrubbed */ 564 | /* scrubbed */ 565 | /* scrubbed */ 566 | /* scrubbed */ 567 | /* scrubbed */ 568 | /* scrubbed */ 569 | /* scrubbed */ 570 | /* scrubbed */ 571 | /* scrubbed */ 572 | /* scrubbed */ 573 | /* scrubbed */ 574 | /* scrubbed */ 575 | /* scrubbed */ 576 | /* scrubbed */ 577 | /* scrubbed */ 578 | /* scrubbed */ 579 | /* scrubbed */ 580 | /* scrubbed */ 581 | /* scrubbed */ 582 | /* scrubbed */ 583 | /* scrubbed */ 584 | /* scrubbed */ 585 | /* scrubbed */ 586 | /* scrubbed */ 587 | /* scrubbed */ 588 | /* scrubbed */ 589 | /* scrubbed */ 590 | /* scrubbed */ 591 | /* scrubbed */ 592 | /* scrubbed */ 593 | /* scrubbed */ 594 | /* scrubbed */ 595 | /* scrubbed */ 596 | /* scrubbed */ 597 | /* scrubbed */ 598 | /* scrubbed */ 599 | /* scrubbed */ 600 | /* scrubbed */ 601 | /* scrubbed */ 602 | /* scrubbed */ 603 | /* scrubbed */ 604 | /* scrubbed */ 605 | /* scrubbed */ 606 | /* scrubbed */ 607 | /* scrubbed */ 608 | /* scrubbed */ 609 | /* scrubbed */ 610 | /* scrubbed */ 611 | /* scrubbed */ 612 | /* scrubbed */ 613 | /* scrubbed */ 614 | /* scrubbed */ 615 | /* scrubbed */ 616 | /* scrubbed */ 617 | /* scrubbed */ 618 | /* scrubbed */ 619 | /* scrubbed */ 620 | /* scrubbed */ 621 | /* scrubbed */ 622 | /* scrubbed */ 623 | /* scrubbed */ 624 | /* scrubbed */ 625 | /* scrubbed */ 626 | /* scrubbed */ 627 | /* scrubbed */ 628 | /* scrubbed */ 629 | /* scrubbed */ 630 | /* scrubbed */ 631 | /* scrubbed */ 632 | /* scrubbed */ 633 | /* scrubbed */ 634 | /* scrubbed */ 635 | /* scrubbed */ 636 | /* scrubbed */ 637 | /* scrubbed */ 638 | /* scrubbed */ 639 | /* scrubbed */ 640 | /* scrubbed */ 641 | /* scrubbed */ 642 | /* scrubbed */ 643 | /* scrubbed */ 644 | /* scrubbed */ 645 | /* scrubbed */ 646 | /* scrubbed */ 647 | /* scrubbed */ 648 | /* scrubbed */ 649 | /* scrubbed */ 650 | /* scrubbed */ 651 | /* scrubbed */ 652 | /* scrubbed */ 653 | /* scrubbed */ 654 | /* scrubbed */ 655 | /* scrubbed */ 656 | /* scrubbed */ 657 | /* scrubbed */ 658 | /* scrubbed */ 659 | /* scrubbed */ 660 | /* scrubbed */ 661 | /* scrubbed */ 662 | /* scrubbed */ 663 | /* scrubbed */ 664 | /* scrubbed */ 665 | /* scrubbed */ 666 | /* scrubbed */ 667 | /* scrubbed */ 668 | /* scrubbed */ 669 | /* scrubbed */ 670 | /* scrubbed */ 671 | /* scrubbed */ 672 | /* scrubbed */ 673 | /* scrubbed */ 674 | /* scrubbed */ 675 | /* scrubbed */ 676 | /* scrubbed */ 677 | /* scrubbed */ 678 | /* scrubbed */ 679 | /* scrubbed */ 680 | /* scrubbed */ 681 | /* scrubbed */ 682 | /* scrubbed */ 683 | /* scrubbed */ 684 | /* scrubbed */ 685 | /* scrubbed */ 686 | /* scrubbed */ 687 | /* scrubbed */ 688 | /* scrubbed */ 689 | /* scrubbed */ 690 | /* scrubbed */ 691 | /* scrubbed */ 692 | /* scrubbed */ 693 | /* scrubbed */ 694 | /* scrubbed */ 695 | /* scrubbed */ 696 | /* scrubbed */ 697 | /* scrubbed */ 698 | /* scrubbed */ 699 | /* scrubbed */ 700 | /* scrubbed */ 701 | /* scrubbed */ 702 | /* scrubbed */ 703 | /* scrubbed */ 704 | /* scrubbed */ 705 | /* scrubbed */ 706 | /* scrubbed */ 707 | /* scrubbed */ 708 | /* scrubbed */ 709 | /* scrubbed */ 710 | /* scrubbed */ 711 | /* scrubbed */ 712 | /* scrubbed */ 713 | /* scrubbed */ 714 | /* scrubbed */ 715 | /* scrubbed */ 716 | /* scrubbed */ 717 | /* scrubbed */ 718 | /* scrubbed */ 719 | /* scrubbed */ 720 | /* scrubbed */ 721 | /* scrubbed */ 722 | /* scrubbed */ 723 | /* scrubbed */ 724 | /* scrubbed */ 725 | /* scrubbed */ 726 | /* scrubbed */ 727 | /* scrubbed */ 728 | /* scrubbed */ 729 | /* scrubbed */ 730 | /* scrubbed */ 731 | /* scrubbed */ 732 | /* scrubbed */ 733 | /* scrubbed */ 734 | /* scrubbed */ 735 | /* scrubbed */ 736 | /* scrubbed */ 737 | /* scrubbed */ 738 | /* scrubbed */ 739 | /* scrubbed */ 740 | /* scrubbed */ 741 | /* scrubbed */ 742 | /* scrubbed */ 743 | /* scrubbed */ 744 | /* scrubbed */ 745 | /* scrubbed */ 746 | /* scrubbed */ 747 | /* scrubbed */ 748 | /* scrubbed */ 749 | /* scrubbed */ 750 | /* scrubbed */ 751 | /* scrubbed */ 752 | /* scrubbed */ 753 | /* scrubbed */ 754 | /* scrubbed */ 755 | /* scrubbed */ 756 | /* scrubbed */ 757 | /* scrubbed */ 758 | /* scrubbed */ 759 | /* scrubbed */ 760 | /* scrubbed */ 761 | /* scrubbed */ 762 | /* scrubbed */ 763 | /* scrubbed */ 764 | /* scrubbed */ 765 | /* scrubbed */ 766 | /* scrubbed */ 767 | /* scrubbed */ 768 | /* scrubbed */ 769 | /* scrubbed */ 770 | /* scrubbed */ 771 | /* scrubbed */ 772 | /* scrubbed */ 773 | /* scrubbed */ 774 | /* scrubbed */ 775 | /* scrubbed */ 776 | /* scrubbed */ 777 | /* scrubbed */ 778 | /* scrubbed */ 779 | /* scrubbed */ 780 | /* scrubbed */ 781 | /* scrubbed */ 782 | /* scrubbed */ 783 | /* scrubbed */ 784 | /* scrubbed */ 785 | /* scrubbed */ 786 | /* scrubbed */ 787 | /* scrubbed */ 788 | /* scrubbed */ 789 | /* scrubbed */ 790 | /* scrubbed */ 791 | /* scrubbed */ 792 | /* scrubbed */ 793 | /* scrubbed */ 794 | /* scrubbed */ 795 | /* scrubbed */ 796 | /* scrubbed */ 797 | /* scrubbed */ 798 | /* scrubbed */ 799 | /* scrubbed */ 800 | /* scrubbed */ 801 | /* scrubbed */ 802 | /* scrubbed */ 803 | /* scrubbed */ 804 | /* scrubbed */ 805 | /* scrubbed */ 806 | /* scrubbed */ 807 | /* scrubbed */ 808 | /* scrubbed */ 809 | /* scrubbed */ 810 | /* scrubbed */ 811 | /* scrubbed */ 812 | /* scrubbed */ 813 | /* scrubbed */ 814 | /* scrubbed */ 815 | /* scrubbed */ 816 | /* scrubbed */ 817 | /* scrubbed */ 818 | /* scrubbed */ 819 | /* scrubbed */ 820 | /* scrubbed */ 821 | /* scrubbed */ 822 | /* scrubbed */ 823 | /* scrubbed */ 824 | /* scrubbed */ 825 | /* scrubbed */ 826 | /* scrubbed */ 827 | /* scrubbed */ 828 | /* scrubbed */ 829 | /* scrubbed */ 830 | /* scrubbed */ 831 | /* scrubbed */ 832 | /* scrubbed */ 833 | /* scrubbed */ 834 | /* scrubbed */ 835 | /* scrubbed */ 836 | /* scrubbed */ 837 | /* scrubbed */ 838 | /* scrubbed */ 839 | /* scrubbed */ 840 | /* scrubbed */ 841 | /* scrubbed */ 842 | /* scrubbed */ 843 | /* scrubbed */ 844 | /* scrubbed */ 845 | /* scrubbed */ 846 | /* scrubbed */ 847 | /* scrubbed */ 848 | /* scrubbed */ 849 | /* scrubbed */ 850 | /* scrubbed */ 851 | /* scrubbed */ 852 | /* scrubbed */ 853 | /* scrubbed */ 854 | /* scrubbed */ 855 | /* scrubbed */ 856 | /* scrubbed */ 857 | /* scrubbed */ 858 | /* scrubbed */ 859 | /* scrubbed */ 860 | /* scrubbed */ 861 | /* scrubbed */ 862 | /* scrubbed */ 863 | /* scrubbed */ 864 | /* scrubbed */ 865 | /* scrubbed */ 866 | /* scrubbed */ 867 | /* scrubbed */ 868 | /* scrubbed */ 869 | /* scrubbed */ 870 | /* scrubbed */ 871 | /* scrubbed */ 872 | /* scrubbed */ 873 | /* scrubbed */ 874 | /* scrubbed */ 875 | /* scrubbed */ 876 | /* scrubbed */ 877 | /* scrubbed */ 878 | /* scrubbed */ 879 | /* scrubbed */ 880 | /* scrubbed */ 881 | #endif 882 | 883 | void noreturn S2Start(void) { 884 | #ifdef PROPRIETARY 885 | /* scrubbed */ 886 | /* scrubbed */ 887 | /* scrubbed */ 888 | /* scrubbed */ 889 | /* scrubbed */ 890 | /* scrubbed */ 891 | /* scrubbed */ 892 | /* scrubbed */ 893 | /* scrubbed */ 894 | /* scrubbed */ 895 | /* scrubbed */ 896 | /* scrubbed */ 897 | /* scrubbed */ 898 | /* scrubbed */ 899 | /* scrubbed */ 900 | /* scrubbed */ 901 | /* scrubbed */ 902 | /* scrubbed */ 903 | /* scrubbed */ 904 | /* scrubbed */ 905 | /* scrubbed */ 906 | /* scrubbed */ 907 | /* scrubbed */ 908 | /* scrubbed */ 909 | /* scrubbed */ 910 | /* scrubbed */ 911 | /* scrubbed */ 912 | /* scrubbed */ 913 | /* scrubbed */ 914 | /* scrubbed */ 915 | /* scrubbed */ 916 | /* scrubbed */ 917 | /* scrubbed */ 918 | /* scrubbed */ 919 | /* scrubbed */ 920 | /* scrubbed */ 921 | /* scrubbed */ 922 | /* scrubbed */ 923 | /* scrubbed */ 924 | /* scrubbed */ 925 | /* scrubbed */ 926 | /* scrubbed */ 927 | /* scrubbed */ 928 | /* scrubbed */ 929 | /* scrubbed */ 930 | /* scrubbed */ 931 | /* scrubbed */ 932 | /* scrubbed */ 933 | /* scrubbed */ 934 | /* scrubbed */ 935 | /* scrubbed */ 936 | /* scrubbed */ 937 | /* scrubbed */ 938 | /* scrubbed */ 939 | /* scrubbed */ 940 | /* scrubbed */ 941 | /* scrubbed */ 942 | /* scrubbed */ 943 | /* scrubbed */ 944 | /* scrubbed */ 945 | /* scrubbed */ 946 | /* scrubbed */ 947 | /* scrubbed */ 948 | /* scrubbed */ 949 | /* scrubbed */ 950 | /* scrubbed */ 951 | /* scrubbed */ 952 | /* scrubbed */ 953 | /* scrubbed */ 954 | /* scrubbed */ 955 | /* scrubbed */ 956 | /* scrubbed */ 957 | /* scrubbed */ 958 | /* scrubbed */ 959 | /* scrubbed */ 960 | #endif 961 | } 962 | 963 | const char gS2_versionString[] = ""; // Not used for stage2. 964 | 965 | asm( 966 | ".pushsection .textstart,\"ax\",@progbits\n" 967 | ".global S2Entrypoint\n" 968 | ".set noreorder\n" 969 | "S2Entrypoint:\n" 970 | "nop\n" 971 | "b S2Entrypoint2\n" 972 | "nop\n" 973 | ".4byte gS2_versionString\n" // Version string pointer seems unused. 974 | "break 0\n" 975 | "break 0\n" 976 | "\n" 977 | ".popsection\n" 978 | 979 | ".pushsection .textasm,\"ax\",@progbits\n" 980 | ".global S2Entrypoint2\n" 981 | "S2Entrypoint2:\n" 982 | "la $sp, S2StackTop\n" 983 | "move $fp, $sp\n" 984 | "jal S2Start\n" 985 | "nop\n" 986 | ".popsection\n" 987 | ); 988 | 989 | extern int S2SizeInBytes; 990 | 991 | const __attribute__((section(".header"))) otg_s2header gS2_otgHeader = { 992 | .magic = HEADER_MAGIC, 993 | .s2Size = (uint32_t)&S2SizeInBytes, 994 | }; 995 | 996 | const __attribute__((section(".crc"))) uint32_t gS2_crc = 0xDEADBEEF; 997 | -------------------------------------------------------------------------------- /otg_stage2.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | ENTRY(S2Entrypoint) 3 | 4 | SECTIONS { 5 | . = 0x08000000 - 8; 6 | .header . : { 7 | *(.header) 8 | *(.textstart) 9 | } 10 | 11 | ASSERT(ADDR(.header) == 0x07FFFFF8, "header VMA") 12 | ASSERT(SIZEOF(.header) == 0x00000020, "header size") 13 | 14 | . = 0x08000018; 15 | .text . : ALIGN(4) SUBALIGN(4) { 16 | *(.textasm) 17 | *(.text) 18 | *(.data) 19 | *(.bss) 20 | *(.rodata) 21 | *(.rodata.*) 22 | } 23 | 24 | .crc : ALIGN(4) SUBALIGN(4) { 25 | *(.crc) 26 | } 27 | 28 | ASSERT(S2Entrypoint == 0x08000000, "S2Entrypoint") 29 | ASSERT(S2Entrypoint2 == 0x08000018, "S2Entrypoint2") 30 | ASSERT(SIZEOF(.crc) == 4, "CRC size") 31 | 32 | S2SizeInBytes = ADDR(.crc) + SIZEOF(.crc) - 0x08000000; 33 | S2StackTop = 0x08007000; 34 | 35 | /* lld doesn't support --orphan-sections=discard, ugh. */ 36 | /DISCARD/ : { 37 | *(.comment) 38 | *(.note.GNU-stack) 39 | *(.eh_frame) 40 | *(.MIPS.abiflags) 41 | *(.reginfo) 42 | *(.got) 43 | *(.mdebug.abi32) 44 | *(.pdr) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /otgimg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "otg.h" 13 | #include "otg_common.c" 14 | 15 | static const size_t HUMAN_SIZE_LEN = 64; 16 | 17 | static char *_Humanize(uint32_t sz, char *buf) { 18 | const char *suffix = "B"; 19 | if (sz >= 1024) { 20 | suffix = "KiB"; 21 | sz /= 1024; 22 | } 23 | if (sz >= 1024) { 24 | suffix = "MiB"; 25 | sz /= 1024; 26 | } 27 | snprintf(buf, HUMAN_SIZE_LEN, "%u %s", sz, suffix); 28 | return buf; 29 | } 30 | 31 | static const struct argp _argpInfo = { 32 | .doc = "Show information about a firmware image.\n", 33 | .args_doc = "", 34 | }; 35 | 36 | static void _DumpVPDInner(const uint8_t *vpd, size_t vpdLen, size_t *ckOffsetPtr) { 37 | enum { 38 | VSTATE_KW1 = 0, 39 | VSTATE_KW2, 40 | VSTATE_LEN, 41 | VSTATE_DATA, 42 | }; 43 | enum { 44 | VPD_KW_PART_NUMBER = 0x504E, // 'PN' 45 | VPD_KW_ENGINEERING_CHANGES = 0x4543, // 'EC' 46 | VPD_KW_SERIAL_NUMBER = 0x534E, // 'SN' 47 | VPD_KW_MANUFACTURE_ID = 0x4D4E, // 'MN' 48 | VPD_KW_RV = 0x5256, // 'RV' 49 | VPD_KW_ASSET_TAG = 0x5941, // 'YA' 50 | VPD_KW_READ_WRITE_AREA = 0x5257, // 'RW' 51 | VPD_KW_V0 = 0x5630, // 'V0' ] Unknown Vendor-Specific 52 | VPD_KW_V1 = 0x5631, // 'V1' ] 53 | VPD_KW_V2 = 0x5632, // 'V2' ] 54 | VPD_KW_V3 = 0x5633, // 'V3' ] 55 | VPD_KW_V4 = 0x5634, // 'V4' ] 56 | VPD_KW_V5 = 0x5635, // 'V5' ] 57 | VPD_KW_V6 = 0x5636, // 'V6' ] 58 | }; 59 | uint16_t kw; 60 | uint8_t origLen, len; 61 | int state = VSTATE_KW1; 62 | const uint8_t *vpdEnd = vpd + vpdLen; 63 | char *buf = NULL; 64 | char *bufp = NULL; 65 | size_t bufLen = 0; 66 | const uint8_t *vpdStart = vpd; 67 | for (;vpd != vpdEnd || (state == VSTATE_DATA && !len); ++vpd) { 68 | switch (state) { 69 | case VSTATE_KW1: 70 | kw = *vpd; 71 | state = VSTATE_KW2; 72 | break; 73 | case VSTATE_KW2: 74 | kw = (kw<<8) | *vpd; 75 | state = VSTATE_LEN; 76 | break; 77 | case VSTATE_LEN: 78 | origLen = len = *vpd; 79 | state = VSTATE_DATA; 80 | bufp = buf; 81 | break; 82 | case VSTATE_DATA: 83 | if (len > bufLen) { 84 | bufLen = len; 85 | buf = bufp = realloc(buf, len+1); 86 | memset(buf, 0, len+1); 87 | } 88 | if (!len) { 89 | *bufp++ = 0; 90 | switch (kw) { 91 | case VPD_KW_PART_NUMBER: 92 | printf(" Part Number: \"%s\"\n", buf); 93 | break; 94 | case VPD_KW_ENGINEERING_CHANGES: 95 | printf(" Engineering Changes: \"%s\"\n", buf); 96 | break; 97 | case VPD_KW_SERIAL_NUMBER: 98 | printf(" Serial Number: \"%s\"\n", buf); 99 | break; 100 | case VPD_KW_MANUFACTURE_ID: 101 | printf(" Manufacture ID: \"%s\"\n", buf); 102 | break; 103 | case VPD_KW_RV: 104 | printf(" (Checksum/End)\n"); 105 | if (ckOffsetPtr) 106 | *ckOffsetPtr = (vpd - vpdStart); 107 | break; 108 | case VPD_KW_ASSET_TAG: 109 | printf(" Asset Tag: \"%s\"\n", buf); 110 | break; 111 | case VPD_KW_READ_WRITE_AREA: 112 | printf(" (Read/Write Reserved Area)\n"); 113 | break; 114 | case VPD_KW_V0: 115 | case VPD_KW_V1: 116 | case VPD_KW_V2: 117 | case VPD_KW_V3: 118 | case VPD_KW_V4: 119 | case VPD_KW_V5: 120 | case VPD_KW_V6: 121 | printf(" V%c: \"%s\"\n", kw & 0xFF, buf); 122 | break; 123 | default: 124 | printf(" Unknown VPD KW 0x%04X ('%c%c')\n", kw, kw>>8, kw & 0xFF); 125 | break; 126 | } 127 | state = VSTATE_KW1; 128 | --vpd; 129 | break; 130 | } 131 | 132 | *bufp++ = *vpd; 133 | --len; 134 | 135 | break; 136 | default: 137 | abort(); 138 | } 139 | } 140 | } 141 | 142 | static int _DumpVPD(uint8_t *vpd, size_t vpdBufLen) { 143 | uint8_t *vpdEnd = vpd + vpdBufLen; 144 | enum { 145 | STATE_DRIFTING = 0, 146 | STATE_LEN1, 147 | STATE_LEN2, 148 | STATE_DATA, 149 | }; 150 | enum { 151 | VPD_TYPE_IDENTIFIER_STRING = 0x02, 152 | VPD_TYPE_READ_ONLY = 0x10, 153 | VPD_TYPE_READ_WRITE = 0x11, 154 | VPD_TYPE_END = 0x0F, 155 | }; 156 | int state = STATE_DRIFTING; 157 | uint8_t vpdType; 158 | uint16_t vpdLen, vpdOrigLen; 159 | uint8_t *buf = NULL; 160 | size_t bufLen = 0; 161 | uint8_t *bufp = realloc(NULL, 1); 162 | size_t ckOffset = 0; 163 | uint8_t *vpdStart = vpd; 164 | bool errCRC = false; 165 | for (;vpd != vpdEnd || (state == STATE_DATA && !vpdLen); ++vpd) { 166 | switch (state) { 167 | case STATE_DRIFTING: 168 | if (*vpd & 0x80) { 169 | // Large tag 170 | vpdType = *vpd & 0x7F; 171 | state = STATE_LEN1; 172 | } else { 173 | // Small tag 174 | vpdType = ((*vpd) >> 3) & 0x0F; 175 | vpdLen = (*vpd) & 0x07; 176 | state = STATE_DATA; 177 | } 178 | break; 179 | case STATE_LEN1: 180 | vpdLen = vpdOrigLen = *vpd; 181 | state = STATE_LEN2; 182 | bufp = buf; 183 | break; 184 | case STATE_LEN2: 185 | vpdLen |= ((uint32_t)(*vpd)) << 8; 186 | vpdOrigLen = vpdLen; 187 | state = STATE_DATA; 188 | bufp = buf; 189 | break; 190 | case STATE_DATA: 191 | if (vpdLen > bufLen) { 192 | bufLen = vpdLen; 193 | buf = bufp = realloc(buf, bufLen+1); 194 | memset(buf, 0, bufLen+1); 195 | } 196 | 197 | if (!vpdLen) { 198 | *bufp++ = 0; 199 | switch (vpdType) { 200 | case VPD_TYPE_IDENTIFIER_STRING: 201 | printf(" Identifier: \"%s\"\n", buf); 202 | break; 203 | case VPD_TYPE_READ_ONLY: 204 | printf(" Read-Only VPD Data:\n"); 205 | _DumpVPDInner(buf, vpdOrigLen, &ckOffset); 206 | break; 207 | case VPD_TYPE_READ_WRITE: 208 | printf(" Read-Write VPD Data:\n"); 209 | _DumpVPDInner(buf, vpdOrigLen, NULL); 210 | break; 211 | case VPD_TYPE_END: 212 | printf(" End of VPD data\n"); 213 | 214 | if (ckOffset) { 215 | uint8_t sum = 0; 216 | uint8_t *ckRangeEnd = vpdStart + ckOffset + 1; 217 | for (vpd = vpdStart; vpd != ckRangeEnd; ++vpd) 218 | sum += *vpd; 219 | if (sum) { 220 | printf(" VPD RO Checksum: MISMATCH (0x%02X)\n", sum); 221 | errCRC = true; 222 | } else 223 | printf(" VPD RO Checksum: OK\n"); 224 | } 225 | 226 | return errCRC ? 1 : 0; 227 | default: 228 | printf(" (Unknown VPD Data)\n"); 229 | break; 230 | } 231 | 232 | state = STATE_DRIFTING; 233 | --vpd; 234 | break; 235 | } 236 | 237 | *bufp++ = *vpd; 238 | --vpdLen; 239 | break; 240 | 241 | default: 242 | return -1; 243 | } 244 | } 245 | 246 | printf(" Warning: malformed VPD data\n"); 247 | return 1; 248 | } 249 | 250 | static size_t _extVPDIdx = SIZE_MAX; 251 | static size_t _extDirIdx = SIZE_MAX; 252 | static size_t _extVPDOffset = 0; 253 | static size_t _extDirOffset = 0; 254 | static size_t _extVPDSize = 0; 255 | static size_t _extDirSize = 0; 256 | 257 | static int _DumpDirectory(otg_directory_entry *dir, size_t numEntries) { 258 | char typebuf[16]; 259 | for (size_t i=0; i> 22; 263 | uint32_t low22 = (ntohl(dir[i].typeSize) & 0x007FFFFF); 264 | uint32_t loadAddr = (ntohl(dir[i].loadAddr)); 265 | uint32_t offset = (ntohl(dir[i].offset)); 266 | 267 | switch (type) { 268 | case OTG_HEADER_TAG_TYPE__APE_CODE: 269 | typep = "APE code"; 270 | break; 271 | case OTG_HEADER_TAG_TYPE__EXTENDED_VPD: 272 | typep = "Extended VPD"; 273 | if (low22) { 274 | _extVPDIdx = i; 275 | _extVPDOffset = offset; 276 | _extVPDSize = low22*4; 277 | } 278 | break; 279 | case OTG_HEADER_TAG_TYPE__ISCSI_BOOT: 280 | typep = "iSCSI boot ROM"; 281 | break; 282 | case OTG_HEADER_TAG_TYPE__ISCSI_CFG: 283 | typep = "iSCSI configuration"; 284 | break; 285 | case OTG_HEADER_TAG_TYPE__ISCSI_CFG_PRG: 286 | typep = "iSCSI configuration program"; 287 | break; 288 | case OTG_HEADER_TAG_TYPE__ISCSI_CFG_1: 289 | typep = "iSCSI configuration (1)"; 290 | break; 291 | case OTG_HEADER_TAG_TYPE__EXT_DIR: 292 | typep = "Extended directory"; 293 | if (low22) { 294 | _extDirIdx = i; 295 | _extDirOffset = offset; 296 | _extDirSize = low22*4; 297 | } 298 | break; 299 | case OTG_HEADER_TAG_TYPE__PXE: 300 | if (low22) { 301 | typep = "PXE expansion ROM"; 302 | break; 303 | } 304 | default: 305 | snprintf(typebuf, sizeof(typebuf), "type 0x%02X", type>>24); 306 | break; 307 | } 308 | 309 | if (type || low22 || middleBits || loadAddr || offset) 310 | printf(" %2u: [%02X] %-25s [%01X], size=0x%08X, offset=0x%08X, loadAddr=0x%08X\n", i, type>>24, typep, middleBits, low22*4, offset, loadAddr); 311 | /* 312 | switch (type) { 313 | // Expansion ROM Pointer. Offset of Expansion ROM is in tag.v1, size probably 314 | // in header low 24 bits. v2 probably unused. 315 | OTG_HEADER_TAG_TYPE__PXE = 0x00<<24, 316 | 317 | OTG_HEADER_TAG_TYPE__ASF_INIT = 0x01<<24, 318 | OTG_HEADER_TAG_TYPE__ASF_CPUA = 0x02<<24, 319 | OTG_HEADER_TAG_TYPE__ASF_CPUB = 0x03<<24, 320 | OTG_HEADER_TAG_TYPE__ASF_CFG = 0x04<<24, 321 | OTG_HEADER_TAG_TYPE__ISCSI_CFG = 0x05<<24, 322 | OTG_HEADER_TAG_TYPE__ISCSI_CFG_PRG = 0x06<<24, 323 | OTG_HEADER_TAG_TYPE__USER_BLOCK = 0x07<<24, 324 | OTG_HEADER_TAG_TYPE__BRSF_BLOCK = 0x08<<24, 325 | OTG_HEADER_TAG_TYPE__ASF_MBOX = 0x0A<<24, 326 | OTG_HEADER_TAG_TYPE__ISCSI_CFG_1 = 0x0B<<24, 327 | OTG_HEADER_TAG_TYPE__APE_CFG = 0x0C<<24, 328 | 329 | // Pointer to NCSI (that is, APE) executable image. 330 | // v1 is the offset in bytes in flash, header low22 is the size in 331 | // words. v2 unused. 332 | OTG_HEADER_TAG_TYPE__APE_CODE = 0x0D<<24, 333 | 334 | OTG_HEADER_TAG_TYPE__APE_UPDATE = 0x0E<<24, 335 | OTG_HEADER_TAG_TYPE__EXT_CFG = 0x0F<<24, 336 | OTG_HEADER_TAG_TYPE__EXT_DIR = 0x10<<24, 337 | OTG_HEADER_TAG_TYPE__APE_DATA = 0x11<<24, 338 | OTG_HEADER_TAG_TYPE__APE_WEB_DATA = 0x12<<24, 339 | OTG_HEADER_TAG_TYPE__APE_WORKAROUND= 0x13<<24, 340 | 341 | // Some weird thing handed by stage2 main loop to allow update of some sort 342 | // of VPD data during normal operation. Details unknown. 343 | OTG_HEADER_TAG_TYPE__EXTENDED_VPD = 0x14<<24, 344 | 345 | case OTG_HEADER_TAG_TYPE__PXE: 346 | case OTG_HEADER_TAG_TYPE__APE_CODE: 347 | 348 | case OTG_HEADER_TAG_TYPE__APE_CODE: 349 | printf(" APE code, offset=0x%08X, size=0x%08X (%s)\n", 350 | v1, low22*4, _Humanize(low22*4, sizeBuf)); 351 | break; 352 | case OTG_HEADER_TAG_TYPE__EXTENDED_VPD: 353 | printf(" Extended VPD, offset=0x%08X, size=0x%08X (%s)\n", 354 | v1, low22*4, _Humanize(low22*4, sizeBuf)); 355 | break; 356 | case OTG_HEADER_TAG_TYPE__PXE: 357 | if (low22) { 358 | printf(" PXE expansion ROM, offset=0x%08X, probable size=0x%08X (%s)\n", v1, low22*4, _Humanize(low22*4, sizeBuf)); 359 | break; 360 | } 361 | default: 362 | printf(" type 0x%02x, offset=0x%06x, v1=0x%08X, v2=0x%08X\n", 363 | ntohl(hdr->tags[i].header) >> 24, 364 | ntohl(hdr->tags[i].header) & 0x00FFFFFF, 365 | ntohl(hdr->tags[i].v1), 366 | ntohl(hdr->tags[i].v2)); 367 | break; 368 | } 369 | */ 370 | } 371 | 372 | return 0; 373 | } 374 | 375 | static int _CmdInfo(int pargc, int argc, char **argv) { 376 | int ec; 377 | int argidx; 378 | error_t argerr = argp_parse(&_argpInfo, argc, argv, 0, &argidx, NULL); 379 | if (argerr || !argv[argidx] || argv[argidx+1]) { 380 | argp_help(&_argpInfo, stderr, ARGP_HELP_STD_USAGE, argv[0]); 381 | return 2; 382 | } 383 | 384 | int fd = open(argv[argidx], O_RDONLY); 385 | if (fd < 0) { 386 | fprintf(stderr, "error: can't open \"%s\"\n", argv[argidx]); 387 | return 1; 388 | } 389 | 390 | struct stat st; 391 | ec = fstat(fd, &st); 392 | if (ec < 0) 393 | return 1; 394 | 395 | void *virt = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 396 | if (!virt) 397 | return 1; 398 | 399 | void *virtEnd = (uint8_t*)virt + st.st_size; 400 | otg_header *hdr = (otg_header*)virt; 401 | 402 | if ((void*)(hdr+1) > virtEnd) { 403 | fprintf(stderr, "error: file too short to have a valid header\n"); 404 | return 1; 405 | } 406 | 407 | if (ntohl(hdr->magic) != HEADER_MAGIC) { 408 | fprintf(stderr, "error: not a valid image (bad magic)\n"); 409 | return 1; 410 | } 411 | 412 | bool errCRC = false; 413 | bool errS2 = false; 414 | bool errVPD = false; 415 | { 416 | char sizeBuf[HUMAN_SIZE_LEN]; 417 | char name[sizeof(hdr->partNo)+1] = {}; 418 | memcpy(name, hdr->partNo, sizeof(hdr->partNo)); 419 | 420 | uint32_t s1Size = ntohl(hdr->s1Size)*4; 421 | uint32_t s1Offset = ntohl(hdr->s1Offset); 422 | 423 | otg_s2header *s2hdr = (otg_s2header*)((uint8_t*)virt+s1Offset+s1Size); 424 | if ((void*)(s2hdr + sizeof(otg_s2header)) > virtEnd) 425 | s2hdr = NULL; 426 | 427 | if (s2hdr && ntohl(s2hdr->magic) != HEADER_MAGIC) 428 | s2hdr = NULL; 429 | 430 | if (s2hdr && (void*)((uint8_t*)s2hdr + ntohl(s2hdr->s2Size)) > virtEnd) 431 | s2hdr = NULL; 432 | 433 | printf("========== Image Information ==========\n"); 434 | printf("Total Size: 0x%08X (%s)\n", st.st_size, _Humanize(st.st_size, sizeBuf)); 435 | printf("Stage 1 Load Addr/Entry: 0x%08X\n", ntohl(hdr->s1Entrypoint)); 436 | printf("Stage 1 Size: 0x%08X (%s)\n", s1Size, _Humanize(s1Size, sizeBuf)); 437 | printf("Stage 1 Offset: 0x%08X\n", s1Offset); 438 | { 439 | uint32_t expectedCRC = ntohl(hdr->bootHdrCRC); 440 | uint32_t actualCRC = SwapEndian32(ComputeCRC((uint8_t*)hdr, 4, 0xFFFFFFFF) ^ 0xFFFFFFFF); 441 | printf("Boot Header Checksum: %s\n", (actualCRC == expectedCRC) ? "OK" : "MISMATCH"); 442 | if (actualCRC != expectedCRC) { 443 | printf(" got 0x%08X, expected 0x%08X\n", actualCRC, expectedCRC); 444 | errCRC = true; 445 | } 446 | } 447 | { 448 | uint8_t sum = hdr->dirCRC; 449 | for (size_t i=0x14; i<0x74; ++i) 450 | sum += *((uint8_t*)hdr + i); 451 | printf("Directory Checksum: %s\n", (!sum) ? "OK" : "MISMATCH"); 452 | if (sum) 453 | errCRC = true; 454 | } 455 | if (ntohs(hdr->mfrLen) != 0x008C) 456 | printf("WARNING: Unexpected manufacturing data length.\n"); 457 | if (ntohs(hdr->mfr2Len) != 0x008C) 458 | printf("WARNING: Unexpected manufacturing data 2 length.\n"); 459 | { 460 | uint32_t expectedCRC = ntohl(hdr->mfrCRC); 461 | uint32_t actualCRC = SwapEndian32(ComputeCRC(&hdr->mfrFormatRev, 0x008C/4 - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 462 | printf("Manufacturing Data Checksum: %s\n", (actualCRC == expectedCRC) ? "OK" : "MISMATCH"); 463 | if (actualCRC != expectedCRC) { 464 | printf(" got 0x%08X, expected 0x%08X\n", actualCRC, expectedCRC); 465 | errCRC = true; 466 | } 467 | } 468 | { 469 | uint32_t expectedCRC = ntohl(hdr->mfr2CRC); 470 | uint32_t actualCRC = SwapEndian32(ComputeCRC(&hdr->mfr2Unk, 0x008C/4 - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 471 | printf("Manufacturing Data 2 Checksum: %s\n", (actualCRC == expectedCRC) ? "OK" : "MISMATCH"); 472 | if (actualCRC != expectedCRC) { 473 | printf(" got 0x%08X, expected 0x%08X\n", actualCRC, expectedCRC); 474 | errCRC = true; 475 | } 476 | } 477 | { 478 | uint32_t expectedCRC = ntohl(*(uint32_t*)((uint8_t*)hdr + s1Offset + s1Size-4)); 479 | uint32_t actualCRC = SwapEndian32(ComputeCRC((uint8_t*)hdr + s1Offset, (s1Size/4)-1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 480 | printf("Stage 1 Checksum: %s\n", (actualCRC == expectedCRC) ? "OK" : "MISMATCH"); 481 | if (actualCRC != expectedCRC) { 482 | printf(" got 0x%08X, expected 0x%08X\n", actualCRC, expectedCRC); 483 | errCRC = true; 484 | } 485 | } 486 | { 487 | uint32_t virtPtr = ntohl(*(uint32_t*)((uint8_t*)hdr + s1Offset + 8)); 488 | uint32_t offset = virtPtr - ntohl(hdr->s1Entrypoint) + s1Offset; 489 | printf("Stage 1 Image Version String: \"%s\"\n", (char*)((uint8_t*)hdr + offset)); 490 | } 491 | 492 | printf("Stage 2 Offset: 0x%08X\n", s1Offset+s1Size); 493 | if (s2hdr) { 494 | printf("Stage 2 Entrypoint: 0x%08X\n", s1Offset+s1Size+8); 495 | printf("Stage 2 Size: 0x%08X (%s)\n", ntohl(s2hdr->s2Size), _Humanize(ntohl(s2hdr->s2Size), sizeBuf)); 496 | 497 | uint32_t expectedCRC = ntohl(*(uint32_t*)( 498 | (uint8_t*)s2hdr + 8 + ntohl(s2hdr->s2Size) - 4)); 499 | uint32_t actualCRC = SwapEndian32(ComputeCRC((uint8_t*)s2hdr + 8, (ntohl(s2hdr->s2Size)-4)/4, 0xFFFFFFFF) ^ 0xFFFFFFFF); 500 | 501 | printf("Stage 2 Checksum: %s\n", (actualCRC == expectedCRC) ? "OK" : "MISMATCH"); 502 | if (actualCRC != expectedCRC) { 503 | printf(" got 0x%08X, expected 0x%08X\n", actualCRC, expectedCRC); 504 | errCRC = true; 505 | } 506 | } else { 507 | printf("Stage 2 invalid (bad magic or file too short)\n"); 508 | errS2 = true; 509 | } 510 | 511 | printf("Image Name: \"%s\"\n", name); 512 | printf("Directory:\n"); 513 | _DumpDirectory(hdr->dir, ARRAYLEN(hdr->dir)); 514 | 515 | printf("PCI Vendor/Device ID: %04X:%04X\n", ntohs(hdr->pciVendor), ntohs(hdr->pciDevice)); 516 | printf("PCI Subsystem Vendor ID: %04X\n", ntohs(hdr->pciSubsystemVendor)); 517 | printf("PCI Subsystem ID:\n"); 518 | printf(" Fun0 Fun1 Fun2 Fun3\n"); 519 | printf(" GPHY %04X %04X %04X %04X\n", ntohs(hdr->pciSubsystemF0GPHY), ntohs(hdr->pciSubsystemF1GPHY), ntohs(hdr->pciSubsystemF2GPHY), ntohs(hdr->pciSubsystemF3GPHY)); 520 | printf(" SERDES %04X %04X %04X %04X\n", ntohs(hdr->pciSubsystemF0SERDES), ntohs(hdr->pciSubsystemF1SERDES), ntohs(hdr->pciSubsystemF2SERDES), ntohs(hdr->pciSubsystemF3SERDES)); 521 | 522 | printf("F0 MAC: %04X%08X\n", ntohl(hdr->mac0[0]), ntohl(hdr->mac0[1])); 523 | printf("F1 MAC: %04X%08X\n", ntohl(hdr->mac1[0]), ntohl(hdr->mac1[1])); 524 | printf("F2 MAC: %04X%08X\n", ntohl(hdr->mac2[0]), ntohl(hdr->mac2[1])); 525 | printf("F3 MAC: %04X%08X\n", ntohl(hdr->mac3[0]), ntohl(hdr->mac3[1])); 526 | 527 | printf("F0 CFG_1E4: 0x%08X\n", ntohl(hdr->func0CfgFeature)); 528 | printf("F0 CFG_2: 0x%08X\n", ntohl(hdr->func0CfgHW)); 529 | printf("F0 CFG_2A8: 0x%08X\n", ntohl(hdr->func0CfgHW2)); 530 | printf("F1 CFG_1E4: 0x%08X\n", ntohl(hdr->func1CfgFeature)); 531 | printf("F1 CFG_2: 0x%08X\n", ntohl(hdr->func1CfgHW)); 532 | printf("F1 CFG_2A8: 0x%08X\n", ntohl(hdr->func1CfgHW2)); 533 | printf("F2 CFG_1E4: 0x%08X\n", ntohl(hdr->func2CfgFeature)); 534 | printf("F2 CFG_2: 0x%08X\n", ntohl(hdr->func2CfgHW)); 535 | printf("F2 CFG_2A8: 0x%08X\n", ntohl(hdr->func2CfgHW2)); 536 | printf("F3 CFG_1E4: 0x%08X\n", ntohl(hdr->func3CfgFeature)); 537 | printf("F3 CFG_2: 0x%08X\n", ntohl(hdr->func3CfgHW)); 538 | printf("F3 CFG_2A8: 0x%08X\n", ntohl(hdr->func3CfgHW2)); 539 | printf("CFG_3: 0x%08X\n", ntohl(hdr->cfgShared)); 540 | printf("CFG_5: 0x%08X\n", ntohl(hdr->cfg5)); 541 | 542 | printf("VPD:\n"); 543 | errVPD = errVPD || _DumpVPD((uint8_t*)hdr->vpd, sizeof(hdr->vpd)); 544 | } 545 | 546 | printf("Extended VPD:\n"); 547 | if (_extVPDIdx == SIZE_MAX) 548 | printf(" Not present\n"); 549 | else { 550 | uint8_t *extVPD = (uint8_t*)hdr + _extVPDOffset; 551 | uint8_t *extVPDEnd = extVPD + _extVPDSize; 552 | if (extVPDEnd > (uint8_t*)virtEnd) 553 | return 1; 554 | errVPD = errVPD || _DumpVPD(extVPD, _extVPDSize); 555 | } 556 | 557 | printf("Extended Directory:\n"); 558 | if (_extDirIdx == SIZE_MAX) 559 | printf(" Not present\n"); 560 | else { 561 | uint8_t *extDir = (uint8_t*)hdr + _extDirOffset; 562 | uint8_t *extDirEnd = extDir + _extDirSize; 563 | if (extDirEnd > (uint8_t*)virtEnd) 564 | return 1; 565 | if ((_extDirSize-4) % sizeof(otg_directory_entry)) 566 | return 1; 567 | 568 | { 569 | uint32_t expectedCRC = ntohl(*(uint32_t*)(extDirEnd-4)); 570 | uint32_t actualCRC = SwapEndian32(ComputeCRC(extDir, (_extDirSize/4) - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 571 | printf(" Checksum: %s\n", (actualCRC == expectedCRC) ? "OK" : "MISMATCH"); 572 | if (actualCRC != expectedCRC) { 573 | printf(" got 0x%08X, expected 0x%08X\n", actualCRC, expectedCRC); 574 | errCRC = true; 575 | } 576 | } 577 | 578 | _DumpDirectory((otg_directory_entry*)extDir, _extDirSize/sizeof(otg_directory_entry)); 579 | } 580 | 581 | if (errCRC || errS2) 582 | printf("Defects:%s%s%s\n", errCRC ? " crc" : "", errS2 ? " stage2" : "", errVPD ? " vpd" : ""); 583 | else 584 | printf("Defects: none\n"); 585 | return 0; 586 | } 587 | 588 | static const struct argp _argpSet = { 589 | .doc = "Set a parameter in a firmware image.\v" 590 | "Parameters:\n" 591 | " mac0 ] - MAC addresses.\n" 592 | " mac1 ] Format: 1122aabb1122\n" 593 | " mac2 ]\n" 594 | " mac3 ]\n" 595 | " vpd - Set VPD data block. Pass as the value a filename to\n" 596 | " binary VPD data in the correct format (or /dev/stdin).\n" 597 | " The data is copied to the extended VPD region if present;\n" 598 | " otherwise it is copied to the standard VPD region.\n" 599 | " vpdstd - Like vpd, but always copies to the standard VPD region.\n" 600 | " Can be used to update the standard VPD region when an\n" 601 | " extended VPD region is present.\n" 602 | " vpdext - Like vpd, but always copies to the extended VPD region.\n" 603 | " Fails if extended VPD region is not present.\n" 604 | , 605 | .args_doc = " ", 606 | }; 607 | 608 | typedef struct { 609 | const char *name; 610 | size_t offset; 611 | uint8_t type; 612 | } param_t; 613 | 614 | enum { 615 | PARAM_TYPE_MAC = 1, 616 | PARAM_TYPE_VPD, 617 | PARAM_TYPE_VPD_STD, 618 | PARAM_TYPE_VPD_EXT, 619 | }; 620 | 621 | static const param_t _params[] = { 622 | #define X(Name, FieldName, Type) {(#Name), offsetof(otg_header, FieldName), (PP_CAT(PARAM_TYPE_,Type)),}, 623 | X(mac0, mac0, MAC) 624 | X(mac1, mac1, MAC) 625 | X(mac2, mac2, MAC) 626 | X(mac3, mac3, MAC) 627 | X(vpd, vpd, VPD) 628 | X(vpdstd, vpd, VPD_STD) 629 | X(vpdext, vpd, VPD_EXT) 630 | #undef X 631 | {}, 632 | }; 633 | 634 | static int _CmdSet(int pargc, int argc, char **argv) { 635 | int ec; 636 | int argidx; 637 | error_t argerr = argp_parse(&_argpSet, argc, argv, 0, &argidx, NULL); 638 | if (argc < 4 || argerr || !argv[argidx] || !argv[argidx+1] || !argv[argidx+2] || argv[argidx+3]) { 639 | argp_help(&_argpSet, stderr, ARGP_HELP_STD_USAGE, argv[0]); 640 | return 2; 641 | } 642 | 643 | int fd = open(argv[argidx], O_RDWR); 644 | if (fd < 0) { 645 | fprintf(stderr, "error: can't open \"%s\"\n", argv[argidx]); 646 | return 1; 647 | } 648 | 649 | struct stat st; 650 | ec = fstat(fd, &st); 651 | if (ec < 0) 652 | return 1; 653 | 654 | void *virt = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 655 | if (!virt) 656 | return 1; 657 | 658 | void *virtEnd = (uint8_t*)virt + st.st_size; 659 | otg_header *hdr = (otg_header*)virt; 660 | 661 | if ((void*)(hdr+1) > virtEnd) { 662 | fprintf(stderr, "error: file too short to have a valid header\n"); 663 | return 1; 664 | } 665 | 666 | if (ntohl(hdr->magic) != HEADER_MAGIC) { 667 | fprintf(stderr, "error: not a valid image (bad magic)\n"); 668 | return 1; 669 | } 670 | 671 | const char *param = argv[argidx+1]; 672 | const char *value = argv[argidx+2]; 673 | const param_t *pdef; 674 | for (pdef = _params; pdef->name; ++pdef) 675 | if (!strcmp(pdef->name, param)) 676 | break; 677 | if (!pdef->name) { 678 | fprintf(stderr, "error: unknown parameter name \"%s\"\n", param); 679 | return 2; 680 | } 681 | 682 | if (ntohs(hdr->mfrLen) != 0x008C) { 683 | printf("Unexpected manufacturing data length, cannot continue.\n"); 684 | return 1; 685 | } 686 | if (ntohs(hdr->mfr2Len) != 0x008C) { 687 | printf("Unexpected manufacturing data 2 length, cannot continue.\n"); 688 | return 1; 689 | } 690 | 691 | bool goodMfrCRC1, goodMfrCRC2; 692 | { 693 | uint32_t expectedCRC = ntohl(hdr->mfrCRC); 694 | uint32_t actualCRC = SwapEndian32(ComputeCRC(&hdr->mfrFormatRev, 0x008C/4 - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 695 | goodMfrCRC1 = (actualCRC == expectedCRC); 696 | } 697 | { 698 | uint32_t expectedCRC = ntohl(hdr->mfr2CRC); 699 | uint32_t actualCRC = SwapEndian32(ComputeCRC(&hdr->mfr2Unk, 0x008C/4 - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 700 | goodMfrCRC2 = (actualCRC == expectedCRC); 701 | } 702 | 703 | bool changeTouchesMfr = (pdef->offset >= 0x074 && pdef->offset < 0x0FC); 704 | bool changeTouchesMfr2 = (pdef->offset >= 0x200 && pdef->offset < 0x288); 705 | 706 | unsigned mac32[6]; 707 | uint8_t mac[6]; 708 | uint32_t type = pdef->type; 709 | switch (type) { 710 | case PARAM_TYPE_MAC: 711 | if (sscanf(value, "%02x%02x%02x%02x%02x%02x", &mac32[0], &mac32[1], &mac32[2], &mac32[3], &mac32[4], &mac32[5]) < 6) { 712 | fprintf(stderr, "error: malformed MAC address\n"); 713 | return 2; 714 | } 715 | 716 | mac[0] = (uint8_t)mac32[0]; 717 | mac[1] = (uint8_t)mac32[1]; 718 | mac[2] = (uint8_t)mac32[2]; 719 | mac[3] = (uint8_t)mac32[3]; 720 | mac[4] = (uint8_t)mac32[4]; 721 | mac[5] = (uint8_t)mac32[5]; 722 | ssize_t wr = pwrite(fd, mac, 6, pdef->offset+2); 723 | if (wr < 6) { 724 | fprintf(stderr, "error: failed to write value\n"); 725 | return 1; 726 | } 727 | 728 | break; 729 | 730 | case PARAM_TYPE_VPD: 731 | case PARAM_TYPE_VPD_STD: 732 | case PARAM_TYPE_VPD_EXT: { 733 | int vpdIdx = -1; 734 | if (type != PARAM_TYPE_VPD_STD) 735 | for (size_t i=0; idir); ++i) { 736 | if ((ntohl(hdr->dir[i].typeSize) & 0xFF000000) == OTG_HEADER_TAG_TYPE__EXTENDED_VPD && ntohl(hdr->dir[i].offset)) { 737 | vpdIdx = i; 738 | break; 739 | } 740 | } 741 | if (type == PARAM_TYPE_VPD) 742 | type = (vpdIdx >= 0) ? PARAM_TYPE_VPD_EXT : PARAM_TYPE_VPD_STD; 743 | 744 | if (type == PARAM_TYPE_VPD_EXT && vpdIdx < 0) { 745 | fprintf(stderr, "error: extended VPD area not present\n"); 746 | return 1; 747 | } 748 | 749 | uint32_t vpdStart; 750 | uint32_t vpdLen; 751 | if (type == PARAM_TYPE_VPD_STD) { 752 | vpdStart = 0x100; 753 | vpdLen = sizeof(hdr->vpd); 754 | } else { 755 | vpdStart = ntohl(hdr->dir[vpdIdx].offset); 756 | vpdLen = (ntohl(hdr->dir[vpdIdx].typeSize) & 0x3FFFFF)*4; 757 | } 758 | 759 | void *vpdBuf = calloc(1, vpdLen+1); 760 | assert(vpdBuf); 761 | 762 | FILE *fi = fopen(value, "rb"); 763 | if (!fi) { 764 | fprintf(stderr, "error: could not open file: %s\n", value); 765 | return 1; 766 | } 767 | 768 | ssize_t rd = fread(vpdBuf, 1, vpdLen+1, fi); 769 | if (rd < 0) { 770 | fprintf(stderr, "error reading file\n"); 771 | return 1; 772 | } 773 | 774 | if (rd > vpdLen) { 775 | fprintf(stderr, "error: VPD data is too large to fit\n"); 776 | return 1; 777 | } 778 | 779 | ssize_t wr = pwrite(fd, vpdBuf, vpdLen, vpdStart); 780 | if (wr < vpdLen) { 781 | fprintf(stderr, "error: failed to write value\n"); 782 | return 1; 783 | } 784 | 785 | } break; 786 | 787 | default: 788 | abort(); 789 | } 790 | 791 | if ((changeTouchesMfr && !goodMfrCRC1) || (changeTouchesMfr2 && !goodMfrCRC2)) { 792 | fprintf(stderr, 793 | "WARNING: The CRCs for the manufacturing data block containing the specified field\n" 794 | " is not valid in this image. This CRC is not checked during device boot, so in\n" 795 | " practice this is harmless and setting the value will still work.\n" 796 | " \n" 797 | " However, it is possible that some of manufacturing data has been corrupted. Another\n" 798 | " possibility is that the image was simply mis-manufactured and the factory isn't setting\n" 799 | " the manufacturing data block CRCs correctly (because they aren't required for correct\n" 800 | " operation, it's easily conceivable a factory would neglect to do this, and there seem\n" 801 | " to be some instances of this actually happening in the field.)\n" 802 | " \n" 803 | " Because otgimg can't tell which is the case (factory screwup or actual data corruption),\n" 804 | " otgimg won't write a correct manufacturing data block CRC like it normally would, because\n" 805 | " by doing so it might give a correct CRC to data which has in fact been corrupted.\n" 806 | " Instead, it will leave the old CRCs in place and not update them as it normally would.\n" 807 | " Since this CRC is not checked at runtime, this should not adversely affect device\n" 808 | " operation.\n" 809 | " \n" 810 | " Values have been set as asked and CRC field has *not* been updated.\n" 811 | ); 812 | } else { 813 | if (changeTouchesMfr) { 814 | uint32_t crc = htole32(ComputeCRC(&hdr->mfrFormatRev, 0x008C/4 - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 815 | ssize_t wr = pwrite(fd, &crc, sizeof(crc), 0x0FC); 816 | if (wr < sizeof(crc)) 817 | return 1; 818 | } 819 | if (changeTouchesMfr2) { 820 | uint32_t crc = htole32(ComputeCRC(&hdr->mfr2Unk, 0x008C/4 - 1, 0xFFFFFFFF) ^ 0xFFFFFFFF); 821 | ssize_t wr = pwrite(fd, &crc, sizeof(crc), 0x288); 822 | if (wr < sizeof(crc)) 823 | return 1; 824 | } 825 | } 826 | 827 | return 0; 828 | } 829 | 830 | static const struct argp _argp = { 831 | .args_doc = " [command-args...]", 832 | .doc = "otg firmware image servicing tool.\vCommands:\n" 833 | " info show information about a firmware image\n" 834 | " set set a parameter in a firmware image\n" 835 | , 836 | }; 837 | 838 | typedef struct { 839 | const char *name; 840 | int (*func)(int pargc, int argc, char **argv); 841 | } command_def_t; 842 | 843 | static const command_def_t _commands[] = { 844 | { 845 | .name = "info", 846 | .func = _CmdInfo, 847 | }, 848 | { 849 | .name = "set", 850 | .func = _CmdSet, 851 | }, 852 | {}, 853 | }; 854 | 855 | int main(int argc, char **argv) { 856 | int argidx; 857 | error_t argerr = argp_parse(&_argp, argc, argv, ARGP_IN_ORDER, &argidx, NULL); 858 | if (argerr) 859 | return 2; 860 | 861 | const command_def_t *cmd = _commands; 862 | for (; cmd->name; ++cmd) 863 | if (argv[argidx] && !strcmp(cmd->name, argv[argidx])) 864 | break; 865 | 866 | if (!cmd->name) { 867 | argp_help(&_argp, stderr, ARGP_HELP_STD_USAGE, argv[0]); 868 | return 2; 869 | } 870 | 871 | return cmd->func(argidx, argc-argidx, argv+argidx); 872 | } 873 | -------------------------------------------------------------------------------- /regs.css: -------------------------------------------------------------------------------- 1 | .res { margin-bottom: 0.3em; background-color: #e0e0e0; } 2 | .res h2 { font-size: 1em; border-top: solid 0.2em #d0d0d0; 3 | margin: 0; padding: 0; } 4 | .res-addr { font-family: monospace; white-space: pre; } 5 | .res-name {} 6 | .res-attrs { float: right; text-align: right; font-weight: normal; } 7 | .res-symbol { font-family: monospace; } 8 | .res-source { font-family: Verdana, sans-serif; text-transform: uppercase; font-size: 0.6em; opacity: .5; } 9 | .res-body { background-color: #eaeaea; padding: 0 2em; } 10 | .res-body th { text-align: left; font-weight: normal; opacity: .3; font-family: Verdana, sans-serif; } 11 | .res-body table { font-size: small; } 12 | .res-name:link, .res-name:visited { color: inherit; text-decoration: none; } 13 | p:first-child { margin-top: 0; } 14 | p:last-child { margin-bottom: 0; } 15 | table th { font-size: 0.9em; } 16 | th.bits, td.bits, th.value, td.value { text-align: right; } 17 | table.bits td, table.values td { vertical-align: top; } 18 | td.value { font-family: monospace; font-size: 1.22em; line-height: 1.67em; } 19 | 20 | .res-src-manual, .res-src-header {} 21 | .res-src-manual h2, .res-src-header h2 {} 22 | 23 | .res-src-unknown { background-color: #ffd0d0; } 24 | .res-src-unknown h2 { border-top-color: #ffc0c0 !important; } 25 | .res-src-guessed { background-color: #d0d0ff; } 26 | .res-src-guessed h2 { border-top-color: #c0c0ff !important; } 27 | .res-touch { font-family: Verdana, sans-serif; padding-left: 0.1em; } 28 | 29 | a.reflink:link, a.reflink:visited { font-family: monospace; color: #000080; text-decoration: none; } 30 | .legend { font-size: small; padding-top: 1em; } 31 | -------------------------------------------------------------------------------- /regs2xhtml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys, yaml, xml.etree.ElementTree as ET, re, markdown 3 | 4 | re_symbolize = re.compile(r'''[^a-zA-Z0-9]''') 5 | re_ref = re.compile(r'''(REG|GEN)_[A-Za-z0-9_]+''') 6 | re_word = re.compile(r'''[^ /]+''') 7 | 8 | abbrs = dict( 9 | HARDWARE='HW', 10 | SIGNATURE='SIG', 11 | MAILBOX='MBOX', 12 | CONFIG='CFG', 13 | FIRMWARE='FW', 14 | ADDRESS='ADDR', 15 | ) 16 | 17 | def subref(m): 18 | return '' + m.group(0) + '' 19 | 20 | def _abbreviate(m): 21 | word = m.group(0) 22 | abbr = abbrs.get(word.upper()) 23 | if abbr: 24 | return abbr 25 | else: 26 | return word 27 | 28 | def abbreviate(s): 29 | return re_word.sub(_abbreviate, s) 30 | 31 | def symbolize(s): 32 | return re_symbolize.sub('_', abbreviate(s)).upper() 33 | 34 | def lhex(n): 35 | return hex(n)[2:] 36 | def uhex(n): 37 | return lhex(n).upper() 38 | def nicenum(x, pad=1, padch='0'): 39 | chars = hex(x)[2:].upper().rjust(pad, padch) 40 | pchars = '' 41 | while len(chars) > 0: 42 | pchars = chars[-4:] + '_' + pchars 43 | chars = chars[0:len(chars)-len(chars[-4:])] 44 | return pchars.rstrip('_') 45 | 46 | def addBody(reg, e): 47 | bits = reg.get('Bits') 48 | if bits: 49 | table = ET.SubElement(e, 'table', {"class":"bits"}) 50 | headerRow = ET.SubElement(table, 'tr') 51 | ET.SubElement(headerRow, 'th', {"class":"bits"}).text = 'Bits' 52 | ET.SubElement(headerRow, 'th').text = 'Description' 53 | for bitRange, info in sorted(bits.items(), key=lambda x: int(str(x[0]).split('-',1)[0])): 54 | bitRange = str(bitRange) 55 | if type(info) == str: 56 | info = dict(Name=info) 57 | 58 | row = ET.SubElement(table, 'tr') 59 | ET.SubElement(row, 'td', {"class":"bits"}).text = bitRange.replace('-','—') 60 | namee = ET.SubElement(row, 'td') 61 | ET.SubElement(namee, 'div', {"class":"bitname"}).text = str(info.get('Name', '')) 62 | 63 | notes_md = '
' + markdown.markdown(info.get('Notes', '')) + '
' 64 | notes_md = re_ref.sub(subref, notes_md) 65 | namee.append(ET.fromstring(notes_md)) 66 | 67 | addBody(info, namee) 68 | values = reg.get('Values') 69 | if values: 70 | table = ET.SubElement(e, 'table', {"class":"values"}) 71 | headerRow = ET.SubElement(table, 'tr') 72 | ET.SubElement(headerRow, 'th', {"class":"value"}).text = 'Value' 73 | ET.SubElement(headerRow, 'th').text = 'Description' 74 | for value, info in sorted(values.items(), key=lambda x: x[0]): 75 | value = str(value) 76 | if type(info) != dict: 77 | info = dict(Name=str(info)) 78 | row = ET.SubElement(table, 'tr') 79 | ET.SubElement(row, 'td', {"class":"value"}).text = value 80 | namee = ET.SubElement(row, 'td') 81 | ET.SubElement(namee, 'div', {"class":"valuename"}).text = str(info.get('Name', '')) 82 | 83 | notes_md = '
' + markdown.markdown(info.get('Notes', '')) + '
' 84 | notes_md = re_ref.sub(subref, notes_md) 85 | namee.append(ET.fromstring(notes_md)) 86 | 87 | def run(): 88 | f = open('regs.yaml','r') 89 | data = yaml.load(f) 90 | dRes = data['Resources'] 91 | 92 | groups = dict( 93 | MEM=dict(order=0, title='RX CPU Memory Map', numpad=8), 94 | REG=dict(order=1, title='Device Registers', symbolPrefix='REG_'), 95 | APE=dict(order=2, title='Device APE Registers', symbolPrefix='REG_APE__'), 96 | GEN=dict(order=3, title='GENCOM', numpad=3, symbolPrefix='GEN_'), 97 | MII=dict(order=4, title='MII Registers', numpad=8), 98 | NVM=dict(order=5, title='NVM Layout', numpad=3, defaultSource='Header'), 99 | DIRENTRY=dict(order=6, title='NVM Directory Entries', numpad=2, defaultSource='Header'), 100 | PORT=dict(order=7, title='Ports', numpad=1), 101 | AM=dict(order=8, title='APE Memory Map', numpad=8), 102 | ) 103 | for k, v in dRes.items(): 104 | resType, resNum = k.split(' ', 1) 105 | resNum = int(resNum, 0) 106 | if not v: 107 | print(k) 108 | v['Num'] = resNum 109 | g = groups.get(resType) 110 | if not g: 111 | g = dict() 112 | groups[resType] = g 113 | if 'members' not in g: 114 | g['members'] = [] 115 | g['type'] = resType 116 | g['members'].append(v) 117 | 118 | root = ET.Element('html', dict(xmlns='http://www.w3.org/1999/xhtml', lang='en')) 119 | head = ET.SubElement(root, 'head') 120 | ET.SubElement(head, 'link', dict(href='regs.css', rel='stylesheet')) 121 | title = ET.SubElement(head, 'title') 122 | title.text = 'Resources' 123 | body = ET.SubElement(root, 'body') 124 | 125 | toc = ET.SubElement(body, 'section', {"class":"toc"}) 126 | ET.SubElement(toc, 'h2').text = 'Table of Contents' 127 | tocL = ET.SubElement(toc, 'ul') 128 | 129 | sgroups = sorted(list(groups.values()), key=lambda x: x.get('order', 99)) 130 | for group in sgroups: 131 | e = ET.SubElement(tocL, 'li') 132 | ET.SubElement(e, 'a', {"href":"#"+group['type']}).text = group['title'] 133 | 134 | for group in sgroups: 135 | regSec = ET.SubElement(body, 'section', {"id": group['type']}) 136 | htext = group.get('title', '') 137 | pfx = group.get('pfx', group['type'] + ' ') 138 | groupcl = group['type'].lower() 139 | defaultSource = group.get('defaultSource', 'Manual') 140 | groupType = group['type'] 141 | numpad = group.get('numpad', 4) 142 | ET.SubElement(regSec, 'h1').text = htext 143 | for reg in group['members']: 144 | e = ET.SubElement(regSec, 'div', {"class":"res res-%s res-src-%s" % (groupcl, reg.get('Source', defaultSource).lower()), "id":"%s-%04x" % (groupcl,reg['Num'])}) 145 | header = ET.SubElement(e, 'h2') 146 | if groupType == 'MEM' or groupType == 'AM': 147 | opener = '[%s%s—%s] ' % (pfx, nicenum(reg['Num'],numpad), nicenum(reg['End'],numpad)) 148 | else: 149 | opener = '[%s%s] ' % (pfx, nicenum(reg['Num'],numpad,' ')) 150 | ET.SubElement(header, 'span', {"class":"res-addr"}).text = opener 151 | ET.SubElement(header, 'a', {"class":"res-name", "href":"#%s-%04x" % (groupcl,reg['Num'])}).text = reg.get('Name', uhex(reg['Num'])) 152 | 153 | attrs = ET.SubElement(header, 'span', {"class":"res-attrs"}) 154 | symbolPrefix = group.get('symbolPrefix') 155 | if symbolPrefix: 156 | txt = symbolPrefix + symbolize(reg.get('Name', uhex(reg['Num']))) 157 | ET.SubElement(attrs, 'span', {"class":"res-symbol", "id":txt}).text = ' ' + txt + ' ' 158 | ET.SubElement(attrs, 'span', {"class":"res-source"}).text = reg.get('Source', defaultSource) 159 | touch = '◯' 160 | if reg.get('S1') and reg.get('S2'): 161 | touch = '⬤' 162 | elif reg.get('S1'): 163 | touch = '➊' 164 | elif reg.get('S2'): 165 | touch = '➋' 166 | ET.SubElement(attrs, 'span', {"class":"res-touch res-touch-s1"}).text = touch 167 | bodye = ET.SubElement(e, 'div', {"class":"res-body"}) 168 | notes_md = '
' + markdown.markdown(reg.get('Notes', '')) + '
' 169 | notes_md = re_ref.sub(subref, notes_md) 170 | bodye.append(ET.fromstring(notes_md)) 171 | addBody(reg, bodye) 172 | 173 | legend = ET.SubElement(body, 'div', {"class":"legend"}) 174 | legend.text = ''' 175 | ◯: Not read or written by S1 or S2. 176 | ⬤: Read or written by both S1 and S2. 177 | ➊: Read or written by S1 only. 178 | ➋: Read or written by S2 only. 179 | ''' 180 | 181 | with open('regs.xhtml', 'wb') as fo: 182 | fo.write(b'') 183 | fo.write(ET.tostring(root, 'utf-8')) 184 | 185 | return 0 186 | 187 | if __name__ == '__main__': 188 | sys.exit(run()) 189 | -------------------------------------------------------------------------------- /s1stamp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "otg.h" 10 | #include "otg_common.c" 11 | 12 | static void *g_virt; 13 | 14 | static uint32_t L32(uint32_t offset) { 15 | return ntohl(*(uint32_t*)(((uint8_t*)g_virt) + offset)); 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | int ec; 20 | 21 | if (argc < 2) { 22 | fprintf(stderr, "usage: \n"); 23 | fprintf(stderr, "Stamps S1 image with CRC and performs sanity checks. Build system use only.\n"); 24 | return 2; 25 | } 26 | 27 | int fd = open(argv[1], O_RDWR); 28 | if (fd < 0) 29 | return 1; 30 | 31 | struct stat st; 32 | ec = fstat(fd, &st); 33 | if (ec < 0) 34 | return 1; 35 | 36 | void *virt = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 37 | if (!virt) 38 | return 1; 39 | 40 | if (st.st_size % 4) { 41 | fprintf(stderr, "error: filesize not a multiple of 4\n"); 42 | return 1; 43 | } 44 | 45 | g_virt = virt; 46 | 47 | // There's a reason we do all these sanity tests - it's really easy to mess 48 | // up the linker script. Getting it working took a lot of fiddling and it 49 | // could easily be broken. The trouble is that a linker script doesn't afford 50 | // much control over how the sections are appended into the output file - the 51 | // sections are crudely concatenated. The linker expects that the sections 52 | // will be loaded at virtual addresses with proper alignment, etc., but that 53 | // doesn't necessarily correspond with the padding in the output file - after 54 | // all, binary output mode is a bit of a hack and usually you'd have section 55 | // headers specifying what alignment to load everything with, etc. Moreover, 56 | // lld's support for GNU ld linker scripts leaves a lot to be desired. 57 | // 58 | // The consequence of this is that it's quite easy to mess things up in a way 59 | // that the linker believes the virtual address of any given resource in 60 | // the program image (function, etc.) to be different to the one which the 61 | // actual ROM loader will load that resource at. 62 | // 63 | // Here's the real example which motivated the following tests: The 64 | // otg_header is 0x28C bytes long. The s1Size field of otg_header is set to 65 | // 0x28C, indicating that the first opcode of the entrypoint starts 0x28C 66 | // beyond the start of the header. In the original firmware, the first opcode 67 | // indeed appears at this location. 68 | // 69 | // However, getting the first opcode to appear at this location with lld 70 | // turned out to be infeasible, because of its insistence on proper padding 71 | // of sections. It wanted to align .text to 16 bytes, which meant padding the 72 | // header to 0x290 and putting the first opcode there. Even where lld could 73 | // be convinced to put the first opcode at 0x28C, it would believe that the 74 | // ultimate virtual address of .text would be four bytes later, causing an 75 | // off-by-one-word error in all absolute jumps. 76 | // 77 | // To summarise: 78 | // - A zero word is a NOP in MIPS; 79 | // - Thus putting a zero word at 0x28C and having the entrypoint jump at 80 | // 0x290 is harmless; 81 | // - It's easy to mess up the linker script in a way that causes absolute 82 | // jumps to be off by one or two words; 83 | // - We really don't want such a bug to be introduced accidentially, so 84 | // this check ensures that the first jump lines up properly and that the 85 | // first opcodes haven't moved around unexpectedly in the output file. 86 | if (L32(0x00) != HEADER_MAGIC) { 87 | fprintf(stderr, "S1 has bad magic\n"); 88 | return 1; 89 | } 90 | 91 | // We always load at 0x08003800. 92 | uint32_t imageBase = L32(0x04); 93 | if (imageBase != 0x08003800) { 94 | fprintf(stderr, "S1 has wrong image base\n"); 95 | return 1; 96 | } 97 | 98 | // The header should not be undersized, nor should any header padding 99 | // be excessive. 100 | uint32_t s1Size = L32(0x08)*4; 101 | uint32_t s1Offset = L32(0x0C); 102 | if (s1Offset < sizeof(otg_header) || s1Offset > (sizeof(otg_header)+16)) { 103 | fprintf(stderr, "S1 has wrong offset\n"); 104 | return 1; 105 | } 106 | 107 | // We must find an opcode matching our expectations at @s1Offset. Note that 108 | // we do not allow *any* variation in this opcode - it is an absolute jump to 109 | // another, very close instruction which appears in the same block of 110 | // assembly, and it should always appear at the very start of the image 111 | // loaded into memory (which doesn't include the header and its padding), so 112 | // there should be absolutely zero variation in this opcode, and if there is, 113 | // something is wrong. 114 | // 115 | // Since this is an absolute jump, checking this allows us to ensure that the 116 | // linker has the right idea of where the code is loaded in memory, 117 | // preventing any off-by-one-word virtual address errors from going 118 | // unnoticed. 119 | uint32_t firstOpcode = L32(s1Offset); 120 | if (firstOpcode != 0x0A000E03) { 121 | fprintf(stderr, "First opcode is not expected value, got 0x%08X\n", firstOpcode); 122 | return 1; 123 | } 124 | 125 | // Sanity check, the branch delay slot must be a NOP. 126 | uint32_t secondOpcode = L32(s1Offset+4); 127 | if (secondOpcode) { 128 | fprintf(stderr, "Second opcode is not NOP, got 0x%08X\n", secondOpcode); 129 | return 1; 130 | } 131 | 132 | // Beyond the first jump and the NOP in its branch delay slot comes a pointer 133 | // to a version string as a virtual address. First, check that this is in 134 | // the right ballpark. 135 | uint32_t vstrPtr = L32(s1Offset+0x08); 136 | if ((vstrPtr & 0xFFFF0000) != 0x08000000) { 137 | fprintf(stderr, "Unexpected version string pointer value\n"); 138 | return 1; 139 | } 140 | 141 | // Check that the vstr is correct. This allows us to ensure that virtual 142 | // addresses are still correct even for resources at the end of the S1 image, 143 | // which ensures no anomalous padding in the output file has been output that 144 | // causes a mismatch between virtual addresses and file offsets. 145 | uint8_t *vstr = ((uint8_t*)g_virt) + (vstrPtr - imageBase + s1Offset); 146 | 147 | bool isDummy = false; 148 | if (vstr[0] == 'D' && vstr[1] == 'u' && vstr[2] == 'm' && vstr[3] == 'm' && vstr[4] == 'y') 149 | isDummy = true; 150 | else if (vstr[0] != 'O' || vstr[1] != 'T' || vstr[2] != 'G') { 151 | fprintf(stderr, "Bad version string\n"); 152 | return 1; 153 | } 154 | 155 | // Check that the stack pointer load opcodes are correct. We always set the 156 | // stack end to to 0x0800_7000, so this should never change either. 157 | uint32_t thirdOpcode = L32(s1Offset+0x0C); 158 | uint32_t fourthOpcode = L32(s1Offset+0x10); 159 | if (thirdOpcode != 0x3C1D0800 || fourthOpcode != 0x27BD7000) { 160 | fprintf(stderr, "Third/fourth opcode is not expected stack pointer load, got 0x%08X 0x%08X\n", thirdOpcode, fourthOpcode); 161 | return 1; 162 | } 163 | 164 | if (!isDummy) { 165 | // The S2 header should appear at the right location. Just check for the 166 | // magic; the S2 image is included as a fixed binary, so we can do any S2 167 | // sanity checks in s2stamp. We just need to make sure it's at the right 168 | // offset. 169 | if (L32(s1Offset+s1Size) != HEADER_MAGIC) { 170 | fprintf(stderr, "S2 not found at expected offset\n"); 171 | return 1; 172 | } 173 | } 174 | 175 | // Finally we can start setting CRCs. 176 | if (L32(16) != 0xDEADBEEF) { 177 | fprintf(stderr, "Boot header CRC placeholder not found.\n"); 178 | return 1; 179 | } 180 | 181 | // Stage 1 Image CRC. 182 | if (L32(s1Offset+s1Size-4) != 0xDEADBEEF) { 183 | fprintf(stderr, "S1 image CRC placeholder not found.\n"); 184 | return 1; 185 | } 186 | 187 | uint32_t crc; 188 | ssize_t wr; 189 | 190 | // Boot header CRC. 191 | crc = htole32(ComputeCRC(virt, 16/4, 0xFFFFFFFF)^0xFFFFFFFF); 192 | wr = pwrite(fd, &crc, sizeof(crc), 0x10); 193 | if (wr < sizeof(crc)) 194 | return 1; 195 | 196 | // Directory CRC. 197 | uint8_t dirSum = 0; 198 | uint8_t *dirEnd = (uint8_t*)virt + 0x74; 199 | for (uint8_t *p = (uint8_t*)virt + 0x14; p < dirEnd; ++p) 200 | dirSum += *p; 201 | 202 | uint8_t dirCRC = 0 - dirSum; 203 | wr = pwrite(fd, &dirCRC, sizeof(dirCRC), 0x75); 204 | if (wr < sizeof(dirCRC)) 205 | return 1; 206 | 207 | // Manufacturing Section CRC. 208 | crc = htole32(ComputeCRC((uint8_t*)virt + 0x74, 0x88/4, 0xFFFFFFFF)^0xFFFFFFFF); 209 | wr = pwrite(fd, &crc, sizeof(crc), 0xFC); 210 | if (wr < sizeof(crc)) 211 | return 1; 212 | 213 | // Manufacturing Section 2 CRC. 214 | crc = htole32(ComputeCRC((uint8_t*)virt + 0x200, 0x88/4, 0xFFFFFFFF)^0xFFFFFFFF); 215 | wr = pwrite(fd, &crc, sizeof(crc), 0x288); 216 | if (wr < sizeof(crc)) 217 | return 1; 218 | 219 | // S1 Image CRC. 220 | crc = htole32(ComputeCRC((uint8_t*)virt + s1Offset, (s1Size/4)-1, 0xFFFFFFFF)^0xFFFFFFFF); 221 | wr = pwrite(fd, &crc, sizeof(crc), s1Offset+s1Size-4); 222 | if (wr < sizeof(crc)) 223 | return 1; 224 | 225 | close(fd); 226 | return 0; 227 | } 228 | -------------------------------------------------------------------------------- /s2stamp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "otg.h" 10 | #include "otg_common.c" 11 | 12 | static void *g_virt; 13 | 14 | static uint32_t L32(uint32_t offset) { 15 | return ntohl(*(uint32_t*)(((uint8_t*)g_virt) + offset)); 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | int ec; 20 | 21 | if (argc < 2) { 22 | fprintf(stderr, "usage: \n"); 23 | fprintf(stderr, "Stamps S2 image with CRC and performs sanity checks. Build system use only.\n"); 24 | return 2; 25 | } 26 | 27 | int fd = open(argv[1], O_RDWR); 28 | if (fd < 0) 29 | return 1; 30 | 31 | struct stat st; 32 | ec = fstat(fd, &st); 33 | if (ec < 0) 34 | return 1; 35 | 36 | void *virt = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 37 | if (!virt) 38 | return 1; 39 | 40 | if (st.st_size % 4) { 41 | fprintf(stderr, "error: filesize not a multiple of 4\n"); 42 | return 1; 43 | } 44 | 45 | g_virt = virt; 46 | 47 | // See s1stamp.c for an explanation of the motivations behind 48 | // these tests. 49 | if (L32(0x00) != HEADER_MAGIC) { 50 | fprintf(stderr, "S2 has bad magic\n"); 51 | return 1; 52 | } 53 | 54 | uint32_t s2Size = L32(0x04); 55 | 56 | uint32_t firstOpcode = L32(0x08); 57 | if (firstOpcode) { // != 0x0A000005) { 58 | fprintf(stderr, "First opcode is not expected value, got 0x%08X\n", firstOpcode); 59 | return 1; 60 | } 61 | 62 | uint32_t secondOpcode = L32(0x0C); 63 | if (secondOpcode != 0x10000004) { 64 | fprintf(stderr, "Second opcode is not expected value, got 0x%08X\n", secondOpcode); 65 | return 1; 66 | } 67 | 68 | uint32_t thirdOpcode = L32(0x10); 69 | if (thirdOpcode) { 70 | fprintf(stderr, "Third opcode is not expected NOP, got 0x%08X\n", thirdOpcode); 71 | return 1; 72 | } 73 | 74 | uint32_t fourthOpcode = L32(0x20); 75 | uint32_t fifthOpcode = L32(0x24); 76 | if (fourthOpcode != 0x3C1D0800 || fifthOpcode != 0x27BD7000) { 77 | fprintf(stderr, "Fourth/fifth opcode is not expected stack pointer load, got 0x%08X 0x%08X\n", fourthOpcode, fifthOpcode); 78 | return 1; 79 | } 80 | 81 | if (L32(s2Size+8-4) != 0xDEADBEEF) { 82 | fprintf(stderr, "S2 CRC placeholder not found.\n"); 83 | return 1; 84 | } 85 | 86 | // Write the correct CRC. 87 | uint32_t crc = ComputeCRC((uint8_t*)virt+8, (st.st_size-4-8)/4, 0xFFFFFFFF); 88 | crc = htole32(crc^0xFFFFFFFF); 89 | 90 | ssize_t wr = pwrite(fd, &crc, sizeof(crc), st.st_size-4); 91 | if (wr < sizeof(crc)) 92 | return 1; 93 | 94 | close(fd); 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /setdriver: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | [ -z "$1" ] && { 5 | echo >&2 "usage: " 6 | echo >&2 "" 7 | echo >&2 "This command can be used to switch a tg3 device between the tg3 driver (for normal use)" 8 | echo >&2 "and the pci-stub driver (which does nothing for the device and ensures that the kernel" 9 | echo >&2 "doesn't try and fiddle with it while one is messing with it. The pci-stub mode can also" 10 | echo >&2 "be used to pass the device through to a qemu/KVM virtual machine." 11 | echo >&2 "" 12 | echo >&2 "commands:" 13 | echo >&2 " stub - assign a device to the pci-stub driver" 14 | echo >&2 " virt - assign a device to the vfio-pci driver" 15 | echo >&2 " tg3 - assign a device to the tg3 driver" 16 | echo >&2 " reset - reset a PCI device" 17 | exit 2 18 | } 19 | 20 | [ "$(id -u)" -eq 0 ] || { 21 | echo >&2 Must be run as root. 22 | exit 1 23 | } 24 | 25 | case "$1" in 26 | stub) 27 | ADDR="$2" 28 | [ -z "$ADDR" ] && { 29 | echo >&2 Must specify PCI address. 30 | exit 1 31 | } 32 | 33 | modprobe pci_stub 34 | VENDOR_ID="$(cat /sys/bus/pci/devices/"$ADDR"/vendor)" 35 | DEVICE_ID="$(cat /sys/bus/pci/devices/"$ADDR"/device)" 36 | echo "$VENDOR_ID $DEVICE_ID" > /sys/bus/pci/drivers/pci-stub/new_id 37 | [ -e "/sys/bus/pci/devices/$ADDR/driver/unbind" ] && { 38 | echo "$ADDR" > /sys/bus/pci/devices/"$ADDR"/driver/unbind 39 | } 40 | echo pci-stub > /sys/bus/pci/devices/"$ADDR"/driver_override 41 | echo "$ADDR" > /sys/bus/pci/drivers/pci-stub/bind 42 | ;; 43 | 44 | virt) 45 | ADDR="$2" 46 | [ -z "$ADDR" ] && { 47 | echo >&2 Must specify PCI address. 48 | exit 1 49 | } 50 | 51 | modprobe vfio-pci 52 | [ -e "/sys/bus/pci/devices/"$ADDR"/vendor" ] || { 53 | echo >&2 Bad PCI address. 54 | exit 1 55 | } 56 | 57 | echo vfio-pci > /sys/bus/pci/devices/"$ADDR"/driver_override 58 | [ -e "/sys/bus/pci/devices/$ADDR/driver/unbind" ] && { 59 | echo "$ADDR" > /sys/bus/pci/devices/"$ADDR"/driver/unbind 60 | } 61 | echo "$ADDR" > /sys/bus/pci/drivers_probe 62 | #drivers/vfio-pci/bind 63 | ;; 64 | 65 | tg3) 66 | ADDR="$2" 67 | [ -z "$ADDR" ] && { 68 | echo >&2 Must specify PCI address. 69 | exit 1 70 | } 71 | 72 | echo tg3 > /sys/bus/pci/devices/"$ADDR"/driver_override 73 | [ -e "/sys/bus/pci/devices/$ADDR/driver/unbind" ] && { 74 | echo "$ADDR" > /sys/bus/pci/devices/"$ADDR"/driver/unbind 75 | } 76 | echo "$ADDR" > /sys/bus/pci/drivers/tg3/bind 77 | ;; 78 | 79 | reset) 80 | ADDR="$2" 81 | [ -z "$ADDR" ] && { 82 | echo >&2 Must specify PCI address. 83 | exit 1 84 | } 85 | 86 | echo 1 > /sys/bus/pci/devices/"$ADDR"/reset 87 | ;; 88 | 89 | *) 90 | echo >&2 "Unknown command." 91 | exit 1 92 | ;; 93 | esac 94 | -------------------------------------------------------------------------------- /unknownregs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | grep -RE '[A-Z][^ ]*Reg\(0x' *.c *.h|cut -d: -f2|sed 's/^.*\(Get\|Set\|Mask\|MaskOr\|Or\)Reg(\(0x[0-9a-fA-F]*\).*$/\2/g'|sort|uniq -c|sort -h 3 | --------------------------------------------------------------------------------