a9_free/doc/notes.txt

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.