↓ Skip to main content

Hacking My Cameras to stream Local | Exploration first

Introduction
#

So i wanted to do some hardware hacking project again, also to take a bit of the frustrations with compiling and getting uboot working on my radxa cm3 board and top park those things for later. So friday the 1st of May i went to the barber to get my hair cut. After that i decided to go in the kruidvat (drugs store in the Netherlands) near it. There i found a nice small camera for 20 euro which i bought. I know pretty much all IPCameras run Embedded linux under the hood with the vendor’s application stack. I also know a opensource local privacy focussed firmware for some cameras exist called OpenIPC or Thingino-firmware. Because i did not feel comfortable with video footage of my camera going to some chinese/american/Russian cloud for processing, due to privacy reasons and the fact most of the time with cheap products, the consumer themselevs end up being the product for those big companies, i wanted to try and flash those on this little camera. Also because i wanted to expand my Homeassistant smarthome with cameras so i can viewm whats going on. This post is a part2’er and the first part (what you are currently reading) is for exploring the product in more detail. If you are not interested into that and want to go to the part of flashing the custom firmware you can Move to part 2.

The camera
#

So below some pictures of the camera and it really is a small thing.

box_front
box_back
oob

So this is the camera itself. Now i will open up the camera and show you some insides.

opening

SO after using a little pry tol it popped open. No screws, just plastic clips holding it together.

open

The insides
#

So here some pictures of the insides.First lets unscrew the board so we can view it closer.

unscrew_bd

The SOC
#

So when we get the PCb out we can seee some important chips that make the camera work. Okay, first on the back of the board is the main SOC (System on Chip), this contains some ram, the video processor, main cpu core and some other stuff.

SOC
We can see its made by Ingenic Semiconductor and its model is the T23. On the Ingenic Website we can see that it is a energy effcient videprocessor powered by a single core Xburst cpu running fdrom 1.2-1.4GHz and it can encode H264 encoded video and can support 3 MP. It also includes a low power 600MHz RISC-V coprocessor which can be used for low power tasks. Below is a picture showing the block diagram which is also found on the Ingenic website.
Block_diagram

SO all and all a pretty integrated capable Camera SOC. But obviously it needs something to load its Linux based firmware from, thats where the flash chip comes in.

The FLASH storage.
#

Flash

As you can see on the picture above its made by PUYA semiconductor and is in a SOIC-8 Package (Small outline Integrated Circuit). Its model number is p25q64sh. Chips starting with 25xxxx are pretty popular and are most if not al of the time a flash chip. The 25 series flash is an SPI flash chip and typically the letter after the ‘25’ means a specific model or revision (some flash chips differ in things like voltage (1.8v and 3.3v flash exists) and other differences a product engineer might want/encounter. The bit after the letter often is the storage size in megaBIT (Not megaBYTES!!!). You can convert megaBIT to megaBYTE by doing this equation: size in megaBIT // 8 = size in megaBYTE. SO that means 64//8 = 8 MegaBYTE. The chances of it running emebdded linux are even bigger now as embeded linux typically needs atleast 8 megaBYTE of storage (unless its a very old 2.x.x version kernel as some old products run 2.2.x kernel from 4 megaBYTE of flash or run a smaller variant of it like UCLINUX). This stores the bootloader (Highly likely U-BOOT) and a small Linux kernel and Userland with the vendor’s application stack and a few drivers.

Now we have SOC and flash but we also need something that makes it connect with WIFI as the Ingenic SOC has no wifi inside, so we need an external wifi chip and i am sure there is also one on this board.

The wifi chip
#

Wifi_chip
Wifi_chip_closeup
The picture is not fully clear but its brand and part number is visible: Altobeam 6012B. The Altobeam ATBM6012B is a single chip 802.11b WLAN chip which works over USB. It integrates a 32-bit cpu core and some perhipherals like UART, SPI and GPIO interface. Here is the datasheet. Alongside the single black trace in a funky shape above it which is the antenna, it takes care of the wireless functions of the camera and communicates with the SOC via USB and the firmware will have a wireless-driver/kernel module for it.

Those are the important chips. There is some support and power circuitry on the board but thats not important. There is an amplifier chip for the speaker so it can play those startup tones and status tones and i believe it even has a siren functionality so it obviously can be quite loud.

Exploring the Software
#

So now we go to explore the stock firmware thats running on it. Because of my past experiences with these types of no-name-brand general-housebrand-sold cameras, i bet these also use tuya software under the hood. This kruidvat one works with the kruidvat smarthome app but i also had ones from Action which had a LSC smart connect app and all those apps are basically the smartlife/tuya app in a different jacket/theme. And thus the smartlife/tuya app can work with LSC or Kruidvat sold devices and vice versa. Also tuya is next to amazon, google, apple one of the major IOT platforms. Many devices work with the Tuya cloud and use Tuya software under the hood as its cheap to produce and Tuya provides both hardware and software support/modules.

So i bet this uses a bogstandard embedded stripped down Linux os with ontop of that some custom Tuya application stack which handles all the functionality of processing the camera footage, packing it, sending it to the cloud and communicating with the app for control or viewing. And because these use an Ingenic chip, its probably part of the Ingenic Smart Video Platform (ISVP) which provides the basic linux os ontop of which whatever vendor can put their own custom software. You will see a reference to that a bit later so keep this in mind! More About ISVP structually

UART
#

So to dig into the software and look at whats happening under the hood in terms of software, we need to find whats called a UART. UART stands for “Universal Asynchronous Receiver Transmitter” and its a protocol thats used for communication. It has a TX line and an RX line and a common ground. It is used for specific sensors or chips but also in the case of devices running an embedded Operating System (Windows-CE, Android, Embeded Linux, an RTOS (realtime OS), any other firmware) for debugging the software/firmware and or to program it in. Althought debugging is the biggest reason for adding a UART as you can talk straight to the underlying bootloader/OS/firmware if the device allows it/is built with UART debugging in mind.

Some vendors disable this debug port after production, either in software by either setting a variable or compiling software with UART debug disabled, or by removing components inbetween the CPU/SOC and the UART header/port. But many vendors leave them enabled. But in some security focussed products or products that Imply strict rules related to media or copyright (DRM, Digital Rights media), they disable it so people cant pirate or possibly find ways around those rules or to lower the attack vector (example is IP-phones). But many of these no-brand products have them enabled and vendors dont bother shutting the UART port closed.

So as i said, UART consist of a TX and RX and GND wire. TX of the target gets connected to RX of the receiving end and can be considered “the mouth” of the target device, this wil be the line it speaks over. You also have RX of the target which can be considered “its ears” and this will be the line that the receiving end can use to talk to the device and send commands/data. Often UART is either a header or a spot for a header or some test-pads aranged close to the cpu. Sometimes its marked and sometimes not. In my case they where not marked but its also often close to some resistors and thus i bet a good candidate is the 3 dots above the speaker connector. Normally you would use a multimeter or scope to verify them (TX fluctuates at boot fromm 0v to 3.3v and RX is often next to it. And GND can be found by probing any metal part or shell like the sdcard slot or USB connector shell), but because i already have experience and i have been doing this for quite some time and done it a lot already, i just straight up soldered wires to it, but i did measure them of-camera and indeed 1 fluctuates between 0v and 3.3v.

UART
UART_soldered

because they are pcb pads, to avoid tearing them off the PCB mechanically, i reinforced them with a bit of hot-glue as i did have this happen before and ripping off pcb pads on a multilayer pcb with traces going everywhere thats not fun as then there goes your chance of communicating with the software of the device physically and i dont like having to reverse engineer where the traces go and sometimes they are directly going to a CPU/SOC and sometimes those have BGA balls or very tiny pins that are hard/impossible to solder a wire to in the case of my T23 SOC.

Okay with that done lets see if we can get some debug info.

Bootlogs.
#

So lets attach the camera via the wires to my USB to UART converter. I bought a Ft4232H which has 4 channels but any USB to UART chip (ch340/ch341, cp2102) can work fine. Then i started a terminal session on my computer. I used picocom on Linux but you can use any terminal emulator that supports serial ports and on windows you can use Putty terminal. Just make sure to select the proper serial port. In my case i attached it to chanel 1 so i use /dev/ttyUSB0. Also baudrate needs to be set. In a lot of cases thats 115200 but i also came across devices with weirder baudrates like 38400/57600/1500000. Because i have experience i know its highly likely 115200 but you also got to try a bit. If you get output but giberish, try another baud rate until you get clear text. After preparing that and getting the terminal session ready we can now turn on the target device and see what it does at boot if it even outputs anything.

And bingo we get output! I truncated the bootlog a bit but you can find the whole unmodified un-cleaned-up bootlog by clicking This button which loads the whole file from my CDN.

U-Boot SPL 2013.07 (Nov 18 2023 - 10:39:44)
Board info: T23N
apll_freq = 1188000000 
mpll_freq = 1200000000 
sdram init start
DDR clk rate 600000000
image entry point: 0x80100000


U-Boot 2013.07 (Nov 18 2023 - 10:39:44)

Board: ISVP (Ingenic XBurst T23 SoC)
DRAM:  64 MiB
Top of RAM usable for U-Boot at: 84000000
Reserving 215k for U-Boot at: 83fc8000
Reserving 32784k for malloc() at: 81fc4000
Reserving 32 Bytes for Board Info at: 81fc3fe0
Reserving 124 Bytes for Global Data at: 81fc3f64
Reserving 128k for boot params() at: 81fa3f64
Stack Pointer at: 81fa3f48
Now running in RAM - U-Boot at: 83fc8000
MMC:   msc: 0
the manufacturer 85
SF: Detected P25Q64H

In:    serial
Out:   serial
Err:   serial
Net:   ====>PHY not found!Jz4775-9161
Hit any key to stop autoboot:  1  0 
the manufacturer 85
SF: Detected P25Q64H

--->probe spend 4 ms
otabakpaddr:0x0
Card did not respond to voltage select!
Error, mout fatfs failed, error num: 3
the manufacturer 85
SF: Detected P25Q64H

--->probe spend 4 ms
SF: 1835008 bytes @ 0x40000 Read: OK
--->read spend 591 ms
## Booting kernel from Legacy Image at 80600000 ...
   Image Name:   Linux-3.10.14__isvp_pike_1.0__
   Image Type:   MIPS Linux Kernel Image (lzma compressed)
   Data Size:    1589036 Bytes = 1.5 MiB
   Load Address: 80010000
   Entry Point:  803c9f70
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

Starting kernel ...

[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 3.10.14__isvp_pike_1.0__ (spock@spock-PC) (gcc version 5.4.0 (Ingenic Linux-Release3.3.0-Default(xburst1(fp32)+uclibc0.9.33.2) Smaller libc 2022.11-07) ) #3 PREEMPT Wed Mar 20 18:27:28 CST 2024
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU0 RESET ERROR PC:CB266305
[    0.000000] CPU0 revision is: 00d00100 (Ingenic Xburst)
[    0.000000] FPU revision is: 00b70000
[    0.000000] cgu_vpu clk should be opened!
[    0.000000] CCLK:1188MHz L2CLK:594Mhz H0CLK:200MHz H2CLK:200Mhz PCLK:100Mhz
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 0047b000 @ 00010000 (usable)
[    0.000000]  memory: 00035000 @ 0048b000 (usable after init)
[    0.000000] User-defined physical RAM map:
[    0.000000]  memory: 02683000 @ 00000000 (usable)
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x00000000-0x02682fff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x00000000-0x02682fff]
[    0.000000] Primary instruction cache 16kB, 8-way, VIPT, linesize 32 bytes.
[    0.000000] Primary data cache 16kB, 8-way, VIPT, no aliases, linesize 32 bytes
[    0.000000] pls check processor_id[0x00d00100],sc_jz not support!
[    0.000000] MIPS secondary cache 64kB, 8-way, linesize 32 bytes.
[    0.000000] Built 1 zonelists in Zone order, mobility grouping off.  Total pages: 9781
[    0.000000] Kernel command line: console=ttyS1,115200n8 mem=39436K@0x0 rmem=25600K@0x2683000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(uboot),1792k(kernel),960k(rootfs),4800k(app),384k(config)
[    0.000000] PID hash table entries: 256 (order: -2, 1024 bytes)
[    0.000000] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Memory: 33572k/39436k available (3850k kernel code, 5864k reserved, 736k data, 212k init, 0k highmem)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000] NR_IRQS:351
[    0.000000] clockevents_config_and_register success.
[    0.000020] Calibrating delay loop... 1185.38 BogoMIPS (lpj=5926912)
[    0.087811] pid_max: default: 32768 minimum: 301
[    0.092706] Mount-cache hash table entries: 512
[    0.097740] Initializing cgroup subsys debug
[    0.101997] Initializing cgroup subsys freezer
[    0.108797] regulator-dummy: no parameters
[    0.113036] NET: Registered protocol family 16
[    0.121301] bio: create slab <bio-0> at 0
[    0.127359] jz-dma jz-dma: JZ SoC DMA initialized
[    0.132376] usbcore: registered new interface driver usbfs
[    0.137982] usbcore: registered new interface driver hub
[    0.143412] usbcore: registered new device driver usb
[    0.148692] i2c-gpio i2c-gpio.1: using pins 57 (SDA) and 58 (SCL)
[    0.154880]  (null): set:249  hold:250 dev=100000000 h=500 l=500
[    0.161026] media: Linux media interface: v0.10
[    0.165619] Linux video capture interface: v2.00
[    0.172063] Switching to clocksource jz_clocksource
[    0.177016] cfg80211: Calling CRDA to update world regulatory domain
[    0.184032] NET: Registered protocol family 2
[    0.188947] TCP established hash table entries: 512 (order: 0, 4096 bytes)
[    0.195902] TCP bind hash table entries: 512 (order: -1, 2048 bytes)
[    0.202302] TCP: Hash tables configured (established 512 bind 512)
[    0.208631] TCP: reno registered
[    0.211837] UDP hash table entries: 256 (order: 0, 4096 bytes)
[    0.217800] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[    0.224346] NET: Registered protocol family 1
[    0.229030] freq_udelay_jiffys[0].max_num = 10
[    0.233500] cpufreq 	udelay 	loops_per_jiffy	
[    0.237871] 12000	 59867	 59867	
[    0.241138] 24000	 119735	 119735	
[    0.244594] 60000	 299338	 299338	
[    0.248025] 120000	 598677	 598677	
[    0.251556] 200000	 997796	 997796	
[    0.255100] 300000	 1496694	 1496694	
[    0.258796] 600000	 2993389	 2993389	
[    0.262504] 792000	 3951274	 3951274	
[    0.266225] 1008000	 5028895	 5028895	
[    0.270010] 1200000	 5986779	 5986779	
[    0.278269] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.284384] jffs2: version 2.2. © 2001-2006 Red Hat, Inc.
[    0.290210] msgmni has been set to 65
[    0.295987] io scheduler noop registered
[    0.299912] io scheduler cfq registered (default)
[    0.304945] wait stable.[290][cgu_vpu]
[    0.310054] jz-uart.1: ttyS1 at MMIO 0x10031000 (irq = 58) is a uart1
[    0.317843] console [ttyS1] enabled, bootconsole disabled
[    0.317843] console [ttyS1] enabled, bootconsole disabled
[    0.332824] brd: module loaded
[    0.338188] loop: module loaded
[    0.342071] zram: Created 2 device(s) ...
[    0.346390] logger: created 256K log 'log_main'
[    0.363556] jz SADC driver registeres over!
[    0.368500] jz TCU driver register completed
[    0.373354] the id code = 856017, the flash name is P25Q64H
[    0.379183] jz-sfc jz-sfc: sfc use DMA mode
[    0.383531] jz-sfc jz-sfc: nor flash now use standard mode!
[    0.389298] JZ SFC Controller for SFC channel 0 driver register
[    0.395453] 5 cmdlinepart partitions found on MTD device jz_sfc
[    0.401580] Creating 5 MTD partitions on "jz_sfc":
[    0.406558] 0x000000000000-0x000000040000 : "uboot"
[    0.412147] 0x000000040000-0x000000200000 : "kernel"
[    0.417870] 0x000000200000-0x0000002f0000 : "rootfs"
[    0.423606] 0x0000002f0000-0x0000007a0000 : "app"
[    0.428980] 0x0000007a0000-0x000000800000 : "config"
[    0.434678] SPI NOR MTD LOAD OK
[    0.437988] tun: Universal TUN/TAP device driver, 1.6
[    0.443216] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
[    0.449774] wait stable.[290][cgu_macphy]
[    0.553860] Bus Mode Reg after reset: 0x00020101, cnt=0
[    0.560266] Bus Mode Reg after reset: 0x00020101, cnt=1
[    0.566684] Bus Mode Reg after reset: 0x00020101, cnt=2
[    0.573087] Bus Mode Reg after reset: 0x00020101, cnt=3
[    0.579502] Bus Mode Reg after reset: 0x00020101, cnt=4
[    0.585920] Bus Mode Reg after reset: 0x00020101, cnt=5
[    0.592325] Bus Mode Reg after reset: 0x00020101, cnt=6
[    0.598740] Bus Mode Reg after reset: 0x00020101, cnt=7
[    0.605160] Bus Mode Reg after reset: 0x00020101, cnt=8
[    0.611565] Bus Mode Reg after reset: 0x00020101, cnt=9
[    0.617980] func:jz_mii_bus_probe, synopGMAC_reset failed
[    0.623591] jz_mii_bus: probe of jz_mii_bus.0 failed with error -1
[    0.630075] =======>gmacdev = 0x819cd280<================
[    0.635710] =========>gmacdev->MacBase = 0xb34b0000 DmaBase = 0xb34b1000
[    0.642646] Bus Mode Reg after reset: 0x00020101, cnt=0
[    0.649064] Bus Mode Reg after reset: 0x00020101, cnt=1
[    0.655480] Bus Mode Reg after reset: 0x00020101, cnt=2
[    0.661885] Bus Mode Reg after reset: 0x00020101, cnt=3
[    0.668300] Bus Mode Reg after reset: 0x00020101, cnt=4
[    0.674716] Bus Mode Reg after reset: 0x00020101, cnt=5
[    0.681120] Bus Mode Reg after reset: 0x00020101, cnt=6
[    0.687536] Bus Mode Reg after reset: 0x00020101, cnt=7
[    0.693951] Bus Mode Reg after reset: 0x00020101, cnt=8
[    0.700354] Bus Mode Reg after reset: 0x00020101, cnt=9
[    0.706769] func:jz_mac_probe, synopGMAC_reset failed
[    0.712007] jz_mac: probe of jz_mac.0 failed with error -1
[    0.717800] usbcore: registered new interface driver cdc_ether
[    0.723931] usbcore: registered new interface driver rndis_host
[    0.730119] usbcore: registered new interface driver int51x1
[    0.736149] DWC IN OTG MODE
[    0.739695] dwc2 dwc2: Keep PHY ON
[    0.743217] dwc2 dwc2: Using Buffer DMA mode
[    0.747683] dwc2 dwc2: Core Release: 3.00a
[    0.751961] dwc2 dwc2: DesignWare USB2.0 High-Speed Host Controller
[    0.758486] dwc2 dwc2: new USB bus registered, assigned bus number 1
[    0.765835] hub 1-0:1.0: USB hub found
[    0.769736] hub 1-0:1.0: 1 port detected
[    0.773980] dwc2 dwc2: DWC2 Host Initialized
[    0.778656] dwc2 dwc2: enter dwc2_gadget_plug_change:2589: plugin = 1 pullup_on = 0 suspend = 0
[    0.787861] usbcore: registered new interface driver cdc_acm
[    0.793764] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[    0.802195] usbcore: registered new interface driver usbserial
[    0.808335] usbcore: registered new interface driver usbserial_generic
[    0.815159] usbserial: USB Serial support registered for generic
[    0.821419] usbcore: registered new interface driver option
[    0.827256] usbserial: USB Serial support registered for GSM modem (1-port)
[    0.834652] using random self ethernet address
[    0.839267] using random host ethernet address
[    0.844270] usb0: HOST MAC ee:38:d4:1e:1c:ff
[    0.848759] usb0: MAC 2e:da:66:49:a7:06
[    0.852741] dwc2 dwc2: ID PIN CHANGED!
[    0.856743] using random self ethernet address
[    0.861355] using random host ethernet address
[    0.866057] g_ether gadget: Ethernet Gadget, version: Memorial Day 2008
[    0.872909] g_ether gadget: g_ether ready
[    0.877263] i2c /dev entries driver
[    0.881358] jzmmc_v1.2 jzmmc_v1.2.0: vmmc regulator missing
[    0.887561] jzmmc_v1.2 jzmmc_v1.2.0: register success!
[    0.893162] TCP: cubic registered
[    0.897159] NET: Registered protocol family 10
[    0.902693] sit: IPv6 over IPv4 tunneling driver
[    0.908519] ip6_gre: GRE over IPv6 tunneling driver
[    0.914164] NET: Registered protocol family 17
[    0.918793] lib80211: common routines for IEEE802.11 drivers
[    0.925191] soc_vpu probe success,version:1.0.0-03203fd46d
[    0.930887] drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
[    0.942551] VFS: Mounted root (squashfs filesystem) readonly on device 31:2.
[    0.950275] Freeing unused kernel memory: 212K (8048b000 - 804c0000)

can't run '/sbin/swapoff': No such file or directory
[    1.313547] usb 1-1: new full-speed USB device number 2 using dwc2
mdev is ok......
hwclock: can't open '/dev/misc/rtc': No such file or directory
[    1.463857] zram0: detected capacity change from 0 to 8388608
Setting up swapspace version 1, size = 8384512 bytes
UUID=e9bf6f1c-ebdf-43ad-9e61-e[    1.474644] Adding 8188k swap on /dev/zram0.  Priority:-1 extents:1 across:8188k SS
0ac69758f93
insmod: can't insert '/app/jz/modules/exfat.ko': No such file or directory
[    1.523548] usb 1-1: device descriptor read/64, error -88
[    1.536888] name : i2c-gpio1 nr : 1
[    1.540503] name : i2c0 nr : 0


Ingenic-uc1_1 login: [    1.653998]  sensor_read: addr=0x3107 value = 0xcb
[    1.764040]  sensor_read: addr=0x3107 value = 0xcb
[    1.823491] usb 1-1: device descriptor read/64, error -88
[    1.873990]  sensor_read: addr=0x3107 value = 0xcb
[    1.879468]  sensor_read: addr=0x3108 value = 0x3a
[    1.993992]  sensor_read: addr=0x3107 value = 0xcb
[    1.999473]  sensor_read: addr=0x3108 value = 0x3a
[    2.004897] info: success sensor find : sc2336

The interesting bits
#

And indeed i was right! Below i will explain the interesting bits a bit more clearer.

SPL
#

So as we can see it starts with: U-boot SPL 2013.07. But what is this SPL? The SPL stands for “Secondary program loader” and as its name applies it is a small loader itself. After the bootrom (Primary loader often) it then starts probing storage devices to boot from but the bootrom is often to limited to initialise the full system/important bits (Like DRAM and PLL clock circuits and othe perhipherals) and thus it needs a bit of help. It does have the basic functionality to probe for storage devices and then find a specific header for an SPL and start the SPL. The job of the SPL is to initialise the DRAM and clock circuitry (PLL’s) and make the system ready for booting a next stage. And we can see it says “dram init” and it initialises the PLLs which take care of the clock frequency and timing of the cpu and other blocks. After the SPL it then finds and loads the main U-Boot Image.

U-boot
#

And as i already guessed it runs U-boot. Quite an old version but it’ll do. And boom, we also get our first reference to that “ISVP” as it says the board is ISVP T23. Here we also see that it has 64 Megabytes of RAM and it detected our flash chip the P25Q64. We also see that U-boot itself tries to probe for an SDcard which is not there and after that probes for the SPI flash and initialises that. But before that we see this little bit below: Hit any key to stop autoboot. This will be interesting later but lets continue for now with exploring it first. Ater that probe bussiness it loads the Linux kernel and we see a nice information bit before it jumpts to linux itself.

Linux
#

## Booting kernel from Legacy Image at 80600000 ...
   Image Name:   Linux-3.10.14__isvp_pike_1.0__
   Image Type:   MIPS Linux Kernel Image (lzma compressed)
   Data Size:    1589036 Bytes = 1.5 MiB
   Load Address: 80010000
   Entry Point:  803c9f70
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

And here we get the 2nd reference to ISVP. often the bit after ISVP_ is the codename of the chip. Seems they are all references to starcraft. ANd it indeed is the ISVP platform. See more about ISVP structure. And it runs kernel 3.10.14 which is common for ingenic chips and i guess thats what the ISVP pike SDK supports. The kernel is 1.5Megabytes and is packaged as a Uboot uImage. And it seems that it was read from flash into ram at address 0x80010000. And after this some bogstandard Linux bootup stuff. WE also get to see who compiled it and at what time/date which is fun, hello Spock! Also Linux if its loaded/booted on a system with SPI flash/booted formm SPI flash you can also see what other partitions are on the flash.

Flash partitions
#

[    0.401580] Creating 5 MTD partitions on "jz_sfc":
[    0.406558] 0x000000000000-0x000000040000 : "uboot"
[    0.412147] 0x000000040000-0x000000200000 : "kernel"
[    0.417870] 0x000000200000-0x0000002f0000 : "rootfs"
[    0.423606] 0x0000002f0000-0x0000007a0000 : "app"
[    0.428980] 0x0000007a0000-0x000000800000 : "config"
[    0.434678] SPI NOR MTD LOAD OK

So we have 5 partitions in total as indicated by the kernel. First partition is Uboot which stores Uboot and its SPL and also its environment which stores variables we will have a look at later. 2nd partition is “kernel” which probbaly stores the Linux kernel Image. The 3rd partition is the rootfs which contains the stripped down Linux userland and those 3 partitions are where most stuff like software and support is provided by the Ingenic SMart Video Platform (ISVP) from Ingenic. The 4th partition is called “App” which will hold our custom tuay application stack and its files. Then the last partition holds configurations as i bet the rootfs and App partition are Squashfs which is a read-only filesystem and can only be modified if you unpack and repack those parts, the configuration partition is often a read-write filesystem like JFFS (Journaling Flash File System).

App
#

So the Application bootlog stuff is not that much of interest but we can see that it simply loads a couple drivers and then starts a main binary. It seems like it first loads a binary called “daemon” which then starts the main aplication called “IPC”. And its indeed from Tuya as expected.

0:00:03.205][I][daemon]Start app -> [@ipc]

And here we see that it uses the TUYA IOT SDK

Dbg:SDK Version:< TUYA IOT SDK V:5.3.41 BS:40.00_PT:2.2_LAN:3.4_CAD:1.0.4_CD:1.0.0 >

< BUILD AT:2023_11_02_09_24_38 BY ci_manage FOR ty_ipc_wr_wl_linux_sdk AT mips-gcc540-uclibc0-9-33-2-64bit-r3-3-0-smaller >

Diving deeper.
#

SO lets dive a bit deeper into it. First thing i would like to explore is the bootloader as the OS has a password protected login and i dont know that password as i obviously did not make the product. If i can enter the bootloader i can skip the normal initialisation stuff and thus avoid the password protected login prompt which gets loaded by the normal init process and its scripts: /sbin/init. Remember that interesting message i previously mentioned?: Hit any key to stop Autoboot. Lets try interupting Autoboot by hittin a key. This differs per product ofcourse. Some vendors set a variable called ‘bootdelay’ to 0 seconds meaning the prompt shows but it wont wait for you to interupt it (done so you cant interrupt uboot easily or to make the product boot 1 second faster). But most vendors leave bootdelay at 1 or 2, so lets try it.

Interrupting U-boot
#

Spamming/holding down enter or “Ctrl+C” and plugging it in, voila we are allowed to enter a prompt and now it wont load the kernel and the rest of the os until you exit this prompt.

U-Boot SPL 2013.07 (Nov 18 2023 - 10:39:44)
Board info: T23N

apll_freq = 1188000000 
mpll_freq = 1200000000 
sdram init start
DDR clk rate 600000000
image entry point: 0x80100000


U-Boot 2013.07 (Nov 18 2023 - 10:39:44)

Board: ISVP (Ingenic XBurst T23 SoC)
DRAM:  64 MiB
Top of RAM usable for U-Boot at: 84000000
Reserving 215k for U-Boot at: 83fc8000
Reserving 32784k for malloc() at: 81fc4000
Reserving 32 Bytes for Board Info at: 81fc3fe0
Reserving 124 Bytes for Global Data at: 81fc3f64
Reserving 128k for boot params() at: 81fa3f64
Stack Pointer at: 81fa3f48
Now running in RAM - U-Boot at: 83fc8000
MMC:   msc: 0
the manufacturer 85
SF: Detected P25Q64H

In:    serial
Out:   serial
Err:   serial
Net:   ====>PHY not found!Jz4775-9161
Hit any key to stop autoboot:  0 
isvp_t23# <INTERRUPT>
isvp_t23# <INTERRUPT>
isvp_t23# <INTERRUPT>

Lets try some commands
#

In this prompt we can enter some commands. This shell is called the “hush shell” and its a bash-like shell but instead of regular linux/*nix commands we enter uboot specific commands as we are in the stage before the Linux os even has booted, aka the bootloader. One command pretty much all U-boot versions and implementations have is a simple ‘help’ which shows the list of commands that where compiled/built-in into the U-boot binary. Some vendors leave a lot of stuff in, some strip it down to the bare core.

isvp_t23# help
?       - alias for 'help'
base    - print or set address offset
boot    - boot default, i.e., run 'bootcmd'
boota   - boot android system
bootd   - boot default, i.e., run 'bootcmd'
bootm   - boot application image from memory
bootp   - boot image via network using BOOTP/TFTP protocol
cmp     - memory compare
coninfo - print console devices and information
cp      - memory copy
crc32   - checksum calculation
env     - environment handling commands
ethphy  - ethphy contrl
go      - start application at address 'addr'
gvsdupdate- just run

help    - print command description/usage
loop    - infinite loop on address range
md      - memory display
mm      - memory modify (auto-incrementing address)
mmc     - MMC sub system
mmcinfo - display MMC info
mw      - memory write (fill)
nm      - memory modify (constant address)
printenv- print environment variables
reset   - Perform RESET of the CPU
run     - run commands in an environment variable
setenv  - set environment variables
sf      - SPI flash sub-system
sleep   - delay execution for some time
tftpboot- boot image via network using TFTP protocol
version - print monitor, compiler and linker version
watchdog- open or colse the watchdog

And indeed its not a big list but the important ones are there. bootm is used to load a kernel Image in the Uboot uImage format (which is used in this system) and is prety much standard on 99% of U-boot powered products. We also see other pretty standard commands are thingsa like printenv which shows the environment variables that are set (basically variables that the system uses to determine/modify behaviour or know how to bootup) and sf which stands for “SPi-flash” and can be used to interface with the SPI flash and do things like initialise flash, erase flash, write flash and read it. Also we have a command to interact with the sdcard which is that “mmc” command.

Lets dump the environment settings and variables.
#

So executing ‘printenv’ shows us what variables are set.

isvp_t23# printenv
bootargs=console=ttyS1,115200n8 mem=39436K@0x0 rmem=25600K@0x2683000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(uboot),1792k(kernel),960k(rootfs),4800k(app),384k(config)
bootcmd=gvsdupdate;sf probe;sf read 0x80600000 0x40000 0x1C0000; bootm 0x80600000
bootdelay=1
ethact=Jz4775-9161
stderr=serial
stdin=serial
stdout=serial

Environment size: 374/16380 bytes

And its a pretty slim list. Wow this is the first time i have seen a product contain such little variables. Normally i see products with like 10+ variables set. But we got the important ones here. Also indeed bootdelay is set to 1 so thats why it allowed us to interupt it.

‘bootcmd’
#

bootcmd=gvsdupdate;sf probe;sf read 0x80600000 0x40000 0x1C0000; bootm 0x80600000

So as this variable is named, this is litteraly the commands it executes at boot (when you let it boot without interupting it). We can see that it utilizes the ‘sf’ command to probe the flash (initialise) and then it reads from flash from ‘offset’ with lenght of ’len’ into memory (ram) at ‘address’ and after that it uses the ‘bootm’ command to execute the kernel. We can see that it reads the kernel from flash at address/offset in flash: 0x40000. And it reads it and puts it into memory at: 0x80600000 and thats the same thing we saw when it loaded linux, the same address. And we can also utilise ‘md’ to dump memory (ram) at a specific address. SO lets try that and see if its really Linux that gets loaded at that address.

isvp_t23# sf probe 
the manufacturer 85
SF: Detected P25Q64H

--->probe spend 4 ms
isvp_t23# sf read 0x80600000 0x40000 0x1C0000
SF: 1835008 bytes @ 0x40000 Read: OK
--->read spend 590 ms
isvp_t23# md 0x80600000
80600000: 56190527 e02f9fb2 12bafa65 2c3f1800    '..V../.e.....?,
80600010: 00000180 709f3c80 5f2f8ba4 03020505    .....<.p../_....
80600020: 756e694c 2e332d78 312e3031 695f5f34    Linux-3.10.14__i
80600030: 5f707673 656b6970 302e315f 00005f5f    svp_pike_1.0__..
80600040: 0000005d ffffff04 ffffffff 6f0000ff    ]..............o
80600050: a3fffffd f77a7fb7 e071f6e8 aa878952    ......z...q.R...
80600060: 39912dec 3776260f 15dc3544 c16f70af    .-.9.&v7D5...po.
80600070: 89204a8b bd4dd10d 1b818a6c 3d568489    .J ...M.l.....V=
80600080: 582f0ed9 60b69f41 b5c32b97 2e3e82fd    ../XA..`.+....>.
80600090: f662677b 70cf124b 4bc18786 2b0b86da    {gb.K..p...K...+
806000a0: d3dff194 bea384af adb3f0a1 b61159a9    .............Y..
806000b0: a6c7ca76 fcbba101 515802d0 13f9e9dd    v.........XQ....
806000c0: 59da4b04 ccdcbd4f 83d19711 34ba044d    .K.YO.......M..4
806000d0: 20591cf1 45732e48 fe7d4274 81e4e287    ..Y H.sEtB}.....
806000e0: 5386311e 311bf0da 2cc00526 1967cc19    .1.S...1&..,..g.
806000f0: ec6e540c 295bc035 5e884a1a d7e88ceb    .Tn.5.[).J.^....

And indeed we see the Linux Image header!

‘bootargs’
#

bootargs=console=ttyS1,115200n8 mem=39436K@0x0 rmem=25600K@0x2683000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(uboot),1792k(kernel),960k(rootfs),4800k(app),384k(config)

SO this bootargs variable is also what it says, boot arguments. These get passed in our case to the Linux kernel itself so the kernel knows how to boot up the system and knows how to behave. We can se typicall stuff the Linux kernel expects like what console it has to use/output too and how mucch memory its allowed to take and also what to use as Init.

Init in Linux and Unix is the process that basically gets started first and dies off last and it is the process with highest priority (PID1, process id 1) and it must never die and is responsible for initialising the system and is always running in the background. This Init variable could be interesting if we could change it to something else as now we see that it uses the Busybox Init binary located in: /sbin/init. And this busybox Init starts the device normally and also spawns that password protected login. But lets talk about that a bit later.

We also see that it gets passed a root device which will tell the kernel which device has a root filesystem on it with all directories and stuff the kernel needs. We see it uses /dev/mtdblock2. Mtdblock is a interface for mounting flash partitions and makes it so the kernel can access whats on those partitons. And indeed mtdblock2 is our rootfs partition, although here you need to count starting from 0. So mtdblock0 is Uboot, mtdblock1 is Linux and mtdblock2 is the rootfs. Also we see that it gets passed a rootfstype parameter which tells the kernel what format the rootfs is. Also we se that it gets mounted as rw.

And alast we see it mounts the partitions with mtdparts={partitions}.

lets try changing ‘Init’
#

So now we should know all the variables and stuff. Lets try messing with init as that will be our way to possibly boot the system using any other proces than standard Init and maybe get in a very early boot stage. I know Linux and Unix have a shell thats often called ‘sh’ often located at path: /bin/sh, this is a shell but Linux does allow using a Shell as the init process and basically anything that can stay alive and does not terminate instantly. A ton of devices have this shell but some use another shell called the Almquist shell (/bin/ash).

For this we can use the ‘setenv’ command to set an environment variable to a value. Lets do that. SO i copied the whole bootargs string to a text editor and then changed /sbin/init to /bin/sh. I wrapped it in a nice single liner that can be pasted into the uboot shell instantly.

setenv bootargs 'console=ttyS1,115200n8 mem=39436K@0x0 rmem=25600K@0x2683000 init=/bin/sh rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(uboot),1792k(kernel),960k(rootfs),4800k(app),384k(config)'

Lets try entering that. This is not persistent unless you run ‘saveenv’ but that command is not in our uboot version sadly, but that does mean we can break the system less easy. So now to boot the system we simply run the command below.

run bootcmd

which will run the stuff defined in bootcmd and start the system.

isvp_t23# setenv bootargs 'console=ttyS1,115200n8 mem=39436K@0x0 rmem=25600K@0x2683000 init=/bin/sh rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(uboot),1792k(kernel),960k(rootfs),4800k(app),384k(config)'
isvp_t23# saveenv
Unknown command 'saveenv' - try 'help'
isvp_t23# run bootcmd
the manufacturer 85
SF: Detected P25Q64H

--->probe spend 4 ms
otabakpaddr:0x0
Card did not respond to voltage select!
Error, mout fatfs failed, error num: 3
the manufacturer 85
SF: Detected P25Q64H

--->probe spend 4 ms
SF: 1835008 bytes @ 0x40000 Read: OK
--->read spend 591 ms
## Booting kernel from Legacy Image at 80600000 ...
   Image Name:   Linux-3.10.14__isvp_pike_1.0__
   Image Type:   MIPS Linux Kernel Image (lzma compressed)
   Data Size:    1589036 Bytes = 1.5 MiB
   Load Address: 80010000
   Entry Point:  803c9f70
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

Starting kernel ...

[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 3.10.14__isvp_pike_1.0__ (spock@spock-PC) (gcc version 5.4.0 (Ingenic Linux-Release3.3.0-Default(xburst1(fp32)+uclibc0.9.33.2) Smaller libc 2022.11-07) ) #3 PREEMPT Wed Mar 20 18:27:28 CST 2024
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU0 RESET ERROR PC:CB226B05
[    0.000000] CPU0 revision is: 00d00100 (Ingenic Xburst)
[    0.000000] FPU revision is: 00b70000
[    0.000000] cgu_vpu clk should be opened!
[    0.000000] CCLK:1188MHz L2CLK:594Mhz H0CLK:200MHz H2CLK:200Mhz PCLK:100Mhz
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 0047b000 @ 00010000 (usable)
[    0.000000]  memory: 00035000 @ 0048b000 (usable after init)
[    0.000000] User-defined physical RAM map:
[    0.000000]  memory: 02683000 @ 00000000 (usable)
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x00000000-0x02682fff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x00000000-0x02682fff]
[    0.000000] Primary instruction cache 16kB, 8-way, VIPT, linesize 32 bytes.
[    0.000000] Primary data cache 16kB, 8-way, VIPT, no aliases, linesize 32 bytes
[    0.000000] pls check processor_id[0x00d00100],sc_jz not support!
[    0.000000] MIPS secondary cache 64kB, 8-way, linesize 32 bytes.
[    0.000000] Built 1 zonelists in Zone order, mobility grouping off.  Total pages: 9781
[    0.000000] Kernel command line: console=ttyS1,115200n8 mem=39436K@0x0 rmem=25600K@0x2683000 init=/bin/sh rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(uboot),1792k(kernel),960k(rootfs),4800k(app),384k(config)
[    0.000000] PID hash table entries: 256 (order: -2, 1024 bytes)
[    0.000000] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Memory: 33572k/39436k available (3850k kernel code, 5864k reserved, 736k data, 212k init, 0k highmem)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000] NR_IRQS:351
[    0.000000] clockevents_config_and_register success.
[    0.000020] Calibrating delay loop... 1185.38 BogoMIPS (lpj=5926912)
[    0.087812] pid_max: default: 32768 minimum: 301
[    0.092707] Mount-cache hash table entries: 512
[    0.097741] Initializing cgroup subsys debug
[    0.102000] Initializing cgroup subsys freezer
[    0.108801] regulator-dummy: no parameters
[    0.113040] NET: Registered protocol family 16
[    0.121305] bio: create slab <bio-0> at 0
[    0.127362] jz-dma jz-dma: JZ SoC DMA initialized
[    0.132378] usbcore: registered new interface driver usbfs
[    0.137985] usbcore: registered new interface driver hub
[    0.143414] usbcore: registered new device driver usb
[    0.148695] i2c-gpio i2c-gpio.1: using pins 57 (SDA) and 58 (SCL)
[    0.154883]  (null): set:249  hold:250 dev=100000000 h=500 l=500
[    0.161028] media: Linux media interface: v0.10
[    0.165621] Linux video capture interface: v2.00
[    0.172068] Switching to clocksource jz_clocksource
[    0.177020] cfg80211: Calling CRDA to update world regulatory domain
[    0.184036] NET: Registered protocol family 2
[    0.188950] TCP established hash table entries: 512 (order: 0, 4096 bytes)
[    0.195905] TCP bind hash table entries: 512 (order: -1, 2048 bytes)
[    0.202306] TCP: Hash tables configured (established 512 bind 512)
[    0.208634] TCP: reno registered
[    0.211840] UDP hash table entries: 256 (order: 0, 4096 bytes)
[    0.217803] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[    0.224348] NET: Registered protocol family 1
[    0.229033] freq_udelay_jiffys[0].max_num = 10
[    0.233503] cpufreq  udelay  loops_per_jiffy
[    0.237874] 12000     59867   59867
[    0.241140] 24000     119735  119735
[    0.244597] 60000     299338  299338
[    0.248028] 120000    598677  598677
[    0.251559] 200000    997796  997796
[    0.255103] 300000    1496694         1496694
[    0.258799] 600000    2993389         2993389
[    0.262508] 792000    3951274         3951274
[    0.266228] 1008000   5028895         5028895
[    0.270012] 1200000   5986779         5986779
[    0.278269] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.284384] jffs2: version 2.2. © 2001-2006 Red Hat, Inc.
[    0.290209] msgmni has been set to 65
[    0.295989] io scheduler noop registered
[    0.299914] io scheduler cfq registered (default)
[    0.304947] wait stable.[290][cgu_vpu]
[    0.310055] jz-uart.1: ttyS1 at MMIO 0x10031000 (irq = 58) is a uart1
[    0.317844] console [ttyS1] enabled, bootconsole disabled
[    0.317844] console [ttyS1] enabled, bootconsole disabled
[    0.332828] brd: module loaded
[    0.338189] loop: module loaded
[    0.342071] zram: Created 2 device(s) ...
[    0.346390] logger: created 256K log 'log_main'
[    0.363560] jz SADC driver registeres over!
[    0.368502] jz TCU driver register completed
[    0.373355] the id code = 856017, the flash name is P25Q64H
[    0.379184] jz-sfc jz-sfc: sfc use DMA mode
[    0.383532] jz-sfc jz-sfc: nor flash now use standard mode!
[    0.389300] JZ SFC Controller for SFC channel 0 driver register
[    0.395453] 5 cmdlinepart partitions found on MTD device jz_sfc
[    0.401580] Creating 5 MTD partitions on "jz_sfc":
[    0.406559] 0x000000000000-0x000000040000 : "uboot"
[    0.412147] 0x000000040000-0x000000200000 : "kernel"
[    0.417868] 0x000000200000-0x0000002f0000 : "rootfs"
[    0.423602] 0x0000002f0000-0x0000007a0000 : "app"
[    0.428978] 0x0000007a0000-0x000000800000 : "config"
[    0.434675] SPI NOR MTD LOAD OK
[    0.437985] tun: Universal TUN/TAP device driver, 1.6
[    0.443211] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
[    0.449768] wait stable.[290][cgu_macphy]
[    0.553857] Bus Mode Reg after reset: 0x00020101, cnt=0
[    0.560263] Bus Mode Reg after reset: 0x00020101, cnt=1
[    0.566681] Bus Mode Reg after reset: 0x00020101, cnt=2
[    0.573085] Bus Mode Reg after reset: 0x00020101, cnt=3
[    0.579501] Bus Mode Reg after reset: 0x00020101, cnt=4
[    0.585916] Bus Mode Reg after reset: 0x00020101, cnt=5
[    0.592321] Bus Mode Reg after reset: 0x00020101, cnt=6
[    0.598737] Bus Mode Reg after reset: 0x00020101, cnt=7
[    0.605157] Bus Mode Reg after reset: 0x00020101, cnt=8
[    0.611562] Bus Mode Reg after reset: 0x00020101, cnt=9
[    0.617979] func:jz_mii_bus_probe, synopGMAC_reset failed
[    0.623591] jz_mii_bus: probe of jz_mii_bus.0 failed with error -1
[    0.630076] =======>gmacdev = 0x819cd280<================
[    0.635711] =========>gmacdev->MacBase = 0xb34b0000 DmaBase = 0xb34b1000
[    0.642649] Bus Mode Reg after reset: 0x00020101, cnt=0
[    0.649066] Bus Mode Reg after reset: 0x00020101, cnt=1
[    0.655481] Bus Mode Reg after reset: 0x00020101, cnt=2
[    0.661886] Bus Mode Reg after reset: 0x00020101, cnt=3
[    0.668301] Bus Mode Reg after reset: 0x00020101, cnt=4
[    0.674718] Bus Mode Reg after reset: 0x00020101, cnt=5
[    0.681122] Bus Mode Reg after reset: 0x00020101, cnt=6
[    0.687538] Bus Mode Reg after reset: 0x00020101, cnt=7
[    0.693953] Bus Mode Reg after reset: 0x00020101, cnt=8
[    0.700356] Bus Mode Reg after reset: 0x00020101, cnt=9
[    0.706770] func:jz_mac_probe, synopGMAC_reset failed
[    0.712007] jz_mac: probe of jz_mac.0 failed with error -1
[    0.717801] usbcore: registered new interface driver cdc_ether
[    0.723930] usbcore: registered new interface driver rndis_host
[    0.730118] usbcore: registered new interface driver int51x1
[    0.736147] DWC IN OTG MODE
[    0.739694] dwc2 dwc2: Keep PHY ON
[    0.743218] dwc2 dwc2: Using Buffer DMA mode
[    0.747683] dwc2 dwc2: Core Release: 3.00a
[    0.751959] dwc2 dwc2: DesignWare USB2.0 High-Speed Host Controller
[    0.758485] dwc2 dwc2: new USB bus registered, assigned bus number 1
[    0.765837] hub 1-0:1.0: USB hub found
[    0.769737] hub 1-0:1.0: 1 port detected
[    0.773981] dwc2 dwc2: DWC2 Host Initialized
[    0.778656] dwc2 dwc2: enter dwc2_gadget_plug_change:2589: plugin = 1 pullup_on = 0 suspend = 0
[    0.787861] usbcore: registered new interface driver cdc_acm
[    0.793765] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[    0.802195] usbcore: registered new interface driver usbserial
[    0.808335] usbcore: registered new interface driver usbserial_generic
[    0.815159] usbserial: USB Serial support registered for generic
[    0.821419] usbcore: registered new interface driver option
[    0.827257] usbserial: USB Serial support registered for GSM modem (1-port)
[    0.834654] using random self ethernet address
[    0.839269] using random host ethernet address
[    0.844273] usb0: HOST MAC a6:29:61:f4:d5:59
[    0.848763] usb0: MAC b6:7b:af:0d:ee:11
[    0.852745] dwc2 dwc2: ID PIN CHANGED!
[    0.856748] using random self ethernet address
[    0.861361] using random host ethernet address
[    0.866061] g_ether gadget: Ethernet Gadget, version: Memorial Day 2008
[    0.872913] g_ether gadget: g_ether ready
[    0.877267] i2c /dev entries driver
[    0.881361] jzmmc_v1.2 jzmmc_v1.2.0: vmmc regulator missing
[    0.887565] jzmmc_v1.2 jzmmc_v1.2.0: register success!
[    0.893167] TCP: cubic registered
[    0.897164] NET: Registered protocol family 10
[    0.902697] sit: IPv6 over IPv4 tunneling driver
[    0.908523] ip6_gre: GRE over IPv6 tunneling driver
[    0.914168] NET: Registered protocol family 17
[    0.918795] lib80211: common routines for IEEE802.11 drivers
[    0.925193] soc_vpu probe success,version:1.0.0-03203fd46d
[    0.930888] drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
[    0.942551] VFS: Mounted root (squashfs filesystem) readonly on device 31:2.
[    0.950276] Freeing unused kernel memory: 212K (8048b000 - 804c0000)
~ # [    1.313491] usb 1-1: new full-speed USB device number 2 using dwc2
[    1.503490] usb 1-1: device descriptor read/64, error -88
[    1.803485] usb 1-1: device descriptor read/64, error -88
[    2.103485] usb 1-1: new full-speed USB device number 3 using dwc2
[    2.293494] usb 1-1: device descriptor read/64, error -88
[    2.593482] usb 1-1: device descriptor read/64, error -88

~ # [    2.893492] usb 1-1: new full-speed USB device number 4 using dwc2
[    3.313479] usb 1-1: device not accepting address 4, error -88

~ # [    3.503490] usb 1-1: new full-speed USB device number 5 using dwc2
[    3.923475] usb 1-1: device not accepting address 5, error -88
[    3.929555] hub 1-0:1.0: unable to enumerate USB device on port 1

~ # 

Exploring the Linux os in more detail.
#

Bingo we got a shell! And this time there is no login or password required. Now we can start exploring the Linux os itself without any borders or the terminal flooding with debug messages from the application itself.

Inittab
#

First thing i do is look at what the normal init process runs. Typically it first runs whats inside /etc/inittab and afterwards moves on to scripts inside /etc/init.d/rcS. And also because we skiped the normal init stuff, we wont have all partitions or stuff mounted. Linux typically requires that we mount and populate /dev, /proc and /sys. So lets view what /etc/inittab contains. For this we use the ‘cat’ command and no, it wont summon a kittycat :).

~ # cat /etc/inittab
# /etc/inittab
#
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels.  The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id        == tty to run on, or empty for /dev/console
# runlevels == ignored
# action    == one of sysinit, respawn, askfirst, wait, and once
# process   == program to run

# Startup the system
::sysinit:/sbin/swapoff -a
::sysinit:/bin/mount -t tmpfs tmpfs /dev
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
::sysinit:/bin/hostname -F /etc/hostname

# now run any rc scripts
::sysinit:/etc/init.d/rcS

# Put a getty on the serial port
console::respawn:/sbin/getty -L console 115200 vt100 # GENERIC_SERIAL

# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot

# Stuff to do before rebooting
::shutdown:/bin/umount -a -r

And here we see it mounts at sysinit (system initialisation) a tmpfs (temporary filesystem) at /dev, creates some entries in /dev and then mounts the rest using: mount -a. This wont mount our custom partitons like app or configuration as thats the task of the other init scripts. And it indeed movoes after that really early init bussiness on to the scripts in /etc/init.d. We see it calls a script called rcS which i think stands for ‘rc Start’ or ‘run commands start’ RC/OpenRC used to be an Init system and i think it stil is used in some Linux/Unix(-like) Operating systems/distros. Anyways that script probably contains the next boot stages that load the drivers or mount the partitions and starts the App or something like that. Lets use ‘cat’ on that too!

/etc/init.d/rcS
#

#!/bin/sh

# Set mdev
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s && echo "mdev is ok......"

# create console and null node for nfsroot
#mknod -m 600 /dev/console c 5 1
#mknod -m 666 /dev/null c 1 3

# Set Global Environment
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH=/app/bin:$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/lib

# networking
ifconfig lo up
#ifconfig eth0 192.168.1.80

# Start telnet daemon
#telnetd &

# Set the system time from the hardware clock
hwclock -s

# Mount system partition
mount -t squashfs /dev/mtdblock3 /app
mount -t jffs2 -o sync /dev/mtdblock4 /conf

#mkdir -p bin init etc/sensor lib/firmware lib/modules

MODULE_DIR=$(uname -r)
mkdir -p /tmp/modules/${MODULE_DIR}

# Run init script
if [ -f /app/init/app_init.sh ]; then
    /app/init/app_init.sh &
fi

And it seems some entries are commented out. Perhaps for other models with more functions… Anyways we see it starts mdev which further populates /dev and then sets the system’s path PATH= variable. Also it sets the path of the libraries used by the application. Then it raises the loopback interface which is used for some internal stuff and then we see that it sets the hwclock. After that it mounts the App and configuration partitions and indeed they are squashfs and jffs. And it seems to create a temporary module directory which holds all the kernel modules it requires.

After that it moves to the last stage which is the Application stuff. It seems to hand over control to a script called: app_init.sh. But lets first have a look at the files the App partition contains.

/App partition
#

~ # ls /app
web      osd      lib      init     bin
sound    music    jz       drivers

And we see some folders. going through them all a lot of them are empty so perhaps for other products using the ISVP. I think Music might be for stuff like a baby monitor or such so when you have a baby monitor that is powered by the same ISVP and chip, you can play music to calm your baby or something like that, idk. /web is also empty but that might be for a webinterface for viewing or managing if the vendor chooses to add that. /sound does contain stuff but thats obvious as the camera does play some status tones/sounds so you know what status it is in. And it indeed also has a siren sound file funnily enough. Also we see that it has some factory test sound and some other sounds that my product does not have functionality for, so in that case they where a bit messy or did not bother to clean up and left some littering files. Also /osd contains some stuff for what i think is an On-screen-display but i think thats shown in the app.

/lib
#

/app # ls lib
libgv_vad.so                        libgv_device_id_decryption.so
libgv_rtsp.so                       libgv_core.so
libgv_onvif_rtsp.so                 libgv_aec.so
libgv_ipc.so                        libaudioProcess.so
libgv_device_id_decryption.so.sign  firmware

/lib contains some libraries and support stuff for the application/ISVP.

/jz
#

/app # ls jz
start_param        modules            load.sh
sensor             load_jz_driver.sh  etc

/sensor contains binary files for the sensor chip. Idk what they do but they are there. /modules contains kernel modules for the sensor which is a sc2336 and contains the kernel module for the audio/sound, the ISP (Image signal processor) and some PWM hardware abstraction layer (HAL). the file called ‘Start-param’ contains sensor parameters and parameters for the ISP (Ingenic Image signal processor).

And the rest (load.sh and load_jz_driver.sh) are simply for loading drivers and files for the hardware.

/init
#

/app # ls init
app_init.sh

This contains the app_init.sh script and nothing else.

/drivers
#

/app # ls drivers
set_rate_power.txt    gv_step_motor.ko      atbm603x_wifi_usb.ko
gvfs.ko               gv-gpio.ko

So this contains some more kernel drivers/kernel modules. It contains the main wifi chip driver: atbm603x_wifi_usb.ko. Contains a driver for a stepper motor for PTZ variant cameras and some GPIO kernel module.

/bin
#

/app # ls bin
wpa_supplicant  iwpriv          gv_led_flash    daemon
wifi            iwlist          gv_apns.bin
update          iwconfig        flashcp
log             ipc             dhcp.pre

Now this is the interesting directory! Here are all the (custom) binaries it has. ‘update’ is probably for upgrading firmware probably over the air (OTA). ‘wpa_supplicant’ is a pretty standard Linux binary used for interfacing with the wireless stuff. ‘gv_led_flash’ probably controls the statusled. ‘wifi’ is probably used for the wireless stuff. the binaries starting with ‘iw’ also are some known Linux binaries for wireless mangement i believe. ‘flashcp’ is used to erase the flash and a partition or device (eg mtd4) and or copy files to it. Daemon is i guess the watchdog or some monitor/runtime process as when i ran it it spammed a ton of messages

[0:34:19.277][E][daemon]Exit value=[127]
[0:34:19.278][W][daemon]Rerun app -> [--help]
[0:34:19.278][E][daemon]Exit value=[127]
[0:34:19.278][W][daemon]Rerun app -> [--help]
[0:34:19.278][E][daemon]Exit value=[127]
[0:34:19.278][W][daemon]Rerun app -> [--help]
[0:34:19.279][E][daemon]Exit value=[127]
[0:34:19.279][W][daemon]Rerun app -> [--help]
[0:34:19.279][E][daemon]Exit value=[127]
[0:34:19.279][W][daemon]Rerun app -> [--help]
[0:34:19.279][E][daemon]Exit value=[127]
[0:34:19.280][W][daemon]Rerun app -> [--help]
[0:34:19.280][E][daemon]Exit value=[127]
[0:34:19.280][W][daemon]Rerun app -> [--help]
[0:34:19.280][E][daemon]Exit value=[127]
[ 2059.584718] watchdog watchdog0: watchdog did not stop!

and after like 2 seconds it caused the system to rebooted.

That ipc binary is obviously the main process that handles all the hevay lifting and m,akes this camera work as an IP camera. Lets have a look at the app_init.sh script and see how it starts those binaries.

app_init.sh
#

So lets have a look at that. SO i ran everything in both scripts except the last bit which summons the Application.

#!/bin/sh

ulimit -s 256

echo 1 > /proc/sys/vm/overcommit_memory
echo 5 > /proc/sys/vm/dirty_background_ratio 
echo 10 >  /proc/sys/vm/dirty_ratio 
echo 50 > /proc/sys/vm/dirty_writeback_centisecs 
echo 100 > /proc/sys/vm/dirty_expire_centisecs 
echo 10000 > /proc/sys/vm/vfs_cache_pressure
echo 1 > /proc/sys/kernel/panic_on_oops

if [ -e /sbin/swapon ] ;then
        echo 100 > /proc/sys/vm/swappiness
        echo 8388608 > /sys/block/zram0/disksize
        mkswap /dev/zram0
        swapon /dev/zram0
fi

if [ -e /app/lib/firmware/atbm_txpwer_dcxo_cfg.txt ] ;then
        cp /app/lib/firmware/atbm_txpwer_dcxo_cfg.txt  /tmp/atbm_txpwer_dcxo_cfg.txt
fi

/app/jz/load.sh

cp /app/bin/daemon /tmp/
chmod +x /tmp/daemon

echo 0 > /proc/sys/kernel/printk 

# telnetd &

insmod /app/drivers/gv-gpio.ko
insmod /app/drivers/gvfs.ko
/tmp/daemon @ipc

sleep 10

echo 3 > /proc/sys/vm/drop_caches

And we can see it writes some values to some entries in /proc and then it enables swap memory as the system needs more than the 64 meagbytes of ram to work reliably. Then it runs that script in jz that loads all drivers and then it inserts the gpio module and a module called ‘gvfs’ which i think is something with filesystem but no clue. It also copies the ‘daemon’ process into /tmp and makes it executable and then from /tmp runs the daemon and the daemon runs the ipc app. And seems you can pass the ‘daemon’ process any app and then i think it will take care of it and if that process dies it tries to respawn it or else reboots the system. And when i tried it with ‘sh’ passed to it and then tried to exit the shell it reboots the system so its indeed some watchdog process.

Anyways thats it for part1 of this post. Pretty nice and interesting device and clever scripts and filesystem layout. You can now move to part 2 by clicking the arrows down below or This button

Author
309Electronics