liminix

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

default.nix (14714B)


      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 =
    157     {
    158       pkgs,
    159       config,
    160       lib,
    161       lim,
    162       ...
    163     }:
    164     let
    165       inherit (pkgs.liminix.services) oneshot;
    166       inherit (pkgs) liminix;
    167       mtd_by_name_links = pkgs.liminix.services.oneshot rec {
    168         name = "mtd_by_name_links";
    169         up = ''
    170           mkdir -p /dev/mtd/by-name
    171           cd /dev/mtd/by-name
    172           for i in /sys/class/mtd/mtd*[0-9]; do
    173             ln -s ../../$(basename $i) $(cat $i/name)
    174           done
    175         '';
    176       };
    177     in
    178     {
    179       imports = [
    180         ../../modules/arch/arm.nix
    181         ../../modules/outputs/tftpboot.nix
    182         ../../modules/outputs/mbrimage.nix
    183       ];
    184 
    185       config = {
    186         rootfsType = lib.mkDefault "btrfs"; # override this if you are building tftpboot
    187         rootOptions = lib.mkDefault "subvol=@";
    188 
    189         services.mtd-name-links = mtd_by_name_links;
    190         kernel = {
    191           src = pkgs.pkgsBuildBuild.fetchurl {
    192             name = "linux.tar.gz";
    193             url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.4.tar.gz";
    194             hash = "sha256-wIrmL0BS63nRwWfm4nw+dRNVPUzGh9M4X7LaHzAn5tU=";
    195           };
    196           version = "6.7.4";
    197           config = {
    198             PCI = "y";
    199             OF = "y";
    200             MEMORY = "y"; # for MVEBU_DEVBUS
    201             DMADEVICES = "y"; # for MV_XOR
    202             CPU_V7 = "y";
    203             ARCH_MULTIPLATFORM = "y";
    204             ARCH_MVEBU = "y";
    205             ARCH_MULTI_V7 = "y";
    206             PCI_MVEBU = "y";
    207             AHCI_MVEBU = "y";
    208 
    209             RTC_CLASS = "y";
    210             RTC_DRV_ARMADA38X = "y"; # this may be useful anyway?
    211 
    212             EXPERT = "y";
    213             ALLOW_DEV_COREDUMP = "n";
    214 
    215             # dts has a compatible for this but dmesg is not
    216             # showing it
    217             EEPROM_AT24 = "y"; # atmel,24c64
    218 
    219             I2C = "y";
    220             I2C_MUX = "y";
    221             I2C_MUX_PCA954x = "y";
    222 
    223             MACH_ARMADA_38X = "y";
    224             SMP = "y";
    225             # this is disabled for the moment because it relies on a
    226             # GCC plugin that requires gmp.h to build, and I can't see
    227             # right now how to confgure it to find gmp
    228             STACKPROTECTOR_PER_TASK = "n";
    229             NR_CPUS = "4";
    230             VFP = "y";
    231             NEON = "y";
    232 
    233             # WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
    234             ATA = "y";
    235 
    236             BLOCK = "y";
    237             MMC = "y";
    238             PWRSEQ_EMMC = "y"; # ???
    239             PWRSEQ_SIMPLE = "y"; # ???
    240             MMC_BLOCK = "y";
    241 
    242             MMC_SDHCI = "y";
    243             MMC_SDHCI_PLTFM = "y";
    244             MMC_SDHCI_PXAV3 = "y";
    245             MMC_MVSDIO = "y";
    246 
    247             SERIAL_8250 = "y";
    248             SERIAL_8250_CONSOLE = "y";
    249             SERIAL_OF_PLATFORM = "y";
    250             SERIAL_MVEBU_UART = "y";
    251             SERIAL_MVEBU_CONSOLE = "y";
    252 
    253             SERIAL_8250_DMA = "y";
    254             SERIAL_8250_DW = "y";
    255             SERIAL_8250_EXTENDED = "y";
    256             SERIAL_8250_MANY_PORTS = "y";
    257             SERIAL_8250_SHARE_IRQ = "y";
    258             OF_ADDRESS = "y";
    259             OF_MDIO = "y";
    260 
    261             WATCHDOG = "y"; # watchdog is enabled by u-boot
    262             ORION_WATCHDOG = "y"; # so is non-optional to keep feeding
    263 
    264             MVEBU_DEVBUS = "y"; # "Device Bus controller ...  flash devices such as NOR, NAND, SRAM, and FPGA"
    265             MVMDIO = "y";
    266             MVNETA = "y";
    267             MVNETA_BM = "y";
    268             MVNETA_BM_ENABLE = "y";
    269             SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM
    270             PHY_MVEBU_A38X_COMPHY = "y"; # for eth2
    271             MARVELL_PHY = "y";
    272 
    273             MVPP2 = "y";
    274             MV_XOR = "y";
    275 
    276             # there is NOR flash on this device, which is used for U-Boot
    277             # and the rescue system (which we don't interfere with) but
    278             # also for the U-Boot environment variables (which we might
    279             # need to meddle with)
    280             MTD_SPI_NOR = "y";
    281             SPI = "y";
    282             SPI_MASTER = "y";
    283             SPI_ORION = "y";
    284 
    285             NET_DSA = "y";
    286             NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL
    287           };
    288           conditionalConfig = {
    289             USB = {
    290               USB_XHCI_MVEBU = "y";
    291               USB_XHCI_HCD = "y";
    292             };
    293             WLAN = {
    294               WLAN_VENDOR_ATH = "y";
    295               ATH_COMMON = "m";
    296               ATH9K = "m";
    297               ATH9K_PCI = "y";
    298               ATH10K = "m";
    299               ATH10K_PCI = "m";
    300               ATH10K_DEBUG = "y";
    301             };
    302           };
    303         };
    304         boot = {
    305           loader.extlinux.enable = lib.mkDefault true; # override this if you are building tftpboot
    306           commandLine = [
    307             "console=ttyS0,115200"
    308             "pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
    309           ];
    310         };
    311         filesystem =
    312           let
    313             inherit (pkgs.pseudofile) dir symlink;
    314             firmware = pkgs.stdenv.mkDerivation {
    315               name = "wlan-firmware";
    316               phases = [ "installPhase" ];
    317               installPhase = ''
    318                 mkdir $out
    319                 cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
    320               '';
    321             };
    322           in
    323           dir {
    324             lib = dir {
    325               firmware = dir {
    326                 ath10k = symlink firmware;
    327               };
    328             };
    329             etc = dir {
    330               "fw_env.config" =
    331                 let
    332                   f = pkgs.writeText "fw_env.config" ''
    333                     /dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000
    334                   '';
    335                 in
    336                 symlink f;
    337             };
    338           };
    339 
    340         boot.tftp = {
    341           loadAddress = lim.parseInt "0x1700000";
    342           kernelFormat = "zimage";
    343           compressRoot = true;
    344         };
    345 
    346         hardware =
    347           let
    348             mac80211 = pkgs.kmodloader.override {
    349               inherit (config.system.outputs) kernel;
    350               targets = [
    351                 "ath9k"
    352                 "ath10k_pci"
    353               ];
    354             };
    355           in
    356           {
    357             defaultOutput = "updater";
    358             loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
    359             entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
    360             rootDevice = "/dev/mmcblk0p1";
    361 
    362             dts = {
    363               src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
    364               includePaths = [
    365                 "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
    366               ];
    367             };
    368             flash.eraseBlockSize = 65536; # only used for tftpboot
    369             networkInterfaces =
    370               let
    371                 inherit (config.system.service.network) link;
    372               in
    373               rec {
    374                 en70000 = link.build {
    375                   # in armada-38x.dtsi this is eth0.
    376                   # It's connected to port 5 of the 88E6176 switch
    377                   devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet";
    378                   # name is unambiguous but not very semantic
    379                   ifname = "en70000";
    380                 };
    381                 en30000 = link.build {
    382                   # in armada-38x.dtsi this is eth1
    383                   # It's connected to port 6 of the 88E6176 switch
    384                   devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet";
    385                   # name is unambiguous but not very semantic
    386                   ifname = "en30000";
    387                 };
    388                 # the default (from the dts? I'm guessing) behavour for
    389                 # lan ports on the switch is to attach them to
    390                 # en30000. It should be possible to do something better,
    391                 # per
    392                 # https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
    393                 # but apparently OpenWrt doesn't either so maybe it's more
    394                 # complicated than it looks.
    395 
    396                 wan = link.build {
    397                   # in armada-38x.dtsi this is eth2. It may be connected to
    398                   # an ethernet phy or to the SFP cage, depending on a gpio
    399                   devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet";
    400                   ifname = "wan";
    401                 };
    402 
    403                 lan0 = link.build { ifname = "lan0"; };
    404                 lan1 = link.build { ifname = "lan1"; };
    405                 lan2 = link.build { ifname = "lan2"; };
    406                 lan3 = link.build { ifname = "lan3"; };
    407                 lan4 = link.build { ifname = "lan4"; };
    408                 lan5 = link.build { ifname = "lan5"; };
    409                 lan = lan0; # maybe we should build a bridge?
    410 
    411                 wlan = link.build {
    412                   ifname = "wlan0";
    413                   dependencies = [ mac80211 ];
    414                 };
    415                 wlan5 = link.build {
    416                   ifname = "wlan1";
    417                   dependencies = [ mac80211 ];
    418                 };
    419               };
    420           };
    421       };
    422     };
    423 }