Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 23 additions & 32 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,35 +108,27 @@ The kernel still needs the blob (see "Hand-off to the kernel").
`0x60004004` (128 vs 64 MiB). `dram_init()` reads the same word back.
- **Byte order.** The flash controller packs bytes MSB first. U-Boot, the
ROM and the vendor kernel all move whole little-endian words through it, so
the chip holds every 32-bit word byte-swapped, and a camera dump reads as
big-endian ("GK7101" in a hex editor).
- `output/u-boot-<soc>-nor.bin` is **in chip order**: one file for every
way it gets onto a camera.
- A programmer writes it as it is; qemu takes it as `flash-file=` as it
is.
- `sf write` at offset 0 of a buffer starting with the byte-swapped magic
swaps it back first. The vendor U-Boot does this (`check_magic_number()`
in its `gk7101_sflash.c`), and so does `gk_sflash.c`. `run ub` from
either bootloader therefore writes exactly the bytes a programmer
would. A CPU-order image (the SDK's prebuilts) is written unchanged.
- `gk_sflash.c` swaps the boot image's span on read as well (the image
found at offset 0 at probe, or the one last written there). Every
caller (`sf read`, SD recovery's "already on the flash" check) sees
the boot image as the file. Everything past it is the controller's
plain round trip.
- A whole-chip dump in chip order (a programmer's) restores with one
`sf write` at 0: a write at 0 that starts with the swapped magic is
swapped over its whole length. `sf update` splits it into sectors and
only the boot image comes out right. `sf read` of the whole chip is
not a dump either: the boot image in chip order, the rest in CPU
order.
- Exception: the first release (2026-10-18) wrote CPU-order images and
its `gk_sflash.c` has no swap check. `run ub` on a camera running it
would write a chip-order image swapped twice and brick it. Upgrade
those cameras with a programmer.
- Anything else written to flash (kernel, rootfs, environment) is in CPU
order through the drivers; only a raw dump or programmer image of the
whole chip is swapped.
the chip holds every 32-bit word byte-swapped, everywhere: boot image,
env, kernel, rootfs. A programmer's dump reads as big-endian ("GK7101" in
a hex editor, "sqsh" for a squashfs).
- **Every published file is in CPU order**: `u-boot-<soc>-nor.bin`, as
`gk710x-image.py` writes it, like OpenIPC/firmware's uImage and rootfs.
They load by tftp, mmc or usb and go to flash with `sf write` or SD
recovery as they are. Through the drivers the flash reads and writes in
CPU order; `gk_sflash.c` never swaps a read.
- **Programmer and qemu `flash-file=` images are in chip order**, the
whole image: lay the CPU-order files out at their offsets, then swap
every word, `objcopy -I binary -O binary --reverse-bytes=4 full.bin
programmer.bin` (the openipc branch's README has the full recipe). The
same command turns a programmer's dump back into CPU order. Writing
the files with a programmer as they are puts them on the chip
unswapped: the ROM finds no boot header, U-Boot no kernel.
- A programmer image written through the driver anyway (`sf write`, SD
recovery) is recognised by the swapped magic at 0 and swapped back: the
boot image however the caller splits it, the whole write when one write
covers more. `sf update` of a whole chip-order image therefore only gets
the boot image right. The vendor U-Boot's flash write does the same
(`check_magic_number()` in its `gk7101_sflash.c`).
- `gk_sflash.c` keeps the word mapping for unaligned offsets as well:
whole words only, writes padded with `0xff`.

Expand Down Expand Up @@ -272,9 +264,8 @@ bash build.sh gk7102 # one or more SoCs (CROSS_COMPILE defaults to arm-li

The emulator is widgetii/qemu-hisilicon, machines `gk7101` and `gk7102`
(`docs/goke-gk710x.md` there). `-M gk7102,flash-file=<image>` does the boot
ROM's job from the header, so it boots exactly the flashed bytes. The
release image goes in as it is; firmware parts written behind it (uImage,
rootfs) must be word-swapped, since the chip holds them that way.
ROM's job from the header, so it boots exactly the flashed bytes: a
programmer image (the whole file word-swapped, see "Byte order").

- Guest `reset` re-enters the U-Boot qemu loaded at start-up, not what is in
flash now. To check a newly written bootloader, start qemu again on the
Expand Down
68 changes: 24 additions & 44 deletions drivers/mtd/spi/gk_sflash.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,30 +160,28 @@ static int sf_program(u32 addr, const u8 *buf, unsigned int words)
#define GK_BOOT_MAGIC 0x474b3731

/*
* The boot ROM reads the boot image as raw chip bytes, while everything
* read or written through the controller is packed MSB first: a 32-bit
* word goes to the chip byte-swapped and comes back swapped again. For the
* kernel, the rootfs and the environment that round trip is invisible.
* For the boot image it is not: a programmer writes it in the chip's
* order, and the vendor U-Boot's flash write recognises such an image by
* its byte-swapped magic and swaps it back (check_magic_number() in its
* gk7101_sflash.c).
* The controller packs bytes MSB first: every 32-bit word goes to the chip
* byte-swapped and comes back swapped again, so through this driver the
* flash reads and writes in CPU order, the order of every published file
* (U-Boot, kernel, rootfs) and of what tftp, mmc or usb load. The chip
* itself, which the boot ROM and a programmer see, holds every word
* swapped; a programmer image is the files laid out and every word
* swapped, and a dump swapped back is in CPU order.
*
* This driver does the same, both ways, for every caller: from offset 0
* to sf_swap_end -- header table plus the U-Boot length the header gives
* -- data is swapped on write and on read, so the boot image reads back
* as the file a programmer writes, byte for byte. The window is the image
* on the flash at probe, and a write at 0 moves it: one that starts with
* the swapped magic opens it as long as that image, however the caller
* splits the rest; any other write at 0 closes it.
*
* A write at 0 that starts with the swapped magic and runs past the boot
* image -- a programmer's whole-chip dump in one sf write, or recovery
* image -- is chip order throughout, and is swapped over its whole
* length. Split into sectors (sf update), only its first sector is seen
* at 0, so only the boot image is swapped back.
* A programmer image may still be written through here (sf write, SD
* recovery). A write at 0 that starts with the swapped boot magic is
* taken for one and swapped back: over sf_swap_end -- header table plus
* the U-Boot length the header gives -- however the caller splits the
* rest, and over its whole length when that one write is longer (a
* whole-chip image). Any other write at 0 closes the window, and so does
* a write elsewhere that does not start where the last one ended: the
* window carries over only the pieces of one image written in a row, so a
* later sf update whose unchanged first sector is skipped does not inherit
* it. The vendor U-Boot's flash write does the same swap
* (check_magic_number() in its gk7101_sflash.c). Reads are never swapped.
*/
static u32 sf_swap_end;
static loff_t sf_swap_next; /* where the last write ended */

/* The length of a chip-order boot image starting at buf, or 0 */
static u32 sf_boot_len(const u_char *buf, size_t len)
Expand Down Expand Up @@ -213,8 +211,11 @@ static u32 sf_boot_len(const u_char *buf, size_t len)

static void sf_swap_detect(loff_t to, size_t len, const u_char *buf)
{
if (to)
if (to) {
if (to != sf_swap_next)
sf_swap_end = 0;
return;
}
sf_swap_end = sf_boot_len(buf, len);
if (sf_swap_end)
printf("SF: boot image in the chip's byte order, swapping it back\n");
Expand All @@ -239,10 +240,6 @@ static int gk_sf_read(struct mtd_info *mtd, loff_t from, size_t len,
u32 w = readl(SF_DATA);
unsigned int n = min_t(size_t, len, 4 - skip);

if (addr < sf_swap_end)
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
w = swab32(w);
addr += 4;

if (!skip && n == 4) {
put_unaligned_le32(w, buf);
} else {
Expand Down Expand Up @@ -275,6 +272,7 @@ static int gk_sf_write(struct mtd_info *mtd, loff_t to, size_t len,
if (to + len > mtd->size)
return -EINVAL;
sf_swap_detect(to, len, buf);
sf_swap_next = to + len;
end = !to && sf_swap_end ? max_t(u32, sf_swap_end, len) : sf_swap_end;
*retlen = 0;
while (len) {
Expand Down Expand Up @@ -320,21 +318,6 @@ static int gk_sf_erase(struct mtd_info *mtd, struct erase_info *instr)
static struct spi_flash gk_flash;
static int gk_announced;

/* The swap window over the boot image already on the flash */
static void sf_swap_probe(u32 size)
{
u8 hdr[SZ_2K];
size_t n;
unsigned int i;

sf_swap_end = 0;
gk_sf_read(&gk_flash.mtd, 0, sizeof(hdr), &n, hdr);
for (i = 0; i < sizeof(hdr); i += 4)
put_unaligned_le32(swab32(get_unaligned_le32(hdr + i)),
hdr + i);
sf_swap_end = min(sf_boot_len(hdr, sizeof(hdr)), size);
}

struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
Expand Down Expand Up @@ -376,9 +359,6 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
mtd->_write = gk_sf_write;
mtd->_erase = gk_sf_erase;
mtd->priv = flash;
/* Once: later writes at 0 keep the window up to date */
if (!gk_announced)
sf_swap_probe(size);

/* Once: the environment and common/openipc.c probe again and again */
if (!gk_announced++)
Expand Down
13 changes: 6 additions & 7 deletions openipc/gk710x-image.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
words ("version:", "ddrtest:") and the table ("setup:", "rdcom:",
"delay:"), as the header of the vendor image it came from has them.

The flash controller packs bytes MSB first, so the chip holds every 32-bit
word byte-swapped (drivers/mtd/spi/gk_sflash.c). The output is in that
chip order: a programmer writes it as it is, qemu takes it as flash, and
sf write -- the vendor U-Boot's and this one's -- recognises the swapped
magic at offset 0 and swaps it back.
The output is in CPU order, like every other published file: tftp, mmc or
usb load it and sf write puts it at 0. The chip holds every 32-bit word
byte-swapped (drivers/mtd/spi/gk_sflash.c), so a programmer, or qemu's
flash-file, needs every word swapped (the openipc branch's README shows
how).
"""
import re
import struct
Expand Down Expand Up @@ -78,8 +78,7 @@ def main():
sys.exit('%s: %d bytes, %d over the %d bytes before the environment'
% (dst, len(image), len(image) - limit, limit))
with open(dst, 'wb') as f:
f.write(b''.join(image[i:i + 4][::-1]
for i in range(0, len(image), 4)))
f.write(image)


if __name__ == '__main__':
Expand Down
Loading