liminix

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

default.nix (14468B)


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