Is the source code for the boot rom for the VisionFive 2 available?

Interesting. I recall it generated trashy code even for x86, but I also used it as radare2 plugin, maybe that’s why. But my current work is heavily bound to r2dec anyway, so I’ll check it out for future projects I plan.


28608 bytes=0x6FC0

2A006FA0: 0700 0000 0800 0000 0804 0804 0100 F8BF  ................
2A006FB0: 0002 0000 0000 0040 0002 0000 0000 FFFF  .......@........
2A006FC0: FFFF 0000 0000 7F7F 7F7F 7F7F 7F7F 0000  ................
2A006FD0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
2A006FE0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

To me it looks like you have truncated some bytes. I probably would have called the end 0x2A006FCF which would be a file size of 28624 bytes.

1 Like

Do you notice any strong similarity between the following snipets of code:
From u-boot/drivers/mmc/mmc.c

if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
  pr_debug("%s: Failed to read blocks\n", __func__);
  return 0;
 blocks_todo -= cur;
 start += cur;
 dst += cur * mmc->read_bl_len;

gcc C source code generated by Ghidra from the dumped BOOTROM’s machine code.

 if (uVar7 != uVar1) {
   FUN_2a0012a6((byte *)"%s: Failed to read blocks\r\n",
         (ulong *)"mmc_bread",plVar4,lVar5,param_4,uVar7,param_5,param_6);
   return 0;
  uVar8 -= uVar1;
  param_1 += uVar1;
  param_3 += uVar1 * _DAT_01100904;

They are not exactly the same, but they are extremely similar. More similar than random chance alone. Enough to make me believe that there definitely is unreleased GPL source code used inside the BOOTROM.

I have to say that I’m shocked at the quality of code generated by Ghidra (even though it is not perfect). I achieved in minutes what would have taken me days or weeks if I was using pencil and paper. But the downside of using such a powerful tool is that you actually learn far less low level knowledge (Most of which I have gained thanks to backward engineering 6502 and 8086 machine code many many years ago, in the dark days long before the Internet was accessible by many).


Indeed some interesting finds! I had previously looked into but haven’t really bothered looking at the SPI/eMMC/etc drivers, as I assumed they did what I expect them to.

The part that really interested me, however, seems to be that it would read some of the one-time-programmable bits, which could enable a ‘secure-boot’ mode. The cryptography was implemented with the ‘security engine’ and by referencing the addresses and commands with those found in the StarFive-provided Linux drivers. I was reasonably sure that in secure-boot mode there would be extra data found in the various otherwise unused bytes in the SPL header, like a hash and signature or something, and it would be decrypted/verified with AES-256-CBC and ECDSA-SHA256.

Given how StarFive seemed to be guarding the secrets of their secure boot mechanism (see: source code, document behavior · Issue #1 · starfive-tech/Tools · GitHub) I think StarFive would be guarding the source code of their ROM as well. Which is unfortunate.

In the non-secure-boot case though, as far as I can tell there really isn’t much going on. It is pretty much just the bare minimum to read from the boot device and jumping to it. And the driver code is probably all somewhere in u-boot. So if you’re not looking into permanently modifying your chip by setting the OTP to secure-boot mode, I don’t think we’ve been missing much.


I can see one reason why:

"In terms of the smart gas pipeline system and network security, StarFive and WINICSSEC, based on JH7110, will jointly develop industrial Internet security products with fully independent IPR. At present, this product is in field testing and will be promoted to cover urban gas. The product empowers Towngas Smart Energy to strengthen cyber security protection of the gas network, and consolidate data security for critical facilities.

Dan Xi, VP for Engineering Quality and Construction of Towngas, said that Towngas Smart Energy operates pipeline gas projects in more than 300 cities and towns across Mainland China, with more than 40 million customers. Applying JH7110 to the company’s industrial-grade cyber security system can ensure information security for its critical facility - the operation platform. The application strengthens the security level to realize a safe, reliable, and stable gas supply to ensure the implementation of smart gas projects."

For security systems the algorithms should all be public so that the can be audited (security through obscurity has always failed +++) the only thing that should be protected are the keys.

+++ EDIT: A recent example would be TETRA:BURST. Where in public TErrestrial Trunked RAdio has been referred to as 80-bit encryption key-length (~1.2 x 10^24 keys) for nearly the the past 30 years, in reality after the secret TEA1 (European commercial Use), TEA2 (European public safety organisations - police, emergency services), TEA3 (Public safety organizations outside EU) and TEA4 (Commercial organisations outside EU) encryption algorithms where all finally dumped and backward engineered using zero-day exploits it was finally revealed to be effectively 32-bit encryption key-length (4,294,967,296 keys), which can be brute forced after capturing only four packets in less than a minute using a laptop with a medium to high end GPU via OpenCL (Ultimately the secret encryption algorithms were deliberately broken by design, to allow government agencies a backdoor to provide the ability to access “confidential” communications). :person_facepalming: :woman_facepalming: :man_facepalming:


Damn ghidra is so huge, but it’s results are quite useful. At least with these, I’ll pinpoint “public” functions (borrowed gpl code) cause almost all functions I disassembled are unnamed and not tied to anything.

1 Like

Running Ghidra, in Windows->Memory Map
I defined from 0x2a000000 to 0x2a006784 Length 0x6785 as readable and executable (code)

I defined from 0x2a006785 to 0x2a006fd0 Length 0x84c as readable (data). I then manually went through all of the data and defined any obvious strings of text as strings.

I initially defined from 0x2a006fd1 to 0x2a007fff Length 0x102f as not readable, not writable, not executable (null). And then I generated a new firmware with all the null data truncated, because it was only a distraction.


Although I’m amazed by amount of work put into today’s disassemblers (and compared to proprietary tools of previous era like IDA Pro HexRays back in 2008 when I started getting interested in this topic), jugding by ghidra output I feel it’s still very far from what I would like it to be.

Same obscure and very hard readable teeth-crushing casts-through-casts like *(int *)(long), mixed together with decompiler messy naming of intermediates like uVar1 (why they just can’t adopt some common programming alphabet around or even use register names?). Forget about decompiling libgcc/gcc jumptables right, these register calls confuse everyone. Meh, I’ve seen this already with r2ghidra, it appears just decompiler extracted directly from it.

On the bright side, they seem to get function parameters counting done right, so I can polish my c-asm output there. And some strings references. But these I would get done even with my bare hands anyway.

EDIT I was wrong and I have to admit that ghidra is quite valuable tool once you start reconstructing the logic of function (refactoring all the goto’s into readable if-else blocks). Once you provide all the required information, it starts doing some other magic like dereferencing function pointers and at least trying to get through jumptab garbage. Often not correct, but something at least.

Also, if you compare two pieces of code with reference source you have on the hands (like those GPL functions we’ve got stuck into this bootrom), it actually helps to sieve them out so they will not annoy you.

Still, the default naming of variables can make you mad, but it’s to your taste.

Modern code don’t even needs to be much obfuscated. Want obfuscate the code and release the dump? Just compile it with insane optimization switches, then pass it through decompiler and attach some C-asm header. Remember to turn off jumptabs with -fno-jump-tables or it won’t run.


i find myself continuously referring back to the raw assembly output when when I see something odd.

e.g references to memory addresses like 0x1100???, 0x16000???, 0x170????, 0x400?????, 0x800?????, etc.

I suspect that I need to define more blocks of memory to make Ghidra slightly more happy. Most of which at a guess become inaccessible after the BOOTROM hands over control to the next boot stage. Or at best are remapped to somewhere else at different memory addresses.


Funny thing is that most of these addressess are freely available even after OpenSBI. Even if 0x40000000 is not accessible in normal mode after OpenSBI had explicitly protected it, it is still accessible from M-mode (from U-Boot SPL) once you patch OpenSBI out or write your own micro SPL in place of U-Boot SPL, so I believe every address is freely accessible once BootROM gives up control to SPL. That’s good.

Even more: earlier when I received my boards in Dec22, I discovered that 0x40000000 is actually simply readonly-aliased to 0x240000000 in S-mode at least on my two 8GB boards of different revisions, v1.2a and v1.3b, and you can see OpenSBI code contents at this address! (this address wrapping phenomena is common on embedded actually)

0x01100000 is not listed anywhere in TRM nor in U74 reference tho. Strange, because it contains some data actually and physically ends at 0x01102000 after that, load fault occur. But BootROM is riddled with this offset almost everywhere!

0x16000000 and 0x17000000 are normally accessible from S-mode.

0x01700000 also accessible from S-mode and 0x80000000 is normally an S-mode boot base, also accessible.

I keep digging into BootROM, slowly. Just locating strings and standard GPL functions for now, plus µ-printf at 0x2a0012a6. Fixed quite fundamental bugs in r2dec on my side. Hopefully there are no any restrictions left, and I’ll finally reconstruct it to plain C code someday.


Interesting thread.

I think its quite common for soc`s to not protect anything in non secure boot mode. I reverse engineered allwinner H3 secure boot, and in that chip it was the same.

When secure mode bit was set in OTP flash, a completely different boot rom was mapped to the reset vector for chip start up. It locked down all the sensible things for later boot stages (including itself).

Luckily there is a bug, and I could dump the secure boot rom.


Yeah, VF2 and based on JH7110 are development boards so what’s the point of locking it away.

To me all that secure boot fuss is quite useless. Anything man made can be broken by someone else. There should be open and community reviewed mechanism if you want real security, like it’s done with cryptographic ciphers today, period. That said, I’m glad you found the bug.


is it possible to have the datasheet for AXP15060 outside China?

1 Like

There is a very old revision of the document (0.1 2018-04-10) at


states that S7 is actually used for BootROM. (at very bottom of the page). My suspect is that 0x01100000 is start of physical ITIM/DTIM region of S7 core. The TRM hints about that, but probably incomplete, listing only DTIM. 0x01100000 contains function pointers (interrupt vector table?) into main BootROM or CLINT.

SHA256 of my dump from there is be17f6ea8c7d6f7ad6dd6f64e6e4218da5cc81172fe3982a268a6b2b7e446fb2, and it’s same for different board revisions. Somewhere in middle it contains some randomly looking data.

Someone would like to check if S7 is actually executing something.


I think, one has to look such things on the grand scale of things too. Just a small example from the everyday world of politics in my home country Austria: In 2019 we had Corona lockdown and all our human rights kicked with feet. The friends of our government cabinets of first Kurz (discharged dishonorably from office, the first one to happen so in the second republic of Austria) and Nehammer, were emptying the state budget leaving a lot of really needy people die in the dirt and wrecking the public health systems by replacing a good health care system with World War I Lazarett technology. Then came Ukraine and now the government Nehammer has hardened the laws for child abuse, sexual abuse, is permanently reorganising security police and state intelligence, every second day we have a femicide or family massacre in news headlines and still government Nehammer has not resigned and its members are not yet dangling down from ropes in front of the chancellors office. When Nehammer speaks in public, he is barely able to utter a meaningful sentence, much less connecting some words together to build a sentence. Our president Alexander van der Bellen gives his human rights and government critical speeches somewhere in the outskirts aso. This seems quite representative of what is going on here in this discussion about compromised and vermin-infested boot procedure. Like this it happens in all governments around the world nowadays…

Related: Bootloader recovery tool

1 Like

@RcL : waves hand… these are not the firmware sources you are looking for.

This thread is about the boot rom; even lower level than U-Boot and SBI; the first code that runs and loads U-Boot & co. from disk.

1 Like

0x40000000 is the start of DRAM. 0x240000000 is the uncached alias of DRAM. Max supported DRAM size is 8G, as you can see the size between start of DRAM and start of DRAM alias is 8G. This is a trick to avoid keeping flushing caches when talk to some devices who does non-cache-coherent DMA.

I believe we can pretty much rule out any access between 0x40000000 to 0x440000000, because before SPL, DRAM is not yet initialized, and BootROM should not access that.

1 Like