**This page is only for jotting down information as we figure it out. For a more understandable and cleaner documentation, see [[documentation:ISL38XX hardware]]** ====== Tools and documentation ====== ===== ARM references ===== ==== GNU arm toolchain ==== http://gnuarm.com I am using the version based on gcc-3.4 available from the site. Seb, are you sure this is not what you use ? -> well, not, I have a blend between 3.3 and 3.4 because some parts of them didn't compile. 4.0 compiles OK. This doesn't matter, anyway. This is the script which compiles it : {{re:build-toolchain.txt}} Beware ! options for newlib are wrong (to be corrected later). ==== General architecture ==== {{re:arm_dev_guide.pdf}} ==== Assembly reference card ==== {{re:arm_assembly_card.pdf|Official reference card}} Very well-done site on the arm instruction set : http://www.heyrick.co.uk/assembler/ Here are its {{re:asmsite.zip|contents}}. ==== ARM946E ==== The arm we're running : ARM946E {{re:arm946.pdf}} ==== Arm simulator ==== http://gro.clinux.org/projects/skyeye/ http://www.virtera.com Armulator : integrated in gdb in the gnu toolchain ===== Intersil and Conexant documentation ===== ==== Baseband controller chipsets ==== {{re:isl3877.pdf}} {{re:isl3890.pdf}} {{re:isl3880.pdf}} {{re:isl3886.pdf}} ==== Radio chipset ==== The Zero-IF integrated transceiver : {{re:isl3686a.pdf}} Interesting information, including pinouts and signal names. The power amplifier : ISL3980 These 2 chipsets are in my Siemens Gigaset (version 1). The ISL3865A datasheet (mentioned in the ISL3686 doc) should be very interesting ; I bet that's the radio interface the ARM "sees". Unfortunately, Google yields nothing of interest :( ==== Full designs ==== A Conexant product brief : {{re:prismgt_isl3686.pdf}} See the schematics diagram at the end. It resembles a lot my Gigaset. ===== Misc ===== ==== Access point firmwares ==== AP firmwares (WL122, AP-600) : in the Software/firmware/apfw directory of their publicly released source, they have information about the Prism firmware (BRA parser and some scripts used to build it) ==== Quickhack to turn ISLMON hexdumps into binary ==== {{re:hexdump2bin.c.gz}} You may want to use the command ''cut -f 2-6 -d \ file'' to strip the address column. ====== Methodology ====== ===== Start of firmware dump ===== (2.13.1.0 for the ISL3886 USB) nop (mov r0,r0) ldr pc, [pc, #908] ; 398 <_binary_LMAC00_arm_start+0x398> At _binary_LMAC00_arm_start+0x398, there is the start address, 0x00020104. Here's the code there : /* disable IRQ+FIQ */ msr CPSR_c, #0xd3 /* configure TCM */ mov r2, #0x00 add r3, r2, #0x10 mcr 15, 0, r3, cr9, cr1, 1 /* configure TCM */ mov r2, #0xa0000000 add r3, r2, #0x08 mcr 15, 0, r3, cr9, cr1, 0 /* 0xc0000204 and 0xc0000210 remain to be understood */ /* write 0x8 to 0xc0000204 */ mov r4, #0xc0000000 mov r3, #0x08 str r3, [r4, #0x204] /* delay loop */ mov r2, #0x2000 1: subs r2, r2, #0x01 bne 1b /* write 0x9 to 0xc0000204 */ orr r3, r3, #0x01 str r3, [r4, #0x204] /* write 0x0 to 0xc0000210 */ mov r3, #0x00 str r3, [r4, #0x210] /* MCR */ /* Core configuration register : write 0x00040078 to it */ /* reserved SBO bits */ mov r3, #0x78 /* instruction TCM enable */ orr r3, r3, #0x40000 mcr 15, 0, r3, cr1, cr0, 0 ===== have a look in the source ===== Basically the firmware upload loops over this: Write the firmware image block somewhere in the device memory. I think this is handled directly by the net2280, with no intervention of the arm ; no code is currently running on the arm. p54u_bulk_msg(netdev, P54U_PIPE_DATA, fw_buf, len); Then we setup the transfer of this chunk of data from where it was stored by the net2280 to its proper location in the arm memory. We don't know if the following actions are done by the net2280 or some hardware logic. Bootstrap code on the arm is ruled out because the arm cpu is not booted yet (it was brought down during ''p54u_reset_dev'') Then we program the transfer in two steps. First step is destination. In this step we're writing directly in the arm memory (writes above the memory window). /* set the base memory for register writes */ p54u_dev_writel(netdev, ISL38XX_DIR_MEM_BASE_REG, 0xc0000f00); /* program some transfert : destination */ /* latch something. There may be a udelay needed, by the way */ /* or could be word size ? */ p54u_dev_writel(netdev, 0x1020, 0); p54u_dev_writel(netdev, 0x1020, 1); // could this be replaced with : // p54u_dev_writel(netdev, 0x0620, 0); // p54u_dev_writel(netdev, 0x0620, 1); /* length (in bytes ?) */ p54u_dev_writel(netdev, 0x1024, len); //p54u_dev_writel(netdev, 0x0624, len); p54u_dev_writel(netdev, 0x1028, j | 0x00020000); //p54u_dev_writel(netdev, 0x0628, j | 0x00020000); Second step is origin? Here we're writing /* program some transfert : origin */ /* origin address */ p54u_dev_writel(netdev, 0x0060, 0x20000000); /* number of words to transfert */ p54u_dev_writel(netdev, 0x0064, len/4); /* word length */ p54u_dev_writel(netdev, 0x0068, 4); /* trigger the transfert operation */ reg = p54u_dev_readl(netdev, 0x102c); So i think the destination address of the bulk_message in endpoint PIPE_DATA is on the arm at address ''0x20000000''. To be checked. The firmware is loaded at address ''0x00020000'', as can be confirmed with reading back from the isl chip with the memory acces mechanism described above. I think that what we're doing above may be programming the DMA engine of the isl chip. Doing this from the ARM : ISLMON > mw 0xc0000f20 0 c0000f20 ISLMON > mw 0xc0000f20 1 c0000f20 ISLMON > mw 0xc0000f24 4 c0000f24 ISLMON > mw 0xc0000f28 0x00020000 c0000f28 ISLMON > mw 0xc0000960 0x20000000 c0000960 ISLMON > mw 0xc0000964 1 c0000964 ISLMON > mw 0xc0000968 4 c0000968 ISLMON > mr 0xc0000f2c c0000f2c: 00000002 ISLMON > mr 0x00020000 00020000: e1a00000 ISLMON > Does not work :( But this may be because no data has been sent on endpoint 1 since the last DMA transfer (we can see in the driver that 0xc0000f2c reads 3 after a successful transfer). Sending data to EP1 and doing the same thing : (...) ISLMON > mr 0xc0000f2c c0000f2c: 00000003 ISLMON > mr 0x00020000 00020000: deadbeef :) (By the way, there is no interrupt when data is sent on an endpoint, on the contrary, you must send it manually by writing the ISL register - this is called the "announcement" in the driver. When I sent data on EP1, I didn't lose control of the chip because of a mishandled ARM IRQ, but because of a software bug in ISLLDR which is now fixed) ===== I/O with the ISL chip ===== ==== Dump of the 0xc0000000 memory area ==== With ISLMON (little endian) : {{re:c0000000.txt}} With the official firmware and smctl -m (beware of endianness) : {{re:c0000000_official.bin.gz}} With the official firmware, reading some registers doesn't look harmless, after the whole dump the device is unresponsive. ==== Hardware registers ==== The pci I/O space from ISL38XX_HARDWARE_REG to ISL38XX_CARDBUS_CIS-1 is directly mapped to the memory range [0xc0000900-0xc00010ff] in the arm's memory. Well it's not sure that the whole range is valid. We need something to check this out :) ==== Direct Memory Window ==== The USB and PCI protocols allow direct access to the arm's memory via a window mechanism. The card's PCI I/O space, from address ''ISL38XX_DIRECT_MEM_WIN'' to address ''ISL38XX_DIRECT_MEM_WIN + ISL38XX_MEMORY_WINDOW_SIZE'', is mapped to a window in the arm's memory whose start address is address contained in the ISL38XX_DIR_MEM_BASE_REG register (and of the same size). You can access it by chunks of 32-bit words. This window obviously does not cover up the whole range of memory available. The base address we're seeing often is ''0xc0000f00'' in USB. On PCI, we write directly the firmware to its right location ISL38XX_DEV_FIRMWARE_ADDRESS; on USB, in order to avoid consuming an awfull log of bandwidth with 32 bit writes, we're programming the write somewhat differently. === Writing to the arm's memory === USB : the code belows write ''0x00000000'' at address ''0xc0000f20'' in the arm's memory. /* set the base memory for memory writes */ p54u_dev_writel(netdev, ISL38XX_DIR_MEM_BASE_REG, 0xc0000f00); p54u_dev_writel(netdev, 0x20 | ISL38XX_DIRECT_MEM_WIN, 0); PCI /* set the base memory for memory writes */ isl38xx_w32_flush(device_base, 0xc0000f00, ISL38XX_DIR_MEM_BASE_REG); u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN + 0x20; __raw_writel(0, dev_fw_ptr); /* maybe flush ? */ TODO (much later) : unify the API. === Reading from the arm's memory === Same thing... See below. ==== CPU Memory map ==== Firmware repeats at offsets 0x00010000, 0x00020000, and 0x00030000. Reading from 0x00040000 crashes the ARM. The whole range 0xc0000000-0xc0000fff is readable. Registers are mapped into it at offset 0xc0000900. ==== DMA engine of the ISL chip ==== The sequence in the firmware upload on USB is merely a programmation of the DMA engine of the ISL chip. The RX part, that is. The TX part still needs to be reverse-engeneered. ===== monitoring ISL registers ===== #define ISLLDR_READREG_DELAY hz static void islldr_readreg(void *arg) { struct islldr_softc *sc = arg; uint32_t r1, r2, r3, r4; r1 = islldr_dev_readl(sc, ISL38XX_GEN_PURP_COM_REG_1); r2 = islldr_dev_readl(sc, ISL38XX_GEN_PURP_COM_REG_2); r3 = islldr_dev_readl(sc, ISL38XX_INT_EN_REG); r4 = islldr_dev_readl(sc, ISL38XX_INT_IDENT_REG); ISLLDR_PRINT("GP1=0x%08x GP2=0x%08x INTEN=0x%08x INTID=0x%08x", r1, r2, r3, r4); callout_reset(&sc->reg_ch, ISLLDR_READREG_DELAY, islldr_readreg, sc); } static void islldr_setup_regch(struct islldr_softc *sc) { if(sc->callout_setup) return; islldr_readreg(sc); callout_init(&sc->reg_ch, 0); callout_reset(&sc->reg_ch, ISLLDR_READREG_DELAY, islldr_readreg, sc); sc->callout_setup = 1; } static void islldr_stop_regch(struct islldr_softc *sc) { if(!sc->callout_setup) return; callout_stop(&sc->reg_ch); sc->callout_setup = 0; } ===== retreiving memory mappings of ISL registers ===== #define SCAN_BASE 0xc0000000 #define MAGIC1 0xdeadbeef #define MAGIC2 0x12345678 islldr_dev_writel(sc, 0x20, MAGIC1); islldr_dev_writel(sc, ISL38XX_INT_EN_REG, MAGIC2); ISLLDR_PRINT("Scanning memory..."); islldr_dev_writel(sc, ISL38XX_DIR_MEM_BASE_REG, SCAN_BASE); for(j=0;j<0x1000;j+=4) { reg = islldr_dev_readl(sc, (ISL38XX_DIRECT_MEM_WIN+j)); if((reg == MAGIC1) || (reg == MAGIC2)) { ISLLDR_PRINT("Found offset 0x%08x = 0x%08x", j+SCAN_BASE, reg); } if(sc->err) { ISLLDR_PRINT("Error at offset 0x%08x", j); break; } } ISLLDR_PRINT("...done"); This code shown that ISL registers are available at address 0xc0000900+their offset defined in isl38xx.h. The two general purpose communication registers have been successfully written with a firmware built from scratch and read back via USB. Writing to the ISL38XX_INT_IDENT_REG register causes the appropriate IRQ to be sent. ===== execute the code in a virtual machine ===== This seems to work with both skyeye and virtera's simulator (see above for references). I find virtera much more easy to use, though not as complete as skyeye (you cannot define the memory mapping for instance). ==== Virtera simulation ==== Create the memory file. The memory wraps @0x10000, so this seems to be a reasonable size to go with. dd if=/dev/zero of=memory.dump bs=1 count=32768 /opt/virtera/vm-arm-se-1.0.2/bin/i686/vm-arm -b p54u_2.4.3.4.arm 0x20000 \ -B memory.dump 0x00000 \ --pc 0x20000 Then run the simulation for a few million cycles... For the particulat version of the firmware, it seems that decompression yields something from ''0x000000'' to ''0x7a80'' in less than 1M cycles. The simulation has no problem. After the job is complete, though, the pc gets stuck and ends up at address 0xc (in what would be the interrupt vector table). This happens after 543534 cycles of simulation for this firmware, after what seems to be a countdown loop. The pc is then at 0x203c4, the processor is in UND (undefined) state, which is not good, then a prefetch fault happens (see http://ivs.cs.uni-magdeburg.de/~pure/manual/refplatform.html). I'll try to see what happens before this ; in particular we need to know when the processor goes from SVC to UND mode, so as to make sure that the data we get from memory is correct. Looking briefly at it, an IRQ happens. We should not rely on the data after this, because the IRQ is not serviced correctly : The code jumps to above address ''0x20000'' ; but we know that the data there is still the uncompressed image, whereas on the real isl chip, the data there is the uncompressed firmware. Hence the ''UND'' mode, which you jump into when seeing unknown instructions. **DONT FORGET TO RESET THE MEMORY BETWEEN SIMULATIONS** The resulting memory dumps are : /opt/virtera/vm-arm-se-1.0.2/bin/i686/vm-arm -b p54u_2.4.3.4.arm 0x20000 \ -B ./memory_dump_100k.bin 0x0 \ --pc 0x20000 -n 100k 1k cycles : {{re:memory_dump_1k.bin.gz}} 10k cycles : {{re:memory_dump_10k.bin.gz}} 100k cycles : {{re:memory_dump_100k.bin.gz}} 1M cycles : {{re:memory_dump_1m.bin.gz}} strings yields this file : {{re:lmac_strings.txt}} Disassembling it yields this : {{re:memory_dump_1m.asm.gz}} (to disassemble : arm-elf-objcopy -I binary -O elf32-littlearm memdump temp.o && arm-elf-objdump -marm9 -D temp.o > output.asm) The beginning is : 0: e3a0f4e0 mov pc, #-536870912 ; 0xe0000000 4: ea0003f1 b fd0 <_binary_memory_dump_1m_bin_start+0xfd0> 8: ea0003f0 b fd0 <_binary_memory_dump_1m_bin_start+0xfd0> c: ea0003ff b 1010 <_binary_memory_dump_1m_bin_start+0x1010> 10: ea000402 b 1020 <_binary_memory_dump_1m_bin_start+0x1020> 14: ea0003ed b fd0 <_binary_memory_dump_1m_bin_start+0xfd0> 18: ea000366 b db8 <_binary_memory_dump_1m_bin_start+0xdb8> The instruction loading 0xe0000000 into the program counter runs the ROM. I've dumped it with ISLMON : {{re:isl3886_rom.arm.gz}} ==== Understanding the firmware using disassembly ==== The following page contains code and comment about the 2.4.3.4 USB firmware [[disassembly_2_4_3_4]] ==== Simulating with the hardware registers mapped to the physical device ==== The principle is to emulate the firmware and map the unknown registers in the 0xc0000000 memory bank to the real device, using ISLMON or the memory window. This allows logging everything would happen inside the chipset ! The DMA engine and PCI communication registers should be mapped to a virtual device, and their data send to FIFOs, for instance. Then, we should only have to send the SoftMAC/FullMAC protocol packets on the FIFO, and look what the firmware does with the hardware. I hope the USB and the emulator will be fast enough so that the RF processor does not time out... I have implemented this using ARMulator as a source code base. This is still very unstable, ARMulator needs modifications because it doesn't support the ARM9. The firmware starts booting and initializing the device, though :) ===== The RF interface ===== See [[BB reverse-engeneering methodology]] ===== The 3887 chipset ===== Looks similar to the 3886, except for the host interface. [[Reverse engineering of the 3887 host interface]]