From 9d477055c3785283e60c5d5a1da701a5aa3882ca Mon Sep 17 00:00:00 2001 From: Extrems Date: Sun, 11 Aug 2024 17:24:08 -0400 Subject: [PATCH] - Support Swiss on PicoBoot flash. --- Makefile | 9 +- buildtools/dol2ipl.py | 173 +++++++++++++++++++++++++++++++++++++ cube/packer/Makefile | 12 ++- cube/packer/source/xz/xz.h | 43 ++++----- 4 files changed, 213 insertions(+), 24 deletions(-) create mode 100755 buildtools/dol2ipl.py diff --git a/Makefile b/Makefile index 75e3fc3c..fdf8b607 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ else DOLLZ = $(BUILDTOOLS)/dollz3 DOL2GCI = $(BUILDTOOLS)/dol2gci endif +DOL2IPL = $(BUILDTOOLS)/dol2ipl.py ifneq ($(shell which mkisofs),) MKISOFS = mkisofs @@ -36,7 +37,7 @@ GECKOSERVER = pc/usbgecko .NOTPARALLEL: # Ready to go .7z file with every type of DOL we can think of -all: clean compile-patches compile compile-packer build recovery-iso build-gci build-AR build-geckoserver package +all: clean compile-patches compile compile-packer build recovery-iso build-gci build-AR build-geckoserver build-pico package # For dev use only, avoid the unnecessary fluff dev: clean compile-patches compile @@ -143,3 +144,9 @@ build-geckoserver: @cd $(GECKOSERVER) && $(MAKE) @mkdir $(DIST)/USBGeckoRemoteServer @mv $(GECKOSERVER)/swissserver* $(DIST)/USBGeckoRemoteServer/ + +#------------------------------------------------------------------ + +build-pico: + @mkdir $(DIST)/PicoBoot + @$(DOL2IPL) $(DIST)/PicoBoot/$(SVN_REVISION).uf2 $(PACKER)/reboot.dol diff --git a/buildtools/dol2ipl.py b/buildtools/dol2ipl.py new file mode 100755 index 00000000..ae2df295 --- /dev/null +++ b/buildtools/dol2ipl.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 + +import math +import struct +import sys + +# bootrom descrambler reversed by segher +def scramble(data, *, qoobsx=False): + acc = 0 + nacc = 0 + + t = 0x2953 + u = 0xD9C2 + v = 0x3FF1 + + x = 1 + + if qoobsx: + # Qoob SX scrambling starts at 0x210000 + # Use a custom initialization vector to speed things up + acc = 0xCB + nacc = 0 + + t = 0x9E57 + u = 0xC7C5 + v = 0x2EED + + x = 1 + + it = 0 + while it < len(data): + t0 = t & 1 + t1 = (t >> 1) & 1 + u0 = u & 1 + u1 = (u >> 1) & 1 + v0 = v & 1 + + x ^= t1 ^ v0 + x ^= u0 | u1 + x ^= (t0 ^ u1 ^ v0) & (t0 ^ u0) + + if t0 == u0: + v >>= 1 + if v0: + v ^= 0xB3D0 + + if t0 == 0: + u >>= 1 + if u0: + u ^= 0xFB10 + + t >>= 1 + if t0: + t ^= 0xA740 + + nacc = (nacc + 1) % 256 + acc = (acc * 2 + x) % 256 + if nacc == 8: + data[it] ^= acc + nacc = 0 + it += 1 + + return data + +def flatten_dol(data): + header = struct.unpack(">64I", data[:256]) + offsets = header[:18] + addresses = header[18:36] + sizes = header[36:54] + bss_address = header[54] + bss_size = header[55] + entry = header[56] + + dol_min = min(a for a in addresses if a) + dol_max = max(a + s for a, s in zip(addresses, sizes)) + + img = bytearray(dol_max - dol_min) + + for offset, address, size in zip(offsets, addresses, sizes): + img[address - dol_min:address + size - dol_min] = data[offset:offset + size] + + # Entry point, load address, memory image + return entry, dol_min, img + +def pack_uf2(data, base_address): + ret = bytearray() + + seq = 0 + addr = base_address + chunk_size = 256 + total_chunks = math.ceil(len(data) / chunk_size) + while data: + chunk = data[:chunk_size] + data = data[chunk_size:] + + ret += struct.pack( + "< 8I 476B I", + 0x0A324655, # Magic 1 "UF2\n" + 0x9E5D5157, # Magic 2 + 0x00002000, # Flags (family ID present) + addr, + chunk_size, + seq, + total_chunks, + 0xE48BFF56, # Board family: Raspberry Pi RP2040 + *chunk.ljust(476, b"\x00"), + 0x0AB16F30, # Final magic + ) + + seq += 1 + addr += chunk_size + + return ret + +def main(): + if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} ") + return 1 + + output = sys.argv[1] + executable = sys.argv[2] + + with open(executable, "rb") as f: + exe = bytearray(f.read()) + + if executable.endswith(".dol"): + entry, load, img = flatten_dol(exe) + entry &= 0x017FFFFF + entry |= 0x80000000 + load &= 0x017FFFFF + size = len(img) + + print(f"Entry point: 0x{entry:0{8}X}") + print(f"Load address: 0x{load:0{8}X}") + print(f"Image size: {size} bytes ({size // 1024}K)") + elif executable.endswith(".elf"): + pass + else: + print("Unknown input format") + return -1 + + if output.endswith(".uf2"): + if entry != 0x81300000 or load != 0x01300000: + print("Invalid entry point and base address (must be 0x81300000)") + return -1 + + img = scramble(bytearray(0x720) + img)[0x720:] + + align_size = 4 + img = ( + img.ljust(math.ceil(len(img) / align_size) * align_size, b"\x00") + + b"PICO" + ) + + header_size = 32 + header = struct.pack( + "> 8B I 20x", + *b"IPLBOOT ", + len(img) + header_size, + ) + assert len(header) == header_size + + out = pack_uf2(header + img, 0x10080000) + + else: + print("Unknown output format") + return -1 + + with open(output, "wb") as f: + f.write(out) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/cube/packer/Makefile b/cube/packer/Makefile index 60df4ae8..94a03b4d 100644 --- a/cube/packer/Makefile +++ b/cube/packer/Makefile @@ -51,6 +51,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR))) export OUTPUT := $(CURDIR)/$(TARGET) export SDLOADER := $(CURDIR)/SDLOADER +export REBOOT := $(CURDIR)/reboot export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) @@ -97,17 +98,18 @@ export LIBPATHS := -L$(LIBOGC_LIB) $(foreach dir,$(LIBDIRS),-L$(dir)/lib) export OUTPUT := $(CURDIR)/$(TARGET) export SDLOADER := $(CURDIR)/SDLOADER +export REBOOT := $(CURDIR)/reboot .PHONY: $(BUILD) clean #--------------------------------------------------------------------------------- $(BUILD): @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile $(OUTPUT).dol $(SDLOADER).BIN + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile $(OUTPUT).dol $(SDLOADER).BIN $(REBOOT).dol #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol $(SDLOADER).ELF $(SDLOADER).BIN + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol $(SDLOADER).ELF $(SDLOADER).BIN $(REBOOT).elf $(REBOOT).dol #--------------------------------------------------------------------------------- run: @@ -165,6 +167,12 @@ $(SDLOADER).ELF : $(OFILES) $(SILENTMSG) linking ... $(notdir $@) $(SILENTCMD)$(LD) $^ $(LDFLAGS) -Wl,--section-start,.init=0x01700000 $(LIBPATHS) $(LIBS) -o $@ +#--------------------------------------------------------------------------------- +$(REBOOT).elf : $(OFILES) +#--------------------------------------------------------------------------------- + $(SILENTMSG) linking ... $(notdir $@) + $(SILENTCMD)$(LD) $^ $(LDFLAGS) -Wl,--section-start,.init=0x01300000 $(LIBPATHS) $(LIBS) -o $@ + -include $(DEPENDS) #--------------------------------------------------------------------------------- diff --git a/cube/packer/source/xz/xz.h b/cube/packer/source/xz/xz.h index 0bf42825..d7628bad 100644 --- a/cube/packer/source/xz/xz.h +++ b/cube/packer/source/xz/xz.h @@ -146,7 +146,7 @@ struct xz_buf { size_t out_size; }; -/** +/* * struct xz_dec - Opaque type to hold the XZ decoder state */ struct xz_dec; @@ -278,23 +278,33 @@ XZ_EXTERN void xz_dec_reset(struct xz_dec *s); */ XZ_EXTERN void xz_dec_end(struct xz_dec *s); -/* - * Decompressor for MicroLZMA, an LZMA variant with a very minimal header. - * See xz_dec_microlzma_alloc() below for details. +/** + * DOC: MicroLZMA decompressor + * + * This MicroLZMA header format was created for use in EROFS but may be used + * by others too. **In most cases one needs the XZ APIs above instead.** + * + * The compressed format supported by this decoder is a raw LZMA stream + * whose first byte (always 0x00) has been replaced with bitwise-negation + * of the LZMA properties (lc/lp/pb) byte. For example, if lc/lp/pb is + * 3/0/2, the first byte is 0xA2. This way the first byte can never be 0x00. + * Just like with LZMA2, lc + lp <= 4 must be true. The LZMA end-of-stream + * marker must not be used. The unused values are reserved for future use. * * These functions aren't used or available in preboot code and thus aren't * marked with XZ_EXTERN. This avoids warnings about static functions that * are never defined. */ -/** + +/* * struct xz_dec_microlzma - Opaque type to hold the MicroLZMA decoder state */ struct xz_dec_microlzma; /** * xz_dec_microlzma_alloc() - Allocate memory for the MicroLZMA decoder - * @mode XZ_SINGLE or XZ_PREALLOC - * @dict_size LZMA dictionary size. This must be at least 4 KiB and + * @mode: XZ_SINGLE or XZ_PREALLOC + * @dict_size: LZMA dictionary size. This must be at least 4 KiB and * at most 3 GiB. * * In contrast to xz_dec_init(), this function only allocates the memory @@ -307,30 +317,21 @@ struct xz_dec_microlzma; * On success, xz_dec_microlzma_alloc() returns a pointer to * struct xz_dec_microlzma. If memory allocation fails or * dict_size is invalid, NULL is returned. - * - * The compressed format supported by this decoder is a raw LZMA stream - * whose first byte (always 0x00) has been replaced with bitwise-negation - * of the LZMA properties (lc/lp/pb) byte. For example, if lc/lp/pb is - * 3/0/2, the first byte is 0xA2. This way the first byte can never be 0x00. - * Just like with LZMA2, lc + lp <= 4 must be true. The LZMA end-of-stream - * marker must not be used. The unused values are reserved for future use. - * This MicroLZMA header format was created for use in EROFS but may be used - * by others too. */ extern struct xz_dec_microlzma *xz_dec_microlzma_alloc(enum xz_mode mode, uint32_t dict_size); /** * xz_dec_microlzma_reset() - Reset the MicroLZMA decoder state - * @s Decoder state allocated using xz_dec_microlzma_alloc() - * @comp_size Compressed size of the input stream - * @uncomp_size Uncompressed size of the input stream. A value smaller + * @s: Decoder state allocated using xz_dec_microlzma_alloc() + * @comp_size: Compressed size of the input stream + * @uncomp_size: Uncompressed size of the input stream. A value smaller * than the real uncompressed size of the input stream can * be specified if uncomp_size_is_exact is set to false. * uncomp_size can never be set to a value larger than the * expected real uncompressed size because it would eventually * result in XZ_DATA_ERROR. - * @uncomp_size_is_exact This is an int instead of bool to avoid + * @uncomp_size_is_exact: This is an int instead of bool to avoid * requiring stdbool.h. This should normally be set to true. * When this is set to false, error detection is weaker. */ @@ -340,7 +341,7 @@ extern void xz_dec_microlzma_reset(struct xz_dec_microlzma *s, /** * xz_dec_microlzma_run() - Run the MicroLZMA decoder - * @s Decoder state initialized using xz_dec_microlzma_reset() + * @s: Decoder state initialized using xz_dec_microlzma_reset() * @b: Input and output buffers * * This works similarly to xz_dec_run() with a few important differences.