1
0
forked from dan/liminix

Compare commits

..

10 Commits

Author SHA1 Message Date
503c8526d1 use "tftpboot" instead of "tftp" in u-boot commands
openwrt's u-boot installation doesn't accept the short form
2023-10-07 22:53:26 +01:00
32237467b3 rt3200: swap wireless driver load order
mt7515e loads first, so that wlan0 is 2.4GHz
mt7515e loads after, so that 5GHz gets wlan1
2023-10-07 22:53:26 +01:00
b213e39756 rt3200: enable flash/mtd 2023-10-07 22:53:26 +01:00
42bfadc059 delete some inapplicable kconfig 2023-10-07 22:53:26 +01:00
9bb475386c add mt7915, 7615 wifi modules
7915 won't work until we have working MTD, because it needs to
read calibration data from flash
2023-10-07 22:53:26 +01:00
8ecaad7945 rt3200: add DSA
this creates a bunch of network interfaces {lan[1234],wan}@eth0
2023-10-07 22:53:26 +01:00
124bd404bf aarch64: make tftpboot work
- patch dtb to add reserved-memory stanza for the phram device to use
  (aarch64 does not accept memmap= command line option)

- patch phram driver to use memremap() instead of ioremap() as
  ioremap can't be used for system ram on arm devices
2023-10-07 22:53:26 +01:00
07ff405339 rt3200: enable serial console 2023-10-07 22:53:26 +01:00
1a58501cb1 don't use ttyAMA0 console on all aarch64, just qemu 2023-10-07 22:53:26 +01:00
e4b14bf15c WIP kernel config for belkin rt3200 2023-10-07 22:53:26 +01:00
109 changed files with 924 additions and 4283 deletions

27
NEWS
View File

@ -1,27 +0,0 @@
A brief guide to backward-incompatible changes
that are likely to break configurations or workflows
2023-07-13
* a significant re-arrangement of modules and services, which will
probably break any configuration written before this time. For a
detailed explanation, see
https://www.liminix.org/doc/configuration.html#modules
2023-12-10
* configurations (usually) need no longer import modules from
modules/outputs because devices are expected to do this instead. This
change is because the outputs that make sense in any given context are
usually a property of the device being installed onto.
2023-12-11
* rename outputs.flashimage to outputs.mtdimage (and also diskimage to
mbrimage). This change is made in the expectation that "fooimage" is
the name of an outputs that gloms together other filesystem-like
outputs with some kind of partition table - so we might in future have
gptimage or lvmimage or ubimage.

View File

@ -18,14 +18,22 @@ outside word goes across it.
Liminix is pre-1.0. We are still finding new and better ways to do things,
and there is no attempt to maintain backward compatibility with the old
ways.
ways. This will change when it settles down.
The [NEWS](NEWS) file (available wherever you found this README) is
a high-level overview of breaking changes.
_In general:_ development mostly happens on the `main` branch, which is
therefore not guaranteed to build or to work on every commit. For the
latest functioning version, see [the CI system](https://build.liminix.org/jobset/liminix/build) and pick a revision with all jobs green.
Development mostly happens on the `main` branch, which is therefore
not guaranteed to build or to work on every commit. For the latest
functioning version, see [the CI system](https://build.liminix.org/jobset/liminix/build) and pick a revision with all jobs green.
_In particular:_ as of July 2023, a significant re-arrangement of
modules and services is ongoing:
* if you are using out-of-tree configurations created before commit
2e50368, especially if they reference things under pkgs.liminix,
they will need updating. Look at changes to examples/rotuer.nix
for guidance
* the same is intermittently true for examples/{extensino,arhcive}.nix
where I've updated rotuer and not updated them to match.
## Documentation

File diff suppressed because it is too large Load Diff

View File

@ -99,16 +99,14 @@ in {
};
};
};
environment.systemPackages =
let wireshark-nogui = pkgs.wireshark.override { withQt = false ; };
in with pkgs; [
tcpdump
wireshark-nogui
socat
tufted
iptables
usbutils
];
environment.systemPackages = with pkgs; [
tcpdump
wireshark
socat
tufted
iptables
usbutils
];
security.sudo.wheelNeedsPassword = false;
networking = {
hostName = "border";

73
ci.nix
View File

@ -8,63 +8,48 @@ let
pkgs = (import nixpkgs {});
borderVmConf = ./bordervm.conf-example.nix;
inherit (pkgs.lib.attrsets) genAttrs;
devices = [
"gl-ar750" "gl-mt300n-v2" "gl-mt300a"
"qemu" "qemu-aarch64" "qemu-armv7l"
];
devices = {
virt = [ "qemu" "qemu-aarch64" ];
hw = [ "gl-ar750" "gl-mt300n-v2" "gl-mt300a" ];
};
vanilla = ./vanilla-configuration.nix;
for-device = name:
for-device = cfg: name:
(import liminix {
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/${name}");
liminix-config = vanilla;
liminix-config = cfg;
}).outputs.default;
tests = import ./tests/ci.nix;
jobs =
(genAttrs devices for-device) //
(genAttrs devices.hw (name: for-device ./vanilla-configuration-hw.nix name)) //
(genAttrs devices.virt (name: for-device vanilla name)) //
tests //
{
buildEnv = (import liminix {
inherit nixpkgs borderVmConf;
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config = vanilla;
}).buildEnv;
doc =
let json =
(import liminix {
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config = {...} : {
imports = [ ./modules/all-modules.nix ];
};
}).outputs.optionsJson;
installers = map (f: "system.outputs.${f}") [
"vmroot"
"mtdimage"
"ubimage"
];
inherit (pkgs.lib) concatStringsSep;
in pkgs.stdenv.mkDerivation {
name = "liminix-doc";
nativeBuildInputs = with pkgs; [
gnumake sphinx fennel luaPackages.lyaml
];
src = ./.;
buildPhase = ''
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/modules-generated.rst
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.rst
cp ${(import ./doc/hardware.nix)} doc/hardware.rst
make -C doc html
'';
installPhase = ''
mkdir -p $out/nix-support $out/share/doc/
cd doc
cp *-generated.rst $out
ln -s ${json} $out/options.json
cp -a _build/html $out/share/doc/liminix
echo "file source-dist \"$out/share/doc/liminix\"" \
> $out/nix-support/hydra-build-products
'';
doc = pkgs.stdenv.mkDerivation {
name = "liminix-doc";
nativeBuildInputs = with pkgs; [
gnumake sphinx
fennel luaPackages.lyaml
];
src = ./doc;
buildPhase = ''
cat ${(import ./doc/extract-options.nix).doc} > options.json
cat options.json | fennel --correlate parse-options.fnl > modules-generated.rst
cp ${(import ./doc/hardware.nix)} hardware.rst
make html
'';
installPhase = ''
mkdir -p $out/nix-support $out/share/doc/
cp modules.rst options.json $out
cp -a _build/html $out/share/doc/liminix
echo "file source-dist \"$out/share/doc/liminix\"" \
> $out/nix-support/hydra-build-products
'';
};
with-unstable = (import liminix {
nixpkgs = unstable;

View File

@ -13,14 +13,13 @@ let
allowUnsupportedSystem = true; # mipsel
permittedInsecurePackages = [
"python-2.7.18.6" # kernel backports needs python <3
"python-2.7.18.7"
];
};
});
eval = pkgs.lib.evalModules {
config = (pkgs.lib.evalModules {
modules = [
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
{ _module.args = { inherit pkgs; lib = pkgs.lib; }; }
./modules/hardware.nix
./modules/base.nix
./modules/busybox.nix
@ -31,8 +30,7 @@ let
./modules/users.nix
./modules/outputs.nix
];
};
config = eval.config;
}).config;
borderVm = ((import <nixpkgs/nixos/lib/eval-config.nix>) {
system = builtins.currentSystem;
@ -45,12 +43,6 @@ let
in {
outputs = config.system.outputs // {
default = config.system.outputs.${config.hardware.defaultOutput};
optionsJson =
let o = import ./doc/extract-options.nix {
inherit pkgs eval;
lib = pkgs.lib;
};
in pkgs.writeText "options.json" (builtins.toJSON o);
};
# this is just here as a convenience, so that we can get a

View File

@ -1,44 +1,15 @@
# This "device" generates images that can be used with the QEMU
# emulator. The default output is a directory containing separate
# kernel ("Image" format) and root filesystem (squashfs or jffs2)
# images
{
description = ''
Belkin RT-3200 / Linksys E8450
******************************
This device is based on a 64 bit Mediatek MT7622 ARM platform,
and is "work in progress" in Liminix.
.. note:: The factory flash image contains ECC errors that make it
incompatible with Liminix: you need to use the `OpenWrt
UBI Installer <https://github.com/dangowrt/owrt-ubi-installer>`_ to
rewrite the partition layout before you can flash
Liminix onto it (or even use it with
:ref:`system-outputs-tftpboot`, if you want the wireless
to work).
Hardware summary
================
- MediaTek MT7622BV (1350MHz)
- 128MB NAND flash
- 512MB RAM
- b/g/n wireless using MediaTek MT7622BV (MT7615E driver)
- a/n/ac/ax wireless using MediaTek MT7915E
Installation
============
Installation is currently a manual process (you need a :ref:`serial <serial>` conection and
TFTP) following the instructions at :ref:`system-outputs-ubimage`
'';
system = {
crossSystem = {
config = "aarch64-unknown-linux-musl";
};
};
module = {pkgs, config, lib, lim, ... }:
module = {pkgs, config, lib, ... }:
let firmware = pkgs.stdenv.mkDerivation {
name = "wlan-firmware";
phases = ["installPhase"];
@ -48,12 +19,7 @@
'';
};
in {
imports = [
../../modules/arch/aarch64.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/ubifs.nix
];
config = {
imports = [ ../../modules/arch/aarch64.nix ];
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
@ -100,6 +66,7 @@
PCIE_MEDIATEK = "y";
BLOCK = "y"; # move this to base option
NETDEVICES = "y"; # and this probably also
SPI_MASTER = "y";
SPI = "y";
@ -120,7 +87,10 @@
MTD_SPI_NOR= "y";
MTD_SPLIT_FIRMWARE= "y";
MTD_SPLIT_FIT_FW= "y";
MTD_UBI="y";
MTD_UBI_BEB_LIMIT="20";
MTD_UBI_BLOCK="y";
MTD_UBI_WL_THRESHOLD="4096";
MMC = "y";
MMC_BLOCK = "y";
@ -146,11 +116,9 @@
SERIAL_OF_PLATFORM="y";
};
};
boot = {
commandLine = [ "console=ttyS0,115200" ];
tftp.loadAddress = lim.parseInt "0x4007ff28";
imageFormat = "fit";
};
boot.commandLine = [
"console=ttyS0,115200"
];
filesystem =
let inherit (pkgs.pseudofile) dir symlink;
in
@ -173,36 +141,19 @@
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
ubi = {
minIOSize = "2048";
eraseBlockSize = "126976";
maxLEBcount = "1024"; # guessing
};
defaultOutput = "ubimage";
# the kernel expects this to be on a 2MB boundary. U-Boot
# (I don't know why) has a default of 0x41080000, which isn't.
# We put it at the 32MB mark so that tftpboot can put its rootfs
# image and DTB underneath, but maybe this is a terrible waste of
# RAM unless the kernel is able to reuse it later. Oh well
loadAddress = lim.parseInt "0x42000000";
entryPoint = lim.parseInt "0x42000000";
rootDevice = "ubi0:liminix";
defaultOutput = "flashimage";
loadAddress = "0x41080000";
entryPoint = "0x41080000";
rootDevice = "/dev/mtdblock0";
dts = {
src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450-ubi.dts";
src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450.dts";
includes = [
"${openwrt.src}/target/linux/mediatek/dts"
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
];
};
# - 0x000000000000-0x000008000000 : "spi-nand0"
# - 0x000000000000-0x000000080000 : "bl2"
# - 0x000000080000-0x0000001c0000 : "fip"
# - 0x0000001c0000-0x0000002c0000 : "factory"
# - 0x0000002c0000-0x000000300000 : "reserved"
# - 0x000000300000-0x000008000000 : "ubi"
flash.eraseBlockSize = "65536"; # c.f. pkgs/mips-vm/mips-vm.sh
networkInterfaces =
let
inherit (config.system.service.network) link;
@ -225,6 +176,6 @@
};
};
};
};
};
}

View File

@ -1,57 +0,0 @@
{ config, pkgs, ... }:
{
imports = [
../../modules/outputs/jffs2.nix
];
config = {
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
config = {
MTD = "y";
MTD_BLOCK = "y";
MTD_CMDLINE_PARTS = "y";
MTD_PHRAM = "y";
VIRTIO_MENU = "y";
PCI = "y";
VIRTIO_PCI = "y";
BLOCK = "y";
VIRTIO_BLK = "y";
VIRTIO_NET = "y";
};
};
hardware =
let
mac80211 = pkgs.mac80211.override {
drivers = ["mac80211_hwsim"];
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
defaultOutput = "vmroot";
rootDevice = "/dev/mtdblock0";
dts.src = pkgs.lib.mkDefault null;
flash.eraseBlockSize = 65536;
networkInterfaces =
let inherit (config.system.service.network) link;
in {
wan = link.build {
devpath = "/devices/pci0000:00/0000:00:13.0/virtio0";
ifname = "wan";
};
lan = link.build {
devpath = "/devices/pci0000:00/0000:00:14.0/virtio1";
ifname = "lan";
};
wlan_24 = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
};
};
};
}

View File

@ -1,3 +1,11 @@
# I like GL.iNet devices because they're relatively accessible to
# DIY users: the serial port connections have headers preinstalled
# and don't need soldering
# Mainline linux 5.19 doesn't have device-tree support for this device
# or even for the SoC, so we use the extensive OpenWrt kernel patches
{
system = {
crossSystem = {
@ -13,9 +21,6 @@
GL.iNet GL-AR750
****************
Hardware summary
================
The GL-AR750 "Creta" travel router features:
- QCA9531 @650Mhz SoC
@ -25,34 +30,29 @@
- 16MB NOR Flash
- supported in OpenWrt by the "ath79" SoC family
As with many GL.iNet devices, the stock vendor firmware
is a fork of OpenWrt, meaning that the plain binary
``firmware.bin`` that Liminix builds can be flashed using the
vendor web UI and the U-Boot emergency "unbrick" routine
The GL-AR750 has two distinct sets of wifi hardware. The 2.4GHz
radio is part of the QCA9531 SoC, i.e. it's on the same silicon as
the CPU, the Ethernet, the USB etc. The device is connected to the
host via `AHB <https://en.wikipedia.org/wiki/Advanced_Microcontroller_Bus_Architecture>`_ and it is
supported in Linux using the ath9k driver. 5GHz wifi
is provided by a QCA9887 PCIe (PCI embedded) WLAN chip,
host via AHB, the "Advanced High-Performance Bus" and it is
supported in Linux using the ath9k driver. The 5GHz support, on the
other hand, is provided by a QCA9887 PCIe (PCI embedded) WLAN chip:
I haven't looked closely at the router innards to see if this is
actually physically a separate board that could be unplugged, but
as far as Linux is concerned it behaves as one. This is
supported by the ath10k driver.
Installation
============
As with many GL.iNet devices, the stock vendor firmware
is a fork of OpenWrt, meaning that the binary created by
:ref:`system-outputs-mtdimage` can be flashed using the
vendor web UI or the U-Boot emergency "unbrick" routine.
For flashing from an existing Liminix system (we believe that) it
is necessary to first boot into a :ref:`system-outputs-kexecboot`
system, otherwise you'll be overwriting flash partitions while
they're in use - and that might not end well.
Vendor web page: https://www.gl-inet.com/products/gl-ar750/
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-ar750
'';
module = {pkgs, config, lim, ... }:
module = {pkgs, config, ... }:
let
openwrt = pkgs.openwrt;
firmwareBlobs = pkgs.pkgsBuildBuild.fetchFromGitHub {
@ -77,8 +77,8 @@
};
ath10k_cal_data =
let
offset = lim.parseInt "0x5000";
size = lim.parseInt "0x844";
offset = 1024 * 20; # 0x5000
size = 2048 + 68; # 0x844
in pkgs.liminix.services.oneshot rec {
name = "ath10k_cal_data";
up = ''
@ -96,22 +96,19 @@
imports = [
../../modules/network
../../modules/arch/mipseb.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/mtdimage.nix
../../modules/outputs/jffs2.nix
];
programs.busybox.options = {
FEATURE_DD_IBS_OBS = "y"; # ath10k_cal_data needs skip_bytes,fullblock
};
hardware = {
defaultOutput = "mtdimage";
loadAddress = lim.parseInt "0x80060000";
entryPoint = lim.parseInt "0x80060000";
defaultOutput = "flashimage";
loadAddress = "0x80060000";
entryPoint = "0x80060000";
flash = {
address = lim.parseInt "0x9F060000";
size = lim.parseInt "0xfa0000";
eraseBlockSize = 65536;
address = "0x9F060000";
size ="0xfa0000";
eraseBlockSize = "65536";
};
rootDevice = "/dev/mtdblock5";
dts = {
@ -147,7 +144,7 @@
};
};
boot.tftp = {
loadAddress = lim.parseInt "0x00A00000";
loadAddress = "0x00A00000";
};
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
@ -155,14 +152,9 @@
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
# Mainline linux 5.19 doesn't have device-tree support for
# this device or even for the SoC, so we use the extensive
# OpenWrt kernel patches
extraPatchPhase = ''
${openwrt.applyPatches.ath79}
'';
config = {
ATH79 = "y";
PCI = "y";
@ -183,6 +175,7 @@
CONSOLE_LOGLEVEL_QUIET = "4";
NET = "y";
NETDEVICES = "y";
ETHERNET = "y";
NET_VENDOR_ATHEROS = "y";
AG71XX = "y"; # ethernet (qca,qca9530-eth)

View File

@ -13,11 +13,34 @@
description = ''
GL.iNet GL-MT300A
*****************
********************
The GL-MT300A is based on a MT7620 chipset.
For flashing from U-Boot, the firmware partition is from
The GL.iNet pocket router range makes nice cheap hardware for
playing with Liminix or similar projects. The manufacturers seem
open to the DIY market, and the devices have a reasonable amount
of RAM and are much easier to get serial connections than many
COTS routers.
Wire up the serial connection: this probably involves opening
the box, locating the serial header pins (TX, RX and GND) and
connecting a USB TTL converter - e.g. a PL2303 based device - to
it. The defunct OpenWRT wiki has a guide with some pictures. (If
you don't have a USB TTL converter to hand, other options are
available. For example, use the GPIO pins on a Raspberry Pi.)
Run a terminal emulator such as Minicom on whatever is on the
other end of the link. I use 115200 8N1 and find it also helps
to set "Line tx delay" to 1ms, "backspace sends DEL" and
"lineWrap on".
When you turn the router on you should be greeted with some
messages from U-Boot and a little bit of ASCII art, followed by
the instruction to hit SPACE to stop autoboot. Do this and you
will get a gl-mt300a> prompt.
For flashing from uboot, the firmware partition is from
0xbc050000 to 0xbcfd0000.
WiFi on this device is provided by the rt2800soc module. It
@ -25,17 +48,6 @@
- assuming we want to use the wireless - we need to build MTD
support into the kernel even if we're using TFTP root.
Installation
============
The stock vendor firmware is a fork of OpenWrt, meaning that the
binary created by :ref:`system-outputs-mtdimage` can be flashed
using the vendor web UI or the U-Boot emergency "unbrick" routine.
For flashing from an existing Liminix system (we think) it
is necessary to first boot into a :ref:`system-outputs-kexecboot`
system, otherwise you'll be overwriting flash partitions while
they're in use - and that might not end well.
Vendor web page: https://www.gl-inet.com/products/gl-mt300a/
@ -43,7 +55,7 @@
'';
module = { pkgs, config, lib, lim, ...}:
module = { pkgs, config, lib, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs) openwrt;
@ -52,16 +64,11 @@
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
imports = [
../../modules/arch/mipsel.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/mtdimage.nix
../../modules/outputs/jffs2.nix
];
imports = [ ../../modules/arch/mipsel.nix ];
hardware = {
defaultOutput = "mtdimage";
loadAddress = lim.parseInt "0x80000000";
entryPoint = lim.parseInt "0x80000000";
defaultOutput = "flashimage";
loadAddress = "0x80000000";
entryPoint = "0x80000000";
# Creating 5 MTD partitions on "spi0.0":
# 0x000000000000-0x000000030000 : "u-boot"
@ -74,9 +81,9 @@
# 0x000000260000-0x000000f80000 : "rootfs"
flash = {
address = lim.parseInt "0xbc050000";
size = lim.parseInt "0xf80000";
eraseBlockSize = 65536;
address = "0xbc050000";
size ="0xf80000";
eraseBlockSize = "65536";
};
rootDevice = "/dev/mtdblock5";
@ -125,7 +132,7 @@
};
};
boot.tftp = {
loadAddress = lim.parseInt "0x00A00000";
loadAddress = "0x00A00000";
};
kernel = {
@ -152,6 +159,7 @@
CONSOLE_LOGLEVEL_QUIET = "4";
NET = "y";
NETDEVICES = "y";
ETHERNET = "y";
NET_VENDOR_RALINK = "y";
NET_RALINK_MDIO = "y";

View File

@ -13,22 +13,10 @@
GL.iNet GL-MT300N-v2
********************
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`MT300A <GL.iNet GL-MT300A>, but is
based on the MT7628 chipset instead of MT7620. It's also marginally cheaper
and comes in a yellow case not a blue one. Be sure your device is
v2 not v1, which is a different animal and has only half as much RAM.
Installation
============
The stock vendor firmware is a fork of OpenWrt, meaning that the
binary created by :ref:`system-outputs-mtdimage` can be flashed
using the vendor web UI or the U-Boot emergency "unbrick" routine.
For flashing from an existing Liminix system (we think) it
is necessary to first boot into a :ref:`system-outputs-kexecboot`
system, otherwise you'll be overwriting flash partitions while
they're in use - and that might not end well.
The GL-MT300N-v2 "Mango" is is very similar to the MT300A, but is
based on MT7628 instead of MT7620. It's also marginally cheaper
and comes in a yellow case not a blue one. It's different again
to the v1, which has only half the RAM.
Vendor web page: https://www.gl-inet.com/products/gl-mt300n-v2/
@ -36,7 +24,7 @@
'';
module = { pkgs, config, lib, lim, ...}:
module = { pkgs, config, lib, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs.liminix.services) oneshot;
@ -52,12 +40,7 @@
hash = "sha256:1dkhfznmdz6s50kwc841x3wj0h6zg6icg5g2bim9pvg66as2vmh9";
};
in {
imports = [
../../modules/arch/mipsel.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/mtdimage.nix
../../modules/outputs/jffs2.nix
];
imports = [ ../../modules/arch/mipsel.nix ];
filesystem = dir {
lib = dir {
firmware = dir {
@ -66,14 +49,14 @@
};
};
hardware = {
defaultOutput = "mtdimage";
loadAddress = lim.parseInt "0x80000000";
entryPoint = lim.parseInt "0x80000000";
defaultOutput = "flashimage";
loadAddress = "0x80000000";
entryPoint = "0x80000000";
flash = {
address = lim.parseInt "0xbc050000";
size = lim.parseInt "0xfb0000";
eraseBlockSize = 65536;
address = "0xbc050000";
size = "0xfb0000";
eraseBlockSize = "65536";
};
rootDevice = "/dev/mtdblock5";
@ -121,7 +104,7 @@
boot.tftp = {
# 20MB seems to give enough room to uncompress the kernel
# without anything getting trodden on. 10MB was too small
loadAddress = lim.parseInt "0x1400000";
loadAddress = "0x1400000";
};
kernel = {
@ -160,6 +143,7 @@
REGULATOR_FIXED_VOLTAGE = "y";
NET = "y";
NETDEVICES = "y";
ETHERNET = "y";
PHYLIB = "y";

View File

@ -9,33 +9,35 @@
};
};
description = ''
QEMU Aarch64
************
This target produces an image for
the `QEMU "virt" platform <https://www.qemu.org/docs/master/system/arm/virt.html>`_ using a 64 bit CPU type.
ARM targets differ from MIPS in that the kernel format expected
by QEMU is an "Image" (raw binary file) rather than an ELF
file, but this is taken care of by :command:`run.sh`. Check the
documentation for the :ref:`QEMU` (MIPS) target for more information.
'';
# this device is described by the "qemu" device
installer = "vmroot";
description = "";
module = {pkgs, config, lim, ... }: {
imports = [
../../modules/arch/aarch64.nix
../families/qemu.nix
];
module = {pkgs, config, ... }: {
imports = [ ../../modules/arch/aarch64.nix ];
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
config = {
VIRTUALIZATION = "y";
PCI_HOST_GENERIC="y";
MTD = "y";
MTD_BLOCK2MTD = "y";
MTD_BLOCK = "y";
VIRTIO_MENU = "y";
PCI = "y";
VIRTIO_PCI = "y";
BLOCK = "y";
VIRTIO_BLK = "y";
NETDEVICES = "y";
VIRTIO_NET = "y";
SERIAL_EARLYCON_ARM_SEMIHOST = "y"; # earlycon=smh
SERIAL_AMBA_PL011 = "y";
SERIAL_AMBA_PL011_CONSOLE = "y";
};
@ -43,9 +45,31 @@
boot.commandLine = [
"console=ttyAMA0,38400"
];
hardware = let addr = lim.parseInt "0x40010000"; in {
loadAddress = addr;
entryPoint = addr;
};
hardware =
let
mac80211 = pkgs.mac80211.override {
drivers = ["mac80211_hwsim"];
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
defaultOutput = "vmroot";
loadAddress = "0x0";
entryPoint = "0x0";
rootDevice = "/dev/mtdblock0";
flash.eraseBlockSize = "65536"; # c.f. pkgs/mips-vm/mips-vm.sh
networkInterfaces =
let inherit (config.system.service.network) link;
in {
wan = link.build { ifname = "eth0"; };
lan = link.build { ifname = "eth1"; };
wlan_24 = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
};
};
};
}

View File

@ -1,53 +0,0 @@
# This "device" generates images that can be used with the QEMU
# emulator. The default output is a directory containing separate
# kernel ("Image" format) and root filesystem (squashfs or jffs2)
# images
{
system = {
crossSystem = {
config = "armv7l-unknown-linux-musleabihf";
};
};
# this device is described by the "qemu" device
description = ''
QEMU ARM v7
***********
This target produces an image for
the `QEMU "virt" platform <https://www.qemu.org/docs/master/system/arm/virt.html>`_ using a 32 bit CPU type.
ARM targets differ from MIPS in that the kernel format expected
by QEMU is an "Image" (raw binary file) rather than an ELF
file, but this is taken care of by :command:`run.sh`. Check the
documentation for the :ref:`QEMU` (MIPS) target for more information.
'';
installer = "vmroot";
module = {pkgs, config, lim, ... }: {
imports = [
../../modules/arch/arm.nix
../families/qemu.nix
];
kernel = {
config = {
PCI_HOST_GENERIC = "y";
ARCH_VIRT = "y";
VFP = "y";
NEON = "y";
AEABI = "y";
SERIAL_AMBA_PL011 = "y";
SERIAL_AMBA_PL011_CONSOLE = "y";
};
};
boot.commandLine = [
"console=ttyAMA0"
];
hardware = let addr = lim.parseInt "0x40008000"; in {
loadAddress = addr;
entryPoint = addr;
};
};
}

View File

@ -13,21 +13,24 @@
};
description = ''
QEMU MIPS
*********
QEMU
****
This target produces an image for
This is not a hardware device. This target produces an image for
QEMU, the "generic and open source machine emulator and
virtualizer".
Liminix can build QEMU for both MIPS (:code:`qemu` device) and Aarch64 (:code:`qemu-aarch64` device)
MIPS QEMU emulates a "Malta" board, which was an ATX form factor
evaluation board made by MIPS Technologies, but mostly in Liminix
we use paravirtualized devices (Virtio) instead of emulating
hardware.
hardware. For Aarch64 we use the QEMU "virt" board.
Building an image for QEMU results in a :file:`result/` directory
containing ``run.sh`` ``vmlinux``, and ``rootfs`` files. To invoke
the emulator, run ``run.sh``.
containing ``run.sh`` ``vmlinux``, ``rootfs`` and possibly
(architecture-dependent) ``Image``. To invoke the emulator,
run ``run.sh``.
The configuration includes two emulated "hardware" ethernet
devices and the kernel :code:`mac80211_hwsim` module to
@ -36,41 +39,56 @@
in the Development manual.
'';
module = {pkgs, config, lib, lim, ... }: {
imports = [
../../modules/arch/mipseb.nix
../families/qemu.nix
];
module = {pkgs, config, ... }: {
imports = [ ../../modules/arch/mipseb.nix ];
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
config = {
MIPS_MALTA= "y";
CPU_MIPS32_R2= "y";
POWER_RESET = "y";
POWER_RESET_SYSCON = "y";
MTD = "y";
MTD_BLOCK2MTD = "y";
MTD_BLOCK = "y";
VIRTIO_MENU = "y";
PCI = "y";
VIRTIO_PCI = "y";
BLOCK = "y";
VIRTIO_BLK = "y";
NETDEVICES = "y";
VIRTIO_NET = "y";
SERIAL_8250= "y";
SERIAL_8250_CONSOLE= "y";
};
};
hardware =
# from arch/mips/mti-malta/Platform:load-$(CONFIG_MIPS_MALTA) += 0xffffffff80100000
let addr = lim.parseInt "0x80100000";
in {
loadAddress = addr;
entryPoint = addr;
# Unlike the arm qemu targets, we need a static dts when
# running u-boot-using tests, qemu dumpdtb command doesn't
# work for this board. I am not at all sure this dts is
# *correct* but it does at least boot
dts = lib.mkForce {
src = "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/mti/malta.dts";
includes = [
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
];
let
mac80211 = pkgs.mac80211.override {
drivers = ["mac80211_hwsim"];
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
defaultOutput = "vmroot";
rootDevice = "/dev/mtdblock0";
flash.eraseBlockSize = "65536"; # c.f. pkgs/run-liminix-vm/run-liminix-vm.sh
networkInterfaces =
let inherit (config.system.service.network) link;
in {
wan = link.build { ifname = "eth0"; };
lan = link.build { ifname = "eth1"; };
wlan_24 = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
};
};
};
}

View File

@ -1,238 +0,0 @@
{
description = ''
Turris Omnia
************
'';
system = {
crossSystem = {
config = "armv7l-unknown-linux-musleabihf";
};
};
module = {pkgs, config, lib, lim, ... }:
let
openwrt = pkgs.openwrt;
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs) liminix;
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
name = "mtd_by_name_links";
up = ''
mkdir -p /dev/mtd/by-name
cd /dev/mtd/by-name
for i in /sys/class/mtd/mtd*[0-9]; do
ln -s ../../$(basename $i) $(cat $i/name)
done
'';
};
in {
imports = [
../../modules/arch/arm.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/ext4fs.nix
../../modules/outputs/mbrimage.nix
../../modules/outputs/extlinux.nix
];
config = {
services.mtd-name-links = mtd_by_name_links;
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
extraPatchPhase = ''
${pkgs.openwrt.applyPatches.mvebu}
'';
config = {
PCI = "y";
OF = "y";
MEMORY = "y"; # for MVEBU_DEVBUS
DMADEVICES = "y"; # for MV_XOR
CPU_V7 = "y";
ARCH_MULTIPLATFORM = "y";
ARCH_MVEBU = "y";
ARCH_MULTI_V7= "y";
PCI_MVEBU = "y";
AHCI_MVEBU = "y";
MACH_ARMADA_38X = "y";
SMP = "y";
# this is disabled for the moment because it relies on a GCC
# plugin that requires gmp.h to build, and I can't see right now
# how to confgure it to find gmp
STACKPROTECTOR_PER_TASK = "n";
NR_CPUS = "4";
VFP = "y";
NEON= "y";
# WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
ATA = "y";
PSTORE = "y";
PSTORE_RAM = "y";
PSTORE_CONSOLE = "y";
PSTORE_DEFLATE_COMPRESS = "n";
BLOCK = "y";
MMC="y";
PWRSEQ_EMMC="y"; # ???
PWRSEQ_SIMPLE="y"; # ???
MMC_BLOCK="y";
MMC_SDHCI= "y";
MMC_SDHCI_PLTFM= "y";
MMC_SDHCI_PXAV3= "y";
MMC_MVSDIO= "y";
SERIAL_8250 = "y";
SERIAL_8250_CONSOLE = "y";
SERIAL_OF_PLATFORM="y";
SERIAL_MVEBU_UART = "y";
SERIAL_MVEBU_CONSOLE = "y";
SERIAL_8250_DMA= "y";
SERIAL_8250_DW= "y";
SERIAL_8250_EXTENDED= "y";
SERIAL_8250_MANY_PORTS= "y";
SERIAL_8250_SHARE_IRQ= "y";
OF_ADDRESS= "y";
OF_MDIO= "y";
WATCHDOG = "y"; # watchdog is enabled by u-boot
ORION_WATCHDOG = "y"; # so is non-optional to keep feeding
MVEBU_DEVBUS = "y"; # "Device Bus controller ... flash devices such as NOR, NAND, SRAM, and FPGA"
MVMDIO = "y";
MVNETA = "y";
MVNETA_BM = "y";
MVNETA_BM_ENABLE = "y";
SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM
PHY_MVEBU_A38X_COMPHY = "y"; # for eth2
MVPP2 = "y";
MV_XOR = "y";
# there is NOR flash on this device, which is used for U-Boot
# and the rescue system (which we don't interfere with) but
# also for the U-Boot environment variables (which we might
# need to meddle with)
MTD_SPI_NOR = "y";
SPI = "y";
SPI_MASTER = "y";
SPI_ORION = "y";
NET_DSA = "y";
NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL
};
};
rootfsType = "ext4";
boot = {
commandLine = [
"console=ttyS0,115200"
"pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
];
imageFormat = "fit";
};
filesystem =
let
inherit (pkgs.pseudofile) dir symlink;
firmware = pkgs.stdenv.mkDerivation {
name = "wlan-firmware";
phases = ["installPhase"];
installPhase = ''
mkdir $out
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
'';
};
in dir {
lib = dir {
firmware = dir {
ath10k = symlink firmware;
};
};
etc = dir {
"fw_env.config" =
let f = pkgs.writeText "fw_env.config" ''
/dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000
'';
in symlink f;
};
};
boot.tftp = {
loadAddress = lim.parseInt "0x1000000";
kernelFormat = "zimage";
compressRoot = true;
};
hardware = let
mac80211 = pkgs.mac80211.override {
drivers = ["ath9k_pci" "ath10k_pci"];
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
defaultOutput = "mtdimage";
loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
rootDevice = "/dev/mtdblock0";
dts = {
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/armada-385-turris-omnia.dts";
includes = [
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/"
];
};
flash.eraseBlockSize = 65536; # only used for tftpboot
networkInterfaces =
let
inherit (config.system.service.network) link;
inherit (config.system.service) bridge;
in rec {
en70000 = link.build {
# in armada-38x.dtsi this is eth0.
# It's connected to port 5 of the 88E6176 switch
devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet";
# name is unambiguous but not very semantic
ifname = "en70000";
};
en30000 = link.build {
# in armada-38x.dtsi this is eth1
# It's connected to port 6 of the 88E6176 switch
devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet";
# name is unambiguous but not very semantic
ifname = "en30000";
};
# the default (from the dts? I'm guessing) behavour for
# lan ports on the switch is to attach them to
# en30000. It should be possible to do something better,
# per
# https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
# but apparently OpenWrt doesn't either so maybe it's more
# complicated than it looks
wan = link.build {
# in armada-38x.dtsi this is eth2. It may be connected to
# an ethernet phy or to the SFP cage, depending on a gpio
devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet";
ifname = "wan";
};
lan = link.build {
ifname = "lan1";
};
wlan = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
wlan5 = link.build {
ifname = "wlan1";
dependencies = [ mac80211 ];
};
};
};
};
};
}

View File

@ -117,7 +117,7 @@ If you are prepared to open the device and have a TTL serial adaptor
of some kind to connect it to, you can probably use U-Boot and a TFTP
server to download and flash the image. This is quite
hardware-specific, and sometimes involves soldering: please refer
to :ref:`serial`.
to the :ref:`development manual <tftp server>`.
Flashing from OpenWrt

View File

@ -27,16 +27,19 @@ To build it,
nix-build -I liminix-config=path/to/your/configuration.nix --arg device "import ./devices/qemu" -A outputs.default
This creates a :file:`result/` directory containing a :file:`vmlinux`
and a :file:`rootfs`, and also a shell script :file:`run.sh` which
invokes QEMU to run that kernel with that filesystem. It connects the Liminix
In a ``buildEnv`` nix-shell, you can use the :command:`run-liminix-vm` command
to run Qemu with appropriate options. It connects the Liminix
serial console and the `QEMU monitor <https://www.qemu.org/docs/master/system/monitor.html>`_ to stdin/stdout. Use ^P (not ^A) to switch to the monitor.
.. code-block:: console
nix-shell --run "run-liminix-vm result/vmlinux result/squashfs"
If you run with ``--background /path/to/some/directory`` as the first
parameter, it will fork into the background and open Unix sockets in
that directory for console and monitor. Use :command:`nix-shell --run
connect-vm` to connect to either of these sockets, and ^O to
disconnect.
that directory for console and monitor. Use :command:`connect-vm`
(also in the ``buildEnv`` environment) to connect to either of these
sockets, and ^O to disconnect.
.. _qemu-networking:
@ -52,11 +55,9 @@ the right way:
* multicast 230.0.0.1:1235 : lan
* multicast 230.0.0.1:1236 : world (the internet)
Any VM started by a :command:`run.sh` script is connected to "lan" and
"access", and the emulated border network gateway (see below) runs
PPPoE and is connected to "access" and "world".
.. _border-network-gateway:
A VM started with :command:`run-liminix-vm` is connected to "lan" and "access", and
the emulated border network gateway (see below) runs PPPoE and is
connected to "access" and "world".
Border Network Gateway
----------------------
@ -88,68 +89,6 @@ time with configurations for RP-PPPoE and/or Accel PPP.`
Hardware devices
****************
.. _serial:
U-Boot and serial shenanigans
=============================
Every device that we have so far encountered in Liminix uses `U-Boot,
the "Universal Boot Loader" <https://docs.u-boot.org/en/latest/>`_ so
it's worth knowing a bit about it. "Universal" is in this context a
bit of a misnomer, though: encountering *mainline* U-Boot is very rare
and often you'll find it is a fork from some version last updated
in 2008. Upgrading U-Boot is more or less complicated depending on the
device and is outside scope for Liminix.
To speak to U-Boot on your device you'll usually need a serial
connection to it. This is device-specific. Usually it involves
opening the box, locating the serial header pins (TX, RX and GND) and
connecting a USB TTL converter to them.
The Rolls Royce of USB/UART cables is the `FTDI cable
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_,
but there are cheaper alternatives based on the PL2303 and CP2102 chipsets. Or
get creative and use the `UART GPIO pins <https://pinout.xyz/>`_ on a Raspberry Pi. Whatever you do, make sure
that the voltages are compatible: if your device is 3.3V (this is
typical but not universal), you don't want to be sending it 5v or
(even worse) 12v.
Run a terminal emulator such as Minicom on the computer at other end
of the link. 115200 8N1 is the typical speed.
.. NOTE::
TTL serial connections typically have no form of flow control and
so don't always like having massive chunks of text pasted into
them - and U-Boot may drop characters while it's busy. So don't
necessarily expect to copy-paste large chunks of text into the
terminal emulator and have it work just like that.
If using Minicom, you may find it helps to bring up the "Termimal
settings" dialog (C^A T), then configure "Newline tx delay" to
some small but non-zero value.
When you turn the router on you should be greeted with some messages
from U-Boot, followed by the instruction to hit some key to stop
autoboot. Do this and you will get to the prompt. If you didn't see
anything, the strong likelihood is that TX and RX are the wrong way
around. If you see garbage, try a different speed.
Interesting commands to try first in U-Boot are :command:`help` and
:command:`printenv`.
To do anything useful with U-Boot you will probably need a way to get
large binary files onto the device, and the usual way to do this is by
adding a network connection and using TFTP to download them. It's
quite common that the device's U-Boot doesn't speak DHCP so it will
need a static LAN address. You might also want to keep it away from
your "real" LAN: see :ref:`bng` for some potentially useful tooling
to use it on an isolated network.
TFTP
====
.. _tftp server:
How you get your image onto hardware will vary according to the
@ -180,7 +119,7 @@ Now add the device and server IP addresses to your configuration:
};
and then build the derivation for ``outputs.default`` or
``outputs.mtdimage`` (for which it will be an alias on any device
``outputs.flashimage`` (for which it will be an alias on any device
where this is applicable). You should find it has created
* :file:`result/firmware.bin` which is the file you are going to flash
@ -213,8 +152,6 @@ U-Boot to transfer the kernel and filesystem over TFTP and boot the
kernel from RAM.
.. _bng:
Networking
==========

View File

@ -1,10 +1,31 @@
{ eval, lib, pkgs }:
let
overlay = import ../overlay.nix;
pkgs = import <nixpkgs> ( {
overlays = [overlay];
config = {
allowUnsupportedSystem = true; # mipsel
permittedInsecurePackages = [
"python-2.7.18.6" # kernel backports needs python <3
];
};
});
inherit (pkgs) lib;
inherit (lib) types;
modulenames =
builtins.attrNames
(lib.filterAttrsRecursive
(n: t:
(n != "arch") &&
((t=="directory") ||
((t=="regular") && ((builtins.match ".*\\.nix$" n) != null))))
(builtins.readDir ../modules));
modulefiles = builtins.map (n: builtins.toPath "${../modules}/${n}") modulenames;
eval = (lib.evalModules {
modules = [
{ _module.args = { inherit pkgs; lib = pkgs.lib; }; }
] ++ modulefiles;
});
conf = eval.config;
rootDir = builtins.toPath ./..;
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix)
["${rootDir}/"];
optToDoc = name: opt : {
inherit name;
description = opt.description or null;
@ -21,12 +42,16 @@ let
then
let sd = lib.attrByPath item.loc ["not found"] conf;
in item // {
declarations = map stripAnyPrefixes item.declarations;
parameters =
let x = lib.mapAttrsToList optToDoc sd.parameters; in x;
}
else
item // { declarations = map stripAnyPrefixes item.declarations; };
in
builtins.map spliceServiceDefn
(pkgs.lib.optionAttrSetToDocList eval.options)
item;
o = builtins.map spliceServiceDefn
(pkgs.lib.optionAttrSetToDocList eval.options);
in {
doc = pkgs.writeText "options.yaml" ''
# ${./..}
${builtins.toJSON o}
'';
}

View File

@ -1,23 +1,12 @@
with import <nixpkgs> {} ;
let
inherit (builtins) stringLength readDir filter;
devices = filter (n: n != "families")
(lib.mapAttrsToList (n: t: n) (readDir ../devices));
texts = map (n:
devices =
builtins.readDir ../devices;
texts = lib.mapAttrsToList (n: t:
let d = import ../devices/${n}/default.nix;
d' = {
description = "${n}\n${substring 0 (stringLength n) "********************************"}\n";
} // d;
installer =
if d ? description && d ? installer
then ''
The default installation route for this device is
:ref:`system-outputs-${d.installer}`
''
else "";
in d'.description)
d' = { description = "no description for ${n}"; } // d;
in d'.description )
devices;
in
writeText "hwdoc" ''

View File

@ -12,7 +12,6 @@ Liminix
development
modules
hardware
outputs
Indices and tables

View File

@ -1,13 +0,0 @@
Outputs
#######
Liminix *outputs* are artefacts that can be installed somehow on a
target device, or "installers" which run on the target device to
perform the installation.
There are different outputs because different target devices need
different artefacts, or have different ways to get that artefact
installed. The options available for a particular device are described in
the section for that device.
.. include:: outputs-generated.rst

View File

@ -1,19 +0,0 @@
(local yaml (require :lyaml))
;; (local { : view } (require :fennel))
(fn output? [option]
(match option.loc
["system" "outputs" & _] true
_ false))
(fn sorted-options [options]
(table.sort
options
(fn [a b] (< a.name b.name)))
options)
(each [_ option (ipairs (sorted-options (yaml.load (io.read "*a"))))]
(when (and (output? option) (not option.internal))
(print (.. ".. _" (string.gsub option.name "%." "-") ":") "\n")
(print option.description)))

View File

@ -2,25 +2,21 @@
(local { : view } (require :fennel))
(fn basename [str ext]
(-> str
(string.gsub "(.*/)(.*)" "%2")
(string.gsub (.. ext "$") "")))
(fn headline [name]
(let [title (assert (basename name ".nix"))
len (title:len)]
(.. title "\n" (string.rep "=" len))))
(let [(_ _ basename) (string.find name ".*/([^/].*).nix")
len (basename:len)]
(.. basename "\n" (string.rep "=" len))))
(fn read-preamble [pathname]
(let [pathname (if (string.match pathname ".nix$")
pathname
(.. pathname "/default.nix"))]
(with-open [f (assert (io.open pathname :r))]
(accumulate [lines nil
l (f:lines)
:until (not (= (string.sub l 1 2) "##"))]
(.. (or lines "") (string.gsub l "^## *" "") "\n")))))
(if (= (pathname:sub 1 1) "/")
(let [pathname (if (string.match pathname ".nix$")
pathname
(.. pathname "/default.nix"))]
(with-open [f (assert (io.open pathname :r))]
(accumulate [lines nil
l (f:lines)
:until (not (= (string.sub l 1 2) "##"))]
(.. (or lines "") (string.gsub l "^## *" "") "\n"))))))
(fn relative-pathname [pathname]
(let [pathname

View File

@ -69,11 +69,10 @@ device's serial console and the `QEMU monitor
stdin/stdout.
You should now see Linux boot messages and after a few seconds be
presented with a root shell prompt. You can run commands to look at
the filesystem, see what processes are running, view log messages (in
:file:/run/uncaught-logs.current), etc. To kill the emulator, press ^P
(Control P) then c to enter the "QEMU Monitor", then type ``quit`` at
the ``(qemu)`` prompt.
presented with a login prompt. You can login on the console as
``root`` (password is "secret") and poke around to see what processes are
running. To kill the emulator, press ^P (Control P) then c to enter the
"QEMU Monitor", then type ``quit`` at the ``(qemu)`` prompt.
To see that it's running network services we need to connect to its
emulated network. Start the machine again, if you had stopped it, and
@ -138,12 +137,20 @@ unbrick if necessary.
work here, but you accept the slightly greater bricking
risk if it doesn't.
You may want to read and inwardly digest the Develoment Manual section
:ref:`serial` when you start working with Liminix on real hardware. You
won't *need* serial access for this example, assuming it works, but it
You may want to acquire a `USB TTL serial cable
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_
when you start working with Liminix on real hardware. You
won't *need* it for this example, assuming it works, but it
allows you
to see the boot monitor and kernel messages, and to login directly to
the device if for some reason it doesn't bring its network up.
the device if for some reason it doesn't bring its network up. You have options
here: the FTDI-based cables are the Rolls Royce of serial cables,
whereas the ones based on PL2303 and CP2102 chipsets are cheaper but
also fussier - or you could even get creative and use e.g. a
`Raspberry Pi <https://pinout.xyz/#>`_ or other SBC with a UART and
TX/RX/GND header pins. Make sure that the voltages are compatible:
this is a 3.3v device and you don't want to be sending it 5v or (even
worse) 12v.
Now we can build Liminix. Although we could use the same example
configuration as we did for Qemu, you might not want to plug a DHCP

View File

@ -24,6 +24,7 @@ in rec {
};
imports = [
../modules/standard.nix
../modules/wlan.nix
../modules/network
../modules/vlan
@ -31,6 +32,7 @@ in rec {
../modules/watchdog
../modules/mount
];
hostname = "arhcive";
kernel = {

View File

@ -33,6 +33,7 @@ in rec {
../modules/ntp
../modules/ppp
../modules/ssh
../modules/standard.nix
../modules/vlan
../modules/wlan.nix
];

View File

@ -30,6 +30,7 @@ in rec {
../modules/hostapd
../modules/bridge
../modules/ssh
../modules/standard.nix
];
hostname = "extneder";

View File

@ -8,6 +8,7 @@ in rec {
../modules/network
../modules/ssh
../modules/vlan
../modules/flashimage.nix
];
boot.tftp = {

View File

@ -34,6 +34,7 @@ in rec {
imports = [
../modules/wlan.nix
../modules/standard.nix
../modules/network
../modules/ppp
../modules/dnsmasq
@ -44,6 +45,7 @@ in rec {
../modules/ntp
../modules/ssh
];
rootfsType = "jffs2";
hostname = "rotuer";
services.hostap = svc.hostapd.build {
@ -181,8 +183,4 @@ in rec {
defaultProfile.packages = with pkgs; [
min-collect-garbage
];
programs.busybox.applets = [
"fdisk" "sfdisk"
];
}

View File

@ -1,109 +0,0 @@
{ config, pkgs, lib, lim, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {
imports = [
../modules/network
../modules/ssh
../modules/vlan
../modules/wlan.nix
../modules/hostapd
../modules/bridge
../modules/ext4fs.nix
../modules/tftpboot.nix
];
rootfsType = "ext4";
boot.tftp = {
# IP addresses to use in the boot monitor when flashing/ booting
# over TFTP. If you are flashing using the stock firmware's Web UI
# then these dummy values are fine
ipaddr = "10.0.0.8"; # my address
serverip = "10.0.0.1"; # build machine or other tftp server
loadAddress = lim.parseInt "0x40000800";
};
hostname = "omnia";
services.hostap =
let secrets = {
ssid = "not-the-internet";
channel = 4;
wpa_passphrase = "diamond dogs";
};
in svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan;
params = {
country_code = "GB";
hw_mode = "g";
wmm_enabled = 1;
ieee80211n = 1;
inherit (secrets) ssid channel wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
};
services.hostap5 =
let secrets = {
ssid = "not-the-internet";
channel = 36;
wpa_passphrase = "diamond dogs";
};
in svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan5;
params = {
country_code = "GB";
hw_mode = "a";
ht_capab = "[HT40+]";
vht_oper_chwidth = 1;
vht_oper_centr_freq_seg0_idx = secrets.channel + 6;
ieee80211ac = 1;
wmm_enabled = 1;
inherit (secrets) ssid channel wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
};
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.dhcpc = svc.network.dhcp.client.build {
interface = services.int;
dependencies = [ config.services.hostname ];
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces; [
lan
wlan
];
};
services.sshd = svc.ssh.build { };
users.root = {
# the password is "secret". Use mkpasswd -m sha512crypt to
# create this hashed password string
passwd = "$6$y7WZ5hM6l5nriLmo$5AJlmzQZ6WA.7uBC7S8L4o19ESR28Dg25v64/vDvvCN01Ms9QoHeGByj8lGlJ4/b.dbwR9Hq2KXurSnLigt1W1";
};
defaultProfile.packages = with pkgs; [
figlet pciutils
];
}

View File

@ -1,39 +0,0 @@
# Import all of the modules, used in the documentation generator. Not
# currently expected to work in an actual configuration, but it would
# be nice if it did.
{
imports = [
./base.nix
./bridge
./busybox.nix
./dhcp6c
./dnsmasq
./outputs/ext4fs.nix
./firewall
./hardware.nix
./hostapd
./hostname.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./kernel.nix
./outputs/kexecboot.nix
./mount
./network
./ntp
./outputs.nix
./outputs/vmroot.nix
./outputs/ubimage.nix
./outputs/mtdimage.nix
./ppp
./ramdisk.nix
./squashfs.nix
./ssh
./outputs/tftpboot.nix
./outputs/ubifs.nix
./users.nix
./vlan
./watchdog
./wlan.nix
];
}

View File

@ -1,4 +1,4 @@
{ lib, lim, pkgs, config, ...}:
{ lib, pkgs, config, ...}:
{
config = {
kernel.config = {
@ -12,7 +12,5 @@
OF = "y";
# USE_OF = "y";
};
hardware.ram.startAddress = lim.parseInt "0x40000000";
system.outputs.u-boot = pkgs.ubootQemuAarch64;
};
}

View File

@ -1,11 +0,0 @@
{ lib, lim, pkgs, config, ...}:
{
config = {
kernel.config = {
OF = "y";
};
kernel.makeTargets = ["arch/arm/boot/zImage"];
hardware.ram.startAddress = lim.parseInt "0x40000000";
system.outputs.u-boot = pkgs.ubootQemuArm;
};
}

View File

@ -1,4 +1,4 @@
{ lib, pkgs, config, lim, ...}:
{ lib, pkgs, config, ...}:
{
config = {
kernel.config = {
@ -10,7 +10,6 @@
OF = "y";
USE_OF = "y";
};
hardware.ram.startAddress = lim.parseInt "0x80000000";
boot.commandLine = [
"console=ttyS0,115200" # true of all mips we've yet encountered
];

View File

@ -5,6 +5,5 @@
kernel.config = {
CPU_BIG_ENDIAN = "y";
};
system.outputs.u-boot = pkgs.ubootQemuMips;
};
}

View File

@ -39,12 +39,7 @@ in {
};
rootfsType = mkOption {
default = "squashfs";
type = types.enum [
"ext4"
"jffs2"
"squashfs"
"ubifs"
];
type = types.enum ["squashfs" "jffs2"];
};
boot = {
commandLine = mkOption {
@ -52,13 +47,9 @@ in {
default = [];
description = "Kernel command line";
};
imageFormat = mkOption {
type = types.enum ["fit" "uimage"];
default = "uimage";
};
tftp = {
loadAddress = mkOption {
type = types.ints.unsigned;
type = types.str;
description = ''
RAM address at which to load data when transferring via
TFTP. This is not the address of the flash storage,

View File

@ -11,7 +11,7 @@ in {
options.system.outputs = {
firmware = mkOption {
type = types.package;
internal = true; # component of mtdimage
internal = true; # component of flashimage
description = ''
Binary image (combining kernel, FDT, rootfs, initramfs
if needed, etc) for the target device.
@ -19,40 +19,17 @@ in {
};
flash-scr = mkOption {
type = types.package;
internal = true; # component of mtdimage
internal = true; # component of flashimage
description = ''
Copy-pastable U-Boot commands to TFTP download the
image and write it to flash
'';
};
mtdimage = mkOption {
flashimage = mkOption {
type = types.package;
description = ''
mtdimage
**********
This creates an image called :file:`firmware.bin` suitable for
squashfs or jffs2 systems. It can be flashed from U-Boot (if
you have a serial console connection), or on some devices from
the vendor firmware, or from a Liminix kexecboot system.
If you are flashing from U-Boot, the file
:file:`flash.scr` is a sequence of commands
which you can paste at the U-Boot prompt to
to transfer the firmware file from a TFTP server and
write it to flash. **Please read the script before
running it: flash operations carry the potential to
brick your device**
.. NOTE::
TTL serial connections typically have no form of flow
control and so don't always like having massive chunks of
text pasted into them - and U-Boot may drop characters
while it's busy. So don't necessarily expect to copy-paste
the whole of :file:`flash.scr` into a terminal emulator and
have it work just like that. You may need to paste each
line one at a time, or even retype it.
Flashable image for the target device, and the script to
install it
'';
};
};
@ -75,15 +52,15 @@ in {
firmware =
let
o = config.system.outputs;
bs = toString config.hardware.flash.eraseBlockSize;
bs = config.hardware.flash.eraseBlockSize;
in pkgs.runCommand "firmware" {} ''
dd if=${o.uimage} of=$out bs=${bs} conv=sync
dd if=${o.rootfs} of=$out bs=${bs} conv=sync,nocreat,notrunc oflag=append
'';
mtdimage =
flashimage =
let o = config.system.outputs; in
# could use trivial-builders.linkFarmFromDrvs here?
pkgs.runCommand "mtdimage" {} ''
pkgs.runCommand "flashimage" {} ''
mkdir $out
cd $out
ln -s ${o.firmware} firmware.bin
@ -106,9 +83,9 @@ in {
cat > $out << EOF
setenv serverip ${tftp.serverip}
setenv ipaddr ${tftp.ipaddr}
tftp 0x${toHexString tftp.loadAddress} result/firmware.bin
erase 0x${toHexString flash.address} +0x${toHexString flash.size}
cp.b 0x${toHexString tftp.loadAddress} 0x${toHexString flash.address} \''${filesize}
tftp 0x$(printf %x ${tftp.loadAddress}) result/firmware.bin
erase 0x$(printf %x ${flash.address}) +${flash.size}
cp.b 0x$(printf %x ${tftp.loadAddress}) 0x$(printf %x ${flash.address}) \''${filesize}
echo command line was ${builtins.toJSON (concatStringsSep " " config.boot.commandLine)}
EOF
'';

View File

@ -16,14 +16,8 @@ in {
hardware = {
dts = {
src = mkOption {
type = types.nullOr types.path;
description = ''
If the device requires an external device tree to be loaded
alongside the kernel, this is the path to the device tree source
(we usually get these from OpenWrt). This value may be null if the
platform creates the device tree - currently this is the case
only for QEMU.
'';
type = types.path;
description = "Path to the device tree source (usually from OpenWrt)";
};
includes = mkOption {
default = [];
@ -32,15 +26,9 @@ in {
};
};
defaultOutput = mkOption {
description = "\"Default\" output: what gets built for this device when outputs.default is requested. Typically this is \"mtdimage\" or \"vmroot\"";
description = "\"Default\" output: what gets built for this device when outputs.default is requested. Typically this is \"flashimage\" or \"vmroot\"";
type = types.nonEmptyStr;
};
ram = {
startAddress = mkOption {
type = types.int;
};
};
flash = {
# start address and size of whichever partition (often
# called "firmware") we're going to overwrite with our
@ -54,19 +42,19 @@ in {
kernel uimage and root fs. Usually not the entire flash, as
we don't want to clobber the bootloader, calibration data etc
'';
type = types.ints.unsigned;
type = types.str;
};
size = mkOption {
type = types.ints.unsigned;
type = types.str;
description = "Size in bytes of the firmware partition";
};
eraseBlockSize = mkOption {
description = "Flash erase block size in bytes";
type = types.ints.unsigned;
type = types.str;
};
};
loadAddress = mkOption { type = types.ints.unsigned; default = null; };
entryPoint = mkOption { type = types.ints.unsigned; };
loadAddress = mkOption { default = null; };
entryPoint = mkOption { };
radios = mkOption {
description = ''
Kernel modules (from mac80211 package) required for the

View File

@ -13,23 +13,13 @@ in
boot.initramfs = {
enable = mkEnableOption "initramfs";
};
system.outputs = {
initramfs = mkOption {
type = types.package;
internal = true;
description = ''
Initramfs image capable of mounting the real root
filesystem
'';
};
systemConfiguration = mkOption {
type = types.package;
description = ''
pkgs.systemconfig for the configured filesystem,
contains 'activate' and 'init' commands
'';
internal = true;
};
system.outputs.initramfs = mkOption {
type = types.package;
internal = true;
description = ''
Initramfs image capable of mounting the jffs2 root
filesystem
'';
};
};
config = mkIf config.boot.initramfs.enable {
@ -47,14 +37,18 @@ in
dir /proc 0755 0 0
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
nod /dev/mtdblock0 0600 0 0 b 31 0
nod /dev/mtdblock1 0600 0 0 b 31 1
nod /dev/mtdblock2 0600 0 0 b 31 2
nod /dev/mtdblock3 0600 0 0 b 31 3
nod /dev/mtdblock4 0600 0 0 b 31 4
nod /dev/mtdblock5 0600 0 0 b 31 5
dir /target 0755 0 0
dir /target/persist 0755 0 0
dir /target/nix 0755 0 0
file /init ${pkgs.preinit}/bin/preinit 0755 0 0
SPECIALS
'';
systemConfiguration =
pkgs.systemconfig config.filesystem.contents;
};
};
}

54
modules/jffs2.nix Normal file
View File

@ -0,0 +1,54 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkOption types;
in
{
imports = [
./initramfs.nix
];
options.system.outputs = {
systemConfiguration = mkOption {
type = types.package;
description = ''
pkgs.systemconfig for the configured filesystem,
contains 'activate' and 'init' commands
'';
internal = true;
};
};
config = mkIf (config.rootfsType == "jffs2") {
kernel.config = {
JFFS2_FS = "y";
JFFS2_LZO = "y";
JFFS2_RTIME = "y";
JFFS2_COMPRESSION_OPTIONS = "y";
JFFS2_ZLIB = "y";
JFFS2_CMODE_SIZE = "y";
};
boot.initramfs.enable = true;
system.outputs = rec {
systemConfiguration =
pkgs.systemconfig config.filesystem.contents;
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
endian = if pkgs.stdenv.isBigEndian
then "--big-endian" else "--little-endian";
in runCommand "make-jffs2" {
depsBuildBuild = [ mtdutils ];
} ''
mkdir -p $TMPDIR/empty/nix/store/ $TMPDIR/empty/secrets
cp ${systemConfiguration}/bin/activate $TMPDIR/empty/activate
ln -s ${pkgs.s6-init-bin}/bin/init $TMPDIR/empty/init
grafts=$(sed < ${systemConfiguration}/etc/nix-store-paths 's/^\(.*\)$/--graft \1:\1/g')
mkfs.jffs2 --compression-mode=size ${endian} -e ${config.hardware.flash.eraseBlockSize} --enable-compressor=lzo --pad --root $TMPDIR/empty --output $out $grafts --squash --faketime
'';
};
};
}

View File

@ -9,14 +9,13 @@ let
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.networking) address interface;
inherit (pkgs.liminix.services) bundle;
inherit (pkgs) liminix;
type_service = pkgs.liminix.lib.types.service;
in {
options = {
kernel = {
src = mkOption { type = types.path; } ;
src = mkOption { type = types.package; } ;
modular = mkOption {
type = types.bool;
default = true;
@ -42,25 +41,11 @@ in {
};
'';
};
makeTargets = mkOption {
type = types.listOf types.str;
};
};
};
config = {
system.outputs =
let k = liminix.builders.kernel.override {
inherit (config.kernel) config src extraPatchPhase;
targets = config.kernel.makeTargets;
};
in {
kernel = k.vmlinux;
zimage = k.zImage;
};
kernel = rec {
modular = true; # disabling this is not yet supported
makeTargets = ["vmlinux"];
config = {
IKCONFIG = "y";
IKCONFIG_PROC = "y";
@ -79,8 +64,6 @@ in {
PACKET = "y"; # for ppp, tcpdump ...
SYSVIPC= "y";
NETDEVICES = "y"; # even PPP needs this
# disabling this option causes the kernel to use an "empty"
# initramfs instead: it has a /dev/console node and not much
# else. Note that pid 1 is started *before* the root

View File

@ -7,7 +7,7 @@
let
inherit (lib) mkOption mkForce types concatStringsSep;
in {
imports = [ ../ramdisk.nix ];
imports = [ ./ramdisk.nix ];
options.system.outputs = {
kexecboot = mkOption {
type = types.package;

View File

@ -68,21 +68,7 @@ in {
ifname = mkOption {
type = types.str;
example = "eth0";
description = ''
Device name as used by the kernel (as seen in "ip link"
or "ifconfig" output). If devpath is also specified, the
device will be renamed to the name provided.
'';
};
devpath = mkOption {
type = types.nullOr types.str;
default = null;
example = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet";
description = ''
Path to the sysfs node of the device. If you provide this
and the ifname option, the device will be renamed to the
name given by ifname.
''; };
# other "ip link add" options could go here as well
mtu = mkOption {
type = types.nullOr types.int;

View File

@ -4,27 +4,13 @@
, serviceFns
, lib
}:
{
ifname
, devpath ? null
, mtu} :
# if devpath is supplied, we rename the interface at that
# path to have the specified name.
{ifname, mtu} :
let
inherit (liminix.services) longrun oneshot;
inherit (lib) concatStringsSep;
name = "${ifname}.link";
rename = if devpath != null
then ''
oldname=$(cd /sys${devpath} && cd net/ && echo *)
ip link set ''${oldname} name ${ifname}
''
else "";
up = liminix.networking.ifup name ifname;
in oneshot {
inherit name;
up = ''
${rename}
${liminix.networking.ifup name ifname}
'';
inherit name up;
down = "ip link set down dev ${ifname}";
}

View File

@ -7,132 +7,103 @@
let
inherit (lib) mkOption types concatStringsSep;
inherit (pkgs) liminix callPackage writeText;
o = config.system.outputs;
arch = let s = pkgs.stdenv; in
if s.isAarch64
then "aarch64"
else if s.isMips
then "mips"
else throw "can't determine arch";
in
{
imports = [
./squashfs.nix
./outputs/vmroot.nix
./outputs/extlinux.nix
];
options = {
system.outputs = {
# the convention here is to mark an output as "internal" if
# it's not a complete system (kernel plus userland, or installer)
# but only part of one.
kernel = mkOption {
type = types.package;
internal = true;
description = ''
kernel
******
Kernel vmlinux file (usually ELF)
'';
};
zimage = mkOption {
type = types.package;
internal = true;
description = ''
zimage
******
Kernel in compressed self-extracting package
'';
};
dtb = mkOption {
type = types.package;
internal = true;
description = ''
dtb
***
Compiled device tree (FDT) for the target device
'';
};
uimage = mkOption {
type = types.package;
internal = true;
description = ''
uimage
******
Combined kernel and FDT in uImage (U-Boot compatible) format
'';
};
u-boot = mkOption {
vmroot = mkOption {
type = types.package;
description = ''
Directory containing separate kernel and rootfs image for
use with qemu (see run-liminix-vm)
'';
};
manifest = mkOption {
type = types.package;
internal = true;
description = ''
Debugging aid. JSON rendition of config.filesystem, on
which can run "nix-store -q --tree" on it and find
out what's in the image, which is nice if it's unexpectedly huge
'';
};
rootdir = mkOption {
type = types.package;
internal = true;
description = ''
directory of files to package into root filesystem
'';
};
bootablerootdir = mkOption {
type = types.package;
internal = true;
description = ''
directory of files to package into root filesystem, including
a kernel and appropriate associated gubbins for the
selected bootloader
'';
};
rootfs = mkOption {
type = types.package;
internal = true;
description = ''
root filesystem (squashfs or jffs2) image
'';
internal = true;
};
};
};
config = {
system.outputs = rec {
# tftpd = pkgs.buildPackages.tufted;
kernel = liminix.builders.kernel.override {
inherit (config.kernel) config src extraPatchPhase;
};
dtb = liminix.builders.dtb {
inherit (config.boot) commandLine;
dts = config.hardware.dts.src;
includes = config.hardware.dts.includes ++ [
"${o.kernel.headers}/include"
"${kernel.headers}/include"
];
};
uimage = liminix.builders.uimage {
commandLine = concatStringsSep " " config.boot.commandLine;
inherit (config.hardware) loadAddress entryPoint;
inherit (config.boot) imageFormat;
inherit (o) kernel dtb;
inherit kernel;
inherit dtb;
};
rootdir =
# could use trivial-builders.linkFarmFromDrvs here?
vmroot =
let
inherit (pkgs.pkgsBuildBuild) runCommand;
in runCommand "mktree" { } ''
mkdir -p $out/nix/store/ $out/secrets $out/boot
cp ${o.systemConfiguration}/bin/activate $out/activate
ln -s ${pkgs.s6-init-bin}/bin/init $out/init
mkdir -p $out/nix/store
for path in $(cat ${o.systemConfiguration}/etc/nix-store-paths) ; do
(cd $out && cp -a $path .$path)
done
'';
bootablerootdir =
let inherit (pkgs.pkgsBuildBuild) runCommand;
in runCommand "add-slash-boot" { } ''
cp -a ${o.rootdir} $out
${if config.boot.loader.extlinux.enable
then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.extlinux} boot)"
else ""
}
'';
cmdline = builtins.toJSON (concatStringsSep " " config.boot.commandLine);
makeBootableImage = pkgs.runCommandCC "objcopy" {}
(if pkgs.stdenv.isAarch64
then "${pkgs.stdenv.cc.targetPrefix}objcopy -O binary -S ${kernel} $out"
else "cp ${kernel} $out");
in pkgs.runCommandCC "vmroot" {} ''
mkdir $out
cd $out
ln -s ${config.system.outputs.rootfs} rootfs
ln -s ${kernel} vmlinux
ln -s ${manifest} manifest
ln -s ${kernel.headers} build
echo ${cmdline} > commandline
cat > run.sh << EOF
#!${pkgs.runtimeShell}
CMDLINE=${cmdline} ${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --arch ${arch} \$* ${makeBootableImage} ${config.system.outputs.rootfs}
EOF
chmod +x run.sh
'';
manifest = writeText "manifest.json" (builtins.toJSON config.filesystem.contents);
};
};

View File

@ -1,38 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkOption types;
o = config.system.outputs;
in
{
imports = [
./initramfs.nix
];
config = mkIf (config.rootfsType == "ext4") {
kernel.config = {
EXT4_FS = "y";
EXT4_USE_FOR_EXT2 = "y";
FS_ENCRYPTION = "y";
};
boot.initramfs.enable = true;
system.outputs = {
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand e2fsprogs;
in runCommand "mkfs.ext4" {
depsBuildBuild = [ e2fsprogs ];
} ''
tree=${o.bootablerootdir}
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
# add 25% for filesystem overhead
size=$(( 5 * $size / 4))
dd if=/dev/zero of=$out bs=1024 count=$size
mke2fs -t ext4 -j -d $tree $out
'';
};
};
}

View File

@ -1,39 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
cfg = config.boot.loader.extlinux;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
in {
options.system.outputs.extlinux = mkOption {
type = types.package;
# description = "";
};
options.boot.loader.extlinux.enable = mkEnableOption "extlinux";
config = { # mkIf cfg.enable {
system.outputs.extlinux = pkgs.runCommand "extlinux" {} ''
mkdir $out
cd $out
${if wantsDtb then "cp ${o.dtb} dtb" else "true"}
cp ${o.initramfs} initramfs
cp ${o.zimage} kernel
mkdir extlinux
cat > extlinux/extlinux.conf << _EOF
menu title Liminix
timeout 100
label Liminix
kernel /boot/kernel
# initrd /boot/initramfs
append ${cmdline} root=/dev/vda1
${if wantsDtb then "fdt /boot/dtb" else ""}
_EOF
'';
};
}

View File

@ -1,40 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkOption types;
o = config.system.outputs;
in
{
imports = [
./initramfs.nix
];
config = mkIf (config.rootfsType == "jffs2") {
kernel.config = {
JFFS2_FS = "y";
JFFS2_LZO = "y";
JFFS2_RTIME = "y";
JFFS2_COMPRESSION_OPTIONS = "y";
JFFS2_ZLIB = "y";
JFFS2_CMODE_SIZE = "y";
};
boot.initramfs.enable = true;
system.outputs = {
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
endian = if pkgs.stdenv.isBigEndian
then "--big-endian" else "--little-endian";
in runCommand "make-jffs2" {
depsBuildBuild = [ mtdutils ];
} ''
tree=${o.bootablerootdir}
(cd $tree && mkfs.jffs2 --compression-mode=size ${endian} -e ${toString config.hardware.flash.eraseBlockSize} --enable-compressor=lzo --pad --root . --output $out --squash --faketime )
'';
};
};
}

View File

@ -1,51 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkOption types concatStringsSep;
o = config.system.outputs;
phram_address = lib.toHexString (config.hardware.ram.startAddress + 256 * 1024 * 1024);
in {
options.system.outputs = {
mbrimage = mkOption {
type = types.package;
description = ''
mbrimage
*********
This creates a disk image file with a partition table containing
the contents of ``outputs.rootfs`` as its only partition.
'';
};
vmdisk = mkOption { type = types.package; };
};
config = {
system.outputs = {
mbrimage =
let
o = config.system.outputs;
in pkgs.runCommand "mbrimage" {
depsBuildBuild = [ pkgs.pkgsBuildBuild.util-linux ];
} ''
# leave 4 sectors at start for partition table
# and alignment to 2048 bytes (does that help?)
dd if=${o.rootfs} of=$out bs=512 seek=4 conv=sync
echo '4,-,L,*' | sfdisk $out
'';
vmdisk = pkgs.runCommand "vmdisk" {} ''
mkdir $out
cd $out
ln -s ${o.mbrimage} ./mbrimage
cat > run.sh <<EOF
#!${pkgs.runtimeShell}
${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --arch ${pkgs.stdenv.hostPlatform.qemuArch} --u-boot ${o.u-boot}/u-boot.bin --phram-address 0x${phram_address} --disk-image ${o.mbrimage} \$* /dev/null /dev/null
EOF
chmod +x run.sh
'';
};
};
}

View File

@ -1,127 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkOption types concatStringsSep;
cfg = config.boot.tftp;
in {
imports = [ ../ramdisk.nix ];
options.boot.tftp = {
freeSpaceBytes = mkOption {
type = types.int;
default = 0;
};
kernelFormat = mkOption {
type = types.enum [ "zimage" "uimage" ];
default = "uimage";
};
compressRoot = mkOption {
type = types.bool;
default = false;
};
};
options.system.outputs = {
tftpboot = mkOption {
type = types.package;
description = ''
tftpboot
********
This output is intended for developing on a new device.
It assumes you have a serial connection and a
network connection to the device and that your
build machine is running a TFTP server.
The output is a directory containing kernel and
root filesystem image, and a script :file:`boot.scr` of U-Boot
commands that will load the images into memory and
run them directly,
instead of first writing them to flash. This saves
time and erase cycles.
It uses the Linux `phram <https://github.com/torvalds/linux/blob/master/drivers/mtd/devices/phram.c>`_ driver to emulate a flash device using a segment of physical RAM.
'';
};
};
config = {
boot.ramdisk.enable = true;
system.outputs = rec {
tftpboot =
let
inherit (pkgs.lib.trivial) toHexString;
o = config.system.outputs;
image = let choices = {
uimage = o.uimage;
zimage = o.zimage;
}; in choices.${cfg.kernelFormat};
bootCommand = let choices = {
uimage = "bootm";
zimage = "bootz";
}; in choices.${cfg.kernelFormat};
cmdline = concatStringsSep " " config.boot.commandLine;
in
pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc ]; } ''
mkdir $out
cd $out
binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); }
binsize64k() { local s=$(stat -L -c %s $1); echo $(($s + 0x10000 &(~0xffff))); }
hex() { printf "0x%x" $1; }
rootfsStart=${toString cfg.loadAddress}
rootfsSize=$(binsize64k ${o.rootfs} )
dtbStart=$(($rootfsStart + $rootfsSize))
dtbSize=$(binsize ${o.dtb} )
imageStart=$(($dtbStart + $dtbSize))
imageSize=$(binsize ${image})
ln -s ${o.manifest} manifest
ln -s ${image} image
ln -s ${o.kernel} vmlinux # handy for gdb
${if cfg.compressRoot
then ''
lzma -z9cv ${o.rootfs} > rootfs.lz
rootfsLzStart=$(($imageStart + $imageSize))
rootfsLzSize=$(binsize rootfs.lz)
''
else "ln -s ${o.rootfs} rootfs"
}
cat ${o.dtb} > dtb
address_cells=$(fdtget dtb / '#address-cells')
size_cells=$(fdtget dtb / '#size-cells')
if [ $address_cells -gt 1 ]; then ac_prefix=0; fi
if [ $size_cells -gt 1 ]; then sz_prefix=0; fi
fdtput -p dtb /reserved-memory '#address-cells' $address_cells
fdtput -p dtb /reserved-memory '#size-cells' $size_cells
fdtput -p dtb /reserved-memory ranges
node=$(printf "phram-rootfs@%x" $rootfsStart)
fdtput -p -t s dtb /reserved-memory/$node compatible phram
fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize)
cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0";
fdtput -t s dtb /chosen bootargs "$cmd"
# dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1
cat > boot.scr << EOF
setenv serverip ${cfg.serverip}
setenv ipaddr ${cfg.ipaddr}
tftpboot $(hex $imageStart) result/image ; ${
if cfg.compressRoot
then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz"
else "tftpboot $(hex $rootfsStart) result/rootfs"
}; tftpboot $(hex $dtbStart) result/dtb
${if cfg.compressRoot
then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); "
else ""
} ${bootCommand} $(hex $imageStart) - $(hex $dtbStart)
EOF
'';
};
};
}

View File

@ -1,42 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkOption types;
o = config.system.outputs;
in
{
imports = [
./initramfs.nix
];
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
eraseBlockSize = mkOption { type = types.str; }; # LEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
config = mkIf (config.rootfsType == "ubifs") {
kernel.config = {
MTD_UBI="y";
UBIFS_FS = "y";
UBIFS_FS_SECURITY = "n";
};
boot.initramfs.enable = true;
system.outputs = {
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
cfg = config.hardware.ubi;
in runCommand "mkfs.ubifs" {
depsBuildBuild = [ mtdutils ];
} ''
mkdir tmp
tree=${o.bootablerootdir}
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.eraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
'';
};
};
}

View File

@ -1,126 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
cfg = config.boot.tftp;
instructions = pkgs.writeText "env.scr" ''
setenv serverip ${cfg.serverip}
setenv ipaddr ${cfg.ipaddr}
setenv loadaddr ${lib.toHexString cfg.loadAddress}
'';
in {
options.system.outputs = {
ubimage = mkOption {
type = types.package;
description = ''
ubimage
*******
This output provides a UBIFS filesystem image and a small U-Boot script
to make the manual installation process very slightly simpler. You will
need a serial connection and a network connection to a TFTP server
containing the filesystem image it creates.
.. warning:: These steps were tested on a Belkin RT3200 (also known as
Linksys E8450). Other devices may be set up differently,
so use them as inspiration and don't just paste them
blindly.
1) determine which MTD device is being used for UBI, and the partition name:
.. code-block:: console
uboot> ubi part
Device 0: ubi0, MTD partition ubi
In this case the important value is ``ubi0``
2) list the available volumes and create a new one on which to install Liminix
.. code-block:: console
uboot> ubi info l
[ copious output scrolls past ]
Expect there to be existing volumes and for some or all of them to be
important. Unless you know what you're doing, don't remove anything
whose name suggests it's related to uboot, or any kind of backup or
recovery partition. To see how much space is free:
.. code-block:: console
uboot> ubi info
[ ... ]
UBI: available PEBs: 823
Now we can make our new root volume
.. code-block:: console
uboot> ubi create liminix -
3) transfer the root filesystem from the build system and write it
to the new volume. Paste the environment variable settings from
:file:`result/env.scr` into U-Boot, then run
.. code-block:: console
uboot> tftpboot ''${loadaddr} result/rootfs
uboot> ubi write ''${loadaddr} liminix $filesize
Now we have the root filesystem installed on the device. You
can even mount it and poke around using ``ubifsmount ubi0:liminix;
ubifsls /``
4) optional: before you configure the device to boot into Liminix
automatically, you can try booting it by hand to see if it works:
.. code-block:: console
uboot> ubifsmount ubi0:liminix
uboot> ubifsload ''${loadaddr} boot/uimage
uboot> bootm ''${loadaddr}
Once you've done this and you're happy with it, reset the device to
U-Boot. You don't need to recreate the volume but you do need to
repeat step 3.
5) Instructions for configuring autoboot are likely to be very
device-dependent. On the Linksys E8450/Belkin RT3200, the environment
variable `boot_production` governs what happens on a normal boot, so
you could do
.. code-block:: console
uboot> setenv boot_production 'led $bootled_pwr on ; ubifsmount ubi0:liminix; ubifsload ''${loadaddr} boot/uimage; bootm ''${loadaddr}'
On other devices, some detective work may be needed. Try running
`printenv` and look for likely commands, try looking at the existing
boot process, maybe even try looking for documentation for that device.
6) Now you can reboot the device into Liminix
.. code-block:: console
uboot> reset
'';
};
};
config = mkIf (config.rootfsType == "ubifs") {
system.outputs = {
ubimage =
let o = config.system.outputs; in
pkgs.runCommand "ubimage" {} ''
mkdir $out
cd $out
ln -s ${o.rootfs} rootfs
ln -s ${instructions} env.scr
'';
};
};
}

View File

@ -1,73 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkOption types concatStringsSep;
in
{
options = {
system.outputs = {
vmroot = mkOption {
type = types.package;
description = ''
vmroot
******
This target is for use with the qemu, qemu-aarch64, qemu-armv7l
devices. It generates an executable :file:`run.sh` which
invokes QEMU. It connects the Liminix
serial console and the `QEMU monitor <https://www.qemu.org/docs/master/system/monitor.html>`_
to stdin/stdout. Use ^P (not ^A) to switch between monitor and
stdio.
If you call :command:`run.sh` with ``--background
/path/to/some/directory`` as the first parameter, it will
fork into the background and open Unix sockets in that
directory for console and monitor. Use :command:`nix-shell
--run connect-vm` to connect to either of these sockets, and
^O to disconnect.
Liminix VMs are networked using QEMU socket networking. The
default behaviour is to connect
* multicast 230.0.0.1:1234 ("access") to eth0
* multicast 230.0.0.1:1235 ("lan") to eth1
Refer to :ref:`border-network-gateway` for details of how to
start an emulated upstream on the "access" network that
your Liminix device can talk to.
'';
};
};
};
config = {
system.outputs = rec {
vmroot =
let
inherit (config.system.outputs) rootfs kernel manifest;
cmdline = builtins.toJSON (concatStringsSep " " config.boot.commandLine);
makeBootableImage = pkgs.runCommandCC "objcopy" {}
(if pkgs.stdenv.hostPlatform.isAarch
then "${pkgs.stdenv.cc.targetPrefix}objcopy -O binary -R .comment -S ${kernel} $out"
else "cp ${kernel} $out");
phram_address = lib.toHexString (config.hardware.ram.startAddress + 256 * 1024 * 1024);
in pkgs.runCommand "vmroot" {} ''
mkdir $out
cd $out
ln -s ${rootfs} rootfs
ln -s ${kernel} vmlinux
ln -s ${manifest} manifest
ln -s ${kernel.headers} build
echo ${cmdline} > commandline
cat > run.sh << EOF
#!${pkgs.runtimeShell}
${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --command-line ${cmdline} --arch ${pkgs.stdenv.hostPlatform.qemuArch} --phram-address 0x${phram_address} \$* ${makeBootableImage} ${config.system.outputs.rootfs}
EOF
chmod +x run.sh
'';
};
};
}

View File

@ -55,9 +55,6 @@ let
run = {
file = ''
#!${execline}/bin/execlineb -P
importas PATH PATH
export PATH ${s6}/bin:''${PATH}
foreground { echo path is ''${PATH} }
${s6-linux-init}/bin/s6-linux-init-shutdownd -c "/etc/s6-linux-init/current" -g 3000
'';
mode = "0755";
@ -81,44 +78,26 @@ let
};
getty = dir {
run = {
# We can't run a useful shell on /dev/console because
# /dev/console is not allowed to be the controlling
# tty of any process, which means ^C ^Z etc don't work.
# So we work out what the *actual* console device is
# using sysfs and open our shell there instead.
file = ''
#!${execline}/bin/execlineb -P
${execline}/bin/cd /
redirfd -r 0 /sys/devices/virtual/tty/console/active
withstdinas CONSOLETTY
importas CONSOLETTY CONSOLETTY
redirfd -w 2 /dev/''${CONSOLETTY}
fdmove -c 1 2
redirfd -r 0 /dev/''${CONSOLETTY}
/bin/ash -l
'';
#!${execline}/bin/execlineb -P
/bin/getty -l /bin/login 115200 /dev/console
'';
mode = "0755";
};
down-signal = {
file = "HUP\n";
};
};
".s6-svscan" =
let
openConsole = ''
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 2 /dev/console
${execline}/bin/fdmove -c 1 2
'';
quit = message: ''
${openConsole}
${execline}/bin/foreground { ${s6-linux-init}/bin/s6-linux-init-echo -- ${message} }
${s6-linux-init}/bin/s6-linux-init-hpr -fr
'';
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 2 /dev/console
${execline}/bin/fdmove -c 1 2
${execline}/bin/foreground { ${s6-linux-init}/bin/s6-linux-init-echo -- ${message} }
${s6-linux-init}/bin/s6-linux-init-hpr -fr
'';
shutdown = action: ''
#!${execline}/bin/execlineb -P
${s6-linux-init}/bin/s6-linux-init-shutdown -a #{action} -- now
'';
#!${execline}/bin/execlineb -P
${s6-linux-init}/bin/s6-linux-init-hpr -a #{action} -- now
'';
empty = "#!${execline}/bin/execlineb -P\n";
in dir {
crash = {
@ -126,13 +105,7 @@ let
mode = "0755";
};
finish = {
file = ''
${openConsole}
ifelse { test -x /run/maintenance/exec } { /run/maintenance/exec }
foreground { echo "s6-svscan exited. Rebooting." }
wait { }
${s6-linux-init}/bin/s6-linux-init-hpr -fr
'';
file = quit "s6-svscan exited. Rebooting.";
mode = "0755";
};
SIGINT = {
@ -168,6 +141,7 @@ let
};
in {
config = {
programs.busybox.applets = [ "login" "getty" ];
filesystem = dir {
etc = dir {
s6-rc = dir {

View File

@ -1,22 +1,18 @@
#!/bin/sh -e
## s6-linux-init-shutdownd never tells s6-svscan to exit, so if
## you're running s6-linux-init, it's normal that your
## .s6-svscan/finish script is not executed.
### Things to do *right before* the machine gets rebooted or
### powered off, at the very end of the shutdown sequence,
### when all the filesystems are unmounted.
## The place where you want to hack things is /etc/rc.shutdown.final,
## which is run by the stage 4 script right before the hard reboot.
## So you can do dirty stuff [...] which should clean up the
## s6-supervise and the foreground, and give control to
## .s6-svscan/finish.
### This is a last resort hook; normally nothing should be
### done here (your rc.shutdown script should have taken care
### of everything) and you should leave this script empty.
## -- Laurent Bercot on skaware mailing list,
## https://skarnet.org/lists/skaware/1913.html
### Some distributions, however, may need to perform some
### actions after unmounting the filesystems: typically if
### an additional teardown action is required on a filesystem
### after unmounting it, or if the system needs to be
### pivot_rooted before it can be shut down, etc.
exec >/dev/console 2>&1
# down, exit supervisor, wait, stay down
s6-svc -dxwD /run/service/s6-linux-init-shutdownd
# HUP, exit supervisor, wait, down
s6-svc -hxwd /run/service/s6-svscan-log
s6-svscanctl -b /run/service # abort
### Those are all exceptional cases. If you don't know for
### certain that you need to do something here, you don't.

11
modules/standard.nix Normal file
View File

@ -0,0 +1,11 @@
{
# "standard" modules that aren't fundamentally required,
# but are probably useful in most common workflows and
# you should have to opt out of instead of into
imports = [
./tftpboot.nix
./kexecboot.nix
./flashimage.nix
./jffs2.nix
];
}

84
modules/tftpboot.nix Normal file
View File

@ -0,0 +1,84 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkOption types concatStringsSep;
cfg = config.boot.tftp;
in {
imports = [ ./ramdisk.nix ];
options.boot.tftp.freeSpaceBytes = mkOption {
type = types.int;
default = 0;
};
options.system.outputs = {
tftpboot = mkOption {
type = types.package;
description = ''
Directory containing files needed for TFTP booting
'';
};
boot-scr = mkOption {
type = types.package;
description = ''
U-Boot commands to load and boot a kernel and rootfs over TFTP.
Copy-paste into the device boot monitor
'';
};
};
config = {
boot.ramdisk.enable = true;
system.outputs = rec {
tftpboot =
let o = config.system.outputs; in
pkgs.runCommand "tftpboot" {} ''
mkdir $out
cd $out
ln -s ${o.rootfs} rootfs
ln -s ${o.kernel} vmlinux
ln -s ${o.manifest} manifest
ln -s ${o.kernel.headers} build
ln -s ${o.uimage} uimage
ln -s ${o.boot-scr}/dtb dtb
ln -s ${o.boot-scr}/script boot.scr
'';
boot-scr =
let
inherit (pkgs.lib.trivial) toHexString;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
in
pkgs.buildPackages.runCommand "boot-scr" { nativeBuildInputs = [ pkgs.pkgsBuildBuild.dtc ]; } ''
uimageSize=$(($(stat -L -c %s ${o.uimage}) + 0x1000 &(~0xfff)))
rootfsStart=0x$(printf %x $((${cfg.loadAddress} + 0x100000 + $uimageSize &(~0xfffff) )))
rootfsBytes=$(($(stat -L -c %s ${o.rootfs}) + 0x100000 &(~0xfffff)))
rootfsMb=$(($rootfsBytes >> 20))
rootfsBytes=$(($rootfsBytes + ${toString cfg.freeSpaceBytes} ))
cmd="mtdparts=phram0:''${rootfsMb}M(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsBytes},${config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0";
dtbStart=$(printf %x $((${cfg.loadAddress} + $rootfsBytes + 0x100000 + $uimageSize )))
mkdir $out
dtc -I dtb -O dts -o tmp.dts ${o.dtb}
echo "/ { reserved-memory { phram: phram@$rootfsStart {compatible = \"phram\"; reg = <0x0 $rootfsStart 0x0 $(printf "0x%x" $rootfsBytes )>; }; }; } ;" >> tmp.dts
cat tmp.dts
dtc -I dts -O dtb -o $out/dtb tmp.dts
dtbBytes=$(($(stat -L -c %s $out/dtb) + 0x1000 &(~0xfff)))
cat > $out/script << EOF
setenv serverip ${cfg.serverip}
setenv ipaddr ${cfg.ipaddr}
setenv bootargs 'liminix ${cmdline} $cmd'
tftpboot 0x$(printf %x ${cfg.loadAddress}) result/uimage ; tftpboot 0x$(printf %x $rootfsStart) result/rootfs ; tftpboot 0x$dtbStart result/dtb
bootm 0x$(printf %x ${cfg.loadAddress}) - 0x$dtbStart
#
EOF
'';
};
};
}

View File

@ -44,12 +44,20 @@ let
lua = let s = lua_no_readline.override { self = s; }; in s;
in
extraPkgs // {
# liminix library functions
lim = {
parseInt = s : (builtins.fromTOML "r=${s}").r;
};
mtdutils = prev.mtdutils.overrideAttrs(o: {
patches = (if o ? patches then o.patches else []) ++ [
./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch
];
});
rsyncSmall =
let r = prev.rsync.overrideAttrs(o: {
configureFlags = o.configureFlags ++ [
"--disable-openssl"
];
});
in r.override { openssl = null; };
# keep these alphabetical
chrony =
let chrony' = prev.chrony.overrideAttrs(o: {
configureFlags = [
@ -71,6 +79,49 @@ extraPkgs // {
};
strace = prev.strace.override { libunwind = null; };
kexec-tools-static = prev.kexec-tools.overrideAttrs(o: {
# For kexecboot we copy kexec into a ramdisk on the system being
# upgraded from. This is more likely to work if kexec is
# statically linked so doesn't have dependencies on store paths that
# may not exist on that machine. (We can't nix-copy-closure as
# the store may not be on a writable filesystem)
LDFLAGS = "-static";
patches = o.patches ++ [
(fetchpatch {
# merge user command line options into DTB chosen
url = "https://patch-diff.githubusercontent.com/raw/horms/kexec-tools/pull/3.patch";
hash = "sha256-MvlJhuex9dlawwNZJ1sJ33YPWn1/q4uKotqkC/4d2tk=";
})
pkgs/kexec-map-file.patch
];
});
luaFull = prev.lua;
inherit lua;
inherit s6;
s6-linux-init = prev.s6-linux-init.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
s6-rc = prev.s6-rc.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
nftables = prev.nftables.overrideAttrs(o: {
configureFlags = [
"--disable-debug"
"--disable-python"
"--with-mini-gmp"
"--without-cli"
];
});
dnsmasq =
let d = prev.dnsmasq.overrideAttrs(o: {
@ -83,15 +134,6 @@ extraPkgs // {
nettle = null;
};
dropbear = prev.dropbear.overrideAttrs (o: {
postPatch = ''
(echo '#define DSS_PRIV_FILENAME "/run/dropbear/dropbear_dss_host_key"'
echo '#define RSA_PRIV_FILENAME "/run/dropbear/dropbear_rsa_host_key"'
echo '#define ECDSA_PRIV_FILENAME "/run/dropbear/dropbear_ecdsa_host_key"'
echo '#define ED25519_PRIV_FILENAME "/run/dropbear/dropbear_ed25519_host_key"') > localoptions.h
'';
});
hostapd =
let
config = [
@ -119,40 +161,13 @@ extraPkgs // {
});
in h.override { openssl = null; sqlite = null; };
kexec-tools-static = prev.kexec-tools.overrideAttrs(o: {
# For kexecboot we copy kexec into a ramdisk on the system being
# upgraded from. This is more likely to work if kexec is
# statically linked so doesn't have dependencies on store paths that
# may not exist on that machine. (We can't nix-copy-closure as
# the store may not be on a writable filesystem)
LDFLAGS = "-static";
patches = o.patches ++ [
(fetchpatch {
# merge user command line options into DTB chosen
url = "https://patch-diff.githubusercontent.com/raw/horms/kexec-tools/pull/3.patch";
hash = "sha256-MvlJhuex9dlawwNZJ1sJ33YPWn1/q4uKotqkC/4d2tk=";
})
pkgs/kexec-map-file.patch
];
});
luaFull = prev.lua;
inherit lua;
mtdutils = prev.mtdutils.overrideAttrs(o: {
patches = (if o ? patches then o.patches else []) ++ [
./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch
];
});
nftables = prev.nftables.overrideAttrs(o: {
configureFlags = [
"--disable-debug"
"--disable-python"
"--with-mini-gmp"
"--without-cli"
];
dropbear = prev.dropbear.overrideAttrs (o: {
postPatch = ''
(echo '#define DSS_PRIV_FILENAME "/run/dropbear/dropbear_dss_host_key"'
echo '#define RSA_PRIV_FILENAME "/run/dropbear/dropbear_rsa_host_key"'
echo '#define ECDSA_PRIV_FILENAME "/run/dropbear/dropbear_ecdsa_host_key"'
echo '#define ED25519_PRIV_FILENAME "/run/dropbear/dropbear_ed25519_host_key"') > localoptions.h
'';
});
openssl = prev.openssl.overrideAttrs (o: {
@ -169,92 +184,4 @@ extraPkgs // {
});
pppBuild = prev.ppp;
qemuLim = let q = prev.qemu.overrideAttrs (o: {
patches = o.patches ++ [
./pkgs/qemu/arm-image-friendly-load-addr.patch
];
}); in q.override { nixosTestRunner = true; sdlSupport = false; };
rsyncSmall =
let r = prev.rsync.overrideAttrs(o: {
configureFlags = o.configureFlags ++ [
"--disable-openssl"
];
});
in r.override { openssl = null; };
inherit s6;
s6-linux-init = prev.s6-linux-init.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
s6-rc = prev.s6-rc.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
strace = prev.strace.override { libunwind = null; };
ubootQemuAarch64 = final.buildUBoot {
defconfig = "qemu_arm64_defconfig";
extraMeta.platforms = ["aarch64-linux"];
filesToInstall = ["u-boot.bin"];
};
ubootQemuArm = final.buildUBoot {
defconfig = "qemu_arm_defconfig";
extraMeta.platforms = ["armv7l-linux"];
filesToInstall = ["u-boot.bin"];
extraConfig = ''
CONFIG_CMD_UBI=y
CONFIG_CMD_UBIFS=y
CONFIG_BOOTSTD=y
CONFIG_BOOTMETH_DISTRO=y
CONFIG_LZMA=y
CONFIG_CMD_LZMADEC=y
CONFIG_SYS_BOOTM_LEN=0x1000000
'';
};
ubootQemuMips = final.buildUBoot {
defconfig = "malta_defconfig";
extraMeta.platforms = ["mips-linux"];
filesToInstall = ["u-boot.bin"];
# define the prompt to be the same as arm{32,64} so
# we can use the same expect script for both
extraPatches = [ ./pkgs/u-boot/0002-virtio-init-for-malta.patch ];
extraConfig = ''
CONFIG_SYS_PROMPT="=> "
CONFIG_VIRTIO=y
CONFIG_AUTOBOOT=y
CONFIG_DM_PCI=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_NET=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_MMIO=y
CONFIG_QFW_MMIO=y
CONFIG_FIT=y
CONFIG_LZMA=y
CONFIG_CMD_LZMADEC=y
CONFIG_SYS_BOOTM_LEN=0x1000000
CONFIG_SYS_MALLOC_LEN=0x400000
CONFIG_MIPS_BOOT_FDT=y
CONFIG_OF_LIBFDT=y
CONFIG_OF_STDOUT_VIA_ALIAS=y
'';
};
util-linux-small = prev.util-linux.override {
ncursesSupport = false;
pamSupport = false;
systemdSupport = false;
nlsSupport = false;
translateManpages = false;
capabilitiesSupport = false;
};
}

View File

@ -102,8 +102,4 @@ in {
fennel = callPackage ./fennel {};
fennelrepl = callPackage ./fennelrepl {};
anoia = callPackage ./anoia {};
levitate = callPackage ./levitate {};
libubootenv = callPackage ./libubootenv {};
}

View File

@ -7,13 +7,16 @@
, config
, src
, extraPatchPhase ? "echo"
, targets ? ["vmlinux"]
} :
let
writeConfig = import ./write-kconfig.nix { inherit lib writeText; };
kconfigFile = writeConfig "kconfig" config;
arch = stdenv.hostPlatform.linuxArch;
targetNames = map baseNameOf targets;
arch = if stdenv.isMips
then "mips"
else if stdenv.isAarch64
then "arm64"
else throw "unknown arch";
inherit lib; in
stdenv.mkDerivation rec {
name = "kernel";
@ -36,7 +39,7 @@ stdenv.mkDerivation rec {
dontStrip = true;
dontPatchELF = true;
outputs = ["out" "headers" "modulesupport"] ++ targetNames;
outputs = ["out" "headers" "modulesupport"];
phases = [
"unpackPhase"
"butcherPkgconfig"
@ -52,7 +55,6 @@ stdenv.mkDerivation rec {
patches = [
./cmdline-cookie.patch
./phram-allow-cached-mappings.patch
./mips-malta-fdt-from-bootloader.patch
];
# this is here to work around what I think is a bug in nixpkgs
@ -93,12 +95,11 @@ stdenv.mkDerivation rec {
'';
buildPhase = ''
make ${lib.concatStringsSep " " targetNames} modules_prepare -j$NIX_BUILD_CORES
make vmlinux modules_prepare
'';
installPhase = ''
${CROSS_COMPILE}strip -d vmlinux
${lib.concatStringsSep "\n" (map (f: "cp ${f} \$${baseNameOf f}") targets)}
cp vmlinux $out
mkdir -p $headers
cp -a include .config $headers/
@ -107,4 +108,5 @@ stdenv.mkDerivation rec {
make clean modules_prepare
cp -a . $modulesupport
'';
}

View File

@ -1,6 +1,6 @@
/dts-v1/;
// used on arm/aarch64 to provide a U-bootable image that combines
// used on aarch64 to provide a U-bootable image that combines
// kernel and fdt
/ {
@ -12,7 +12,7 @@
description = "Vanilla Linux kernel";
// data = /incbin/("./vmlinux.bin.gz");
type = "kernel";
// arch = "arm64";
arch = "arm64";
os = "linux";
// compression = "gzip";
// load = <00000000>;
@ -28,7 +28,7 @@
description = "Flattened Device Tree blob";
// data = /incbin/("./target.dtb");
type = "flat_dt";
// arch = "arm64";
arch = "arm64";
compression = "none";
hash-1 {
algo = "crc32";

View File

@ -1,15 +0,0 @@
diff --git a/arch/mips/mti-malta/malta-setup.c b/arch/mips/mti-malta/malta-setup.c
index 21cb3ac1237b..52e731f9b4e2 100644
--- a/arch/mips/mti-malta/malta-setup.c
+++ b/arch/mips/mti-malta/malta-setup.c
@@ -192,7 +192,9 @@ static void __init bonito_quirks_setup(void)
void __init *plat_get_fdt(void)
{
- return (void *)__dtb_start;
+ return (fw_arg0 == -2) ?
+ (void *) (KSEG1ADDR(fw_arg1)) :
+ (void *) __dtb_start;
}
void __init plat_mem_setup(void)

View File

@ -7,27 +7,21 @@
} :
let
objcopy = "${stdenv.cc.bintools.targetPrefix}objcopy";
arch = stdenv.hostPlatform.linuxArch;
stripAndZip = ''
${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin
rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin
'';
in {
kernel
, commandLine
, entryPoint
, extraName ? "" # e.g. socFamily
, loadAddress
, imageFormat
, dtb ? null
} : stdenv.mkDerivation {
} :
stdenv.mkDerivation {
name = "kernel.image";
phases = [
"preparePhase"
(if commandLine != null then assert dtb != null; "mungeDtbPhase" else ":")
(if imageFormat == "fit" then "buildPhaseFIT" else "buildPhaseUImage")
"installPhase"
];
(if dtb != null then "dtbPhase" else ":")
"buildPhase"
"installPhase" ];
nativeBuildInputs = [
lzma
dtc
@ -37,43 +31,35 @@ in {
preparePhase = ''
cp ${kernel} vmlinux.elf; chmod +w vmlinux.elf
'';
mungeDtbPhase = ''
dtbPhase = ''
dtc -I dtb -O dts -o tmp.dts ${dtb}
echo '/{ chosen { bootargs = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
dtc -I dts -O dtb -o tmp.dtb tmp.dts
'';
buildPhaseUImage = ''
test -f tmp.dtb && ${objcopy} --update-section .appended_dtb=tmp.dtb vmlinux.elf || ${objcopy} --add-section .appended_dtb=tmp.dtb vmlinux.elf
${stripAndZip}
mkimage -A ${arch} -O linux -T kernel -C lzma -a 0x${lib.toHexString loadAddress} -e 0x${lib.toHexString entryPoint} -n '${lib.toUpper arch} Liminix Linux ${extraName}' -d vmlinux.bin.lzma kernel.uimage
'';
buildPhaseFIT = ''
${stripAndZip}
cat ${./kernel_fdt.its} > mkimage.its
cat << _VARS >> mkimage.its
/ {
images {
kernel {
data = /incbin/("./vmlinux.bin.lzma");
load = <0x${lib.toHexString loadAddress}>;
entry = <0x${lib.toHexString entryPoint}>;
arch = "${arch}";
compression = "lzma";
};
fdt-1 {
data = /incbin/("./tmp.dtb");
arch = "${arch}";
};
};
};
_VARS
mkimage -f mkimage.its kernel.uimage
mkimage -l kernel.uimage
'';
buildPhase =
let arch =
# per output of "mkimage -A list". I *think* these
# are the same as the kernel arch convention, but
# maybe that's coincidence
if stdenv.isMips
then "mips"
else if stdenv.isAarch64
then "arm64"
else throw "unknown arch";
in ''
${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin
rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin
cat ${./kernel_fdt.its} > mkimage.its
echo '/ { images { kernel { data = /incbin/("./vmlinux.bin.lzma"); }; }; };' >> mkimage.its
echo '/ { images { kernel { load = <${loadAddress}>; }; }; };' >> mkimage.its
echo '/ { images { kernel { entry = <${entryPoint}>; }; }; };' >> mkimage.its
echo '/ { images { kernel { compression = "lzma"; }; }; };' >> mkimage.its
echo '/ { images { fdt-1 { data = /incbin/("./tmp.dtb"); }; }; }; ' >> mkimage.its
mkimage -f mkimage.its mkimage.itb
mkimage -l mkimage.itb
'';
installPhase = ''
cp kernel.uimage $out
cp mkimage.itb $out
'';
}

View File

@ -1,75 +0,0 @@
{
writeScriptBin
, writeScript
, systemconfig
, execline
, lib
, services ? null
, liminix
, pseudofile
, pkgs
} :
let
inherit (pseudofile) dir symlink;
inherit (liminix.services) oneshot;
newRoot = "/run/maintenance";
sysconfig =
let
doChroot = writeScript "exec" ''
#!${execline}/bin/execlineb -P
cd ${newRoot}
foreground { mount --move ${newRoot} / }
redirfd -r 0 /dev/console
redirfd -w 1 /dev/console
fdmove -c 2 1
emptyenv chroot . /bin/init
'';
base = {...} : {
config = {
services = services // {
banner = oneshot {
name = "banner";
up = "cat /etc/banner > /dev/console";
down = "true";
};
};
filesystem = dir {
exec = symlink doChroot;
etc = dir {
banner = symlink (pkgs.writeText "banner" ''
LADIES AND GENTLEMEN WE ARE FLOATING IN SPACE
Most services are disabled. The system is operating
with a ram-based root filesystem, making it safe to
overwrite the flash devices in order to perform
upgrades and maintenance.
Don't forget to reboot when you have finished.
'');
};
};
};
};
eval = lib.evalModules {
modules = [
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
../../modules/base.nix
../../modules/users.nix
../../modules/busybox.nix
base
../../modules/s6
];
};
in systemconfig eval.config.filesystem.contents;
in writeScriptBin "levitate" ''
#!/bin/sh
destdir=${newRoot}
mkdir -p $destdir $destdir/nix/store
for path in $(cat ${sysconfig}/etc/nix-store-paths) ; do
(cd $destdir && cp -a $path .$path)
done
${sysconfig}/bin/activate $destdir
''

View File

@ -1,6 +0,0 @@
CFLAGS=$(INC) -D_LINUX_TYPES_H -D_LINUX_STRING_H_
fw_printenv: fw_env_main.o fw_env.o \
crc32.o ctype.o linux_string.o \
env_attr.o env_flags.o
$(CC) -o $@ $^

View File

@ -1,17 +0,0 @@
{ stdenv
, cmake
, zlib
, libyaml
, fetchFromGitHub
} :
stdenv.mkDerivation {
name = "libubootenv";
src = fetchFromGitHub {
owner = "sbabic";
repo = "libubootenv";
rev = "3f4d15e36ceb58085b08dd13f3f2788e9299877b"; # v0.3.5
hash = "sha256-i7gUb1A6FTOBCpympQpndhOG9pCDA4P0iH7ZNBqo+PA=";
};
buildInputs = [ zlib libyaml ];
nativeBuildInputs = [ cmake ];
}

View File

@ -18,7 +18,8 @@ let
${commands}
'';
cleanupScript = name : ''
if test -d ${prefix}/${name} ; then rm -rf ${prefix}/${name} ; fi
#!/bin/sh
test -d ${prefix}/${name} && rm -rf ${prefix}/${name}
'';
service = {
name

View File

@ -14,7 +14,11 @@
, lib
}:
let
arch = stdenv.hostPlatform.linuxArch;
arch = if stdenv.isMips
then "mips"
else if stdenv.isAarch64
then "arm64"
else throw "unknown arch";
openwrtSrc = fetchFromGitHub {
name = "openwrt-source";
repo = "openwrt";
@ -25,52 +29,47 @@ let
inherit (liminix.services) oneshot longrun;
inherit (lib.lists) foldl;
configs = {
ath9k.kconfig = {
ath9k = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH9K = "m";
ATH9K_AHB = "y";
# ATH9K_DEBUGFS = "y";
# ATH_DEBUG = "y";
BACKPORTED_ATH9K_AHB = "y";
};
ath9k_pci = {
module = "ath9k";
kconfig = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH9K = "m";
ATH9K_PCI = "y";
};
};
ath10k_pci.kconfig = {
ath10k_pci = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH10K = "m";
# BACKPORTED_ATH10K_AHB = "y";
# ATH10K_AHB = "y";
ATH10K_PCI = "y";
ATH10K_DEBUG = "y";
};
rt2800soc.kconfig = {
rt2800soc = {
WLAN_VENDOR_RALINK = "y";
RT2800SOC = "m";
RT2X00 = "m";
};
mt7603e.kconfig = { # XXX find a better name for this
mt7603e = { # XXX find a better name for this
WLAN_VENDOR_RALINK = "y";
WLAN_VENDOR_MEDIATEK = "y";
MT7603E = "y";
};
mt7915e.kconfig = {
mt7915e = {
MT7915E = "m";
};
mt7615e.kconfig = {
mt7615e = {
MT7615E = "m";
MT7622_WMAC = "y";
};
mac80211_hwsim.kconfig = {
mac80211_hwsim = {
MAC80211_HWSIM = "y";
};
};
kconfig = (foldl (config: d: (config // configs.${d}.kconfig)) {
kconfig = (foldl (config: d: (config // configs.${d})) {
WLAN = "y";
CFG80211 = "m";
MAC80211 = "m";
@ -87,6 +86,7 @@ let
CFG80211_CRDA_SUPPORT = "n";
MAC80211_MESH = "y";
} drivers) // extraConfig;
writeConfig = name : config: writeText name
@ -166,11 +166,7 @@ let
find . -name \*.ko | cpio --make-directories -p $out/lib/modules/0.0
depmod -b $out -v 0.0
touch $out/load.sh
for i in ${lib.concatStringsSep " "
(map
(d: let c = { module = d; } // configs.${d} ;
in c.module)
drivers)}; do
for i in ${lib.concatStringsSep " " drivers}; do
modprobe -S 0.0 -d $out --show-depends $i >> $out/load.sh
done
tac < $out/load.sh | sed 's/^insmod/rmmod/g' > $out/unload.sh

View File

@ -1,25 +1,18 @@
#!/usr/bin/env bash
ssh_command=${SSH_COMMAND-ssh}
if [ "$1" = "--no-reboot" ] ; then
reboot="true"
shift
else
reboot="reboot"
fi
target_host=$1
shift
if [ -z "$target_host" ] ; then
echo Usage: liminix-rebuild [--no-reboot] target-host params
echo Usage: liminix-rebuild target-host params
exit 1
fi
if toplevel=$(nix-build "$@" -A outputs.systemConfiguration --no-out-link); then
echo systemConfiguration $toplevel
min-copy-closure $target_host $toplevel
$ssh_command $target_host $toplevel/bin/install
$ssh_command $target_host cp -v -fP $toplevel/bin/* $toplevel/etc/* /persist
$ssh_command $target_host "sync; source /etc/profile; reboot"
else
echo Rebuild failed

View File

@ -0,0 +1,2 @@
(local ssh (io.popen "

View File

@ -1,49 +1,20 @@
#!/usr/bin/env bash
ssh_command=${SSH_COMMAND-ssh}
root_prefix=/
verbose=true
while [[ $# -gt 0 ]]; do
case $1 in
-r|--root)
root_prefix="$2"
shift
shift
;;
-q|--quiet)
verbose=""
shift
;;
-*|--*)
echo "Unknown option $1"
exit 1
;;
*)
if test -z "$target_host"; then
target_host="$1"
else
paths+=("$1") # save positional arg
fi
shift # past argument
;;
esac
done
progress() {
test -n "$verbose" && echo $*
}
target_host=$1
shift
if [ -z "$target_host" ] ; then
echo Usage: min-copy-closure [--root /mnt] target-host paths
echo Usage: min-copy-closure target-host paths
exit 1
fi
if [ -z "$IN_NIX_BUILD" ] ; then
if [ -n "$IN_NIX_BUILD" ] ; then
# can't run nix-store in a derivation, so we have to
# skip the requisites when running tests in hydra
paths=$(nix-store -q --requisites "$paths")
paths=$@
else
paths=$(nix-store -q --requisites "$@")
fi
needed=""
@ -54,16 +25,16 @@ coproc remote {
exec 10>&${remote[1]}
for p in $paths; do
progress -n Checking $(basename $p) ...
echo "test -e ${root_prefix}$p && echo skip || echo $p" >&10
echo -n Checking $(basename $p) ...
echo "test -e $p && echo skip || echo $p" >&10
read n <&${remote[0]}
case $n in
skip)
progress skip
echo skip
;;
*)
needed="${needed} $n"
progress will copy
echo will copy
;;
esac
done
@ -73,11 +44,10 @@ if test -z "$needed" ; then
exit 1
fi
echo "cd ${root_prefix} && cpio -d -i >/dev/console" >&10
echo "cd / && cpio -i >/dev/console" >&10
find $needed | cpio -H newc -o >&10
find $needed | cpio -H newc -o >&10
# make sure the connection hasn't died
echo "echo finished" >&10
echo "date" >&10
read n <&${remote[0]}
echo $n

View File

@ -34,5 +34,4 @@ in {
applyPatches.ath79 = doPatch "ath79";
applyPatches.ramips = doPatch "ramips";
applyPatches.mediatek = doPatch "mediatek"; # aarch64
applyPatches.mvebu = doPatch "mvebu"; # arm
}

View File

@ -1,5 +0,0 @@
CFLAGS+=-Os -fno-stack-protector -fpic -fPIC
LDFLAGS=-static
CFLAGS+=-Wall -Werror
preinit: preinit.o parseopts.o

View File

@ -15,6 +15,7 @@ stdenv.mkDerivation {
# NIX_DEBUG=2;
hardeningDisable = [ "all" ];
CFLAGS = "-Os -static -DPREINIT_USE_LIBC -fno-stack-protector -fpic -fPIC -I ./ -I ${kernel}/tools/include/nolibc";
postBuild = ''
$STRIP --remove-section=.note --remove-section=.comment preinit

View File

@ -1,132 +0,0 @@
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
static int begins_with(char * str, char * prefix)
{
while(*prefix) {
if(*str == '\0') return 0;
if(*str != *prefix) return 0;
str++;
prefix++;
}
return 1;
}
char * pr_u32(int32_t input) {
static char buf[9];
const char *digits = "0123456789abcdef";
int i=0;
buf[i] = digits[(input & 0xf0000000) >> 28];
buf[i+1] = digits[(input & 0x0f000000) >> 24];
if(buf[i] != '0' || buf[i+1] != '0') i+=2;
buf[i] = digits[(input & 0x00f00000) >> 20];
buf[i+1] = digits[(input & 0x000f0000) >> 16];
if(buf[i] != '0' || buf[i+1] != '0') i+=2;
buf[i] = digits[(input & 0x0000f000) >> 12];
buf[i+1] = digits[(input & 0x00000f00) >> 8];
if(buf[i] != '0' || buf[i+1] != '0') i+=2;
buf[i] = digits[(input & 0x000000f0) >> 4];
buf[i+1] = digits[(input & 0x0000000f)];
i+=2;
buf[i] ='\0';
write(2, buf, i);
return buf;
}
void parseopts(char * cmdline, char **root, char **rootfstype) {
*root = 0;
*rootfstype = 0;
for(char *p = cmdline; *p; p++) {
if(begins_with(p, "root=")) {
*root = p + strlen("root=");
while(*p && (*p != ' ')) p++;
if(*p) {
*p = '\0';
p++;
};
};
if(begins_with(p, "rootfstype=")) {
*rootfstype = p + strlen("rootfstype=");
while(*p && (*p != ' ')) p++;
if(*p) {
*p = '\0';
p++;
};
};
};
}
#ifdef TESTS
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
// cc -DTESTS -o parseopts parseopts.c && ./parseopts
#define die(fmt, ...) do { printf(fmt, __VA_ARGS__); exit(1); } while(0)
#define S(x) #x
#define expect_equal(actual, expected) \
if(!actual || strcmp(actual, expected)) die("%d: expected \"%s\", got \"%s\"", __LINE__, expected, actual);
int main()
{
char * root = "/dev/hda1";
char * rootfstype = "xiafs";
char *buf;
// finds root= and rootfstype= options
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0 foo");
parseopts(buf, &root, &rootfstype);
expect_equal(root, "/dev/mtdblock0");
expect_equal(rootfstype, "ubifs");
// in case of duplicates, chooses the latter
// also: works if the option is end of string
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0");
parseopts(buf, &root, &rootfstype);
expect_equal(root, "/dev/mtdblock0");
expect_equal(rootfstype, "ubifs");
// options may appear in either order
buf = strdup("liminix fw_devlink=off root=/dev/hda1 rootfstype=ubifs foo");
parseopts(buf, &root, &rootfstype);
expect_equal(root, "/dev/hda1");
expect_equal(rootfstype, "ubifs");
buf = strdup("liminix rootfstype=ubifs fw_devlink=off root=/dev/hda1 foo");
parseopts(buf, &root, &rootfstype);
expect_equal(rootfstype, "ubifs");
expect_equal(root, "/dev/hda1");
// provides NULL for missing options
buf = strdup("liminix rufustype=ubifs fw_devlink=off foot=/dev/hda1");
parseopts(buf, &root, &rootfstype);
if(rootfstype) die("expected null rootfstype, got \"%s\"", rootfstype);
if(root) die("expected null root, got \"%s\"", root);
// provides empty strings for empty options
buf = strdup("liminix rootfstype= fw_devlink=off root= /dev/hda1");
parseopts(buf, &root, &rootfstype);
if(strlen(rootfstype)) die("expected empty rootfstype, got \"%s\"", rootfstype);
if(strlen(root)) die("expected null root, got \"%s\"", root);
expect_equal("01", pr_u32(1));
expect_equal("ab", pr_u32(0xab));
expect_equal("0abc", pr_u32(0xabc));
expect_equal("aabc", pr_u32(0xaabc));
expect_equal("deadcafe", pr_u32(0xdeadcafe));
}
#endif

View File

@ -1,47 +1,37 @@
#ifdef PREINIT_USE_LIBC
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
void parseopts(char * cmdline, char **root, char **rootfstype);
#else
#include <nolibc.h>
#endif
#include <asm/setup.h>
#define ERR(x) write(2, x, strlen(x))
#define AVER(c) do { if(c < 0) { ERR("failed: " #c ": error=0x" ); pr_u32(errno); ERR("\n"); } } while(0)
#define AVER(c) do { if(c < 0) ERR("failed: " #c); } while(0)
char * pr_u32(int32_t input);
static void die() {
/* if init exits, it causes a kernel panic. On the Turris
* Omnia (and maybe other hardware, I don't know), the kernel
* panics _before_ any of the messages from AVER are printed,
* which makes it really hard to tell what went wrong. So
* let's wait a little here to give the console a chance to
* catch up.
*
* Yes, I know that file descriptor IO is supposedly
* non-buffered. Empirical observation suggests that there
* must be a buffer of some kind somewhere though.
*/
sleep(10);
exit(1);
static int begins_with(char * str, char * prefix)
{
while(*prefix) {
if(*str == '\0') return 0;
if(*str != *prefix) return 0;
str++;
prefix++;
}
return 1;
}
static int fork_exec(char * command, char *args[])
static void fork_exec(char * command, char *args[])
{
int fork_pid = fork();
AVER(fork_pid);
if(fork_pid > 0)
return wait(NULL);
else
return execve(command, args, NULL);
int fork_pid = fork();
AVER(fork_pid);
if(fork_pid > 0)
wait(NULL);
else
AVER(execve(command, args, NULL));
}
char banner[] = "Running pre-init...\n";
@ -49,53 +39,52 @@ char buf[COMMAND_LINE_SIZE];
int main(int argc, char *argv[], char *envp[])
{
char *rootdevice = 0;
char *rootfstype = 0;
#ifndef PREINIT_USE_LIBC
asm("la $gp, _gp\nsw $gp,16($sp)");
#endif
char *rootdevice = 0;
char *p = buf;
write(1, banner, strlen(banner));
write(1, banner, strlen(banner));
mount("none", "/proc", "proc", 0, NULL);
AVER(mount("none", "/proc", "proc", 0, NULL));
AVER(mount("none", "/dev", "devtmpfs", 0, NULL));
int cmdline = open("/proc/cmdline", O_RDONLY, 0);
int cmdline = open("/proc/cmdline", O_RDONLY, 0);
if(cmdline>=0) {
int len = read(cmdline, buf, sizeof buf - 1);
buf[len]='\0';
write(1, "cmdline ", 8);
write(1, buf, len);
};
if(cmdline>=0) {
int len = read(cmdline, buf, sizeof buf - 1);
buf[len]='\0';
while(buf[len-1]=='\n') {
buf[len-1]='\0';
len--;
while(*p) {
if(begins_with(p, "root=")) {
rootdevice = p + 5;
while(*p && (*p != ' ')) p++;
*p= '\0';
}
while(*p && (*p != ' ')) p++;
p++;
}
write(1, "cmdline: \"", 10);
write(1, buf, len);
write(1, "\"\n", 2);
} else {
ERR("failed: open(\"/proc/cmdline\")\n");
die();
}
parseopts(buf, &rootdevice, &rootfstype);
if(rootdevice) {
if(!rootfstype) rootfstype = "jffs2"; /* backward compatibility */
write(1, "rootdevice ", 11);
write(1, rootdevice, strlen(rootdevice));
write(1, " (", 2);
write(1, rootfstype, strlen(rootfstype));
write(1, ")\n", 2);
AVER(mount(rootdevice, "/target/persist", rootfstype, 0, NULL));
AVER(mount("/target/persist/nix", "/target/nix",
"bind", MS_BIND, NULL));
if(rootdevice) {
write(1, "rootdevice ", 11);
write(1, rootdevice, strlen(rootdevice));
write(1, "\n", 1);
char *exec_args[] = { "activate", "/target", NULL };
AVER(fork_exec("/target/persist/activate", exec_args));
AVER(chdir("/target"));
AVER(mount(rootdevice, "/target/persist", "jffs2", 0, NULL));
AVER(mount("/target/persist/nix", "/target/nix",
"bind", MS_BIND, NULL));
AVER(mount("/target", "/", "bind", MS_BIND | MS_REC, NULL));
AVER(chroot("."));
char *exec_args[] = { "activate", "/target", NULL };
fork_exec("/target/persist/activate", exec_args);
AVER(chdir("/target"));
argv[0] = "init";
argv[1] = NULL;
AVER(execve("/persist/init", argv, envp));
}
die();
AVER(mount("/target", "/", "bind", MS_BIND | MS_REC, NULL));
AVER(chroot("."));
argv[0] = "init";
argv[1] = NULL;
AVER(execve("/persist/init", argv, envp));
}
}

View File

@ -1,5 +0,0 @@
with import <nixpkgs> {};
mkShell {
name = "preinit-env";
src = ./.;
}

View File

@ -1,31 +0,0 @@
Loading kernel at offset 0x10000 works only for zImage, but not for Image,
because the kernel expect the start of decompressed kernel (.head.text) to be
at an address that's a distance that's 16MB aligned from PAGE_OFFSET +
TEXT_OFFSET (see vmlinux.lds.S). This check is enfornced in __fixup_pv_table in
arch/arm/kernel/head.S TEXT_OFFSET is 0x00008000, so a 16MB alignment needs to
have a "0x8000" in the lower 16 bits so that they cancel out. Currently the
offset Qemu loads it at is 0x10000.
With zImage, this need is met because zImage loads the uncompressed Image
correctly, however when loading an Image and executing directly Qemu is
required it to load it at the correct location. Doing so, doesn't break Qemu's
zImage loading. With this patch, both zImage and Image work correctly.
Original patch from https://patchwork.kernel.org/project/linux-arm-kernel/patch/1395718484-20424-1-git-send-email-joelf@ti.com/ was
Signed-off-by: Joel Fernandes <joelf@ti.com>
(Edited by Daniel Barlow to apply cleanly to more recent QEMU)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index ada2717f76..18bcdd45d2 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -32,7 +32,7 @@
*/
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_NOLOAD_ADDR 0x02000000
-#define KERNEL_LOAD_ADDR 0x00010000
+#define KERNEL_LOAD_ADDR 0x00008000
#define KERNEL64_LOAD_ADDR 0x00080000
#define ARM64_TEXT_OFFSET_OFFSET 8

View File

@ -1,23 +1,19 @@
{
qemuLim
qemu
, socat
, writeShellScript
, writeFennel
, runCommand
, writeShellScriptBin
, symlinkJoin
, lib
, lua
, pkgsBuildBuild
}: let
run-liminix-vm = pkgsBuildBuild.writeFennel "run-liminix-vm" {
packages = [ qemuLim lua.pkgs.luaposix lua.pkgs.fennel ];
} ./run-liminix-vm.fnl;
connect = writeShellScript "connect-vm" ''
run-liminix-vm = writeShellScriptBin "run-liminix-vm" ''
export PATH="${lib.makeBinPath [qemu]}:$PATH"
${builtins.readFile ./run-liminix-vm.sh}
'';
connect = writeShellScriptBin "connect-vm" ''
export PATH="${lib.makeBinPath [socat]}:$PATH"
socat -,raw,echo=0,icanon=0,isig=0,icrnl=0,escape=0x0f unix-connect:$1
'';
in runCommand "vm" {} ''
mkdir -p $out/bin
cd $out/bin
ln -s ${connect} ./connect-vm
ln -s ${run-liminix-vm} ./run-liminix-vm
''
in symlinkJoin {
name = "run-liminix-vm";
paths = [ run-liminix-vm connect ];
}

View File

@ -1,145 +0,0 @@
(local { : fork : execp : unlink } (require :posix.unistd))
(local { : wait } (require :posix.sys.wait))
(local { : mkstemp : setenv } (require :posix.stdlib))
(local { : fdopen } (require :posix.stdio))
(fn pad-file [name kb chr]
(let [(fd out) (mkstemp "run-vm-XXXXXX")
pad-string (string.rep (or chr "\0") 1024)]
(with-open [f (fdopen fd :w)]
(for [i 1 kb] (f:write pad-string))
(f:seek :set 0)
(with-open [input (assert (io.open name :rb))]
(f:write (input:read "*a")))
(f:seek :end 0))
out))
(fn spawn [command args]
(match (fork)
(nil msg) (error (.. "couldn't fork: " msg))
0 (execp command args)
pid (wait pid)))
(fn appendm [t2 t1]
(table.move t1 1 (# t1) (+ 1 (# t2)) t2)
t2)
(fn merge [table1 table2]
(collect [k v (pairs table2) &into table1]
k v))
(fn assoc [tbl k v]
(tset tbl k v)
tbl)
(fn parse-args [args]
(match args
["--background" dir & rest] (assoc (parse-args rest) :background dir)
["--u-boot" bin & rest]
(assoc (parse-args rest) :u-boot bin)
["--disk-image" image & rest ] (assoc (parse-args rest)
:disk-image (pad-file image 1024))
["--arch" arch & rest] (assoc (parse-args rest) :arch arch)
["--phram-address" addr & rest] (assoc (parse-args rest) :phram-address addr)
["--lan" spec & rest] (assoc (parse-args rest) :lan spec)
["--wan" spec & rest] (assoc (parse-args rest) :wan spec)
["--command-line" cmd & rest] (assoc (parse-args rest) :command-line cmd)
["--flag" flag & rest] (let [o (parse-args rest)]
(assoc o :flags (doto o.flags (table.insert 1 flag))))
[kernel rootfsimg]
{ :flags [] :kernel kernel :rootfs (pad-file rootfsimg (* 16 1024)) }
))
(fn pad-u-boot [options]
(if options.u-boot
(let [size (.
{
:mips (* 4 1024)
:aarch64 (* 64 1024)
:arm (* 64 1024)
}
options.arch)]
(assoc options
:u-boot
(pad-file options.u-boot size "\xff")))
options))
(local options
(assert
(pad-u-boot
(merge { :arch "mips" } (parse-args arg)))
(.. "Usage: " (. arg 0) " blah bah")))
(fn background [dir]
(let [pid (.. dir "/pid")
sock (.. dir "/console")
monitor (.. dir "/monitor")]
["--daemonize"
"--pidfile" pid
"-serial" (.. "unix:" sock ",server,nowait")
"-monitor" (.. "unix:" monitor ",server,nowait")]))
(fn access-net [override]
[
"-netdev" (.. (or override
"socket,mcast=230.0.0.1:1234,localaddr=127.0.0.1")
",id=access")
"-device" "virtio-net,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02"
])
(fn local-net [override]
[
"-netdev" (.. (or override "socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1")
",id=lan")
"-device" "virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:1d:ea:21:01"
])
(fn bootable [cmdline uboot disk]
(if uboot
["-drive" (.. "if=pflash,format=raw,file=" uboot )
"-drive" (.. "if=none,format=raw,id=hd0,file=" disk)
"-device" "virtio-blk-pci,drive=hd0"
]
(let [cmdline (.. "root=/dev/mtdblock0" " " cmdline " mem=256M liminix mtdparts=phram0:16M(rootfs) phram.phram=phram0," options.phram-address ",16Mi,65536")]
["-kernel" options.kernel "-append" cmdline])))
(local bin {
:mips ["qemu-system-mips" "-M" "malta"]
:aarch64 ["qemu-system-aarch64" "-M" "virt"
"-cpu" "cortex-a72"]
:arm ["qemu-system-arm" "-M" "virt,highmem=off"
"-cpu" "cortex-a15"]
})
(local exec-args
(-> []
(appendm (. bin options.arch))
(appendm ["-echr" "16"])
(appendm options.flags)
(appendm (if options.phram-address
[
"-m" "272"
"-device"
(.. "loader,file=" options.rootfs ",addr=" options.phram-address)
]
["-m" "256"]))
(appendm
(if options.background
(background options.background)
["-serial" "mon:stdio"]))
(appendm (bootable (or options.command-line "")
options.u-boot options.disk-image))
(appendm (access-net options.wan))
(appendm (local-net options.lan))
(appendm ["-display" "none"])))
(each [n a (ipairs exec-args)]
(print (.. (if (> n 1) " " "") (string.format "%q" a))))
(match exec-args
[cmd & params] (print (spawn cmd params)))
(if options.rootfs (unlink options.rootfs))
(if options.u-boot (unlink options.u-boot))
(if options.disk-image (unlink options.disk-image))

View File

@ -0,0 +1,69 @@
#!/usr/bin/env bash
cleanup(){
test -n "$rootfs" && test -f $rootfs && rm $rootfs
}
trap 'exit 1' INT HUP QUIT TERM ALRM USR1
trap 'cleanup' EXIT
usage(){
echo "usage: $(basename $0) [--background /path/to/state_directory] kernel rootimg [initramfs]"
echo -e "\nWithout --background, use C-p c (not C-a c) to switch to the monitor"
exit 1
}
arch="mips"
if test "$1" = "--arch" ; then
arch=$2
shift;shift
fi
if test "$1" = "--background" ; then
statedir=$2
if test -z "$statedir" || ! test -d $statedir ; then
usage
fi
pid="${statedir}/pid"
socket="${statedir}/console"
monitor="${statedir}/monitor"
echo "running in background, socket is $socket, pid $pid"
flags="--daemonize --pidfile $pid -serial unix:$socket,server,nowait -monitor unix:$monitor,server,nowait"
shift;shift
else
flags="-serial mon:stdio"
fi
test -n "$2" || usage
lan=${LAN-"socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1"}
rootfs=$(mktemp run-liminix-vm-fs-XXXXXX)
dd if=/dev/zero of=$rootfs bs=1M count=16 conv=sync
dd if=$2 of=$rootfs bs=65536 conv=sync,nocreat,notrunc
if test -n "$3"; then
initramfs="-initrd $3"
fi
case "$arch" in
mips)
QEMU="qemu-system-mips -M malta"
;;
aarch64)
QEMU="qemu-system-aarch64 -M virt -semihosting -cpu cortex-a72"
;;
esac
INIT=${INIT-/bin/init}
set -x
$QEMU \
-m 256 \
-echr 16 \
-append "$CMDLINE liminix root=/dev/mtdblock0 block2mtd.block2mtd=/dev/vda,65536" \
-drive file=$rootfs,format=raw,readonly=off,if=virtio,index=0 \
${initramfs} \
-netdev socket,id=access,mcast=230.0.0.1:1234,localaddr=127.0.0.1 \
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02 \
-netdev ${lan},id=lan \
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:1d:ea:21:01 \
-kernel $1 -display none $flags ${QEMU_OPTIONS}

View File

@ -68,7 +68,7 @@ in attrset:
src = ./.;
CFLAGS = "-Os";
LDFLAGS = "-static -Xlinker -static";
LDFLAGS = "-static";
postConfigure = ''
cp ${makedevs} makedevs.c
@ -80,10 +80,5 @@ in attrset:
cp $closure/store-paths $out/etc/nix-store-paths
$STRIP --remove-section=.note --remove-section=.comment --strip-all makedevs -o $out/bin/activate
ln -s ${s6-init-bin}/bin/init $out/bin/init
cat > $out/bin/install <<EOF
#!/bin/sh
cp -v -fP $out/bin/* $out/etc/* \''${1-/}/persist
EOF
chmod +x $out/bin/install
'';
}

View File

@ -195,22 +195,19 @@ function tftp:handle_RRQ(socket, host, port, source, options)
]]
yield(false, true)
end
socket:sendto(self.DATA(data, tid), host, port)
--[[ Now check for an ACK.
RFC1350 requires that for every packet sent, an ACK is received
before the next packet can be sent.
]]
local acked
local retried = 0
local timeout = time() + timeout_secs
local timedout = false
repeat
socket:sendto(self.DATA(data, tid), host, port)
--[[ Now check for an ACK.
RFC1350 requires that for every packet sent, an ACK is received
before the next packet can be sent.
]]
yield(true, false) -- we need to wait until the socket is readable again
local ack, ackhost, ackport = socket:recvfrom(ACKSIZE)
local ack_sequence = self.parse_ACK(ack)
if ackhost ~= host or ackport ~= port then
if ackhost ~= host or ackport ~= port or self.parse_ACK(ack) ~= tid then
--[[https://tools.ietf.org/html/rfc1350#page-5
"If a source TID does not match, the packet should be
discarded as erroneously sent from somewhere else.
@ -219,21 +216,13 @@ function tftp:handle_RRQ(socket, host, port, source, options)
]]
socket:sendto(err(ERR_UNKNOWN_ID), ackhost, ackport)
yield(true, false)
elseif ack_sequence ~= tid then
-- this looks confusing, but the local variable
-- "tid" here is actually block number (aka
-- sequence number), not tid at all.
log(("ack received for old block %d (expecting %d)"):format(ack_sequence, tid))
acked = false
else
acked = true
end
if not acked then log("resending") end
else
acked = true
end
retried = retried + 1
timedout = time() > timeout
until acked or retried > ACK_RETRIES
if retried > ACK_RETRIES then
until acked or retried > ACK_RETRIES or timedout
if timedout or retried > ACK_RETRIES then
--There doesn't seem to be a standard error for timeout.
socket:sendto(err("Ack timeout"), host, port)
error("Timeout waiting for ACK")

View File

@ -1,12 +0,0 @@
diff --git a/include/configs/qemu-arm.h b/include/configs/qemu-arm.h
index 535762ecb2..a947bae2e8 100644
--- a/include/configs/qemu-arm.h
+++ b/include/configs/qemu-arm.h
@@ -72,6 +72,7 @@
BOOT_TARGET_SCSI(func) \
BOOT_TARGET_VIRTIO(func) \
BOOT_TARGET_NVME(func) \
+ func(UBIFS, ubifs, 0, UBI, boot) \
BOOT_TARGET_DHCP(func)
#include <config_distro_bootcmd.h>

View File

@ -1,15 +0,0 @@
diff --git a/board/imgtec/malta/malta.c b/board/imgtec/malta/malta.c
index 9853a0ba82..d95e332d6d 100644
--- a/board/imgtec/malta/malta.c
+++ b/board/imgtec/malta/malta.c
@@ -169,7 +169,9 @@ int board_early_init_f(void)
int misc_init_r(void)
{
rtc_reset();
-
+#if IS_ENABLED(CONFIG_VIRTIO)
+ virtio_init();
+#endif
return 0;
}

View File

@ -27,7 +27,6 @@ name :
echo "#!${lua}/bin/lua ${luaFlags}"
echo "package.path = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luapath)} .. package.path"
echo "package.cpath = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luacpath)} .. package.cpath"
echo "local ok, stdlib = pcall(require,'posix.stdlib'); if ok then stdlib.setenv('PATH',${lib.escapeShellArg (lib.makeBinPath packages)} .. \":\" .. os.getenv('PATH')) end"
fennel ${if correlate then "--correlate" else ""} --compile ${source}
) > ${name}.lua
'';

View File

@ -3,9 +3,7 @@
pseudofiles = import ./pseudofiles/test.nix;
wlan = import ./wlan/test.nix;
pppoe = import ./pppoe/test.nix;
jffs2 = import ./jffs2/test.nix;
ext4 = import ./ext4/test.nix;
jffs2 = import ./jffs2/test.nix;
min-copy-closure = import ./min-copy-closure/test.nix;
fennel = import ./fennel/test.nix;
tftpboot = import ./tftpboot/test.nix;
}

View File

@ -1,19 +0,0 @@
{ config, pkgs, lib, ... } :
let
inherit (pkgs.pseudofile) dir symlink;
in {
imports = [
../../modules/outputs/ext4fs.nix
];
config = {
rootfsType = "ext4";
filesystem = dir {
hello = {
type = "f";
uid = 7;
gid = 24;
file = "hello world";
};
};
};
}

View File

@ -1,10 +0,0 @@
set timeout 10
spawn socat unix-connect:vm/console -
send "\r\n"
expect "#"
send "echo HELLO WORLD > /hello\r\n"
expect "#"
send "cat /hello\r\n"
expect 'HELLO WORLD'
close

View File

@ -1,19 +0,0 @@
{
liminix
, nixpkgs
}:
let img = (import liminix {
device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix;
}).outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [
expect
socat
] ;
} ''
mkdir vm
${img}/run.sh --background ./vm
expect ${./script.expect} >$out
''

View File

@ -5,7 +5,7 @@ in {
imports = [
../../vanilla-configuration.nix
../../modules/squashfs.nix
../../modules/outputs/jffs2.nix
../../modules/jffs2.nix
];
config.rootfsType = "jffs2";
config.filesystem = dir {

View File

@ -2,6 +2,7 @@ set timeout 10
spawn socat unix-connect:vm/console -
send "\r\n"
expect "login:" { send "root\r\n" }
expect "#"
send "echo HELLO WORLD > /hello\r\n"
expect "#"

View File

@ -1,8 +0,0 @@
{ config, pkgs, lib, ... } :
{
imports= [
./configuration.nix
../../modules/outputs/ext4fs.nix
];
rootfsType = lib.mkForce "ext4";
}

View File

@ -13,7 +13,7 @@ let
in {
imports = [
../../vanilla-configuration.nix
../../modules/outputs/jffs2.nix
../../modules/jffs2.nix
];
config = {
services.sshd = longrun {

View File

@ -1,58 +0,0 @@
# This is a test for liminix-rebuild. It's not a CI test because
# liminix-rebuild calls nix-build so won't run inside a derivation,
# meaning you have to remember to run it manually when changing
# liminix-rebuild
# nix-shell -p expect socat --run "sh ./tests/min-copy-closure/test-liminix-rebuild.sh "
. tests/test-helpers.sh
set -e
here=$(pwd)/tests/min-copy-closure
top=$(pwd)
work=$(mktemp -d -t "test-lim-rebuild-XXXXXX")
echo $work
cd $work
deriv(){
(cd $top && nix-build -I liminix-config=${here}/config-ext4.nix --arg device "import ./devices/qemu-armv7l" -A $1 );
}
PATH=$(deriv pkgs.pkgsBuildBuild.min-copy-closure)/bin:$(deriv pkgs.pkgsBuildBuild.run-liminix-vm)/bin:$PATH
rootfs=$(deriv outputs.rootfs)
kernel=$(deriv outputs.zimage)
uboot=$(deriv outputs.u-boot)
test -d ./vm && rm -rf vm
mkdir ./vm
cat ${rootfs} > rootfs
truncate -s 24M rootfs
resize2fs rootfs
dd if=rootfs of=disk-image bs=512 seek=4 conv=sync
echo '4,-,L,*' | sfdisk disk-image
run-liminix-vm --background vm \
--command-line "console=ttyAMA0 panic=10 oops=panic loglevel=8 root=/dev/vda1 rootfstype=ext4" \
--phram-address 0x50000000 --arch arm \
--lan "user,hostfwd=tcp::2022-:22" \
--flag -append --flag "root=/dev/vda1" --flag -hda \
--flag disk-image $kernel /dev/null
expect ${here}/wait-until-ready.expect
echo "READY"
touch known_hosts
export SSH_COMMAND="ssh -o UserKnownHostsFile=${work}/known_hosts -o StrictHostKeyChecking=no -p 2022 -i ${here}/id"
(cd ${top} && liminix-rebuild root@localhost -I liminix-config=${here}/with-figlet.nix --arg device "import ./devices/qemu-armv7l")
ls -l vm
cd ${work} && expect $here/wait-for-reboot.expect
cd / ; rm -rf $work

View File

@ -9,9 +9,11 @@ let lmx = (import liminix {
rogue = lmx.pkgs.rogue;
img = lmx.outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
inherit (pkgs.pkgsBuildBuild) run-liminix-vm;
in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [
expect
run-liminix-vm
socat
min-copy-closure
rogue
@ -19,15 +21,11 @@ in pkgs.runCommand "check" {
} ''
. ${../test-helpers.sh}
(
mkdir vm
${img}/run.sh --lan user,hostfwd=tcp::2022-:22 --background ./vm
LAN=user,hostfwd=tcp::2022-:22 run-liminix-vm --background ./vm ${img}/vmlinux ${img}/rootfs
expect ${./wait-until-ready.expect}
export SSH_COMMAND="ssh -o StrictHostKeyChecking=no -p 2022 -i ${./id}"
$SSH_COMMAND root@localhost echo ready
IN_NIX_BUILD=true min-copy-closure --quiet root@localhost ${rogue}
$SSH_COMMAND root@localhost ls -ld ${rogue}
IN_NIX_BUILD=true min-copy-closure --root /run root@localhost ${rogue}
$SSH_COMMAND root@localhost ls -ld /run/${rogue}
) 2>&1 | tee $out
IN_NIX_BUILD=true min-copy-closure root@localhost ${rogue}
$SSH_COMMAND root@localhost ls -l ${rogue} >$out
''

View File

@ -1,18 +0,0 @@
set timeout 60
spawn socat unix-connect:vm/console -
expect {
"s6-svscan exited" { }
timeout { exit 1 }
}
expect {
"s6-linux-init" { send "\r\n" };
"# " { send "\r\n" };
}
expect {
"# " { send "echo \$PATH; md5sum /persist/activate; figlet Yes\r\n" };
}
expect "#"

View File

@ -2,7 +2,4 @@ set timeout 60
spawn socat unix-connect:vm/console -
send "\r\n"
expect {
"# " { send "hostname\r\n" };
}
expect "(none)"
expect "login:"

View File

@ -1,7 +0,0 @@
{ pkgs, ... } :
{
imports= [./config-ext4.nix];
defaultProfile.packages = with pkgs; [
figlet
];
}

View File

@ -2,6 +2,7 @@ set timeout 60
spawn socat unix-connect:vm/console -
send "\r\n"
expect "login:" { send "root\r\n" }
expect "#"
set FINISHED 0
set EXIT "1"
@ -10,7 +11,7 @@ while { $FINISHED < 10 } {
expect {
"192.168.100.1" { set FINISHED 20; set EXIT 0; }
"can't find device" { send_user "waiting ..." ; send "\r\n"; sleep 3 }
"DOWN" { send_user "waiting ..." ; send "\r\n"; sleep 3 }
"DOWN" { send_user "waiting ..." ; send "\r\n"; sleep 2 }
}
set FINISHED [ expr $FINISHED + 1 ]
}

Some files were not shown because too many files have changed in this diff Show More