Commit f432d69a authored by jan.koester's avatar jan.koester
Browse files

ppc bugfix socket

parent 7c191d4c
Loading
Loading
Loading
Loading
+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;
}
+10 −0
Changes for boards/nas-itx/kernel.config: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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
@@ -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
+28 −2
Changes for build-cross.sh: 28 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -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
@@ -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"
@@ -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 ;;
+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 */
+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