373 lines
16 KiB
Plaintext
373 lines
16 KiB
Plaintext
General notes
|
|
=============
|
|
The A9 is based on a system-on-a-chip namely RDA8955, made by RDA Technologies.
|
|
It is a very powerful and versatile GSM/GPRS-capable microcontroller that
|
|
was definitely conceived for mobile use, and probably used by various mobile
|
|
phone manufacturers during the 2000's (personal opinion).
|
|
|
|
Ai-Thinker sells a variant with GPS called the A9G. According to
|
|
https://blog.zakkemble.net/remote-mail-notifier-and-gps-tracker , the A9G uses
|
|
a GK9501 GPS receiver.
|
|
|
|
Ai-Thinker distributes two packages for the A9:
|
|
- GPRS_C_SDK (https://github.com/ai-thinker-open/gprs_c_sdk): contains
|
|
.sh/.bat scripts to build user projects and demos. It also includes source
|
|
code for some libraries (cjson, vlgl and aliyun, among others) and header
|
|
files for interfacing their closed-source firmware (more on this later).
|
|
|
|
- CSDTK (official link dead, mirror https://github.com/pulkin/csdtk42-linux):
|
|
contains the binaries of modified versions of the GNU toolchain (gcc and
|
|
binutils), a bunch of tools used to communicate with the microcontrollers
|
|
namely "cooltools" and lots of configuration files, some of without clear
|
|
purpose.
|
|
|
|
Ai-Thinker seems to have stopped development of their repositories since 2018,
|
|
where the latest version was released, except from some minor commits. So this
|
|
project aims to keep maintaining the A9/A9G by creating a free (as in freedom)
|
|
version of these repositories developed by the community.
|
|
|
|
On the other hand, since Ai-Thinker are only distributing binaries of the GNU
|
|
toolchain, this means they are violating the GNU General Public License v3 by
|
|
not distribuing the source code. This has already been reported to Ai-Thinker on
|
|
Github (https://github.com/Ai-Thinker-Open/GPRS_C_SDK/issues/431) and e-mail
|
|
both to Ai-Thinker themselves and the Free Software Foundation, with no response
|
|
so far.
|
|
|
|
Two versions of the toolchain are distributed: mips-rda-elf and mips-elf. The
|
|
former relies on versions of gcc and binutils released in 2017 and the latter
|
|
from 2014. It is currently unclear the differences between both toolchains,
|
|
although GPRS_C_SDK uses mips-elf to build a project instead of mips-rda-elf.
|
|
|
|
Despite Ai-Thinker distributing so many resources, the real deal on the A9 is a
|
|
debug/release pair of closed-source firmware blobs that contain the GSM/GPRS
|
|
and GPS stacks, the bootloader, the C standard library implementation and the
|
|
implementation of all the libraries that are provided to the user. The binaries
|
|
are located at GPRS_C_SDK/platform/csdk/[debug/release]. According to
|
|
mips-elf-size, the .text section of the debug version of SW_V2129_csdk.elf is
|
|
1969176 bytes (around 1.88 MiB), so there is a lot going on in there.
|
|
|
|
Fortunately for us, the GPRS_C_SDK repository and also the blobs are under the
|
|
MIT license, which gives us freedom to reverse-engineer them. What's more, the
|
|
original authors compiled these .elf files with full debugging information, so
|
|
they can be de-compiled using a reverse-engineering tool such as NSA's Ghidra
|
|
while keeping the original symbol names.
|
|
|
|
Using this approach, we see open-source libraries such as mbedtls and lwip have
|
|
been used. A major drawback of the closed-source binary is the fact all unused
|
|
code cannot be optimized away by the linker, so if the A9 contains 4 MiB of
|
|
flash memory that leaves the user with 2.12 MiB. It could be worse, but it could
|
|
also be better.
|
|
|
|
Ghidra does a decent job at de-compiling the binaries, but sometimes it does
|
|
not figure out the names of local variables and shows many "Could not recover
|
|
jumptable at 0x88010a48. Too many branches" warning messages at the end of a
|
|
function. Also, while Ghidra allows exporting to a C source file, it apparently
|
|
only allows exporting as a *single* C source file, which might be cumbersome
|
|
for such a big project (> 14 MiB, > 530,000 lines). So it is a good idea to
|
|
compare the de-compiled code against the available documentation (more on this
|
|
later).
|
|
|
|
Memory mapping
|
|
==============================
|
|
build/app/cust.ld (modified by build.sh from
|
|
gprs_c_sdk/platform/compilation.cust.ld)
|
|
- Flash origin at 0x88000000
|
|
- User application at 0x88000000 + 0x00240000
|
|
- RAM starts at 0x82000000 + 0x00300000
|
|
|
|
void boot_Sector(UINT32 param1) is apparently the entry point,
|
|
but bcpu_main does not have any functions calling it, either.
|
|
|
|
Name Addr Off Size ES Flg Lk Inf Al
|
|
.spi_reg_debug a2000000 22d000 000980 00 WA 0 0 4
|
|
.boot_code 88010000 01f000 0010b0 00 AX 0 0 4
|
|
.bcpu_rotext 88011100 020100 000510 00 AX 0 0 4
|
|
.bcpu_rodata 88011610 020610 000010 00 A 0 0 4
|
|
.bcpu_rom_text a1e80000 007000 0137b0 00 AX 0 0 16
|
|
.bcpu_rom_rodata 81e937b0 01a7b0 0046f0 00 A 0 0 4
|
|
.bbsram 81980000 001000 001e00 00 WA 0 0 4
|
|
.bbsramu a1981e00 22ce00 001bf0 00 WA 0 0 4
|
|
.bbsram_globals 81986640 000640 0001b9 00 WA 0 0 4
|
|
.mailbox a1b00580 22c580 000280 00 WA 0 0 4
|
|
.bcpu_rom_version 81e97ff8 01eff8 000004 00 A 0 0 4
|
|
.bcpu_rom.crc 81e97ffc 01effc 000004 00 WA 0 0 1
|
|
.bbsram_patch_tex 81986800 020800 001070 00 AX 0 0 4
|
|
.bbsram_patch_dat 81987870 021870 000030 00 WA 0 0 4
|
|
.bbsram_patch_ucd a19878a0 0218a0 000320 00 WA 0 0 4
|
|
.bbsram_patch_ucb a1987bc0 22cbc0 000100 00 WA 0 0 4
|
|
.bcpu_sramtext 81c00680 022680 001450 00 AX 0 0 4
|
|
.bcpu_sramucdata a1c01ad0 023ad0 000040 00 WA 0 0 4
|
|
.bcpu_bss 82000a00 01fa00 000010 00 WA 0 0 4
|
|
.bcpu_flash_end 88013e70 023e70 000010 00 WA 0 0 1
|
|
.main_entry_secti 88013e80 023e80 000460 00 AX 0 0 4
|
|
.internal_rom 81e00000 001000 0051f0 00 WAX 0 0 128
|
|
.rom_entries_unca a1c000a0 22d0a0 0000f4 00 WA 0 0 4
|
|
.rom_entries_cach 81c00194 001194 000054 00 WA 0 0 4
|
|
.boot_rom_version 81e05ff8 006ff8 000004 00 A 0 0 4
|
|
.internal_rom.crc 81e05ffc 006ffc 000004 00 WA 0 0 1
|
|
.boot_sector_stru 81c00220 001194 00001c 00 WA 0 0 4
|
|
.boot_sector_relo 81c00274 001194 000004 00 WA 0 0 4
|
|
.boot_sector_stru 81c00278 001194 000004 00 WA 0 0 4
|
|
.fixptr 81c0027c 001194 000004 00 WA 0 0 4
|
|
.irqsram 81c00280 025280 0002e0 00 WAX 0 0 16
|
|
|
|
0xa1xx_xxxx and 0x81xx_xxxx could be mirrors. The MIPS R3000 allows using KUSEG
|
|
(0x0000_0000) and KSEG0/1 (0x8000_0000 and 0xA000_0000, respectively) with
|
|
different access permissions (user or kernel modes).
|
|
|
|
So there are apparently flash memory sections on 0xa1, 0x81 and 0x88. If
|
|
KUSEG/KSEG were used, then 0xa1 and 0x81 are essentially kernel-mode mirrors of
|
|
each other. However, I can't tell where 0x88 sections are actually pointing.
|
|
According to the documentation, the A9 features 4 MiB flash memory, and
|
|
0x8800_0000 - 0x8100_0000 > 4 MiB, so could they be two different flash chips
|
|
mapped at different addresses?
|
|
|
|
"rsoft: kuseg is the user segment, while the kernel segments are only accessible
|
|
in kernel mode and differ in caching and translation"
|
|
|
|
Compiling and linking a simple application returns undefined references to:
|
|
__stack
|
|
_fini
|
|
_init
|
|
hardware_init_hook
|
|
get_mem_info
|
|
hardware_hazard_hook
|
|
hardware_exit_hook
|
|
software_init_hook
|
|
|
|
RAM executable bootloader?
|
|
=========================
|
|
coolwatcher seems to be loading a RAM-based bootloader before flashing user
|
|
firmware. This bootloader depends on the SoC model and they are located at
|
|
csdtk42-linux/cooltools/chipgen/Modem2G/toolpool/plugins/fastpf/flash_programmers .
|
|
-> This sounds like a very sensible idea considering the whole flash might
|
|
be erased when downloading new software. That might be reason why a full
|
|
firmare (namely _B.lod) flashing is required but the .lod only containing
|
|
user firmware can be flashed afterwards to save time.
|
|
|
|
coolhost UART configuration
|
|
===========================
|
|
coolhost can only communicate successfully with the A9 using the following
|
|
UART configuration:
|
|
- Baud rate: 921600
|
|
- Data bits: 8
|
|
- Stop bits: 1
|
|
- Parity: None
|
|
- Flow control: None
|
|
Any other configuration simply fails, despite the A9 AT firmware being known for
|
|
baud rate auto detection.
|
|
|
|
Unusual compilation flags
|
|
=========================
|
|
By inspecting the steps performed by gprs_c_sdk's Makefiles (remember to remove
|
|
the -s from them), it definitely uses some peculiar compilation settings:
|
|
|
|
- First, mips-rda-elf-gcc is not used, but mips-elf-gcc.
|
|
- mips-rda-elf-gcc is based on gcc 7.1.0, whereas mips-elf-gcc is based on
|
|
gcc 4.4.2.
|
|
- -ffixed-t4 -ffixed-t5 -ffixed-t6 -ffixed-t7 -ffixed-s2 -ffixed-s3 -ffixed-s4
|
|
-ffixed-s5 -ffixed-s6 -ffixed-s7 -ffixed-fp. According to
|
|
https://stackoverflow.com/questions/35809832/is-the-flag-ffixed-reg-always-bugged-in-gcc ,
|
|
the '-ffixed-reg' flag is used to "treat the register named reg as a fixed
|
|
register; generated code should never refer to it (except perhaps as a stack
|
|
pointer)". This means CPU registers t4-t7, s2-27 and fp are reserved for some
|
|
reason.
|
|
According to https://cgi.cse.unsw.edu.au/~cs3231/doc/mips.php , registers t0-t7
|
|
are used as general-purpose temporary registers, s0-s7 as general-purpose
|
|
saved registers and fp is the frame pointer.
|
|
- Other funky compilation flags are:
|
|
-minterlink-mips16
|
|
-march=xcpu -mtune=xcpu <- Not so rare tbh, even x86 uses them
|
|
-mmemcpy
|
|
-mmips-tfile
|
|
-nostartfiles
|
|
-EL -DEL <- probably used to indicate little-endian?
|
|
-mexplicit-relocs
|
|
-fweb
|
|
-frename-registers
|
|
-mmemcpy
|
|
-mmips-tfile
|
|
-pipe
|
|
-fwide-exec-charset=UTF-16LE -fshort-wchar <- probably used for Chinese encoding
|
|
- As already shown on gprs_c_sdk/libs/utils/src, the engineers working on this
|
|
might hated or simply ignored any reasonable implementation of the C standard
|
|
library e.g.: newlib, instead writing their own wrappers around the unknown,
|
|
proprietary real-time operating running on the A9, hence the -nostdlib
|
|
-nostdinc -nodefaultlibs compilation flags.
|
|
|
|
Link process
|
|
===============
|
|
The link process has been split into two steps:
|
|
- (mips-elf-cpp) Generate a user-specific linker script namely cust.ld by
|
|
reading gprs_c_sdk/build/gpio/cust.ld and applying the following definitions
|
|
on it:
|
|
-DUSER_ROM_BASE=0xFFFFFFFF88000000+0x00240000
|
|
-DUSER_ROM_SIZE=0x00100000
|
|
-DUSER_RAM_BASE=0xFFFFFFFF82000000+0x00300000
|
|
-DUSER_RAM_SIZE=0x00100000
|
|
-DDUSE_BINUTILS_2_19=1
|
|
This is accomplished by using the -C and -P flags. -undef is also indicated,
|
|
but I do not know what it is used for. And, IMHO, I would have used
|
|
a set of predefined linker scripts with defined memory boundaries rather
|
|
than throwing in a bunch of macros.
|
|
- (mips-elf-ld) Perform the usual link operation. As expected when writing
|
|
build scripts with bash+GNU Make, --start-group/--end-group are required
|
|
since it is very hard to solve circular dependencies between libraries
|
|
by hand.
|
|
- An interesting note is the use of the -just-symbols flag, which allows
|
|
mapping unresolved symbols by using another ELF as reference. In this case,
|
|
gprs_c_sdk/platform/chip/rom/8955/lib/mem_bridge_rom_CHIP.elf is used.
|
|
As opposed to the SW_V2129_csdk.elf file pair, mem_bridge_rom_CHIP.elf
|
|
has been stripped from all debugging symbols, so Ghidra has a tougher time
|
|
trying to guess what's going on there. It's considerably smaller than the
|
|
latter (20980 bytes according to mips-elf-size), but I do not think this
|
|
is actually running on the chip since it is not burned by coolwatcher or
|
|
referenced by any other that I am aware of.
|
|
- Ghidra shows a bunch (< 100) of functions, mainly related to USB,
|
|
Ispi (regardless what that means) and UART.
|
|
|
|
LOD combination
|
|
===============
|
|
csdtk and gprs_c_sdk were designed around a set of proprietary tools, file
|
|
formats and communication protocols.
|
|
- coolwatcher and coolhost would be roughly equivalent to OpenOCD or
|
|
avrdude, but GUI-based.
|
|
- .lod files are very similar to .hex files, although IMHO poorly designed.
|
|
More on this later.
|
|
- HST (a seemingly plain old UART despite its fancy name) is used instead
|
|
of JTAG, SWD or any other kind of debug interface. However, coolwatcher
|
|
features mips-elf-insight, so it might be using the HST for source-level
|
|
debugging.
|
|
|
|
The .lod file format includes the following information:
|
|
- Optional fields prefixed by '#' (maybe comments?). This is an example of
|
|
the output generated by the gpio demo:
|
|
#$mode=flsh_spi32m
|
|
#$sectormap=(16 x 4k, 57 x 64k, 96 x 4k)
|
|
#$base=0x08000000
|
|
#$spacesize=0x08000000
|
|
#$FLSH_MODEL=flsh_spi32m
|
|
#$FLASH_SIZE=0x400000
|
|
#$RAM_SIZE=0x00265000
|
|
#$RAM_PHY_SIZE=0x00400000
|
|
#$CALIB_BASE=0x3FA000
|
|
#$FACT_SETTINGS_BASE=0x3FE000
|
|
#$CODE_BASE=0x00000000
|
|
#$USER_DATA_BASE=0x00361000
|
|
#$USER_DATA_SIZE=0x00099000
|
|
#$USER_BASE=0x00240000
|
|
#$USER_SIZE=0x00100000
|
|
- Starting address, prefixed by '@' e.g.: @01c000a0. Must be indicated
|
|
before program information, although several starting addresses can be
|
|
indicated throughout the file. See 'host_8955_flsh_spi32m_ramrun.lod'
|
|
as an example.
|
|
- Program data, with one 32-bit hex per line e.g.: '3c04d9ef'. This is
|
|
the main reason why I disliked this file format, as it tends towards
|
|
*very* long files that are difficult to open with some text editors,
|
|
as opposed to .hex or .mot, where multiple words are packed into one line.
|
|
|
|
Frame structure
|
|
===============
|
|
CID 27 readi8 at 0x00000003
|
|
Write host: AD 00 07 FF 04 03 00 00 00 8A 72
|
|
^ [ ] ^ ^ [ ] ^ ^
|
|
| [ ] | | [ ] | unknown
|
|
| [ ] | | [ ] unknown
|
|
| [ ] | | memory address? little-endian
|
|
| [ ] | unknown
|
|
| [ ] unknown (frame type?)
|
|
| frame size without header or next byte (little-endian)
|
|
header (always 0xAD)
|
|
|
|
Another example:
|
|
Offset
|
|
00|01|02|03|04|05|06|07|08|09|0a|0b|0c|0d|0e|0f
|
|
|
|
|
00 |ad 00 35 80 14 80 3c 2d 2d 20 3c 25 73 3e 20 6f ..5...<-- <%s> o
|
|
10 |6e 20 41 72 66 63 6e 20 30 78 25 78 0a 00 4c 31 n Arfcn 0x%x..L1
|
|
20 |5f 53 59 4e 43 48 5f 4e 4f 54 5f 46 4f 55 4e 44 _SYNCH_NOT_FOUND
|
|
30 |00 6a 00 00 00 00 0a 80 00
|
|
|
|
Coolwatcher reads the following decodified syntax:
|
|
"<-- <L1_SYNCH_NOT_FOUND> on Arcfn 0x6a"
|
|
|
|
Header: 0xAD
|
|
Size: 0x0035
|
|
Frame type?: 0x80
|
|
Timestamp?: 0x8014
|
|
Plain ASCII data from offset 0x06 to 0x1d
|
|
So it means parameters are specified in a printf-like syntax. In this case,
|
|
two parameters are specified:
|
|
- Null-terminated string "L1_SYNCH_NOT_FOUND"
|
|
- 32-bit integer (0x%x) with value 0x0000006a
|
|
- Unknown 32-bit integer with value 00 0a 80 00
|
|
|
|
Hardware registers
|
|
==================
|
|
The csdtk package includes a great deal of information about low-level
|
|
registers in XML format (sometimes .xmd extension is used) which could be
|
|
easily parsed and laid out in a more readable format e.g.: HTML. A WIP
|
|
application is being developed for that very same purpose under the hw_html
|
|
directory.
|
|
|
|
These files are located on csdtk/cooltools/chipgen/Modem2G/toolpool/map/8809,
|
|
where 8955_hard.xml describes low-level registers and 8955_soft_pkg.xmd
|
|
describes software structures and enums. Even C code is embedded into these
|
|
XML files, usually under the <cjoker> tag, and containing macro definitions and
|
|
function declarations. Moreover, this documentation even also includes comments
|
|
on how these low-level registers work.
|
|
|
|
coolwatcher apparently uses this information for its "Register Viewer" (which
|
|
can be accessed from the "Tools" menu), showing the layout of all low-level
|
|
registers, as well as their addresses and real-time values.
|
|
|
|
According to this, there is a rather lengthy list of peripherals featured on
|
|
the A9/RDA8955:
|
|
|
|
- ABB
|
|
- AIF
|
|
- BB
|
|
- BCPU
|
|
- CALENDAR
|
|
- CAMERA
|
|
- CHOLK
|
|
- CIPHER
|
|
- CORDIC
|
|
- CS0/CS1
|
|
- DEBUG_HOST/DEBUG_UART
|
|
- DMA
|
|
- EXCOR
|
|
- FMD
|
|
- GOUDA
|
|
- GPIO
|
|
- I2C
|
|
- IOMUX
|
|
- ITLV
|
|
- KEYPAD
|
|
- PAGE_SPY
|
|
- PMU
|
|
- PWM
|
|
- RF
|
|
- SCI
|
|
- SDMMC
|
|
- SEG_SCAN
|
|
- SPI1/2/3
|
|
- SPI_FLASH
|
|
- TCU
|
|
- TIMER
|
|
- UART
|
|
- USBC
|
|
- VITAC
|
|
|
|
It is currently unknown what some of these peripherals are used for, and some
|
|
of them are not even mentioned on the official specifications e.g.: CAMERA.
|
|
On the other hand, the same website states 2 SPI interfaces are available,
|
|
although as shown above the register viewer listed SPI1/2/3.
|
|
|
|
Dual CPU?
|
|
=========
|
|
BCPU and XCPU are mentioned in various places, which suggests a dual-CPU design.
|
|
However, https://ai-thinker-open.github.io/GPRS_C_SDK_DOC/en/hardware/a9.html
|
|
only mentions "RDA 32 bit RISC core, frequency up to 312MHz, with 4k instruction
|
|
cache, 4k data cache", so it is unknown whether two CPUs are actually present.
|