== Installation

Hardware devices vary wildly in their affordances for installing new
operating systems, so it should be no surprise that the Liminix
installation procedure is hardware-dependent. This section contains
generic instructions, but please refer to the documentation for your
device to find whether and how well they apply.

=== Building a firmware image

Liminix uses the Nix language to provide congruent configuration
management. This means that to change anything about the way in which a
Liminix system works, you make that change in your `+configuration.nix+`
(or one of the other files it references), and rerun `+nix-build+` to
action the change. It is not possible (at least, without shenanigans) to
make changes by logging into the device and running imperative commands
whose effects may later be overridden: `+configuration.nix+` always
describes the entire system and can be used to recreate that system at
any time. You can usefully keep it under version control.

If you are familiar with NixOS, you will notice some similarities
between NixOS and Liminix configuration, and also some differences.
Sometimes the differences are due to the resource-constrained devices we
deploy onto, sometimes due to differences in the uses these devices are
put to.

For a more full description of how to configure Liminix, see
<<_configuration>>. Assuming for the moment that you want a typical home
wireless gateway/router, the best way to get started is to copy
`+examples/rotuer.nix+` and edit it for your requirements.

[source,console]
----
$ cp examples/rotuer.nix configuration.nix
$ vi configuration.nix # other editors are available 
$ # adjust this next command for your hardware device
$ nix-build -I liminix-config=./configuration.nix \
 --arg device "import ./devices/gl-mt300a" -A outputs.default
----

Usually (not always, _please check the documentation for your device_)
this will leave you with a file `+result/firmware.bin+` which you now
need to flash to the device.

=== Flashing from the boot monitor (TFTP install)

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 may even involve soldering - see the
documention for your device. However, it is in some ways the most
"reliable" option: if you can see what's happening (or not happening) in
early boot, the risk of "bricking" is substantially reduced and you have
options for recovering if you misstep or flash a bad image.

[[serial]]
==== Serial connections

Every device that we have so far encountered in Liminix uses
https://docs.u-boot.org/en/latest/[U-Boot&#44; the "Universal Boot
Loader"] 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 typically 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
https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable[FTDI
cable], but there are cheaper alternatives based on the PL2303 and
CP2102 chipsets - or you could even get creative and use the
https://pinout.xyz/[UART GPIO pins] 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 often have no 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 do 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, or your computer is expecting flow control which the 3 wire
connection does not provide. . If you see garbage, try a different
speed.

Interesting commands to try first in U-Boot are `+help+` and
`+printenv+`.

You will also need to configure a TFTP server on a network that's
accessible to the device: how you do that will vary according to which
TFTP server you're using and so is out of scope for this document.

==== Building and installing the image

Follow the device-specific instructions for "TFTP install": usually, the
steps are

* build the [.title-ref]#outputs.mtdimage# output
* copy `+result/firmware.bin+` to your TFTP server
* copy/paste the commands in `+result/flash.scr+` one at a time into the
U-Boot command line
* reset the device

You should now see messages from U-Boot, then from the Linux kernel and
eventually a shell prompt.

[NOTE]
====
Before you reboot, check which networks the device is plugged into, and
disconnect as necessary. If you've just installed a DHCP server or
anything similar that responds to broadcasts, you may not want it to do
that on the network that you temporarily connected it to for installing
it.
====

=== Flashing from OpenWrt

[CAUTION]
====
Untested! A previous version of these instructions (without the -e flag)
led to bricking the device when flashing a jffs2 image. If you are
reading this message, nobody has yet reported on whether the new
instructions are any better.
====

If your device is running OpenWrt then it probably has the `+mtd+`
command installed. Build the [.title-ref]#outputs.mtdimage# output (as
you would for a TFTP install) and then transfer `+result/firmware.bin+`
onto the device using e.g. `+scp+`. Now flash as follows:

[source,console]
----
mtd -e -r write /tmp/firmware.bin firmware
----

The options to this command are for "erase before writing" and "reboot
after writing".

For more information, please see the
https://openwrt.org/docs/guide-user/installation/sysupgrade.cli[OpenWrt
manual] which may also contain (hardware-dependent) instructions on how
to flash an image using the vendor firmware - perhaps even from a web
interface.

=== Flashing from Liminix

If the device is already running Liminix and has been configured with
`+levitate+`, you can use that to safely flash your new image. Refer to
`+levitate+` for an explanation.

If the device is running Liminix but doesn't have `+levitate+` your
options are more limited. You may attempt to use `+flashcp+` but it
doesn't always work: as it copies the new image over the top of the
active root filesystem, surprise may ensue. Consider instead using a
serial connection: you may need one anyway after trying flashcp if it
corrupts the image.

==== flashcp (not generally recommended)

Connect to the device and locate the "firmware" partition, which you can
do with a combination of `+dmesg+` output and the contents of
`+/proc/mtd+`

[source,console]
----
<5>[    0.469841] Creating 4 MTD partitions on "spi0.0":
<5>[    0.474837] 0x000000000000-0x000000040000 : "u-boot"
<5>[    0.480796] 0x000000040000-0x000000050000 : "u-boot-env"
<5>[    0.487056] 0x000000050000-0x000000060000 : "art"
<5>[    0.492753] 0x000000060000-0x000001000000 : "firmware"

# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00040000 00001000 "u-boot"
mtd1: 00010000 00001000 "u-boot-env"
mtd2: 00010000 00001000 "art"
mtd3: 00fa0000 00001000 "firmware"
mtd4: 002a0000 00001000 "kernel"
mtd5: 00d00000 00001000 "rootfs"
----

Copy `+result/firmware.bin+` to the device and now run (in this example)

[source,console]
----
flashcp -v firmware.bin /dev/mtd3
----
