c50423f689
On this device we don't need the openwrt kernel or patches. The newer kernel also fixes the weird one minute pause at boot when it was doing something with either mmc or switch.
412 lines
14 KiB
Nix
412 lines
14 KiB
Nix
{
|
|
description = ''
|
|
Turris Omnia
|
|
************
|
|
|
|
This is a 32 bit ARMv7 MVEBU device, which is usually shipped with
|
|
TurrisOS, an OpenWrt-based system. Rather than reformatting the
|
|
builtin storage, we install Liminix on to the existing btrfs
|
|
filesystem so that the vendor snapshot/recovery system continues
|
|
to work (and provides you an easy rollback if you decide you don't
|
|
like Liminix after all).
|
|
|
|
The install process has two stages, and is intended that you
|
|
should not need to open the device and add a serial console
|
|
(although it may be handy for visibility, and in case anything
|
|
goes wrong). First we build a minimal installation/recovery
|
|
system, then we reboot into that recovery image to prepare the
|
|
device for the full target install.
|
|
|
|
Installation using a USB stick
|
|
==============================
|
|
|
|
First, build the image for the USB stick. Review
|
|
:file:`examples/recovery.nix` in order to change the default
|
|
root password (which is ``secret``) and/or the SSH keys, then
|
|
build it with
|
|
|
|
.. code-block:: console
|
|
|
|
$ nix-build -I liminix-config=./examples/recovery.nix \
|
|
--arg device "import ./devices/turris-omnia" \
|
|
-A outputs.mbrimage -o mbrimage
|
|
$ file -L mbrimage
|
|
mbrimage: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,5), end-CHS (0x6,130,26), startsector 4, 104602 sectors
|
|
|
|
Next, copy the image from your build machine to a USB storage
|
|
medium using :command:`dd` or your other most favoured file copying
|
|
tool, which might be a comand something like this:
|
|
|
|
.. code-block:: console
|
|
|
|
$ dd if=mbrimage of=/dev/path/to/the/usb/stick \
|
|
bs=1M conv=fdatasync status=progress
|
|
|
|
The Omnia's default boot order only checks USB after it has failed
|
|
to boot from eMMC, which is not ideal for our purpose. Unless you
|
|
have a serial cable, the easiest way to change this is by booting
|
|
to TurrisOS and logging in with ssh:
|
|
|
|
.. code-block:: console
|
|
|
|
root@turris:/# fw_printenv boot_targets
|
|
boot_targets=mmc0 nvme0 scsi0 usb0 pxe dhcp
|
|
root@turris:/# fw_setenv boot_targets usb0 mmc0
|
|
root@turris:/# fw_printenv boot_targets
|
|
boot_targets=usb0 mmc0
|
|
root@turris:/# reboot -f
|
|
|
|
It should now boot into the recovery image. It expects a network
|
|
cable to be plugged into LAN2 with something on the other end of
|
|
it that serves DHCP requests. Check your DHCP server logs for a
|
|
request from a ``liminix-recovery`` host and figure out what IP
|
|
address was assigned.
|
|
|
|
.. code-block:: console
|
|
|
|
$ ssh liminix-recovery.lan
|
|
|
|
You should get a "Busybox" banner and a root prompt. Now you can
|
|
start preparing the device to install Liminix on it. First we'll
|
|
mount the root filesystem and take a snapshot:
|
|
|
|
.. code-block:: console
|
|
|
|
# mkdir /dest && mount /dev/mmcblk0p1 /dest
|
|
# schnapps -d /dest create "pre liminix"
|
|
# schnapps -d /dest list
|
|
ERROR: not a valid btrfs filesystem: /
|
|
# | Type | Size | Date | Description
|
|
------+-----------+-------------+---------------------------+------------------------------------
|
|
1 | single | 16.00KiB | 1970-01-01 00:11:49 +0000 | pre liminix
|
|
|
|
(``not a valid btrfs filesystem: /`` is not a real error)
|
|
|
|
then we can remove all the files
|
|
|
|
.. code-block:: console
|
|
|
|
# rm -r /dest/@/*
|
|
|
|
and then it's ready to install the real Liminix system onto. On
|
|
your build system, create the Liminix configuration you wish to
|
|
install: here we'll use the ``rotuer`` example.
|
|
|
|
.. code-block:: console
|
|
|
|
build$ nix-build -I liminix-config=./examples/rotuer.nix \
|
|
--arg device "import ./devices/turris-omnia" \
|
|
-A outputs.systemConfiguration
|
|
|
|
and then use :command:`min-copy-closure` to copy it to the device.
|
|
|
|
.. code-block:: console
|
|
|
|
build$ nix-shell --run \
|
|
"min-copy-closure -r /dest/@ root@liminix-recovery.lan result"
|
|
|
|
and activate it
|
|
|
|
.. code-block:: console
|
|
|
|
build$ ssh root@liminix-recovery.lan \
|
|
"/dest/@/$(readlink result)/bin/install /dest/@"
|
|
|
|
The final steps are performed directly on the device again: add
|
|
a symlink so U-Boot can find :file:`/boot`, then restore the
|
|
default boot order and reboot into the new configuration.
|
|
|
|
.. code-block:: console
|
|
|
|
# cd /dest && ln -s @/boot .
|
|
# fw_setenv boot_targets "mmc0 nvme0 scsi0 usb0 pxe dhcp"
|
|
# cd / ; umount /dest
|
|
# reboot
|
|
|
|
|
|
Installation using a TFTP server and serial console
|
|
===================================================
|
|
|
|
If you have a :ref:`serial` console connection and a TFTP server,
|
|
and would rather use them than fiddling with USB sticks, the
|
|
:file:`examples/recovery.nix` configuration also works
|
|
using the ``tftpboot`` output. So you can do
|
|
|
|
.. code-block:: console
|
|
|
|
build$ nix-build -I liminix-config=./examples/recovery.nix \
|
|
--arg device "import ./devices/turris-omnia" \
|
|
-A outputs.tftpboot
|
|
|
|
and then paste the generated :file:`result/boot.scr` into
|
|
U-Boot, and you will end up with the same system as you would
|
|
have had after booting from USB. If you don't have a serial
|
|
console connection you could probably even get clever with
|
|
elaborate use of :command:`fw_setenv`, but that is left as
|
|
an exercise for the reader.
|
|
|
|
'';
|
|
|
|
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/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/v6.x/linux-6.7.4.tar.gz";
|
|
hash = "sha256-wIrmL0BS63nRwWfm4nw+dRNVPUzGh9M4X7LaHzAn5tU=";
|
|
};
|
|
version = "6.7.4";
|
|
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";
|
|
|
|
RTC_CLASS = "y";
|
|
RTC_DRV_ARMADA38X = "y"; # this may be useful anyway?
|
|
|
|
EXPERT = "y";
|
|
ALLOW_DEV_COREDUMP = "n";
|
|
|
|
|
|
# dts has a compatible for this but dmesg is not
|
|
# showing it
|
|
EEPROM_AT24 = "y"; # atmel,24c64
|
|
|
|
I2C = "y";
|
|
I2C_MUX = "y";
|
|
I2C_MUX_PCA954x = "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
|
|
MARVELL_PHY = "y";
|
|
|
|
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
|
|
};
|
|
conditionalConfig = {
|
|
USB = {
|
|
USB_XHCI_MVEBU = "y";
|
|
USB_XHCI_HCD = "y";
|
|
};
|
|
WLAN = {
|
|
WLAN_VENDOR_ATH = "y";
|
|
ATH_COMMON = "m";
|
|
ATH9K = "m";
|
|
ATH9K_PCI = "y";
|
|
ATH10K = "m";
|
|
ATH10K_PCI = "m";
|
|
ATH10K_DEBUG = "y";
|
|
};
|
|
};
|
|
};
|
|
boot = {
|
|
commandLine = [
|
|
"console=ttyS0,115200"
|
|
"pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
|
|
];
|
|
};
|
|
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 "0x1700000";
|
|
kernelFormat = "zimage";
|
|
compressRoot = true;
|
|
};
|
|
|
|
hardware = let
|
|
mac80211 = pkgs.kmodloader.override {
|
|
inherit (config.system.outputs) kernel;
|
|
targets = ["ath9k" "ath10k_pci"];
|
|
};
|
|
in {
|
|
defaultOutput = "mtdimage";
|
|
loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
|
|
entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
|
|
rootDevice = "/dev/mmcblk0p1";
|
|
|
|
dts = {
|
|
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
|
|
includes = [
|
|
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
|
|
];
|
|
};
|
|
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";
|
|
};
|
|
|
|
lan0 = link.build { ifname = "lan0"; };
|
|
lan1 = link.build { ifname = "lan1"; };
|
|
lan2 = link.build { ifname = "lan2"; };
|
|
lan3 = link.build { ifname = "lan3"; };
|
|
lan4 = link.build { ifname = "lan4"; };
|
|
lan5 = link.build { ifname = "lan5"; };
|
|
lan = lan0; # maybe we should build a bridge?
|
|
|
|
wlan = link.build {
|
|
ifname = "wlan0";
|
|
dependencies = [ mac80211 ];
|
|
};
|
|
wlan5 = link.build {
|
|
ifname = "wlan1";
|
|
dependencies = [ mac80211 ];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|