=============================================================================== HOW TO BUILD PsionLX YOURSELF =============================================================================== Two separate things happen here, and they are worth keeping apart: A. BUILDING THE CF IMAGE from a root filesystem tree. Fast. Minutes. No root privileges needed. You need this if you want to change anything at all. B. COMPILING APPLICATIONS for the netBook Pro. Slow. This machine is ARM OABI, which no modern toolchain produces, so an entire ARM system is booted under emulation and the compiler runs natively inside it. You only need this if you want to rebuild or add a program. Everything was developed on macOS on Apple Silicon. Linux works too and is easier, since you can skip the Homebrew e2fsprogs step. =============================================================================== A. BUILDING THE CF IMAGE =============================================================================== ------------------------------------------------------------------------------- WHAT YOU NEED ------------------------------------------------------------------------------- * python3 * e2fsprogs -- specifically mke2fs, debugfs and e2fsck On macOS: brew install e2fsprogs (it installs to /opt/homebrew/opt/e2fsprogs/sbin, which is where the script looks; edit the E= line in scripts/mkimage.sh if yours differs) On Linux: already present; point E= at /sbin You do NOT need root. That is deliberate and it is why this builds on a Mac at all: macOS can neither mount ext2 nor chown files to foreign user IDs, so the filesystem is populated and then edited directly with debugfs instead. ------------------------------------------------------------------------------- LAY IT OUT ------------------------------------------------------------------------------- The scripts expect the same layout the project used. From this folder: mkdir -p ~/psionlx/workspace/card cp -a scripts ~/psionlx/workspace/ cp -a card-tree/root ~/psionlx/workspace/card/root cp -a card-tree/mknods.debugfs ~/psionlx/workspace/card/ and put a copy of the FAT boot partition at workspace/card/boot.fat. The simplest way to get one is to take it out of a released image: dd if=psionlx-cf-1gb.img of=boot.fat bs=512 skip=2048 count=143360 ------------------------------------------------------------------------------- BUILD IT ------------------------------------------------------------------------------- cd ~/psionlx/workspace scripts/mkimage.sh That is the entire procedure. It regenerates file ownership, builds the ext2 root from card/root, assembles the partitioned image, verifies several things, and writes psionlx-cf-1gb.img with a checksum. To build the as-photographed variant instead: PSION_CARD_DIR=card-archival \ PSION_IMAGE_NAME=psionlx-cf-1gb-asphotographed.img \ scripts/mkimage.sh TO CHANGE ANYTHING, EDIT card/root AND RUN IT AGAIN. That tree *is* the root filesystem. ------------------------------------------------------------------------------- WHAT IT PRODUCES ------------------------------------------------------------------------------- Type Start LBA Sectors Size MBR -- 0 1 512 B Partition 1 0x06 FAT16, bootable 2048 143,360 70 MB Partition 2 0x83 Linux ext2 145,408 1,856,480 906.5 MB Exactly 1,024,966,656 bytes: the full capacity of a 1 GB card, with nothing overrunning and nothing wasted. CHS fields are computed with H=16, S=32. That is the geometry the bootloader expects, and this machine's firmware is old enough to read them, so they matter. ext2 is created with: -b 1024 -O ^resize_inode,^dir_index Both features are switched off deliberately. The 2.6.9-era kernel on this device predates them and will refuse to mount the filesystem otherwise. This is not tidiness; get it wrong and the machine will not boot. ------------------------------------------------------------------------------- OWNERSHIP -- READ THIS BEFORE CHANGING ANYTHING ------------------------------------------------------------------------------- The ownership rule the build applies: /home/lx and everything below it 1000:1000 /home/gpe and everything below it 100:65535 everything else 0:0 /home/lx is the one that matters, and getting it wrong wastes days. 99psion-session runs the whole desktop as user lx. lx must be able to create ~/.matchbox, where 95psion-apps writes the dock, and ~/.gpe, which is gpe-confd's settings database holding the theme and the background. If /home/lx is root-owned, none of that happens and you get NO DOCK, THE DEFAULT THEME AND THE FALLBACK BACKGROUND ALL AT ONCE -- three symptoms that look like three unrelated bugs and are in fact one. Verify it, every time: debugfs -R 'stat /home/lx' card/rootfs.ext2 # must say User: 1000 Group: 1000 Equally: make sure /home/lx is otherwise EMPTY before shipping an image. A leftover mbdock.session or .gpe/settings overrides everything in /etc permanently, because both mechanisms only fall back to /etc when the user's own copy is absent. This bites repeatedly. ------------------------------------------------------------------------------- THE BOOT PARTITION, AND THE THING THAT DESTROYS MACHINES ------------------------------------------------------------------------------- Partition 1 must contain the kernel under the exact name nBkProOs.img. That is the name BooSt looks for. BooSt ALSO automatically loads and runs a file called nBkPro.img from the card, if one is present. That is a script file, and which script it is decides whether your machine survives. SAFE -- what the released images carry, 279 bytes, one line: load nBkProOS.img run FATAL -- the LX prototype script, which contains: nand -ignore erase 18 ee8 nand partition e8 It erases the internal NAND. On a standard 32 MB machine the resulting partition is too small for Windows CE to reinstall. This is not theoretical; it happened to a real machine during this project. If you build your own boot partition, READ THE nBkPro.img OUT OF THE FINISHED IMAGE AND CHECK IT before booting anything: strings boot.fat | grep -i nand # should print nothing And read the CARD, not the image file. Fixing the image does not fix a card that was already written from an older one -- a physical card written on 28 September still carried the live script eleven hours after the image had been corrected. scratchpad/mkscript.py in the project builds BooSt script images. It reuses Psion's own 256-byte header and rewrites only the payload length and the two POSIX cksum checksums, the algorithm taken from their mk-nBkProOsImg.c (in 3-PSION-SOURCES/original-images/) and verified against one of their own scripts first. =============================================================================== B. COMPILING APPLICATIONS =============================================================================== ------------------------------------------------------------------------------- WHY THIS IS AWKWARD ------------------------------------------------------------------------------- The netBook Pro is ARM OABI -- ELF e_flags 0x2, the old ABI. Modern toolchains produce EABI and nothing on a current system produces OABI at all. Debian's "arm" architecture (as opposed to "armel") was OABI, which is why Debian sarge and etch packages still run on this machine unmodified. There is a second constraint that decides everything else: the card carries libstdc++.so.6.0.3, which is GCC 3.4's C++ runtime. Anything C++ must therefore be built with GCC 3.4. Debian's own builds of, say, AbiWord want __gnu_cxx::__pool symbols from libstdc++ 4.1 and simply will not run here. So: an entire Debian etch ARM OABI system is booted as a real ARM machine under QEMU, and the compiler runs natively inside it. Slow, but correct, and it removes every cross-compilation problem at once. ------------------------------------------------------------------------------- HOW IT WORKS ------------------------------------------------------------------------------- scripts/qbuild.sh [timeout-seconds] Each run: 1. packs a FRESH chroot from arm-oabi-chroot/ into a disk image 2. boots it under qemu-system-arm -M versatilepb with the kernel and initramfs in qemu/ 3. runs your job script with its output on the serial console, captured to .log 4. recovers /build/out from the image afterwards THE CRITICAL CONSEQUENCE, AND IT HAS CAUGHT ME MORE THAN ONCE: The chroot is repacked from the template EVERY RUN, and ONLY /build/out is recovered. A "make install" in one job does not exist in the next. If a job needs a library it also builds, it must build, install and use it WITHOUT LEAVING THAT BOOT. Splitting that across two jobs produces results that look plausible and are empty -- an aspell dictionary build did exactly this three times, reporting twelve dictionaries built when every one of them was an empty 304-byte file. Anything you want to persist between jobs must be put into arm-oabi-chroot/ itself, which is the template. ------------------------------------------------------------------------------- WRITING A JOB ------------------------------------------------------------------------------- Look at jobs/ -- there are working examples for AbiWord, Gnumeric, Balsa, Firefox, Samba, aspell, Solitaire and the GPE components, each with the log it produced. They are heavily commented, mostly with the reasons behind choices that look arbitrary. The pattern: set -x rm -rf /build/out; mkdir -p /build/out/bin export CC=gcc-3.4 export CXX=g++-3.4 cd /build/ ./configure --prefix=/usr > /build/out/conf.log 2>&1 make > /build/out/make.log 2>&1 cp /build/out/bin/ echo BUILD_SCRIPT_DONE Rules learned the hard way: * THE JOB MUST NOT REDIRECT ITS OWN STDOUT. The harness watches the console for the completion marker; redirect it and the run waits for the full timeout instead of finishing. * Use Psion's own configure flags, from the matching .oe recipe in 3-PSION-SOURCES/oe-netbook/. They are not arbitrary. Passing a flag the version does not have is silently ignored by autoconf -- Psion's own abiword_2.0.11.oe passes --with-aspell, which does not exist in 2.0.11, and the result is a word processor with no spellchecker. * VERIFY THE OUTPUT IN THE JOB ITSELF, behaviourally. Check the binary's DT_NEEDED with readelf, check e_flags is 0x2, and where it makes sense actually run the thing. A build that exits 0 and produces something useless is the normal failure mode here, not the exotic one. * busybox 1.00's find has neither -maxdepth nor -perm. A find-based check silently returns nothing and a successful build looks like a failed one. Check explicit paths instead. * gcc 3.4 rejects a redundant semicolon after a member declaration at class scope. Several 2004-era trees have them. ------------------------------------------------------------------------------- WHAT IS IN THIS FOLDER ------------------------------------------------------------------------------- scripts/ mkimage.sh builds the CF image (section A) qbuild.sh runs a build job under emulation (section B) install-built.py installs build output into the card tree install-libs.py resolves and installs library dependencies mkicons.py icon generation mkbg.py the desktop background mkgconf.py builds /etc/gconf defaults from .schemas files psionise.py applies Psion's naming to .desktop files elfdeps.py ELF dependency walker elfsyms.py undefined-symbol checker guitest.sh boots the image under QEMU with a framebuffer tapswitch.sh networking for the emulated machine jobs/ Every build job used, each with its console log. card-tree/ card-tree-asphotographed/ The root filesystem trees the two images are built from, plus mknods.debugfs (the /dev node list, hand-maintained, 295 lines) and the generated chown.debugfs. arm-oabi-chroot/ The Debian etch ARM OABI build environment. 828 MB. This is the hard part to recreate and the reason it is shipped whole. debian-packages/ The Debian sarge/etch ARM OABI package cache and indexes used to satisfy build dependencies. gpe-src/ GPE sources, INCLUDING THE MODIFIED ONES. gpe-appmgr here is not upstream: it carries the reconstructed right-hand TODAY / PROGRAMS / TASKS strip and the red power button, in row_view.c. If you want to understand or improve the reconstruction, that file is where to look. built-binaries/ The compiled results, already installed into the card trees. jffs2-extracted/ The community GPE root filesystem unpacked, which is where a good deal of the base system comes from. qemu/ The host kernel and initramfs qbuild.sh boots. The large .ext2 scratch images are NOT included -- they are regenerated on every run and are 4 GB of nothing. overlay-route/ The original, superseded install method: unpack gpe-fs-cf.tar.bz2, apply psionlx-overlay.tar.gz. It predates all the application work and used matchbox-desktop as a stand-in for the launcher. Kept for reference. last-build-output/ /build/out from the final job that ran, as an example of what a job produces. =============================================================================== VERIFYING THAT THIS RELEASE BUILDS WHAT IT SHIPS =============================================================================== It does -- this was checked by staging a workspace from 4-BUILD-SYSTEM alone and building from it. But the result is NOT byte-identical to the shipped image, and that is expected rather than a problem. Here is why, and how to check it properly. WHY THE BYTES DIFFER mke2fs writes three timestamps into the superblock (filesystem created, last write, last checked) and a random directory hash seed. Two builds of an IDENTICAL tree, minutes apart, differ in about 64 KB for that reason alone. The filesystem UUID is already pinned in mkimage.sh for this reason; the timestamps cannot be pinned without an mke2fs that honours SOURCE_DATE_EPOCH, and the one used here (e2fsprogs 1.47.4) does not. So do not expect the SHA-256 of your rebuild to match SHA256SUMS.txt. It will not, and nothing is wrong. HOW TO VERIFY PROPERLY -- COMPARE THE CONTENT 1. Hash the source tree. This is the real test: it covers every file's contents, size and every symlink target. cd 4-BUILD-SYSTEM/card-tree/root find . \( -type f -o -type l \) | sort | while read f; do if [ -L "$f" ]; then printf '%s L %s\n' "$f" "$(readlink "$f")" else printf '%s F %s %s\n' "$f" "$(stat -c%s "$f")" "$(md5sum "$f" | cut -d' ' -f1)" fi done | sha256sum (on macOS use stat -f%z and md5 -q) The tree that produced the shipped images hashes to: 57e1f97c277c0ed1eaf8d9b4b6cfda65b8d3384affcfc7f63279eb53be3ef0af 2. Or compare inside the two filesystems directly: debugfs -R "stat /usr/bin/abiword" rootfs.ext2 debugfs -R "ls -l /usr/lib/cups/backend" rootfs.ext2 3. And confirm the difference really is only metadata: dumpe2fs -h yours.ext2 > a; dumpe2fs -h theirs.ext2 > b; diff a b You should see only "Filesystem created", "Last write time", "Last checked" and "Directory Hash Seed".