liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

default.nix (14531B)


      1 {
      2   description = ''
      3     == Turris Omnia
      4 
      5     This is a 32 bit ARMv7 MVEBU device, which is usually shipped with
      6     TurrisOS, an OpenWrt-based system. Rather than reformatting the builtin
      7     storage, we install Liminix on to the existing btrfs filesystem so that
      8     the vendor snapshot/recovery system continues to work (and provides you
      9     an easy rollback if you decide you don't like Liminix after all).
     10 
     11     The install process has two stages, and is intended that you should not
     12     need to open the device and add a serial console (although it may be
     13     handy for visibility, and in case anything goes wrong). First we build a
     14     minimal installation/recovery system, then we reboot into that recovery
     15     image to prepare the device for the full target install.
     16 
     17     === Installation using a USB stick
     18 
     19     First, build the image for the USB stick. Review
     20     `+examples/recovery.nix+` in order to change the default root password
     21     (which is `+secret+`) and/or the SSH keys, then build it with
     22 
     23     [source,console]
     24     ----
     25     $ nix-build -I liminix-config=./examples/recovery.nix \
     26       --arg device "import ./devices/turris-omnia" \
     27       -A outputs.mbrimage -o mbrimage
     28     $ file -L mbrimage
     29     mbrimage: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,5), end-CHS (0x6,130,26), startsector 4, 104602 sectors
     30     ----
     31 
     32     Next, copy the image from your build machine to a USB storage medium
     33     using `+dd+` or your other most favoured file copying tool, which might
     34     be a comand something like this:
     35 
     36     [source,console]
     37     ----
     38     $ dd if=mbrimage of=/dev/path/to/the/usb/stick \
     39       bs=1M conv=fdatasync status=progress
     40     ----
     41 
     42     The Omnia's default boot order only checks USB after it has failed to
     43     boot from eMMC, which is not ideal for our purpose. Unless you have a
     44     serial cable, the easiest way to change this is by booting to TurrisOS
     45     and logging in with ssh:
     46 
     47     [source,console]
     48     ----
     49     root@turris:/# fw_printenv boot_targets
     50     boot_targets=mmc0 nvme0 scsi0 usb0 pxe dhcp
     51     root@turris:/# fw_setenv boot_targets usb0 mmc0
     52     root@turris:/# fw_printenv boot_targets
     53     boot_targets=usb0 mmc0
     54     root@turris:/# reboot -f
     55     ----
     56 
     57     It should now boot into the recovery image. It expects a network cable
     58     to be plugged into LAN2 with something on the other end of it that
     59     serves DHCP requests. Check your DHCP server logs for a request from a
     60     `+liminix-recovery+` host and figure out what IP address was assigned.
     61 
     62     [source,console]
     63     ----
     64     $ ssh liminix-recovery.lan
     65     ----
     66 
     67     You should get a "Busybox" banner and a root prompt. Now you can start
     68     preparing the device to install Liminix on it. First we'll mount the
     69     root filesystem and take a snapshot:
     70 
     71     [source,console]
     72     ----
     73     # mkdir /dest && mount /dev/mmcblk0p1 /dest
     74     # schnapps -d /dest create "pre liminix"
     75     # schnapps -d /dest list
     76     ERROR: not a valid btrfs filesystem: /
     77         # | Type      | Size        | Date                      | Description
     78     ------+-----------+-------------+---------------------------+------------------------------------
     79         1 | single    |    16.00KiB | 1970-01-01 00:11:49 +0000 | pre liminix
     80     ----
     81 
     82     (`+not a valid btrfs filesystem: /+` is not a real error)
     83 
     84     then we can remove all the files
     85 
     86     [source,console]
     87     ----
     88     # rm -r /dest/@/*
     89     ----
     90 
     91     and then it's ready to install the real Liminix system onto. On your
     92     build system, create the Liminix configuration you wish to install: here
     93     we'll use the `+rotuer+` example.
     94 
     95     [source,console]
     96     ----
     97     build$ nix-build -I liminix-config=./examples/rotuer.nix \
     98       --arg device "import ./devices/turris-omnia" \
     99       -A outputs.systemConfiguration
    100     ----
    101 
    102     and then use `+min-copy-closure+` to copy it to the device.
    103 
    104     [source,console]
    105     ----
    106     build$ nix-shell --run \
    107       "min-copy-closure -r /dest/@  root@liminix-recovery.lan result"
    108     ----
    109 
    110     and activate it
    111 
    112     [source,console]
    113     ----
    114     build$ ssh root@liminix-recovery.lan \
    115       "/dest/@/$(readlink result)/bin/install /dest/@"
    116     ----
    117 
    118     The final steps are performed directly on the device again: add a
    119     symlink so U-Boot can find `+/boot+`, then restore the default boot
    120     order and reboot into the new configuration.
    121 
    122     [source,console]
    123     ----
    124     # cd /dest && ln -s @/boot .
    125     # fw_setenv boot_targets "mmc0 nvme0 scsi0 usb0 pxe dhcp"
    126     # cd / ; umount /dest
    127     # reboot
    128     ----
    129 
    130     === Installation using a TFTP server and serial console
    131 
    132     If you have a <<serial,serial console connection>> and a TFTP server, and would
    133     rather use them than fiddling with USB sticks, the
    134     `+examples/recovery.nix+` configuration also works using the
    135     `+tftpboot+` output. So you can do
    136 
    137     [source,console]
    138     ----
    139     build$ nix-build -I liminix-config=./examples/recovery.nix \
    140       --arg device "import ./devices/turris-omnia" \
    141       -A outputs.tftpboot
    142     ----
    143 
    144     and then paste the generated `+result/boot.scr+` into U-Boot, and you
    145     will end up with the same system as you would have had after booting
    146     from USB. If you don't have a serial console connection you could
    147     probably even get clever with elaborate use of `+fw_setenv+`, but that
    148     is left as an exercise for the reader.
    149 
    150   '';
    151 
    152   system = {
    153     crossSystem = {
    154       config = "armv7l-unknown-linux-musleabihf";
    155     };
    156   };
    157 
    158   module =
    159     {
    160       pkgs,
    161       config,
    162       lib,
    163       lim,
    164       ...
    165     }:
    166     let
    167       inherit (pkgs.liminix.services) oneshot;
    168       inherit (pkgs) liminix;
    169       mtd_by_name_links = pkgs.liminix.services.oneshot rec {
    170         name = "mtd_by_name_links";
    171         up = ''
    172           mkdir -p /dev/mtd/by-name
    173           cd /dev/mtd/by-name
    174           for i in /sys/class/mtd/mtd*[0-9]; do
    175             ln -s ../../$(basename $i) $(cat $i/name)
    176           done
    177         '';
    178       };
    179     in
    180     {
    181       imports = [
    182         ../../modules/arch/arm.nix
    183         ../../modules/outputs/tftpboot.nix
    184         ../../modules/outputs/mbrimage.nix
    185       ];
    186 
    187       config = {
    188         rootfsType = lib.mkDefault "btrfs"; # override this if you are building tftpboot
    189         rootOptions = lib.mkDefault "subvol=@";
    190 
    191         services.mtd-name-links = mtd_by_name_links;
    192         kernel = {
    193           src = pkgs.pkgsBuildBuild.fetchurl {
    194             name = "linux.tar.gz";
    195             url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.54.tar.gz";
    196             hash = "sha256-rHSOttt5N+piPpXCEP6aELvvOzXX+kmoBCkcoP1kOvk=";
    197           };
    198           version = "6.18.54";
    199           config = {
    200             PCI = "y";
    201             OF = "y";
    202             MEMORY = "y"; # for MVEBU_DEVBUS
    203             DMADEVICES = "y"; # for MV_XOR
    204             CPU_V7 = "y";
    205             ARCH_MULTIPLATFORM = "y";
    206             ARCH_MVEBU = "y";
    207             ARCH_MULTI_V7 = "y";
    208             PCI_MVEBU = "y";
    209             AHCI_MVEBU = "y";
    210 
    211             RTC_CLASS = "y";
    212             RTC_DRV_ARMADA38X = "y"; # this may be useful anyway?
    213 
    214             EXPERT = "y";
    215             ALLOW_DEV_COREDUMP = "n";
    216 
    217             # dts has a compatible for this but dmesg is not
    218             # showing it
    219             EEPROM_AT24 = "y"; # atmel,24c64
    220 
    221             I2C = "y";
    222             I2C_MUX = "y";
    223             I2C_MUX_PCA954x = "y";
    224 
    225             MACH_ARMADA_38X = "y";
    226             SMP = "y";
    227             # this is disabled for the moment because it relies on a
    228             # GCC plugin that requires gmp.h to build, and I can't see
    229             # right now how to confgure it to find gmp
    230             STACKPROTECTOR_PER_TASK = "n";
    231             NR_CPUS = "4";
    232             VFP = "y";
    233             NEON = "y";
    234 
    235             # WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
    236             ATA = "y";
    237 
    238             BLOCK = "y";
    239             MMC = "y";
    240             PWRSEQ_EMMC = "y"; # ???
    241             PWRSEQ_SIMPLE = "y"; # ???
    242             MMC_BLOCK = "y";
    243 
    244             MMC_SDHCI = "y";
    245             MMC_SDHCI_PLTFM = "y";
    246             MMC_SDHCI_PXAV3 = "y";
    247             MMC_MVSDIO = "y";
    248 
    249             SERIAL_8250 = "y";
    250             SERIAL_8250_CONSOLE = "y";
    251             SERIAL_OF_PLATFORM = "y";
    252             SERIAL_MVEBU_UART = "y";
    253             SERIAL_MVEBU_CONSOLE = "y";
    254 
    255             SERIAL_8250_DMA = "y";
    256             SERIAL_8250_DW = "y";
    257             SERIAL_8250_EXTENDED = "y";
    258             SERIAL_8250_MANY_PORTS = "y";
    259             SERIAL_8250_SHARE_IRQ = "y";
    260             OF_ADDRESS = "y";
    261             OF_MDIO = "y";
    262 
    263             WATCHDOG = "y"; # watchdog is enabled by u-boot
    264             ORION_WATCHDOG = "y"; # so is non-optional to keep feeding
    265 
    266             MVEBU_DEVBUS = "y"; # "Device Bus controller ...  flash devices such as NOR, NAND, SRAM, and FPGA"
    267             MVMDIO = "y";
    268             MVNETA = "y";
    269             MVNETA_BM = "y";
    270             MVNETA_BM_ENABLE = "y";
    271             SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM
    272             PHY_MVEBU_A38X_COMPHY = "y"; # for eth2
    273             MARVELL_PHY = "y";
    274 
    275             MVPP2 = "y";
    276             MV_XOR = "y";
    277 
    278             # there is NOR flash on this device, which is used for U-Boot
    279             # and the rescue system (which we don't interfere with) but
    280             # also for the U-Boot environment variables (which we might
    281             # need to meddle with)
    282             MTD_SPI_NOR = "y";
    283             SPI = "y";
    284             SPI_MASTER = "y";
    285             SPI_ORION = "y";
    286 
    287             NET_DSA = "y";
    288             NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL
    289           };
    290           conditionalConfig = {
    291             USB = {
    292               USB_XHCI_MVEBU = "y";
    293               USB_XHCI_HCD = "y";
    294             };
    295             WLAN = {
    296               WLAN_VENDOR_ATH = "y";
    297               ATH_COMMON = "m";
    298               ATH9K = "m";
    299               ATH9K_PCI = "y";
    300               ATH10K = "m";
    301               ATH10K_PCI = "m";
    302               ATH10K_DEBUG = "y";
    303             };
    304           };
    305         };
    306         boot = {
    307           loader.extlinux.enable = lib.mkDefault true; # override this if you are building tftpboot
    308           commandLine = [
    309             "console=ttyS0,115200"
    310             "pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
    311           ];
    312         };
    313         filesystem =
    314           let
    315             inherit (pkgs.pseudofile) dir symlink;
    316             firmware = pkgs.stdenv.mkDerivation {
    317               name = "wlan-firmware";
    318               phases = [ "installPhase" ];
    319               installPhase = ''
    320                 mkdir $out
    321                 cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
    322               '';
    323             };
    324           in
    325           dir {
    326             lib = dir {
    327               firmware = dir {
    328                 ath10k = symlink firmware;
    329               };
    330             };
    331             etc = dir {
    332               "fw_env.config" =
    333                 let
    334                   f = pkgs.writeText "fw_env.config" ''
    335                     /dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000
    336                   '';
    337                 in
    338                 symlink f;
    339             };
    340           };
    341 
    342         boot.tftp = {
    343           loadAddress = lim.parseInt "0x1700000";
    344           kernelFormat = "zimage";
    345           compressRoot = true;
    346         };
    347 
    348         hardware =
    349           let
    350             mac80211 = pkgs.kmodloader.override {
    351               inherit (config.system.outputs) kernel;
    352               targets = [
    353                 "ath9k"
    354                 "ath10k_pci"
    355               ];
    356             };
    357           in
    358           {
    359             defaultOutput = "updater";
    360             loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
    361             entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
    362             rootDevice = "/dev/mmcblk0p1";
    363 
    364             dts = {
    365               src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
    366               includePaths = [
    367                 "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
    368               ];
    369             };
    370             flash.eraseBlockSize = 65536; # only used for tftpboot
    371             networkInterfaces =
    372               let
    373                 inherit (config.system.service.network) link;
    374               in
    375               rec {
    376                 en70000 = link.build {
    377                   # in armada-38x.dtsi this is eth0.
    378                   # It's connected to port 5 of the 88E6176 switch
    379                   devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet";
    380                   # name is unambiguous but not very semantic
    381                   ifname = "en70000";
    382                 };
    383                 en30000 = link.build {
    384                   # in armada-38x.dtsi this is eth1
    385                   # It's connected to port 6 of the 88E6176 switch
    386                   devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet";
    387                   # name is unambiguous but not very semantic
    388                   ifname = "en30000";
    389                 };
    390                 # the default (from the dts? I'm guessing) behavour for
    391                 # lan ports on the switch is to attach them to
    392                 # en30000. It should be possible to do something better,
    393                 # per
    394                 # https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
    395                 # but apparently OpenWrt doesn't either so maybe it's more
    396                 # complicated than it looks.
    397 
    398                 wan = link.build {
    399                   # in armada-38x.dtsi this is eth2. It may be connected to
    400                   # an ethernet phy or to the SFP cage, depending on a gpio
    401                   devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet";
    402                   ifname = "wan";
    403                 };
    404 
    405                 lan0 = link.build { ifname = "lan0"; };
    406                 lan1 = link.build { ifname = "lan1"; };
    407                 lan2 = link.build { ifname = "lan2"; };
    408                 lan3 = link.build { ifname = "lan3"; };
    409                 lan4 = link.build { ifname = "lan4"; };
    410                 lan5 = link.build { ifname = "lan5"; };
    411                 lan = lan0; # maybe we should build a bridge?
    412 
    413                 wlan = link.build {
    414                   ifname = "wlan0";
    415                   dependencies = [ mac80211 ];
    416                 };
    417                 wlan5 = link.build {
    418                   ifname = "wlan1";
    419                   dependencies = [ mac80211 ];
    420                 };
    421               };
    422           };
    423       };
    424     };
    425 }