See project logs for all information and updates..
UART hacking a low cost DVB-S2 receiver box based on MStar MIPS32 chipset
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
See project logs for all information and updates..
Introduction-to-MBoot.pdfAdobe Portable Document Format - 556.19 kB - 10/25/2023 at 09:42 |
|
|
FLASH.BINSPI flash dumpoctet-stream - 16.00 MB - 10/24/2023 at 20:03 |
|
|
20230917_144512.jpgWi-Fi controller ICJPEG Image - 792.88 kB - 09/22/2023 at 08:58 |
|
|
Since the application boot sequence is hardcoded (not defined using bootcmd) we need to dive deeper into how this works.
Using Ghidra I have decompiled the U-boot MIPS assembly code to understand more. I simply loaded the DRAM dump performed earlier into Ghidra and disassembled for MIPS32 architecture. The initial output from Ghidra is difficult to interpret, but slowly as you start to give functions and variables more intuitive names the process begins to speed up.
It turns out that the application boot sequence is very complicated with CRC checks and multiple flows for various software upgrades via ethernet and USB. There is also some redundant code that seems to perform no function whatsoever, almost as if this version of M-boot has been further hacked and modified by the author of the final application software. I will provide more detail on this later in the form of a flow chart after I've fully reverse engineered it.
What I have managed to determine is the main application boot sequence. In order to replicate it via the U-boot command line type the following sequence:
spi_rdc 0x81100000 0x300000 0x700000 mscompress7 d 0 0x81100000 0x700000 0x80000180 go 0x80000224
This sequence: 1.) copies the compressed application image from flash to DRAM starting at starting 0x81100000, 2.) decompresses the image to DRAM starting at address 0x80000180, 3.) begin code execution from DRAM address 0x80000224. The main application now boots with display out via HDMI. Note, this sequence bypasses all of the CRC checks in the standard hardcoded sequence.
Interestingly, we can also use the same process to boot the secret software upgrader application:
spi_rdc 0x81100000 0x90000 0x250000 mscompress7 d 0 0x81100000 0x250000 0x80000180 go 0x80000224
Again, this provides a display output via HDMI and goes through a sequence of trying to update software via ethernet, USB and OTA.
In a previous log we saw how the contents of the SPI flash chip can be dumped to a binary file over USB, the .bin file of this dump is attached in the Files area. We will now examine that file using a variety of linux-based tools (hex dump, binwalk, strings).
Here is my best guess of the map of the 16MiB flash chip, the table will be updated as we learn more:
Flash start address | Flash end address | Description |
0x000000 | 0x00316F | BootROM (mapped to base address 0xBFC00000) |
0x003170 | 0x00FFFF | BootRAM (last 32 bytes are the S-Boot version string) |
0x010000 | 0x0103FF | Unknown header |
0x010400 | 0x06FFFF | U-boot image (LZMA compressed) |
0x070000 | 0x08FFFF | JPEG image (displayed during application boot) |
0x090000 | 0x2E7FFF | Upgrader application image (LZMA compressed) |
0x2E8000 | 0x2EFFFF | Application CRC |
0x2F0000 | 0x2F7FFF | Application CRC (backup copy) |
0x2F8000 | 0x2FFFFF | ? |
0x300000 | 0x9FFFFF | Main application image (LZMA compressed) |
0xA00000 | 0xCFFFFF | Application image (ZLIB compressed) |
0xD00000 | 0xFDFFFF | Application data? |
0xFE0000 | 0xFEFFFF | U-boot environment variables |
0xFF0000 | 0xFFFFFF | U-boot environment variables (redundancy) |
The U-boot image starting at 0x010400 is LZMA compressed with a unique string of bytes as a footer. Removing this footer and uncompressing with unlzma reveals a binary containing MIPS instructions.
A JPEG image is held at address 0x070000 and is displayed during boot via HDMI. The image is the Manhattan branding for this unit and has a resolution of 720 x 576 pixels, it is displayed during the boot of the main application. A cool little hack might be to replace this image with something of your own :-) Image shown below.
The two blocks starting at 0x2E8000 and 0x2F0000 are identical redundant copies, clearly containing some important data that must be conserved should a write action to this area fail and brick the unit. Reverse engineering of the U-boot binary reveals them to be CRC checksums related to the main application image, the format of these will be explored later. The third block starting at 0x2F8000 is of unknown format at present.
The application images are compressed with a mixture of LMZA compression and ZLIB compression. They are uncompressed and executed by the U-boot boot sequence. Uncompressing these reveals an eCOS real-time operating system. This is the operating system for the satellite receiver application. One is a separate 'upgrader' application for software updates (more on this later). Disappointingly, it's not Linux, so we won't be able to hack our way to a root shell :-(
Finally we have application data and the 2x identical copies of the U-boot environment variables, again for important redundancy protection.
The initial boot sequence (up to U-boot) has been examined by studying the firmware dump and the M-boot manual (attached in the files area):
1.) Reset vector 0xBFC00000, BootROM code execution direct from flash
2.) DRAM, UART and other initializations
3.) BootRAM code copied to DRAM, at address 0x81000000 and executed
4.) U-boot LMZA image copied to DRAM at address 0x81100000
5.) U-boot image uncompressed to DRAM at address 0x871F0180
6.) Execution of U-boot MIPS code from same address.
From this point, the U-boot boot sequence takes over. In standard U-boot, the boot sequence should be specified in the environment variables (bootcmd). However, we know in this version of U-boot the sequence is instead hardcoded and the bootcmd is a inoperable. In fact, if we modify the bootcmd to include an echo statement + saveenv we do not get any feedback in the console during the full boot flow. It is highly likely the bootcmd is ignored in this version of U-boot and a hardcoded sequence is instead executed. How we therefore modify this sequence to autoboot a future custom OS is unclear, the only viable option at present to boot something custom using the U-boot command prompt.
So what and where is the hardcoded boot sequence in the U-boot binary? The DEBUG level commands in the console can provide...
Read more »On examination of the help output there is a command dbg that can be used to set the debug message level:
kiwi# help .... dbg - set debug message level. Default level is INFO
The default level is INFO. We can use this command to change the level of feedback in the terminal when any commands are executing. Let's set to level DEBUG, this is the highest level of detail.
kiwi# dbg DEBUG Saving Environment to SPI Flash... Write addr=0x00FE0000, size=0x00010000 block erase Write addr=0x00FF0000, size=0x00010000 block erase
This change is made to the environment variables which are stored on the SPI flash. The good news is these changes therefore persist after a reboot. The address locations in flash where the environment variables are stored will be helpful when we later analyze our firmware flash dump. We can check the changes to the environment variables by running the printenv command:
UARTOnOff=on baudrate=115200 bootcmd=if mmc rescan ${mmcdev}; then if run loadbootscript; then run bootscript; else if run loaduimage; then run mmcboot; fi; fi; fi bootdelay=0 bootscript=echo Running bootscript from mmc${mmcdev} ...; source ${loadaddr} console=ttyS2,115200n8 dbgLevel=DEBUG loadaddr=0x82000000 loadbootscript=fatload mmc ${mmcdev} ${loadaddr} boot.scr loaduimage=fatload mmc ${mmcdev} ${loadaddr} uImage mmcargs=setenv bootargs console=${console} vram=${vram} root=${mmcroot} rootfstype=${mmcrootfstype} mmcboot=echo Booting from mmc${mmcdev} ...; run mmcargs; bootm ${loadaddr} mmcdev=0 mmcroot=/dev/mmcblk0p2 rw mmcrootfstype=ext3 rootwait osd_language=English stderr=serial stdin=serial stdout=serial ubispeedup=UBI usbtty=cdc_acm vram=16M Environment size: 805/65532 bytes
A new line dbgLevel=DEBUG has been appended.
Let's reset the unit and allow the normal boot process to proceed so we can review the debug level output:
UART_115200 AC_FLOW [23456789A][23456789A][3456789AB][3456789AB]-6677 BST-OK_RAM[AT][MB][start ub][677] U-Boot 2011.06-svn565 (Mar 01 2018 - 21:27:50) MBOT-1106-0.8.KANO_TEE_NAND.a1 DRAM: 256 MiB Hello U-Boot Stack Pointer at: 87E52E00 mem initial, start 0x86DD0180, len 0x420000 msIR_Initialize [MIU INFO] miu opencreate instance at 86FE7288 with private size 80 bytes at 86FE72D0 SPI: Flash is detected (0x0C05, 0xC8, 0x40, 0x18) MDrv_SERFLASH_GetInfo() u32AccessWidth = 1 u32TotalSize = 16777216 u32SecNum = 256 u32SecSize = 65536 create instance at 86FE7328 with private size 48 bytes at 86FE7370 uboot held at [8F000000~90000000] Now running in RAM - U-Boot at: 871F0180 In: serial Out: serial Err: serial Net: No ethernet found. Set MAC default MAC: 0x0: 0x30: 0x1B: 0xBA:0x2: 0xDB [AT][MB][initDbgLevel][779]_end [TRACE] getNextCmd IN [DEBUG] getNextCmd:159: This is the last cmd [TRACE] MsDrv_GetMIUSize IN [TRACE] MsDrv_GetMIUSize OK [TRACE] MsDrv_GetMIUSize IN [TRACE] MsDrv_GetMIUSize OK [TRACE] MsDrv_GetMIUSize IN [TRACE] MsDrv_GetMIUSize OK Hit any key to stop autoboot: 0 [TRACE] do_spi_rdc IN offset 0x2E0000, size 0x10000 [TRACE] _spi_rdc IN [DEBUG] _spi_rdc:768: dram_addr=0x80700000 [DEBUG] _spi_rdc:769: flash_addr=0x2E0000 [DEBUG] _spi_rdc:770: len=0x10000 Flash is detected (0x0C05, 0xC8, 0x40, 0x18) initialization done! [DEBUG] _spi_rdc:799: Start read 10000 data from serial device... [TRACE] do_spi_rdc OK ERR>Invalid Ldr Sign ERR>Reading LDR sign from backup [TRACE] do_spi_rdc IN offset 0x80000, size 0x10000 [TRACE] _spi_rdc IN [DEBUG] _spi_rdc:768: dram_addr=0x80700000 [DEBUG] _spi_rdc:769: flash_addr=0x80000 [DEBUG] _spi_rdc:770: len=0x10000 [DEBUG] _spi_rdc:799: Start read 10000 data from serial device... [TRACE] do_spi_rdc OK **********************LOADER_INFO********************* @DF.0 #1.0 $1.0 ^1.5 *17 ************************************************************ SSS eLOADER 21:28:06 Mar 1 2018 ************************************************************ CPS SZE[1740] MAIN.C 2484> Checking for key sequence... enInvokemode:0 M.c 712> USB_(0) Check USB port[0]: [USB] usb_lowlevel_init++ ...Read more »
The conventional way of doing this is to use the command md to dump the firmware bytes as text characters to the terminal, pipe to a text file, and run a script to convert from ASCII to binary. Unfortunately, this process is very slow, I calculated that to dump the entire 256MiB of RAM would take 36 hours with a resulting text file larger than 1GB!
Thankfully, there is a quicker and easier way, thanks to some nifty USB tools bundled in with M-boot :-)
kiwi# usb usb - USB sub-system Usage: usb reset [dev] - reset (rescan) USB controller usb start [dev] - start (scan) USB controller usb stop [f] - stop USB [f]=force stop usb tree - show USB device tree usb info [dev] - show available USB devices usb storage - show details of USB storage devices usb dev [dev] - show or set current USB storage device usb part [dev] - print partition table of one or all USB storage devices usb read addr blk# cnt - read `cnt' blocks starting at block `blk#' to memory address `addr' usb write addr blk# cnt - write `cnt' blocks starting at block `blk#' from memory address `addr'
The board has 2x USB2.0 ports. The first (USB 0) is internally connected to the WiFi chip. The second (USB 1) is exposed as a USB2.0 port for us to use. Plug a freshly FAT32 formatted USB pen/thumb drive into this port. Reset the port to discover the device:
kiwi# usb reset 1 (Re)start USB 1... Check USB port[1]: [USB] usb_lowlevel_init++ [USB] USB EHCI LIB VER: 2014.10.02 [USB] Port 1 is Enabled [USB] TV_usb_init (UTMI Init) ++ [USB] UTMI Base BF207400 [USB] UHC Base BF201A00 [USB] USBC Base BF200F00 [USB] BC Base BF240A80 [USB] TV_usb_init-- [USB] Usb_host_Init++ [USB] Async base addr: 0xA7E1A100 [USB] Reg 0x28: 0xA100 0xA7E1 [USB] disable run [USB] Host Speed:2 [USB] enable aynch [USB] Usb_host_Init-- [USB] FAILED [USB] usb_lowlevel_init--[0] scanning bus for devices... [USB] control1 max:40 [USB] interface[0] conf:1 value 8: 1 USB Device(s) found scanning bus for storage devices... [USB] no_of_ep: 2 [USB] find bulk ep: 0 [USB] find bulk ep2: 1 [USB] bulk max packet size: ep(in) 0x200, ep2(out) 0x200 [USB] bulk0 is in max lun:0 1 Storage Device(s) found
Confirm that you have plugged into the port is a USB storage device. The device is registered as storage device 0, not to be confused with port 0:
kiwi# usb storage Device 0: Vendor: Kingston Rev: 1.00 Prod: DT 100 G2 Type: Removable Hard Disk Capacity: 3824.0 MB = 3.7 GB (7831552 x 512)
First task is to dump the contents of the RAM (virtual address range 0x80000000 to 0x8FFFFFFF) to a binary file on the pen drive (USB storage device 0). Remember, this is contents of the RAM with the M-boot bootloader only, we have not yet loaded any application programs to memory. We will use the fatwrite command to achieve this, where the final argument is the number of bytes to write in hex:
kiwi# fatwrite usb 0 0x80000000 RAM.bin 0x10000000 file RAM.bin not found ################################################################# ################################################################ ################################################################
Second task is to use the spi_rdc command to write the contents of the SPI flash chip to RAM, the first argument is the start address in RAM to transfer the data to, the second the start address on the SPI flash chip, the third the number of bytes to transfer, all in hex. We then use fatwrite as before to write this data to a binary file on the pen drive, where the number of bytes to write is now the 16MiB capacity of the flash chip (0x1000000)
kiwi# spi_rdc 0x80000000 0 0x1000000 offset 0x0, size 0x1000000 Flash is detected (0x0C05, 0xC8, 0x40, 0x18) initialization done! kiwi# fatwrite usb 0 0x80000000 flash.bin 0x1000000 file flash.bin not found ################################################################# ################################################################ ################################################################ ################################################################...Read more »
From the U-boot shell prompt we can explore the command options available to us using the help command. There is a lot for us to explore:
kiwi# help ? - alias for 'help' CmdPerformanceTest- gettime - Get the system executing time ac - set a new config to the bootargs base - print or set address offset bdinfo - print Board Info structure boot - boot default, i.e., run 'bootcmd' bootargs_set- Set info exchange and set to boot args. bootcheck- bootcheck - Do boot check bootd - boot default, i.e., run 'bootcmd' bootm - boot application image from memory bootp - boot image via network using BOOTP/TFTP protocol checkfile- check file exist in u disk,and set the partition. checkstr- check_str_resume cleanallenv- cleanall environment variables to persistent storage cmp - memory compare config2env- Set config to environment. config_raw_io- Config the target device for raw I/O coninfo - print console devices and information cp - memory copy crc32 - checksum calculation custar - do usb update from the specified file that is in usb. dbg - set debug message level. Default level is INFO dc - delete the specific cofig that is in the bootargs delay - delay time, time unit is ms dhcp - boot image via network using DHCP/TFTP protocol du - du - Disable UART ebist - PHY loopback test echo - echo args to console editenv - edit environment variable edump - EMAC Register settings dump eloopback- Long loopback test env - environment handling commands epd - emac power down estart - EMAC start ewavetest- EMAC wave test exit - exit script false - do nothing, unsuccessfully fatfilesize- fatfilesize - load binary file from a dos filesystem fatinfo - print information about filesystem fatload - load binary file from a dos filesystem fatls - list files in a directory (default /) fatpartload- fatpartload - load binary file from a dos filesystem fatwrite- fatwrite - write binary file to a dos filesystem filelist- Dump the file list. filelisttest- This command is only for file list test filepartload- load part of a file to RAM get_mmap- get memory info from supernova's mmap gettime - gettime - Get the system executing time go - start application at address 'addr' gpio - GPIO Command: help - print command description/usage if_boot_to_pm- if boot to PM iminfo - print header information for application image imxtract- extract a part of a multi-image initDbgLevel- Initial varaible 'dbgLevel' init_raw_io- init raw_io module itest - return true/false on integer compare kernelProtect- kernelProtect - Protect kernel kernelProtectBist- kernelProtectBist - Protect kernel bist led - See led commands loadb - load binary file over serial line (kermit mode) loadenv - loadenv - load env for nand loads - load S-Record file over serial line loadspi - load data from SPI loady - load binary file over serial line (ymodem mode) loop - infinite loop on address range m2e - Restore the address and len to env from supernova's mmap macaddr - setup EMAC MAC addr mbup - mboot upgrade md - memory display memtest - Get the performance of memory miuProtect- miuProtect - Protect miu mm - memory modify (auto-incrementing address) mscompress7- Compress or decompress lzma files msg - print string - msg [string] mstar - update kernal & root file system automatically by script file mtest - simple RAM read/write test mversion- show changelist - mversion mw - memory write (fill) nm - memory modify (constant address) nuttxProtect- nuttx Protect - Protect nuttx ota_zip_check- do OTA zip package check. ping - send ICMP ECHO_REQUEST to network host pm51 - pm51 command: pm51 [option] pmProtect- runtime pm Protect - Protect runtime PM pop_raw_io_config- pop raw_io last config printenv- print environment variables push_raw_io_config- push raw_io current config raw_io_status- get raw_io status raw_read- Read the raw datas that store in the target device Yo have to execute 'config_raw_io' before using this cmd raw_write- Write the raw datas that store in the target...Read more »
The board has a very convenient 4-pin connector for the UART interface. With a multimeter I've managed to figure out the pinout.
4-pins: VCC(5V), TX, RX, GND
To interface with a computer I've used an FTDI FT232 based USB-UART adapter, the cheap Chinese ones on eBay are more than adequate. The cable should be arranged to connect the ground pins and cross-over TX and RX pins. Do not connect to the VCC pin. It is important to ensure your UART adapter is set to 3.3V mode (this SoC operates on 3.3V logic).
Open your favorite terminal application (I use picocom for Linux) and set the baudrate to 115200. Power on the board and you will see the following output in the terminal:
UART_115200 AC_FLOW [23456789A][23456789A][3456789AB][3456789AB]-6677 BST-OK_RAM[AT][MB][start ub][677] U-Boot 2011.06-svn565 (Mar 01 2018 - 21:27:50) MBOT-1106-0.8.KANO_TEE_NAND.a1 DRAM: 256 MiB Hello U-Boot Stack Pointer at: 87E52E00 mem initial, start 0x86DD0180, len 0x420000 msIR_Initialize [MIU INFO] miu opencreate instance at 86FE7288 with private size 80 bytes at 86FE72D0 SPI: Flash is detected (0x0C05, 0xC8, 0x40, 0x18) MDrv_SERFLASH_GetInfo() u32AccessWidth = 1 u32TotalSize = 16777216 u32SecNum = 256 u32SecSize = 65536 create instance at 86FE7328 with private size 48 bytes at 86FE7370 uboot held at [8F000000~90000000] Now running in RAM - U-Boot at: 871F0180 *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Net: No ethernet found. Set MAC default MAC: 0x0: 0x30: 0x1B: 0xBA:0x2: 0xDB Hit any key to stop autoboot: 0 offset 0x2E0000, size 0x10000 Flash is detected (0x0C05, 0xC8, 0x40, 0x18) initialization done! ERR>Invalid Ldr Sign ERR>Reading LDR sign from backup offset 0x80000, size 0x10000 **********************LOADER_INFO********************* @DF.0 #1.0 $1.0 ^1.5 *17 ************************************************************ SSS eLOADER 21:28:06 Mar 1 2018 ************************************************************ CPS SZE[1740] MAIN.C 2484> Checking for key sequence... enInvokemode:0 M.c 712> USB_(0) Check USB port[0]: [USB] usb_lowlevel_init++ [USB] USB EHCI LIB VER: 2014.10.02 [USB] Port 0 is Enabled [USB] TV_usb_init (UTMI Init) ++ [USB] UTMI Base BF207500 [USB] UHC Base BF204800 [USB] USBC Base BF200E00 [USB] BC Base BF240A00 [USB] TV_usb_init-- [USB] Usb_host_Init++ [USB] Async base addr: 0xA7E1A100 [USB] Reg 0x28: 0xA100 0xA7E1 [USB] disable run [USB] Host Speed:2 [USB] enable aynch [USB] Usb_host_Init-- [USB] FAILED [USB] usb_lowlevel_init--[0] scanning bus for devices... [USB] control1 max:40 [USB] interface[0] conf:1 value FF: 1 USB Device(s) found M.c 716>USB_0_Init_Success offset 0xDC0000, size 0x10000 Marker read success Marker [0xFFFFFFFF] mode[0] Jumping to Application... MsBoot.c E-1174>APP CRC Check..!! offset 0x2E8000, size 0x8000 offset 0x300000, size 0x4B29FC APP CRC Success... Decompression OK! MSBOOT.C 1196-E> Decompression OK[Go] disable interrupts ## Starting application at 0x80000224 ...
Beyond this output the interface goes silent and the unit begins the application boot process.
You will notice this unit is using the U-boot bootloader. In fact, this is M-boot, a proprietary version for MStar chipsets that combines U-boot with a first stage bootloader called S-boot (more on this later). More information on M-boot can be found a the following link (https://mstar.fandom.com/wiki/MBoot). I've also found source code and documentation for a version of M-boot at the following GitHub, (https://github.com/neuschaefer/mstar-mboot/tree/master), although unlikely this is the exact version we are using here.
You will notice the following line:
Hit any key to stop autoboot: 0
This implies we can interrupt the boot process and get a U-boot shell. The make this happen, power cycle the unit and continuously press a keyboard key, you will quickly get the bootloader finishing on a shell prompt:
UART_115200 AC_FLOW [23456789A][23456789A][3456789AB][3456789AB]-6677 ...Read more »
Create an account to leave a comment. Already have an account? Log In.
Thanks, I'm intrigued to see what's possible. With the flash storage being so small it's likely we'll only manage to boot a custom OS from USB stick, unless we can compress it somehow. The lack of documentation on the SoC will preset a challenge also, I haven't yet managed to find anything for this specific SoC, only more general stuff on the MStar platform.
Become a member to follow this project and never miss any updates
Nice project. I am looking forward to seeing where this goes. It seems like a cool platform for experimentation with the MSTAR hardware.