Skip to content

Commit

Permalink
- Support Swiss on PicoBoot flash.
Browse files Browse the repository at this point in the history
  • Loading branch information
Extrems committed Aug 11, 2024
1 parent 1283d5d commit 9d47705
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 24 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ else
DOLLZ = $(BUILDTOOLS)/dollz3
DOL2GCI = $(BUILDTOOLS)/dol2gci
endif
DOL2IPL = $(BUILDTOOLS)/dol2ipl.py

ifneq ($(shell which mkisofs),)
MKISOFS = mkisofs
Expand All @@ -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
Expand Down Expand Up @@ -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
173 changes: 173 additions & 0 deletions buildtools/dol2ipl.py
Original file line number Diff line number Diff line change
@@ -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]} <output> <executable>")
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())
12 changes: 10 additions & 2 deletions cube/packer/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)

#---------------------------------------------------------------------------------
Expand Down
43 changes: 22 additions & 21 deletions cube/packer/source/xz/xz.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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.
*/
Expand All @@ -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.
Expand Down

0 comments on commit 9d47705

Please sign in to comment.