u-root is a "universal root". It's a root file system with mostly Go source with the exception of 5 binaries.
That's the interesting part. This set of utilities is all Go, and mostly source.
When you run a command that is not built, you fall through to the command that does a
go build
of the command, and then execs the command once it is built. From that point on,
when you run the command, you get the one in tmpfs. This is fast.
You need go 1.9. If you don't have that version you'll need to set it up.
You'll need a GOPATH. Be sure to set it to something, e.g.
export GOPATH=/usr/local/src/go
On my machine, my gopath is
export GOPATH=/home/$USER/go
Then
go get github.com/u-root/u-root
cd $GOPATH/src/github.com/u-root/u-root
You may hit a problem where it can't find some standard Go packages, if so, you'll need
to set GOROOT, e.g.
export GOROOT=/path/to/some_go_>=1.9
Both scripts/ramfs.go and bb/bb.go create an initramfs to be used with a Linux kernel. You can build this initramfs into the kernel via the CONFIG_INITRAMFS_SOURCE config variable or you can load it separately via an option in, e.g., Grub or the QEMU command line or coreboot config variable.
bb
creates an initramfs with one binary and symlinks per command to this
binary. Think of it as busy-box mode. This single binary consists of all the
commands that got specified by a glob pattern on the command line (by default all of
them). The following command for example includes the elvish shell:
$ # Required for elvish.
$ go get github.com/boltdb/bolt
$ go get github.com/elves/elvish
$ cd $GOPATH/src/github.com/u-root/u-root/bb
$ go build github.com/u-root/u-root/bb
$ ./bb src/github.com/elves/elvish "s*/g*/u*/u*/c*/*"
Also note that bb
has an -add
flag that is handy to include local files in
the initramfs.
The ramfs
tool also creates an initramfs, but the resulting image contains
the go toolchain, init, and the sources needed to compile the actual binaries
on the fly. Binaries are created only once when they are executed for the
first time.
An initramfs generated with bb
is smaller than one created with ramfs
, and
appropriate for slow machines with a small amount of memory. ramfs
is closer
to the original goal that all source should be seen by the user.
A good way to test the initramfs generated by the bb or ramfs commands is with qemu.
You can use your favorite kernel, and the initramfs
created by either
go run scripts/ramfs.go
or
(cd bb && go build && ./bb)
A typical command line looks like this:
qemu-system-x86_64 -kernel path/to/kernel -initrd /tmp/initramfs.linux_amd64.cpio
Note that you do not have to build a special kernel on your own, it is
sufficient to use an existing one. Usually you find one in /boot
. As example
for Debian, assuming bb
is built and you want to add two kernel modules for
testing, executing your currently booted kernel:
./bb/bb -add "$HOME/hello.ko $HOME/hello2.ko" && \
qemu-system-x86_64 -kernel /boot/vmlinuz-$(uname -r) -initrd /tmp/initramfs.linux_amd64.cpio
Improving existing commands (e.g., additional currently unsupported) flags is
very welcome. In this case it is not even required to build an initramfs, just
enter the cmds/
directory and start coding. A list of commands that are on
the roadmap can be found here. Please make sure to read CONTRIBUTING.md
You can build this environment into a kernel as an initramfs, and further embed that into firmware as a coreboot payload.
In the kernel and coreboot case, you need to configure ethernet. We have a dhclient
command that works for both ipv4 and ipv6. Since v6 does not yet work that well for most
people, a typical invocation looks like this:
dhclient -ipv4 -ipv6=false
Or, on newer linux kernels (> 4.x) boot with ip=dhcp in the command line, assuming your kernel is configured to work that way.
You can install tinycore linux packages for things you want.
You can use QEMU NAT to allow you to fetch packages.
Let's suppose, for example, you want bash. Once u-root is
running, you can do this:
% tcz bash
The tcz command computes and fetches all dependencies.
If you can't get to tinycorelinux.net, or you want package fetching to be faster, you can run your own server for tinycore packages.
You can do this to get a local server using the u-root srvfiles command:
% src/srvfiles/srvfiles -p 80 -d path-to-local-tinycore-packages
Of course you have to fetch all those packages first somehow :-)
In the EXAMPLES directory you can see examples of running in a chroot, kernel, and coreboot.
In default, rush is the shell in u-root. Now, thanks to Qi Xiao (<xiaqqaix@gmail.com>), u-root users are also able to use a friendly and expressive unix-like shell: elvish. Users are free to choose whether to include elvish in u-root or not. Basically, elvish has handy functionalities such as auto completion, command-line existence checks, etc. More info of elvish can be found at: http://github/elves/elvish.
If you prefer to use elvish as shell in u-root, here are the instructions:
-
Get project elvish:
go get github.com/elves/elvish
-
Temporarily, since package
sqlite3
used in elvish has been updated, and its latest version includes codes in C (which u-root does not support), users have to roll back to last good commit of elvish:cd $GOPATH/src/elves/elvish
git checkout bc5543aef2c493b658d6bd1bb81e3de298de8d2f
-
Go to u-root repo. If you did
go get github.com/u-root/u-root
before, do:cd $GOPATH/src/u-root/u-root
-
If you prefer to build under bb mode, please do the following command line in u-root/u-root/:
cd ./bb/
go build .
./bb 'src/github.com/u-root/u-root/cmds/[a-z]*' src/github.com/elves/elvish
which generates a cpio file, /tmp/initramfs.linux_amd64.cpio for you to start up u-root in qemu.If you prefer dynamic buildup mode, do the following command line in u-root/u-root:
go run scripts/ramfs.go 'src/github.com/u-root/u-root/cmds/[a-z]*' src/github.com/elves/elvish
which also generates /tmp/initramfs.linux_amd64.cpio. -
Afterwards, users can type command line
elvish
in u-root and start to use elvish as shell.
If you want to see u-root on real hardware, this board is a good start.
See CONTRIBUTING.md