Loading boards/nas-itx/installer/nas-itx-install.cpp 0 → 100644 +280 −0 Changes for boards/nas-itx/installer/nas-itx-install.cpp: 280 added lines, 0 removed lines. Original line number Diff line number Diff line // nas-itx-install -- put FenrirOS onto the NAND of the Inventronics NAS ITX // from a running (RAM-booted) FenrirOS, through the kernel's MTD and UBI // interfaces (no mtd-utils needed). // // nas-itx-install survey // read only: the MTD partitions, their bad blocks, which blocks of // uboot/env/dtb/kernel hold data, and where a U-Boot environment // ("bootcmd=") is // nas-itx-install install <uImage> <dtb> <rootfs.ubifs> [--force] // writes the DTB to "dtb", the kernel to "kernel" and the UBIFS image // into the UBI volume "rootfs" on "fs" (which is erased and attached // anew). Refuses when a U-Boot environment or image is found in // "dtb"/"kernel" (--force overrides). Needs ubi.allow_mlc=1 (MLC NAND). // // Partition names come from the device tree (boards/nas-itx/nas-itx.dts). #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <mtd/mtd-user.h> #include <mtd/ubi-user.h> #include <string> #include <vector> struct Part { int num; std::string name; unsigned long long size; unsigned erasesize; }; static std::vector<Part> parts() { std::vector<Part> v; FILE* f = fopen("/proc/mtd", "r"); if (!f) return v; char line[256]; while (fgets(line, sizeof line, f)) { int n; unsigned long long sz; unsigned es; char name[128]; if (sscanf(line, "mtd%d: %llx %x \"%127[^\"]\"", &n, &sz, &es, name) == 4) v.push_back({n, name, sz, es}); } fclose(f); // tests (nandsim has no partition names): NAS_ITX_PARTS="dtb=1,kernel=2,fs=3" if (const char* map = getenv("NAS_ITX_PARTS")) { std::string m = map; size_t pos = 0; while (pos < m.size()) { size_t c = m.find(',', pos); if (c == std::string::npos) c = m.size(); std::string kv = m.substr(pos, c - pos); size_t eq = kv.find('='); if (eq != std::string::npos) { int n = atoi(kv.c_str() + eq + 1); for (auto& p : v) if (p.num == n) p.name = kv.substr(0, eq); } pos = c + 1; } } return v; } static const Part* find(const std::vector<Part>& v, const char* name) { for (auto& p : v) if (p.name == name) return &p; return nullptr; } static int open_mtd(const Part& p, int mode) { char dev[32]; snprintf(dev, sizeof dev, "/dev/mtd%d", p.num); int fd = open(dev, mode); if (fd < 0) fprintf(stderr, "%s: %s\n", dev, strerror(errno)); return fd; } static bool is_bad(int fd, unsigned long long off) { long long o = (long long)off; return ioctl(fd, MEMGETBADBLOCK, &o) > 0; } static bool all_ff(const unsigned char* b, size_t n) { for (size_t i = 0; i < n; i++) if (b[i] != 0xff) return false; return true; } // what a block's first page looks like static const char* classify(const unsigned char* b, size_t n) { if (all_ff(b, n)) return "empty"; if (memmem(b, n, "bootcmd=", 8) || memmem(b, n, "baudrate=", 9)) return "U-BOOT ENV"; if (b[0] == 0x27 && b[1] == 0x05 && b[2] == 0x19 && b[3] == 0x56) return "uImage"; if (b[0] == 0xd0 && b[1] == 0x0d && b[2] == 0xfe && b[3] == 0xed) return "DTB"; if (b[0] == 'U' && b[1] == 'B' && b[2] == 'I' && b[3] == '#') return "UBI"; return "data"; } static int survey(const std::vector<Part>& v, bool quiet, bool* env_or_image_in_targets) { if (env_or_image_in_targets) *env_or_image_in_targets = false; std::vector<unsigned char> page(4096); for (auto& p : v) { int fd = open_mtd(p, O_RDONLY); if (fd < 0) return 1; unsigned nblk = p.size / p.erasesize, bad = 0, used = 0; bool detail = p.name != "fs"; if (!quiet) printf("mtd%d %-7s %6llu KiB, %u blocks\n", p.num, p.name.c_str(), p.size >> 10, nblk); for (unsigned b = 0; b < nblk; b++) { unsigned long long off = (unsigned long long)b * p.erasesize; if (is_bad(fd, off)) { bad++; if (!quiet && (detail || bad <= 40)) printf(" block %4u (0x%08llx): bad\n", b, off); continue; } if (!detail && b > 8) continue; // fs: only its start ssize_t r = pread(fd, page.data(), page.size(), (off_t)off); const char* what = r == (ssize_t)page.size() ? classify(page.data(), page.size()) : "UNREADABLE"; if (strcmp(what, "empty")) used++; if (!quiet && strcmp(what, "empty")) printf(" block %4u (0x%08llx): %s\n", b, off, what); // the vendor U-Boot's environment must not be overwritten if (env_or_image_in_targets && (p.name == "dtb" || p.name == "kernel") && !strcmp(what, "U-BOOT ENV")) *env_or_image_in_targets = true; } if (!quiet) printf(" %u bad, %u with data%s\n", bad, used, detail ? "" : " (first 9 blocks checked)"); close(fd); } return 0; } static bool erase_part(const Part& p, int fd) { unsigned nblk = p.size / p.erasesize, n = 0; for (unsigned b = 0; b < nblk; b++) { unsigned long long off = (unsigned long long)b * p.erasesize; if (is_bad(fd, off)) continue; struct erase_info_user64 e = { off, p.erasesize }; if (ioctl(fd, MEMERASE64, &e) < 0) { fprintf(stderr, "erase %s block %u: %s -- marking it bad\n", p.name.c_str(), b, strerror(errno)); long long o = (long long)off; ioctl(fd, MEMSETBADBLOCK, &o); continue; } n++; } printf(" %s: %u blocks erased\n", p.name.c_str(), n); return true; } static bool read_file(const char* path, std::vector<unsigned char>& data) { int fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", path, strerror(errno)); return false; } struct stat st; fstat(fd, &st); data.resize(st.st_size); size_t got = 0; while (got < data.size()) { ssize_t r = read(fd, data.data() + got, data.size() - got); if (r <= 0) { close(fd); return false; } got += r; } close(fd); return true; } // raw image into a partition, skipping bad blocks (as U-Boot's nand read // does), padded with 0xff to whole pages; read back and compared static bool write_raw(const Part& p, const std::vector<unsigned char>& img) { int fd = open_mtd(p, O_RDWR); if (fd < 0) return false; struct mtd_info_user mi; ioctl(fd, MEMGETINFO, &mi); unsigned pg = mi.writesize; erase_part(p, fd); size_t done = 0; unsigned long long off = 0; std::vector<unsigned char> blk(p.erasesize), back(p.erasesize); while (done < img.size()) { if (off >= p.size) { fprintf(stderr, "%s: image does not fit\n", p.name.c_str()); close(fd); return false; } if (is_bad(fd, off)) { off += p.erasesize; continue; } size_t n = img.size() - done < p.erasesize ? img.size() - done : p.erasesize; memset(blk.data(), 0xff, blk.size()); memcpy(blk.data(), img.data() + done, n); size_t wn = (n + pg - 1) / pg * pg; if (pwrite(fd, blk.data(), wn, (off_t)off) != (ssize_t)wn || pread(fd, back.data(), wn, (off_t)off) != (ssize_t)wn || memcmp(blk.data(), back.data(), wn)) { fprintf(stderr, "%s: write/verify at 0x%llx failed: %s\n", p.name.c_str(), off, strerror(errno)); close(fd); return false; } done += n; off += p.erasesize; } printf(" %s: %zu bytes written and verified\n", p.name.c_str(), img.size()); close(fd); return true; } static long sysfs_long(const char* path) { FILE* f = fopen(path, "r"); long v = -1; if (f) { if (fscanf(f, "%ld", &v) != 1) v = -1; fclose(f); } return v; } static bool write_ubi(const Part& p, const std::vector<unsigned char>& img) { int fd = open_mtd(p, O_RDWR); if (fd < 0) return false; erase_part(p, fd); // empty flash: UBI formats it on attach close(fd); int ctl = open("/dev/ubi_ctrl", O_RDONLY); if (ctl < 0) { perror("/dev/ubi_ctrl"); return false; } struct ubi_attach_req at; memset(&at, 0, sizeof at); at.ubi_num = UBI_DEV_NUM_AUTO; at.mtd_num = p.num; if (ioctl(ctl, UBI_IOCATT, &at) < 0) { fprintf(stderr, "attach mtd%d: %s (MLC NAND: boot with ubi.allow_mlc=1)\n", p.num, strerror(errno)); return false; } int ubi = at.ubi_num; printf(" fs: attached as ubi%d\n", ubi); char path[96]; snprintf(path, sizeof path, "/sys/class/ubi/ubi%d/avail_eraseblocks", ubi); long avail = sysfs_long(path); snprintf(path, sizeof path, "/sys/class/ubi/ubi%d/eraseblock_size", ubi); long leb = sysfs_long(path); snprintf(path, sizeof path, "/dev/ubi%d", ubi); int ufd = open(path, O_RDONLY); struct ubi_mkvol_req mk; memset(&mk, 0, sizeof mk); mk.vol_id = 0; mk.alignment = 1; mk.bytes = (long long)avail * leb; mk.vol_type = UBI_DYNAMIC_VOLUME; strcpy(mk.name, "rootfs"); mk.name_len = 6; if (ufd < 0 || avail <= 0 || ioctl(ufd, UBI_IOCMKVOL, &mk) < 0) { fprintf(stderr, "ubi%d: creating volume rootfs (%ld LEBs): %s\n", ubi, avail, strerror(errno)); return false; } close(ufd); printf(" fs: volume rootfs, %ld LEBs (%lld MiB)\n", avail, mk.bytes >> 20); snprintf(path, sizeof path, "/dev/ubi%d_0", ubi); int vfd = open(path, O_RDWR); long long bytes = img.size(); if (vfd < 0 || ioctl(vfd, UBI_IOCVOLUP, &bytes) < 0) { fprintf(stderr, "%s: volume update: %s\n", path, strerror(errno)); return false; } size_t done = 0; while (done < img.size()) { size_t n = img.size() - done > 1 << 20 ? 1 << 20 : img.size() - done; ssize_t w = write(vfd, img.data() + done, n); if (w <= 0) { fprintf(stderr, "%s: write: %s\n", path, strerror(errno)); return false; } done += w; } close(vfd); vfd = open(path, O_RDONLY); std::vector<unsigned char> back(img.size()); done = 0; while (done < back.size()) { ssize_t r = read(vfd, back.data() + done, back.size() - done); if (r <= 0) break; done += r; } close(vfd); if (done != img.size() || memcmp(back.data(), img.data(), img.size())) { fprintf(stderr, "%s: verify failed\n", path); return false; } printf(" fs: rootfs.ubifs (%zu bytes) written and verified\n", img.size()); int32_t n = ubi; if (ioctl(ctl, UBI_IOCDET, &n) < 0) perror("detach"); close(ctl); return true; } int main(int argc, char** argv) { std::vector<Part> v = parts(); if (v.empty()) { fprintf(stderr, "no MTD partitions in /proc/mtd\n"); return 1; } if (argc >= 2 && !strcmp(argv[1], "survey")) return survey(v, false, nullptr); if (argc >= 5 && !strcmp(argv[1], "install")) { bool force = argc >= 6 && !strcmp(argv[5], "--force"); const Part *dtb = find(v, "dtb"), *ker = find(v, "kernel"), *fs = find(v, "fs"); if (!dtb || !ker || !fs) { fprintf(stderr, "partitions dtb/kernel/fs missing\n"); return 1; } bool hit = false; survey(v, true, &hit); if (hit && !force) { fprintf(stderr, "a U-Boot environment lies in dtb/kernel -- see 'survey'; --force overrides\n"); return 1; } std::vector<unsigned char> k, d, r; if (!read_file(argv[2], k) || !read_file(argv[3], d) || !read_file(argv[4], r)) return 1; if (k.size() < 4 || k[0] != 0x27 || k[1] != 0x05) { fprintf(stderr, "%s: no uImage\n", argv[2]); return 1; } if (d.size() < 4 || d[0] != 0xd0 || d[1] != 0x0d) { fprintf(stderr, "%s: no DTB\n", argv[3]); return 1; } printf("installing:\n"); if (!write_raw(*dtb, d) || !write_raw(*ker, k) || !write_ubi(*fs, r)) return 1; printf("done. U-Boot: nand read <addr> 0x%llx 0x%llx (kernel), 0x%llx 0x%llx (dtb)\n", 0x300000ULL, (unsigned long long)k.size(), 0x200000ULL, (unsigned long long)d.size()); return 0; } fprintf(stderr, "usage: %s survey | install <uImage> <dtb> <rootfs.ubifs> [--force]\n", argv[0]); return 2; } boards/nas-itx/kernel.config +10 −0 Changes for boards/nas-itx/kernel.config: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -22,6 +22,14 @@ CONFIG_TOOLCHAIN_DEFAULT_CPU=y CONFIG_CRASH_DUMP=n CONFIG_RELOCATABLE=n # The vendor U-Boot (2012.07) of the board maps the DDR at 0x40000000 # (bdinfo: memstart 0x40000000, memsize 0x7d000000), not at 0: the kernel # is linked for that physical start (not relocatable, see above); the uImage # wrapper takes load/entry address from it CONFIG_ADVANCED_OPTIONS=y CONFIG_PHYSICAL_START_BOOL=y CONFIG_PHYSICAL_START=0x40000000 # --- console --- CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y Loading Loading @@ -127,6 +135,8 @@ CONFIG_TCM_IBLOCK=m CONFIG_TCM_FILEIO=m CONFIG_ISCSI_TARGET=m CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_NAT=m CONFIG_NF_TABLES=m CONFIG_NF_TABLES_INET=y CONFIG_NF_TABLES_IPV4=y Loading build-cross.sh +28 −2 Changes for build-cross.sh: 28 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ # 8. rootfs the root file system tree # 9. image UBI image (UBIFS volume "rootfs") for the board's NAND, # plus uImage and DTB # 10. installer RAM-bootable installer (initramfs as U-Boot ramdisk) with # boards/<board>/installer/<board>-install and the images # # Usage: ./build-cross.sh [step...] (default: all steps in order) # ./build-cross.sh clean Loading Loading @@ -487,6 +489,30 @@ EOF EOF } # installer: a RAM-bootable FenrirOS (initramfs as U-Boot ramdisk) with # nas-itx-install and the images, for boards whose U-Boot can only TFTP # (the vendor U-Boot of the NAS ITX has no UBI) step_installer() { [[ -f $OUT/rootfs.ubifs && -d $ROOTFS/usr ]] || die "run 'rootfs' and 'image' first" [[ -f $BOARD_DIR/installer/$BOARD-install.cpp ]] || die "no installer for $BOARD" command -v mkimage >/dev/null || die "mkimage (u-boot-tools) missing" msg "installer: $BOARD-install" run installer "$TOOLCHAIN/bin/$TRIPLE-c++" -std=c++23 -O2 -Wall \ -o "$OUT/$BOARD-install" "$BOARD_DIR/installer/$BOARD-install.cpp" local ir="$BUILD/initramfs" rm -rf --one-file-system "$ir" cp -a "$ROOTFS" "$ir" mkdir -p "$ir/root/install" cp "$OUT/$BOARD-install" "$ir$PREFIX/sbin/" cp "$OUT/$KIMAGE" "$OUT/$BOARD.dtb" "$OUT/rootfs.ubifs" "$ir/root/install/" # the root is the initramfs itself: syslite must not remount anything msg "installer: initramfs ($(du -sh "$ir" | cut -f1))" (cd "$ir" && find . -print0 | cpio --null -o -H newc -R 0:0 --quiet) | zstd -q -19 -T0 >"$OUT/installer.cpio.zst" run installer mkimage -A "$KARCH" -O linux -T ramdisk -C none -n "FenrirOS $BOARD installer" \ -d "$OUT/installer.cpio.zst" "$OUT/installer.uimg" msg "installer ready: $OUT/installer.uimg ($(du -h "$OUT/installer.uimg" | cut -f1))" } step_clean() { msg "removing $WORK (keeping $SRCDIR)" rm -rf --one-file-system "$WORK" Loading @@ -494,13 +520,13 @@ step_clean() { # --------------------------------------------------------------------------- ALL_STEPS=(headers systempp builtins toolchain fresh syslite kernel rootfs image) ALL_STEPS=(headers systempp builtins toolchain fresh syslite kernel rootfs image installer) steps=("$@") ((${#steps[@]})) || steps=("${ALL_STEPS[@]}") for s in "${steps[@]}"; do case "$s" in headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image) headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer) check_deps; mkdir -p "$WORK" "$BUILD"; "step_$s" ;; clean) step_clean ;; -h|--help) sed -n '2,/^$/s/^# \{0,1\}//p' "$0"; exit 0 ;; Loading src/linux/arch/powerpc/include/bits/socket.h 0 → 100644 +18 −0 Changes for src/linux/arch/powerpc/include/bits/socket.h: 18 added lines, 0 removed lines. Original line number Diff line number Diff line #pragma once /* powerpc numbers of the SOL_SOCKET options that differ from asm-generic * (arch/powerpc/include/uapi/asm/socket.h): LOWAT, TIMEO and the credential * options are 16-21 in another order. systempp's struct timeval/timespec * have a 64-bit time_t on 32-bit powerpc too, so the timeout and timestamp * options are the time64 ones (Linux 5.1+): SO_RCVTIMEO_NEW etc. take the * kernel's __kernel_sock_timeval {s64, s64} -- exactly struct timeval. */ #define SO_RCVLOWAT 16 #define SO_SNDLOWAT 17 #define SO_RCVTIMEO_OLD 18 #define SO_SNDTIMEO_OLD 19 #define SO_PASSCRED 20 #define SO_PEERCRED 21 #define SO_RCVTIMEO 66 /* SO_RCVTIMEO_NEW */ #define SO_SNDTIMEO 67 /* SO_SNDTIMEO_NEW */ #define SO_TIMESTAMP 63 /* SO_TIMESTAMP_NEW */ #define SO_TIMESTAMPNS 64 /* SO_TIMESTAMPNS_NEW */ #define SO_TIMESTAMPING 65 /* SO_TIMESTAMPING_NEW */ src/linux/arch/x86_64/include/bits/socket.h 0 → 100644 +10 −0 Changes for src/linux/arch/x86_64/include/bits/socket.h: 10 added lines, 0 removed lines. Original line number Diff line number Diff line #pragma once /* x86_64 (asm-generic) numbers of the SOL_SOCKET options that differ * between architectures; struct timeval is the kernel's 64-bit one, so the * classic SO_RCVTIMEO/SO_SNDTIMEO/SO_TIMESTAMP* apply. */ #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 #define SO_SNDLOWAT 19 #define SO_RCVTIMEO 20 #define SO_SNDTIMEO 21 Loading
boards/nas-itx/installer/nas-itx-install.cpp 0 → 100644 +280 −0 Changes for boards/nas-itx/installer/nas-itx-install.cpp: 280 added lines, 0 removed lines. Original line number Diff line number Diff line // nas-itx-install -- put FenrirOS onto the NAND of the Inventronics NAS ITX // from a running (RAM-booted) FenrirOS, through the kernel's MTD and UBI // interfaces (no mtd-utils needed). // // nas-itx-install survey // read only: the MTD partitions, their bad blocks, which blocks of // uboot/env/dtb/kernel hold data, and where a U-Boot environment // ("bootcmd=") is // nas-itx-install install <uImage> <dtb> <rootfs.ubifs> [--force] // writes the DTB to "dtb", the kernel to "kernel" and the UBIFS image // into the UBI volume "rootfs" on "fs" (which is erased and attached // anew). Refuses when a U-Boot environment or image is found in // "dtb"/"kernel" (--force overrides). Needs ubi.allow_mlc=1 (MLC NAND). // // Partition names come from the device tree (boards/nas-itx/nas-itx.dts). #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <mtd/mtd-user.h> #include <mtd/ubi-user.h> #include <string> #include <vector> struct Part { int num; std::string name; unsigned long long size; unsigned erasesize; }; static std::vector<Part> parts() { std::vector<Part> v; FILE* f = fopen("/proc/mtd", "r"); if (!f) return v; char line[256]; while (fgets(line, sizeof line, f)) { int n; unsigned long long sz; unsigned es; char name[128]; if (sscanf(line, "mtd%d: %llx %x \"%127[^\"]\"", &n, &sz, &es, name) == 4) v.push_back({n, name, sz, es}); } fclose(f); // tests (nandsim has no partition names): NAS_ITX_PARTS="dtb=1,kernel=2,fs=3" if (const char* map = getenv("NAS_ITX_PARTS")) { std::string m = map; size_t pos = 0; while (pos < m.size()) { size_t c = m.find(',', pos); if (c == std::string::npos) c = m.size(); std::string kv = m.substr(pos, c - pos); size_t eq = kv.find('='); if (eq != std::string::npos) { int n = atoi(kv.c_str() + eq + 1); for (auto& p : v) if (p.num == n) p.name = kv.substr(0, eq); } pos = c + 1; } } return v; } static const Part* find(const std::vector<Part>& v, const char* name) { for (auto& p : v) if (p.name == name) return &p; return nullptr; } static int open_mtd(const Part& p, int mode) { char dev[32]; snprintf(dev, sizeof dev, "/dev/mtd%d", p.num); int fd = open(dev, mode); if (fd < 0) fprintf(stderr, "%s: %s\n", dev, strerror(errno)); return fd; } static bool is_bad(int fd, unsigned long long off) { long long o = (long long)off; return ioctl(fd, MEMGETBADBLOCK, &o) > 0; } static bool all_ff(const unsigned char* b, size_t n) { for (size_t i = 0; i < n; i++) if (b[i] != 0xff) return false; return true; } // what a block's first page looks like static const char* classify(const unsigned char* b, size_t n) { if (all_ff(b, n)) return "empty"; if (memmem(b, n, "bootcmd=", 8) || memmem(b, n, "baudrate=", 9)) return "U-BOOT ENV"; if (b[0] == 0x27 && b[1] == 0x05 && b[2] == 0x19 && b[3] == 0x56) return "uImage"; if (b[0] == 0xd0 && b[1] == 0x0d && b[2] == 0xfe && b[3] == 0xed) return "DTB"; if (b[0] == 'U' && b[1] == 'B' && b[2] == 'I' && b[3] == '#') return "UBI"; return "data"; } static int survey(const std::vector<Part>& v, bool quiet, bool* env_or_image_in_targets) { if (env_or_image_in_targets) *env_or_image_in_targets = false; std::vector<unsigned char> page(4096); for (auto& p : v) { int fd = open_mtd(p, O_RDONLY); if (fd < 0) return 1; unsigned nblk = p.size / p.erasesize, bad = 0, used = 0; bool detail = p.name != "fs"; if (!quiet) printf("mtd%d %-7s %6llu KiB, %u blocks\n", p.num, p.name.c_str(), p.size >> 10, nblk); for (unsigned b = 0; b < nblk; b++) { unsigned long long off = (unsigned long long)b * p.erasesize; if (is_bad(fd, off)) { bad++; if (!quiet && (detail || bad <= 40)) printf(" block %4u (0x%08llx): bad\n", b, off); continue; } if (!detail && b > 8) continue; // fs: only its start ssize_t r = pread(fd, page.data(), page.size(), (off_t)off); const char* what = r == (ssize_t)page.size() ? classify(page.data(), page.size()) : "UNREADABLE"; if (strcmp(what, "empty")) used++; if (!quiet && strcmp(what, "empty")) printf(" block %4u (0x%08llx): %s\n", b, off, what); // the vendor U-Boot's environment must not be overwritten if (env_or_image_in_targets && (p.name == "dtb" || p.name == "kernel") && !strcmp(what, "U-BOOT ENV")) *env_or_image_in_targets = true; } if (!quiet) printf(" %u bad, %u with data%s\n", bad, used, detail ? "" : " (first 9 blocks checked)"); close(fd); } return 0; } static bool erase_part(const Part& p, int fd) { unsigned nblk = p.size / p.erasesize, n = 0; for (unsigned b = 0; b < nblk; b++) { unsigned long long off = (unsigned long long)b * p.erasesize; if (is_bad(fd, off)) continue; struct erase_info_user64 e = { off, p.erasesize }; if (ioctl(fd, MEMERASE64, &e) < 0) { fprintf(stderr, "erase %s block %u: %s -- marking it bad\n", p.name.c_str(), b, strerror(errno)); long long o = (long long)off; ioctl(fd, MEMSETBADBLOCK, &o); continue; } n++; } printf(" %s: %u blocks erased\n", p.name.c_str(), n); return true; } static bool read_file(const char* path, std::vector<unsigned char>& data) { int fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", path, strerror(errno)); return false; } struct stat st; fstat(fd, &st); data.resize(st.st_size); size_t got = 0; while (got < data.size()) { ssize_t r = read(fd, data.data() + got, data.size() - got); if (r <= 0) { close(fd); return false; } got += r; } close(fd); return true; } // raw image into a partition, skipping bad blocks (as U-Boot's nand read // does), padded with 0xff to whole pages; read back and compared static bool write_raw(const Part& p, const std::vector<unsigned char>& img) { int fd = open_mtd(p, O_RDWR); if (fd < 0) return false; struct mtd_info_user mi; ioctl(fd, MEMGETINFO, &mi); unsigned pg = mi.writesize; erase_part(p, fd); size_t done = 0; unsigned long long off = 0; std::vector<unsigned char> blk(p.erasesize), back(p.erasesize); while (done < img.size()) { if (off >= p.size) { fprintf(stderr, "%s: image does not fit\n", p.name.c_str()); close(fd); return false; } if (is_bad(fd, off)) { off += p.erasesize; continue; } size_t n = img.size() - done < p.erasesize ? img.size() - done : p.erasesize; memset(blk.data(), 0xff, blk.size()); memcpy(blk.data(), img.data() + done, n); size_t wn = (n + pg - 1) / pg * pg; if (pwrite(fd, blk.data(), wn, (off_t)off) != (ssize_t)wn || pread(fd, back.data(), wn, (off_t)off) != (ssize_t)wn || memcmp(blk.data(), back.data(), wn)) { fprintf(stderr, "%s: write/verify at 0x%llx failed: %s\n", p.name.c_str(), off, strerror(errno)); close(fd); return false; } done += n; off += p.erasesize; } printf(" %s: %zu bytes written and verified\n", p.name.c_str(), img.size()); close(fd); return true; } static long sysfs_long(const char* path) { FILE* f = fopen(path, "r"); long v = -1; if (f) { if (fscanf(f, "%ld", &v) != 1) v = -1; fclose(f); } return v; } static bool write_ubi(const Part& p, const std::vector<unsigned char>& img) { int fd = open_mtd(p, O_RDWR); if (fd < 0) return false; erase_part(p, fd); // empty flash: UBI formats it on attach close(fd); int ctl = open("/dev/ubi_ctrl", O_RDONLY); if (ctl < 0) { perror("/dev/ubi_ctrl"); return false; } struct ubi_attach_req at; memset(&at, 0, sizeof at); at.ubi_num = UBI_DEV_NUM_AUTO; at.mtd_num = p.num; if (ioctl(ctl, UBI_IOCATT, &at) < 0) { fprintf(stderr, "attach mtd%d: %s (MLC NAND: boot with ubi.allow_mlc=1)\n", p.num, strerror(errno)); return false; } int ubi = at.ubi_num; printf(" fs: attached as ubi%d\n", ubi); char path[96]; snprintf(path, sizeof path, "/sys/class/ubi/ubi%d/avail_eraseblocks", ubi); long avail = sysfs_long(path); snprintf(path, sizeof path, "/sys/class/ubi/ubi%d/eraseblock_size", ubi); long leb = sysfs_long(path); snprintf(path, sizeof path, "/dev/ubi%d", ubi); int ufd = open(path, O_RDONLY); struct ubi_mkvol_req mk; memset(&mk, 0, sizeof mk); mk.vol_id = 0; mk.alignment = 1; mk.bytes = (long long)avail * leb; mk.vol_type = UBI_DYNAMIC_VOLUME; strcpy(mk.name, "rootfs"); mk.name_len = 6; if (ufd < 0 || avail <= 0 || ioctl(ufd, UBI_IOCMKVOL, &mk) < 0) { fprintf(stderr, "ubi%d: creating volume rootfs (%ld LEBs): %s\n", ubi, avail, strerror(errno)); return false; } close(ufd); printf(" fs: volume rootfs, %ld LEBs (%lld MiB)\n", avail, mk.bytes >> 20); snprintf(path, sizeof path, "/dev/ubi%d_0", ubi); int vfd = open(path, O_RDWR); long long bytes = img.size(); if (vfd < 0 || ioctl(vfd, UBI_IOCVOLUP, &bytes) < 0) { fprintf(stderr, "%s: volume update: %s\n", path, strerror(errno)); return false; } size_t done = 0; while (done < img.size()) { size_t n = img.size() - done > 1 << 20 ? 1 << 20 : img.size() - done; ssize_t w = write(vfd, img.data() + done, n); if (w <= 0) { fprintf(stderr, "%s: write: %s\n", path, strerror(errno)); return false; } done += w; } close(vfd); vfd = open(path, O_RDONLY); std::vector<unsigned char> back(img.size()); done = 0; while (done < back.size()) { ssize_t r = read(vfd, back.data() + done, back.size() - done); if (r <= 0) break; done += r; } close(vfd); if (done != img.size() || memcmp(back.data(), img.data(), img.size())) { fprintf(stderr, "%s: verify failed\n", path); return false; } printf(" fs: rootfs.ubifs (%zu bytes) written and verified\n", img.size()); int32_t n = ubi; if (ioctl(ctl, UBI_IOCDET, &n) < 0) perror("detach"); close(ctl); return true; } int main(int argc, char** argv) { std::vector<Part> v = parts(); if (v.empty()) { fprintf(stderr, "no MTD partitions in /proc/mtd\n"); return 1; } if (argc >= 2 && !strcmp(argv[1], "survey")) return survey(v, false, nullptr); if (argc >= 5 && !strcmp(argv[1], "install")) { bool force = argc >= 6 && !strcmp(argv[5], "--force"); const Part *dtb = find(v, "dtb"), *ker = find(v, "kernel"), *fs = find(v, "fs"); if (!dtb || !ker || !fs) { fprintf(stderr, "partitions dtb/kernel/fs missing\n"); return 1; } bool hit = false; survey(v, true, &hit); if (hit && !force) { fprintf(stderr, "a U-Boot environment lies in dtb/kernel -- see 'survey'; --force overrides\n"); return 1; } std::vector<unsigned char> k, d, r; if (!read_file(argv[2], k) || !read_file(argv[3], d) || !read_file(argv[4], r)) return 1; if (k.size() < 4 || k[0] != 0x27 || k[1] != 0x05) { fprintf(stderr, "%s: no uImage\n", argv[2]); return 1; } if (d.size() < 4 || d[0] != 0xd0 || d[1] != 0x0d) { fprintf(stderr, "%s: no DTB\n", argv[3]); return 1; } printf("installing:\n"); if (!write_raw(*dtb, d) || !write_raw(*ker, k) || !write_ubi(*fs, r)) return 1; printf("done. U-Boot: nand read <addr> 0x%llx 0x%llx (kernel), 0x%llx 0x%llx (dtb)\n", 0x300000ULL, (unsigned long long)k.size(), 0x200000ULL, (unsigned long long)d.size()); return 0; } fprintf(stderr, "usage: %s survey | install <uImage> <dtb> <rootfs.ubifs> [--force]\n", argv[0]); return 2; }
boards/nas-itx/kernel.config +10 −0 Changes for boards/nas-itx/kernel.config: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -22,6 +22,14 @@ CONFIG_TOOLCHAIN_DEFAULT_CPU=y CONFIG_CRASH_DUMP=n CONFIG_RELOCATABLE=n # The vendor U-Boot (2012.07) of the board maps the DDR at 0x40000000 # (bdinfo: memstart 0x40000000, memsize 0x7d000000), not at 0: the kernel # is linked for that physical start (not relocatable, see above); the uImage # wrapper takes load/entry address from it CONFIG_ADVANCED_OPTIONS=y CONFIG_PHYSICAL_START_BOOL=y CONFIG_PHYSICAL_START=0x40000000 # --- console --- CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y Loading Loading @@ -127,6 +135,8 @@ CONFIG_TCM_IBLOCK=m CONFIG_TCM_FILEIO=m CONFIG_ISCSI_TARGET=m CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_NAT=m CONFIG_NF_TABLES=m CONFIG_NF_TABLES_INET=y CONFIG_NF_TABLES_IPV4=y Loading
build-cross.sh +28 −2 Changes for build-cross.sh: 28 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ # 8. rootfs the root file system tree # 9. image UBI image (UBIFS volume "rootfs") for the board's NAND, # plus uImage and DTB # 10. installer RAM-bootable installer (initramfs as U-Boot ramdisk) with # boards/<board>/installer/<board>-install and the images # # Usage: ./build-cross.sh [step...] (default: all steps in order) # ./build-cross.sh clean Loading Loading @@ -487,6 +489,30 @@ EOF EOF } # installer: a RAM-bootable FenrirOS (initramfs as U-Boot ramdisk) with # nas-itx-install and the images, for boards whose U-Boot can only TFTP # (the vendor U-Boot of the NAS ITX has no UBI) step_installer() { [[ -f $OUT/rootfs.ubifs && -d $ROOTFS/usr ]] || die "run 'rootfs' and 'image' first" [[ -f $BOARD_DIR/installer/$BOARD-install.cpp ]] || die "no installer for $BOARD" command -v mkimage >/dev/null || die "mkimage (u-boot-tools) missing" msg "installer: $BOARD-install" run installer "$TOOLCHAIN/bin/$TRIPLE-c++" -std=c++23 -O2 -Wall \ -o "$OUT/$BOARD-install" "$BOARD_DIR/installer/$BOARD-install.cpp" local ir="$BUILD/initramfs" rm -rf --one-file-system "$ir" cp -a "$ROOTFS" "$ir" mkdir -p "$ir/root/install" cp "$OUT/$BOARD-install" "$ir$PREFIX/sbin/" cp "$OUT/$KIMAGE" "$OUT/$BOARD.dtb" "$OUT/rootfs.ubifs" "$ir/root/install/" # the root is the initramfs itself: syslite must not remount anything msg "installer: initramfs ($(du -sh "$ir" | cut -f1))" (cd "$ir" && find . -print0 | cpio --null -o -H newc -R 0:0 --quiet) | zstd -q -19 -T0 >"$OUT/installer.cpio.zst" run installer mkimage -A "$KARCH" -O linux -T ramdisk -C none -n "FenrirOS $BOARD installer" \ -d "$OUT/installer.cpio.zst" "$OUT/installer.uimg" msg "installer ready: $OUT/installer.uimg ($(du -h "$OUT/installer.uimg" | cut -f1))" } step_clean() { msg "removing $WORK (keeping $SRCDIR)" rm -rf --one-file-system "$WORK" Loading @@ -494,13 +520,13 @@ step_clean() { # --------------------------------------------------------------------------- ALL_STEPS=(headers systempp builtins toolchain fresh syslite kernel rootfs image) ALL_STEPS=(headers systempp builtins toolchain fresh syslite kernel rootfs image installer) steps=("$@") ((${#steps[@]})) || steps=("${ALL_STEPS[@]}") for s in "${steps[@]}"; do case "$s" in headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image) headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer) check_deps; mkdir -p "$WORK" "$BUILD"; "step_$s" ;; clean) step_clean ;; -h|--help) sed -n '2,/^$/s/^# \{0,1\}//p' "$0"; exit 0 ;; Loading
src/linux/arch/powerpc/include/bits/socket.h 0 → 100644 +18 −0 Changes for src/linux/arch/powerpc/include/bits/socket.h: 18 added lines, 0 removed lines. Original line number Diff line number Diff line #pragma once /* powerpc numbers of the SOL_SOCKET options that differ from asm-generic * (arch/powerpc/include/uapi/asm/socket.h): LOWAT, TIMEO and the credential * options are 16-21 in another order. systempp's struct timeval/timespec * have a 64-bit time_t on 32-bit powerpc too, so the timeout and timestamp * options are the time64 ones (Linux 5.1+): SO_RCVTIMEO_NEW etc. take the * kernel's __kernel_sock_timeval {s64, s64} -- exactly struct timeval. */ #define SO_RCVLOWAT 16 #define SO_SNDLOWAT 17 #define SO_RCVTIMEO_OLD 18 #define SO_SNDTIMEO_OLD 19 #define SO_PASSCRED 20 #define SO_PEERCRED 21 #define SO_RCVTIMEO 66 /* SO_RCVTIMEO_NEW */ #define SO_SNDTIMEO 67 /* SO_SNDTIMEO_NEW */ #define SO_TIMESTAMP 63 /* SO_TIMESTAMP_NEW */ #define SO_TIMESTAMPNS 64 /* SO_TIMESTAMPNS_NEW */ #define SO_TIMESTAMPING 65 /* SO_TIMESTAMPING_NEW */
src/linux/arch/x86_64/include/bits/socket.h 0 → 100644 +10 −0 Changes for src/linux/arch/x86_64/include/bits/socket.h: 10 added lines, 0 removed lines. Original line number Diff line number Diff line #pragma once /* x86_64 (asm-generic) numbers of the SOL_SOCKET options that differ * between architectures; struct timeval is the kernel's 64-bit one, so the * classic SO_RCVTIMEO/SO_SNDTIMEO/SO_TIMESTAMP* apply. */ #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 #define SO_SNDLOWAT 19 #define SO_RCVTIMEO 20 #define SO_SNDTIMEO 21