Earlier, we built a disassembler for the five issue slot TriMedia TM3260 core. [1]
The TM3260 is the VLIW core found in the 2Wire Medusa, Perseus and Ares CPUs and in the dual core TriMedia Denali.
These are the processors that drive the 2Wire 1800, 2071, 2700, 2701, 3600, 3800 and 3801 Home Gateway routers.
Our open source disassembler allows us to examine the Stage One (L1) bootloader found in the boot ROMs of those 2Wire routers.
The L1 bootloader contains a minimal NAND flash driver. This driver is less than 2kBytes in size. It has to be small to fit into a 4kByte 24c32 boot ROM along with the early-stage L0 bootscripts.
The L1 bootloader starts by initialising the PCI/XIO controller on the TriMedia core. The driver code then resets the NAND flash device before copying a ‘memory image’ from NAND into DRAM on the 2Wire PCB.
That memory image contains the next stage (L2) of the bootstrap process. Once the L2 bootloader is loaded in main memory, control is passed to it.
The L2 bootloader copies the kernel of the operating system (rtBSD/tm) from NAND into DRAM. The bootloader checks the kernel image for CRC integrity. Finally, control passes to the kernel and the system continues the boot.
Below, we briefly study TriMedia assembly language. This will help us to understand the content and operation of the minimal NAND flash driver in the bootloader.
Our first foray with the disassembler looks at the initialisation code of the L1 bootloader. We start by examining the very first Decision Tree to that code. A Decision Tree is a basic Scheduling Unit or building block of TriMedia VLIW code. It is akin to a function in a high-level language.
A TM3260 VLIW instruction has five issue slots. Each of these slots executes a separate RISC operation. We can see, below, that the first instruction starts at address 0×4000 0000. The instruction performs a constant assignment operation in both of its first two slots. An arithmetic/logic operation (a subtraction) is found in slot 3. In slot 4, an operation clears the instruction cache. In slot 5, the last slot, another constant assignment operation is performed.
Each of these five primitive operations is executed in parallel. This is called Instruction Level Parallelism (ILP) and it is the defining feature of the VLIW core. ILP distinguishes a VLIW core from a single-pipelined RISC core such as the MIPS. When fully exploited, ILP offers significant performance gains over the single-pipelined core.
Code in this DT flushes the instruction cache (iclr). The value 0xa00 is written to the PCSW register (writepcsw). This configures the byte-sex of the TriMedia CPU to Little Endian. The startup code also sets up the stack. According to TriMedia convention, registers r4 and r3 are used as the stack pointer and the stack frame pointer. Both registers are initialised here to address 0×4000 4000. This means that the top of the stack is 16kB above the base of DRAM for this Ares board. Recall that the stack traditionally grows downwards, so this is a very small program. Register r2 is loaded with address 0×4000 0040. This is the return address where control will pass once the L1 bootloading is done. Finally, control is branched unconditionally out of the first Decision Tree and to the Decision Tree that begins at address 0×4000 009f.
We make the first important observation here.
We can see that each operation is ‘predicated’ by a Guard Register. In TM3260 assembler, this predication takes the form of an “IF register is true then perform operation” expression. We can understand this as meaning that the operation which follows the if-then guard expression is only executed if the least significant bit (lsb) of the Guard Register is set. Guarding is a very useful and efficient mechanism that would otherwise require costly comparison and conditional branch operations.
Those familiar with MIPS assembler will immediately recognise the use of registers r1 and r0 as constants. Registers r1 and r0 always return values of 1 and 0 respectively. In other words, using r1 as a Guard Register ensures that the guarded operation will always be executed, since the lsb of r1 is always true.
MIPS hackers will also be cognisant with the reason for the three unused instructions following the unconditional branch in slot 2 at 0×4000 0029. Each of the five slots in those three instructions contains a nop operation (no operation). These are the branch delay slots. The MIPS core has a single branch delay slot whereas the TriMedia has three slots. We can see here that all three branch delay slots have (inefficiently) been left unfilled by the TriMedia assembler. Ideally these slots should be filled with operations that can execute while the branch operation passes through the instruction pipeline.
We can now look at the code that initialises the TriMedia‘s PCI/XIO controller. The XIO extensions of the PCI controller are used to interface the NAND flash device to the TriMedia core.
The first observation here is that the memory load-store operations (h_st32d) are concentrated in slot 4 and slot 5. This is due to the uneven distribution of Functional Units in the TriMedia.
In the TM3260, the Functional Units for Load/Store (LS) are only available for use by operations in slot 4 and slot 5. A table showing the slot availability of each Functional Unit in the TM3260 core can be found in Jan-Willem Van de Waerdt‘s Doctoral Thesis (see page 19 of [3].)
We especially want to understand the NAND flash driver, and we don’t need the chaff to confuse things. So the Decision Tree above is duplicated again below. But this time after masking-out the operations that are unrelated to the NAND driver:
The code for the PCI/XIO bus controller is isolated above. It can be further clarified by ‘serialising’ it, as if it were running on a single pipeline core:
In the code excerpt above, the constant 0x400ff is stored in the NAND flash controls register in MMIO space. Bits 7:0 of the nand_ctrls register contain the NAND command code. In this case, those 8 bits contain 0xff. The ONFI command code 0xff is to RESET the NAND device.
The next write to MMIO space is to the gpxio_ctrl register. A value of 79 (hex 0x4f) is written to the gpxio_ctrl register. We know from the Philips datasheets for the PNX15xx and the PNX85xx CPUs that bit 6 of the gpxio_ctrl register of the PCI/XIO controller initiates a transaction on the PCI bus, and bit 4 is clear for write transactions. [2]
So this group of operations invokes the RESET command on the NAND device.
This disassembly process can be pursued through the rest of the disassembled L1 bootloader code. At each stage, any reads or writes to the MMIO registers for the PCI/XIO controller are cross-referenced against the Philips datasheets to determine their purpose.
Using this technique we can develop our own NAND flash driver code in C for the Ares-based 27xx boards from 2Wire. The main READ functions of that code are duplicated below:
// Minimal NAND flash driver for the TriMedia TM3260.
// GPL3 (c) asbokid <ballymunboy@gmail.com>
// TM32 JTAG MMIO register offsets
#define JTAG_DATAIN 0x061000
#define JTAG_DATAOUT 0x061004
#define JTAG_CTRL1 0x061008
#define JTAG_CTRL2 0x06100c
// JTAG control bit masks
#define OFULL 0x1
#define IFULL 0x1
#define SLEEPLESS 0x2
// PCI/XIO MMIO register offsets
#define PCI_CFGSTATUS 0x040044
#define PCI_XIOBASE 0x040058
#define XIO_SEL0PRO 0x040814
#define GPXIO_ADDR 0x040820
#define GPXIO_WDATA 0x040824
#define GPXIO_RDATA 0x040828
#define GPXIO_CTRL 0x04082C
#define NAND_CTRLS 0x040830
// ONFI NAND flash command codes
#define NANDRESET 0xff
#define NANDREADSTATUS 0x70
#define NANDIDREAD 0x90
#define NANDREADA 0x00 /* Read from 1st half of 512 byte page */
#define NANDREADB 0x01 /* Read from 2nd half of 512 byte page */
#define NANDREADC 0x50 /* Read from spare 16 bytes */
// Settings for 2Wire Ares
// PCI/XIO Configuration and Status Register settings
#define PCICFGSTATVAL ((0xffff0000) | /* Bits 31:16 are unused */ \
(1 << 2) | /* Enable the PCI bus master */ \
(1 << 1)) /* Enable all memory apertures */
/* PCI/XIO base18 Register setting */
#define XIOBASEVAL 0xf0000000 /* base of XIO aperture (base18 address) */
/* XIO Select 0 Line Profile Register setting */
#define XIOSEL0PROVAL ((1 << 22) | /* sel0_use_ack = wait for ACK */ \
(1 << 21) | /* sel0_re_hi = NAND REN profile high-time */ \
(1 << 19) | /* sel0_re_lo = NAND REN profile low-time */ \
(1 << 17) | /* sel0_we_hi = NAND WEN profile high-time */ \
(1 << 15) | /* sel0_we_lo = NAND WEN profile low-time */ \
(1 << 12) | /* sel0_wait:3 = NAND delay until monitor ACK */ \
(1 << 10) | /* sel0_wait:1 = NAND delay until monitor ACK */ \
(1 << 4) | /* sel0_type = NAND flash device type */ \
(1 << 2) | /* sel0_siz = Alloc. 32MB to sel0 addr space */ \
(1 << 0)) /* en_sel0 = Enable sel0 profile */
#define NANDSIZE 1024 * 1024 * 32
#define MMIO(offset) (_MMIO_base_init[(offset) >> 2])
extern volatile unsigned int _MMIO_base_init[];
/* =====================================================*
* Wait until gpxio_done bit of GPXIO_CONTROl register *
* has been set, signalling the XIO transaction is done *
* =====================================================*/
static void waitgpxiodone(void) {
unsigned int val = 0;
do
val = (MMIO(GPXIO_CTRL) >> 8) & 1;
while (!val); /* bit 8 of GPXIO_CTRL is gpxio_done bit */
return;
}
/* =====================================================*
* Initialise the PCI/XIO controller *
* =====================================================*/
static void xioinit(void) {
// Set PCI Configuration/Status Register (0x04 0040)
MMIO(PCI_CFGSTATUS) = PCICFGSTATVAL;
// Set XIO Base Address (base18) register (0x04 0058) (set to 0xf000 0000 for 2Wire Ares)
MMIO(PCI_XIOBASE) = XIOBASEVAL;
// Set XIO Select 0 line profile register (0x04 0814)
MMIO(XIO_SEL0PRO) = XIOSEL0PROVAL;
return;
}
/* =====================================================*
* Send NAND RESET command to the XIO controller *
* =====================================================*/
static void nandreset(void) {
// Set GPXIO address register to XIO base (0x04 0820) (set to 0xf000 0000 for 2Wire Ares)
MMIO(GPXIO_ADDR) = XIOBASEVAL;
// Set GPXIO write data to zero
MMIO(GPXIO_WDATA) = 0;
// Set NAND controls register
MMIO(NAND_CTRLS) = ((1<<18) |
NANDRESET); /* Number of commands (1) to use in NAND flash access */
/* NAND RESET command code is 0xff */
// Set GPXIO control register
MMIO(GPXIO_CTRL) =((1<<6) | /* gpxio_init : initiate transaction on XIO */ \
(0<<4) | /* gpxio_rd : 1 = read command, 0 = write command */ \
0xf); /* gpxio_ben : active low byte enables used in indirect XIO cycle */
/* : determines number of bytes to access and lower two */
/* address bits for use in GPXIO_ADDR */
// Wait until XIO controller sets gpxio_done flag
waitgpxiodone();
}
/* =====================================================*
* Send NAND ID READ command to the XIO controller *
* =====================================================*/
unsigned int nandidread(void) {
unsigned int val = 0;
// Set GPXIO address register to XIO base (0x04 0820) (set to 0xf000 0000 for 2Wire Ares)
MMIO(GPXIO_ADDR) = XIOBASEVAL;
// Set GPXIO write data to zero
MMIO(GPXIO_WDATA) = 0;
// Set NAND controls register
MMIO(NAND_CTRLS) = ((1<<20) | /* 1 = include data in access cycle, 0 = access has no data phase */
(1<<18) | /* Number of commands (1) to use in NAND flash access */
(1<<16) | /* Number of address phases to be used in NAND flash access */
NANDIDREAD); /* NAND ID READ command code is 0x90 */
// Set GPXIO control register
MMIO(GPXIO_CTRL) =((1<<6) | /* gpxio_init : initiate transaction on XIO */ \
(1<<4) | /* gpxio_rd : 1 = read command, 0 = write command */ \
0xc); /* gpxio_ben : active low byte enables used in indirect XIO cycle */
/* : determines number of bytes to access and lower two */
/* address bits for use in GPXIO_ADDR */
// Wait until XIO controller sets gpxio_done flag
waitgpxiodone();
val = MMIO(GPXIO_RDATA);
return val;
}
/* =====================================================*
* Send NAND READ STATUS command to the XIO controller *
* =====================================================*/
unsigned int nandreadstatus(void) {
unsigned int val = 0;
// Set GPXIO address register to XIO base (0x04 0820) (set to 0xf000 0000 for 2Wire Ares)
MMIO(GPXIO_ADDR) = XIOBASEVAL;
// Set GPXIO write data to zero
MMIO(GPXIO_WDATA) = 0;
// Set NAND controls register
MMIO(NAND_CTRLS) = ((1<<20) | /* 1 = include data in access cycle, 0 = access has no data phase */
(1<<18) | /* Number of commands (1) to use in NAND flash access */
NANDREADSTATUS); /* NAND READ STATUS command code is 0x70 */
// Set GPXIO control register
MMIO(GPXIO_CTRL) =((1<<6) | /* gpxio_init : initiate transaction on XIO */ \
(1<<4) | /* gpxio_rd : 1 = read command, 0 = write command */ \
0xe); /* gpxio_ben : active low byte enables used in indirect XIO cycle */
/* : determines number of bytes to access and lower two */
/* address bits for use in GPXIO_ADDR */
// Wait until XIO controller sets gpxio_done flag
waitgpxiodone();
val = MMIO(GPXIO_RDATA);
return val;
}
/* =====================================================*
* Send NAND READ command to the XIO controller *
* read 4 bytes from XIOBASE + offset *
* =====================================================*/
unsigned int nandread(unsigned int offset) {
unsigned int loff = 0;
unsigned int readcmd = 0;
unsigned int val = 0;
// Set GPXIO write data to zero
MMIO(GPXIO_WDATA) = 0;
// dword align the offset
loff = offset & 0xfffffffc;
// Add offset to XIOBASE
loff |= XIOBASEVAL;
// Set GPXIO address register to offset
MMIO(GPXIO_ADDR) = loff;
// Set NAND read command
readcmd = ((1<<20)| /* 1 = include data in access cycle, 0 = access has no data phase */
(1<<18) | /* Number of commands (1) to use in NAND flash access */
(1<<17) |
(1<<16)); /* Number of address phases to be used in NAND flash access */
if((loff >> 8) & 1)
readcmd |= NANDREADB; /* if offset is in second half of 512 byte page, then use NANDREADB cmd */
else
readcmd |= NANDREADA; /* if offset is in first half of 512 byte page, then use NANDREADA cmd */
// Set NAND controls register with read command
MMIO(NAND_CTRLS) = readcmd;
// Set GPXIO control register
MMIO(GPXIO_CTRL) = ((1<<6) | /* gpxio_init : initiate transaction on XIO */ \
(1<<4) | /* gpxio_rd : 1 = read command, 0 = write command */ \
0x0); /* gpxio_ben : active low byte enables used in indirect XIO cycle */
/* : determines number of bytes to access and lower two */
/* address bits for use in GPXIO_ADDR */
// Wait until XIO controller sets gpxio_done flag
waitgpxiodone();
val = MMIO(GPXIO_RDATA);
return val;
}
/* =====================================================*
* Send NAND READ SPARE command to the XIO controller *
* read 4 bytes from XIOBASE + offset *
* =====================================================*/
unsigned int nandreadspare(unsigned int offset) {
unsigned int loff = 0;
unsigned int readcmd = 0;
unsigned int val = 0;
// Set GPXIO write data to zero
MMIO(GPXIO_WDATA) = 0;
// dword align the offset
loff = offset & 0xfffffffc;
// Add offset to XIOBASE
loff |= XIOBASEVAL;
// Set GPXIO address register to offset
MMIO(GPXIO_ADDR) = loff;
// Set NAND read command
readcmd = ((1<<21)| /* 21 set = enable to spare area */
(1<<20) | /* 20 set = include data in access cycle, 0 = access has no data phase */
(1<<18) | /* 19:18 = Number of commands (1) to use in NAND flash access */
(1<<17) |
(1<<16)); /* 17:16 = Number of address phases to be used in NAND flash access */
readcmd |= NANDREADC; /* NANDREADC cmd (0x50) for reading from spare out-of-band area*/
// Set NAND controls register with read command
MMIO(NAND_CTRLS) = readcmd;
// Set GPXIO control register
MMIO(GPXIO_CTRL) = ((1<<6) | /* gpxio_init : initiate transaction on XIO */ \
(1<<4) | /* gpxio_rd : 1 = read command, 0 = write command */ \
0x0); /* gpxio_ben : active low byte enables used in indirect XIO cycle */
/* : determines number of bytes to access and lower two */
/* address bits for use in GPXIO_ADDR */
// Wait until XIO controller sets gpxio_done flag
waitgpxiodone();
val = MMIO(GPXIO_RDATA);
return val;
}
This embedded NAND driver code runs on the 2Wire Ares board of the 2701 and on later models of the 2Wire 2700. The driver code used by the 3800HGV, another Ares-based board, is likely to be very similar.
Earlier, we developed a JTAG tool for the TriMedia. [4] We can use that JTAG tool to download our NAND flash driver code to the TriMedia target.
The code is used below to hexdump the entire contents of the NAND flash device in a 2Wire 2701HGV-C.
asbokid@home:~/asboapps$ ./2wiglet -c usbblaster -B nanddriver_ares.mi,0x40100000 2Wiglet v1.0 - (c) 2012 asbokid JTAG tool for 2Wire Routers with a TriMedia TM32 core Found USB cable driver for usbblaster Connected to libftdi driver. Connected to UsbBlaster cable Waiting for JTAG chain to stabilise Received IDCODE 3269b4c1 (2Wire Ares) Download 2584 bytes from 'nanddriver_ares.mi' to 40100000 Waiting for L1BOOT_READY from TM32 target L1BOOT_READY <- 12340002 MMIO_BASE <- 1be00000 (expected: 1be00000) DRAM_LO <- 40000000 (expected: 40000000) DRAM_HI <- 44000000 (expected: 44000000) DRAM_CLIMIT <- 44000000 (expected: 44000000) LOAD ADDRESS -> 40100000 CODE SIZE -> 00000a18 (2584) Started L2 download.. L2 load done. Comparing checksums: PC MONITOR=0002f525, TM32 TARGET=0002f525 Checksums good. Download complete Elapsed time 0.19 secs (avg 10.27kB/sec) Starting TM32 execution at 40100000 JTAG Console started: ------------------------------------------------------- <TM32> PCI/XIO INIT command submitted <TM32> NAND RESET command submitted <TM32> NAND READ STATUS command returned: 0xe0 <TM32> NAND ID READ command returned: 0x2075 <TM32> NAND READ command submitted 0000000: 424f 4f54 626f 6f74 0300 0000 0300 0000 BOOTboot........ 0000010: 0400 0000 0002 0000 0040 0000 0000 0000 .........@...... 0000020: c875 0200 0000 0000 0080 0040 0080 0040 .u.........@...@ 0000030: 0000 0000 0300 0000 00c0 0200 0000 0000 ................ 0000040: 0040 fd01 0000 0000 0000 0000 0000 0000 .@.............. 0000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000090: 020c 3439 3130 3139 3130 3239 3438 010c ..491019102948.. 00000a0: c083 0ae6 a660 c083 0ae6 a667 040f 3237 .....`.....g..27 00000b0: 3031 2d31 3030 3633 302d 3030 3805 0400 01-100630-008... 00000c0: 0000 0006 0932 3730 3148 4756 2d43 0308 .....2701HGV-C.. 00000d0: c187 1fd1 22e4 0179 ffff ffff ffff ffff ...."..y........ 00000e0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 00000f0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000100: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000110: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000120: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000130: ffff ffff ffff ffff ffff ffff ffff ffff ................ [....] 0003fc0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0003fd0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0003fe0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0003ff0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0004000: 0ac9 49c8 4b80 1075 4049 0a48 da88 0408 ..I.K..u@I.H.... 0004010: b010 1010 1820 2050 3838 0910 1a81 8820 ..... P88..... 0004020: 1100 3060 3950 0bba 38e0 1420 f80e 4a14 ..0`9P..8.. ..J. 0004030: 0450 1010 7462 4118 3030 0014 1006 0820 .P..tbA.00..... 0004040: 0141 0c0c 4e42 42ce bc2c 4a0c e4e8 9aa8 .A..NBB..,J..... 0004050: d879 ac8c 8c98 a196 cccc 4004 0602 c212 .y........@..... 0004060: 1a0c d040 3406 dd9c 56d0 0001 02da 6576 ...@4...V.....ev 0004070: 0220 0402 cde9 76c8 0c10 0000 4030 1a6a . ....v.....@0.j [....] 1ffff40: 1517 1875 a731 cab5 905b 6fea 7f69 75d1 ...u.1...[o..iu. 1ffff50: 7466 fd04 bb69 6355 2e69 58f3 72de d200 tf...icU.iX.r... 1ffff60: 354c 3df6 fa74 0de1 f3ce 6c0b d679 8c54 5L=..t....l..y.T 1ffff70: 4016 3f0d 4c4f b555 f7a4 083a 30dc 0273 @.?.LO.U...:0..s 1ffff80: 4057 51a2 ed40 5c81 f382 f8a1 4b14 a844 @WQ..@\.....K..D 1ffff90: f06e 32e5 2fc4 e921 81e4 594c 8109 c5fc .n2./..!..YL.... 1ffffa0: 8aee 3bca 003d a1e8 6201 9905 585a 7934 ..;..=..b...XZy4 1ffffb0: 67e2 32c5 3046 d1a9 bdd0 15f5 4da0 fc9b g.2.0F......M... 1ffffc0: 76b6 a887 383d a24b 4e4d cf65 3287 ad76 v...8=.KNM.e2..v 1ffffd0: e17a 63c9 9aff 17b3 0e6a caf1 be0a 0144 .zc......j.....D 1ffffe0: 8c67 8d6b 8120 8413 ed06 ca9b 2555 6b99 .g.k. ......%Uk. 1fffff0: e245 c2bf 0d90 a6d2 1c94 5682 2de9 1121 .E........V.-..! END OF NAND
And now we dump the out-of-band area of the NAND pages:
asbokid@home:~/asboapps$ ./2wiglet -c usbblaster -Bnanddriver_spare_ares.mi,0x40100000 2Wiglet v1.1 - (c) 2012 asbokid <ballymunboy@gmail.com> JTAG tool for 2Wire Routers with a TriMedia TM32 core Found USB cable driver for usbblaster Connected to libftdi driver. Connected to UsbBlaster cable Waiting for JTAG chain to stabilise Received IDCODE 3269b4c1 (2Wire Ares) Download 2908 bytes from 'nanddriver_spare_ares.mi' to 40100000 Waiting for L1BOOT_READY from TM32 target L1BOOT_READY <- 12340002 MMIO_BASE <- 1be00000 (expected: 1be00000) DRAM_LO <- 40000000 (expected: 40000000) DRAM_HI <- 44000000 (expected: 44000000) DRAM_CLIMIT <- 44000000 (expected: 44000000) LOAD ADDRESS -> 40100000 CODE SIZE -> 00000b5c (2908) Started L2 download.. L2 load done. Comparing checksums: PC MONITOR=000352a7, TM32 TARGET=000352a7 Checksums good. Download complete Elapsed time 0.22 secs (avg 9.11kB/sec) Starting TM32 execution at 40100000 JTAG Console started: ------------------------------------------------------- <TM32> PCI/XIO INIT command submitted <TM32> NAND RESET command submitted <TM32> NAND READ STATUS command returned: 0xe0 <TM32> NAND ID READ command returned: 0x2075 <TM32> NAND READ SPARE command submitted 0000200: f003 f000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0000400: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0000600: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0000800: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0000a00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0000c00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0000e00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001000: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001200: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001400: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001600: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001800: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001a00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001c00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0001e00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0002000: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode [....] 0003e00: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0004000: 0000 0000 00ff 0000 426f 6f74 436f 6465 ........BootCode 0004200: 30fc fc33 ffff 0fc0 426f 6f74 436f 6465 0..3....BootCode 0004400: 0303 c033 ffff f030 426f 6f74 436f 6465 ...3...0BootCode 0004600: 5695 5500 ffff 33f0 426f 6f74 436f 6465 V.U...3.BootCode 0004800: c333 0c33 ffff 333c 426f 6f74 436f 6465 .3.3..3<BootCode 0004a00: 3cfc fc0f ffff f30c 426f 6f74 436f 6465 <.......BootCode 0004c00: 5959 a90c ffff 00c0 426f 6f74 436f 6465 YY......BootCode [....] 002ac00: cffc 0c95 ffff 69a9 426f 6f74 436f 6465 ......i.BootCode 002ae00: 6a69 595a ffff a595 426f 6f74 436f 6465 jiYZ....BootCode 002b000: a566 5930 ffff ff00 426f 6f74 436f 6465 .fY0....BootCode 002b200: 0f3c 0cf0 ffff 0300 426f 6f74 436f 6465 .<......BootCode 002b400: aa65 59a5 ffff aa99 426f 6f74 436f 6465 .eY.....BootCode 002b600: 555a 69f0 ffff 30cc 426f 6f74 436f 6465 UZi...0.BootCode 002b800: ffff ffff ffff ffff ffff ffff ffff ffff ................ 002ba00: ffff ffff ffff ffff ffff ffff ffff ffff ................ 002bc00: ffff ffff ffff ffff ffff ffff ffff ffff ................ 002be00: ffff ffff ffff ffff ffff ffff ffff ffff ................ 002c000: ffff ffff ffff ffff ffff ffff ffff ffff ................ 002c200: f33c 005a 24ff 6569 00ff ff81 0600 ff84 .<.Z$.ei........ 002c400: f3cf ccff 24ff f3fc 00ff ff81 0600 ff84 ....$........... 002c600: 03c0 0ccc 24ff 0f30 00ff ff81 0600 ff84 ....$..0........ 002c800: 30cc c066 24ff 9955 00ff ff81 0600 ff84 0..f$..U........ 002ca00: 00c3 f065 24ff 69a5 00ff ff81 0600 ff84 ...e$.i......... 002cc00: cfc0 cc96 24ff 5a99 00ff ff81 0600 ff84 ....$.Z......... 002ce00: 9a99 65c0 24ff 0c00 00ff ff81 0600 ff84 ..e.$........... [....] 002fa00: 33cc fc55 24ff 65a9 00ff ff81 0600 ff84 3..U$.e......... 002fc00: cc0f 3c99 24ff 6555 00ff ff81 0600 ff84 ..<.$.eU........ 002fe00: 0fc0 f0fc 24ff ff0c 00ff ff81 0600 ff84 ....$........... 0030000: fc0c 003f 24ff 0fc0 00ff ff81 0600 ff84 ...?$........... 0030200: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0030400: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0030600: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0030800: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0030a00: ffff ffff ffff ffff ffff ffff ffff ffff ................ [....] 003bc00: ffff ffff ffff ffff ffff ffff ffff ffff ................ 003be00: ffff ffff ffff ffff ffff ffff ffff ffff ................ 003c000: ffff ffff ffff ffff ffff ffff ffff ffff ................ 003c200: 30cc f069 24ff 9969 00ff ff81 0400 ff82 0..i$..i........ 003c400: 959a 9595 24ff 5599 00ff ff81 0400 ff82 ....$.U......... 003c600: 9966 5565 24ff 5995 00ff ff81 0400 ff82 .fUe$.Y......... 003c800: aa55 69a6 24ff 5995 00ff ff81 0400 ff82 .Ui.$.Y......... 003ca00: fcfc 30cc 24ff cff0 00ff ff81 0400 ff82 ..0.$........... 003cc00: cf3f c0aa 24ff 5969 00ff ff81 0400 ff82 .?..$.Yi........ 003ce00: 59a6 5503 24ff cc0c 00ff ff81 0400 ff82 Y.U.$........... [....] 1fff800: a56a 55a9 24ff a555 00ff ff8b 0600 ff8e .jU.$..U........ 1fffa00: 6a95 953c 24ff 0f3c 00ff ff8b 0600 ff8e j..<$..<........ 1fffc00: 3300 ccff 24ff 33f0 00ff ff8b 0600 ff8e 3...$.3......... 1fffe00: f00f 3c33 24ff 0fcc 00ff ff8b 0600 ff8e ..<3$........... 2000000: 0cfc f0cc 24ff c30c 00ff ff8b 0600 ff8e ....$........... END OF NAND
In theory, this open source JTAG tool for the TriMedia, and the minimal NAND driver, could be used to re-flash the 2Wire boards with firmware of arbitrary content.
[1] http://hackingbtbusinesshub.wordpress.com/2011/11/10/an-open-source-trimedia-tm32-disassembler/
[2] http://www.tridentmicro.com/wp-content/uploads/2010/01/UM101041.pdf
[3] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.81.6062&rep=rep1&type=pdf
[4] http://hackingbtbusinesshub.wordpress.com/2011/12/19/open-source-trimedia-jtag-tools/





Does someone have any more information about what is stored in the out-of-band data in the nand. I guess some kind of ecc is used? (typically this should only be 3 bytes)
I’m looking to generate my own image for putting on the router.
Hi Firepinn,
It looks like there are six bytes allocated in the OOB for ECC purposes.
The ECC is found in bytes 0,1,2,4,6,7 of the OOB.
This is an excerpt of the dump of the NAND OOB from a 2Wire 2701HGV-C.
Three ECC bytes are used for each half (256 bytes) of a 512byte NAND page.
The ECC parity bytes for the first page half are in OOB bytes [0:2]
The ECC parity bytes for the second page half are in OOB bytes [3],[6:7]
A modified Hamming Code algorithm is used. The typical 22 parity bits are used per 256 byte block, and an additional bit is used for overall column parity.
We tested several Hamming Code algorithms from Micron, Atmel and Philips. None of those algorithms worked out of the box with the 2Wire flash data.
However, after some minor tweaks to Frans Meulenbroeks’ algorithm, we now have working ECC computation code for the 2Wire.
cheers, a
P.S. I would be very interested to learn about your progress with the 2Wire, Firepinn! The mechanism to re-enable sshd has been discovered. Now that the ECC algorithm is understood, LAN-side shell access to the 2Wire could in theory be re-enabled.
That is already some very useful information. I’m actually using a 2wire 5012vn-002.This board uses a Broadcom 6367 SOC so it’s a completely different story. However, they have kept the same ECC algorithm for the new CPU, which seems strange as the Broadcom CPU does not support this algorithm in hardware. The board still uses a 3-stage bootloader but after this, a modified u-boot is started. Also, I have been unable to get JTAG working. I used the JTAG discovery method, as described on this forum to verify the pin layout on the edge connector and came to the same conclusion. However, my TDO pin always remains high. This might be due to an internal fuse in the Broadcom chip to disable JTAG access for security reasons. So my only way of reverse engineering the board is through a flash programmer. Would it be possible to get the code used for generating the ECC bytes?
PS: On the 5012vn-002, SSH can be enabled over the CLI on the serial port but I still have to find the password for logging in.
Hi again, firepinn,
That’s very interesting. Thanks for sharing this information!
From the online photos, the 2Wire 5012NV looks like it has a very similar enclosure to the 3801HGV. [1] [2]
The 3801HGV also has a CPU that is labelled Broadcom (BCM6901) [2]
Yet the 3801HGV is not MIPS-based like most Broadcom CPE. It is apparently a dual core TriMedia TM3260 and codenamed the Denali. The TM3260 core is a fully synthesizable IP block, so maybe NXP/Trident licensed the core IP to Broadcom for manufacture in its own fab facility, on behalf of 2Wire.
If you have any photos to share, it would be great to see inside the 2Wire 5012NV at the PCB and the support chipsets, etc, to try and discover its origins too!
The ECC code for the 2Wire is at [3]. Looking a bit more at this, it seems there may be a checksum or parity check on the Spare Area – just a single byte. More investigations are needed.
cheers!
asbokid
[1] http://www.tipidpc.com/viewitem.php?iid=12800812
[2] http://hackingbtbusinesshub.wordpress.com/2011/10/03/outside-and-inside-the-2wire-3801hgv/
[3] http://hackingbtbusinesshub.wordpress.com/2012/03/12/2wires-nand-flash-error-correcting-code-ecc/
I think i can give you a password for your 2wire 5012vn-002, i just need your serial number
then we can get root password.
Thanks ADiaz, Sounds interesting! The 5012 has never hit British shores, so far as I know. Though, hopefully, someone will take up your offer
cheers, a