| 16 | |
| 17 | == Ubuntu root filesystem |
| 18 | Creating a root filesystem comprised of Canonical built packages can be done in a couple of ways: |
| 19 | * download a pre-built rootfs tarball |
| 20 | * create one using deboostrap |
| 21 | * create one using qemu/chroot |
| 22 | |
| 23 | This refers to the rootfs only. To easily build an entire system image, including the boot firmware and Ubuntu, look at the BSP for the various product families: |
| 24 | * [wiki:malibu/bsp Malibu BSP] |
| 25 | * [wiki:venice/bsp Venice BSP] |
| 26 | * [wiki:newport/bsp Newport BSP] |
| 27 | |
| 28 | === Downloading pre-built rootfs tarball |
| 29 | Gateworks has pre-built Ubuntu based rootfs tarballs at http://dev.gateworks.com/ubuntu/ that are created with the [https://github.com/gateworks/ubuntu-rootfs ubuntu-rootfs script]. |
| 30 | |
| 31 | [=#qemu-chroot] |
| 32 | === Creating Ubuntu rootfs using qemu and chroot |
| 33 | You can use qemu to create an Ubuntu based rootfs with very little in the way of dependencies. To do this you must boot a virtual machine for your architecture therefore you will need a kernel and a minimal root filesystem which can be easily built using buildroot. |
| 34 | |
| 35 | Example: |
| 36 | * aarch64 rootfs: |
| 37 | 1. Start by defining some parameters |
| 38 | {{{#!bash |
| 39 | IMG=ubuntu.ext4 |
| 40 | SIZE=500M |
| 41 | UBUNTU=22.04 |
| 42 | UBUNTU_REL=.3 |
| 43 | }}} |
| 44 | 1. Create a ramdisk based image (similar concept as rescue image but we add a file here via BR2_ROOTFS_OVERLAY) |
| 45 | {{{#!bash |
| 46 | sudo apt install build-essential git |
| 47 | git clone http://github.com/buildroot/buildroot.git |
| 48 | cd buildroot |
| 49 | cat << EOF > configs/buildroot_defconfig |
| 50 | BR2_aarch64=y |
| 51 | BR2_cortex_a72=y |
| 52 | BR2_TOOLCHAIN_BUILDROOT_GLIBC=y |
| 53 | BR2_TOOLCHAIN_BUILDROOT_CXX=y |
| 54 | BR2_TARGET_GENERIC_HOSTNAME="localhost" |
| 55 | BR2_ROOTFS_OVERLAY="overlay/" |
| 56 | BR2_LINUX_KERNEL=y |
| 57 | BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y |
| 58 | BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y |
| 59 | BR2_PACKAGE_E2FSPROGS=y |
| 60 | # BR2_PACKAGE_E2FSPROGS_FSCK is not set |
| 61 | BR2_PACKAGE_I2C_TOOLS=y |
| 62 | BR2_PACKAGE_RNG_TOOLS=y |
| 63 | BR2_PACKAGE_BASH=y |
| 64 | BR2_PACKAGE_TAR=y |
| 65 | BR2_PACKAGE_UTIL_LINUX_HWCLOCK=y |
| 66 | BR2_TARGET_ROOTFS_CPIO=y |
| 67 | BR2_TARGET_ROOTFS_CPIO_GZIP=y |
| 68 | EOF |
| 69 | # create init script |
| 70 | mkdir -p overlay/etc/init.d |
| 71 | cat <<EOF > overlay/etc/init.d/S99boostrap-ubuntu.sh |
| 72 | #!/bin/sh |
| 73 | |
| 74 | if [ "\$1" != "start" ]; then |
| 75 | exit |
| 76 | fi |
| 77 | |
| 78 | # setup |
| 79 | mkfs.ext4 -F /dev/vda -b 4096 |
| 80 | mount /dev/vda /mnt |
| 81 | cd /mnt/ |
| 82 | udhcpc -i eth0 |
| 83 | # install base rootfs from release tarball |
| 84 | wget -c -P /tmp/ http://cdimage.ubuntu.com/ubuntu-base/releases/${UBUNTU}/release/ubuntu-base-${UBUNTU}${UBUNTU_REL}-base-arm64.tar.gz |
| 85 | tar zxf /tmp/ubuntu-base-${UBUNTU}${UBUNTU_REL}-base-arm64.tar.gz -C /mnt |
| 86 | # setup chroot environment |
| 87 | mount -o bind /proc /mnt/proc/ |
| 88 | mount -o bind /sys/ /mnt/sys/ |
| 89 | mount -o bind /dev/ /mnt/dev/ |
| 90 | mount -o bind /dev/pts /mnt/dev/pts |
| 91 | mount -t tmpfs tmpfs /mnt/var/lib/apt/ |
| 92 | mount -t tmpfs tmpfs /mnt/var/cache/apt/ |
| 93 | echo "nameserver 8.8.8.8" > /mnt/etc/resolv.conf |
| 94 | echo "localhost" > /mnt/etc/hostname |
| 95 | echo "127.0.0.1 localhost" > /mnt/etc/hosts |
| 96 | export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true LC_ALL=C LANGUAGE=C LANG=C |
| 97 | # install additional packages |
| 98 | chroot /mnt apt update |
| 99 | chroot /mnt apt upgrade -y |
| 100 | chroot /mnt apt install --no-install-recommends -y \ |
| 101 | apt-utils locales less wget procps \ |
| 102 | psmisc less sudo kmod rng-tools \ |
| 103 | ifupdown net-tools isc-dhcp-client ntpdate \ |
| 104 | openssh-server iproute2 iptables iputils-ping \ |
| 105 | i2c-tools can-utils usbutils pciutils \ |
| 106 | screen picocom \ |
| 107 | vim nano |
| 108 | # set root password |
| 109 | echo -e "root\nroot" | chroot /mnt passwd |
| 110 | # teardown chroot env |
| 111 | umount /mnt/var/lib/apt/ |
| 112 | umount /mnt/var/cache/apt |
| 113 | chroot /mnt apt clean |
| 114 | chroot /mnt apt autoclean |
| 115 | reboot |
| 116 | EOF |
| 117 | chmod +x overlay/etc/init.d/S99boostrap-ubuntu.sh |
| 118 | make buildroot_defconfig |
| 119 | make -j8 |
| 120 | }}} |
| 121 | * generic aarch64 kernel Image and rootfs.cpio.gz will be in output/images |
| 122 | |
| 123 | 2. Use qemu to kick off a virtual aarch64 machine which will run the init script and create root filesystem on a virtual disk |
| 124 | {{{#!bash |
| 125 | apt install qemu-system-arm |
| 126 | truncate -s $SIZE $IMG |
| 127 | qemu-system-aarch64 -m 1G -M virt -cpu cortex-a57 -nographic -smp 1 -no-reboot \ |
| 128 | -device virtio-net-device,netdev=eth0 -netdev user,id=eth0 \ |
| 129 | -device virtio-blk-device,drive=hd0 -drive file=$IMG,if=none,format=raw,id=hd0 \ |
| 130 | -append "console=ttyAMA0 ip=dhcp" \ |
| 131 | -kernel output/images/Image \ |
| 132 | -initrd output/images/rootfs.cpio.gz |
| 133 | }}} |
| 134 | * ubuntu.ext4 contains your rootfs - add desired changes to the setup script |
| 135 | |
| 136 | |
| 137 | [=#debootstrap] |
| 138 | === Creating Ubuntu rootfs using deboostrap |
| 139 | A popular way to create an Ubuntu root filesystem is to use the {{{deboostrap}}} utility on a Debian or Ubuntu host. This tool provides a 2-stage install where the second stage is within a chroot environment using qemu. |
| 140 | |
| 141 | Gateworks uses a script to do this which you may find at http://github.com/Gateworks/ubuntu-rootfs |
| 142 | |
| 143 | Requirements: |
| 144 | - Linux Ubuntu or Debian System with network connection and sudo permissions |
| 145 | |
| 146 | Important notes: |
| 147 | * We set and use '''target''' and '''distro''' env variables in step 2 and use those env variables in the remaining steps to make this tutorial more version-agnostic. Please be aware of this and do not deviate from the steps unless or until you completely understand what you are doing. |
| 148 | * These steps are not always exactly what we do in our script but give you an idea of how you would go about doing it yourself if you wanted to customize something |
| 149 | |
| 150 | Steps: |
| 151 | 1. Install pre-requisites: |
| 152 | {{{ |
| 153 | #!bash |
| 154 | sudo apt-get install qemu-user-static debootstrap binfmt-support |
| 155 | }}} |
| 156 | |
| 157 | 2. Perform first stage install of minimal filesystem for {{{arm64}}} architecture: |
| 158 | {{{#!bash |
| 159 | distro=focal |
| 160 | arch=arm64 |
| 161 | target=${distro}-${arch} |
| 162 | qemu_arch=aarch64 |
| 163 | sudo debootstrap --arch=$arch --foreign $distro $target |
| 164 | # copy qemu binary for the binfmt packages to find it and copy in resolv.conf from host |
| 165 | sudo cp /usr/bin/qemu-${qemu_arch}-static $target/usr/bin |
| 166 | }}} |
| 167 | * See http://ports.ubuntu.com/ubuntu-ports/dists/ for a list of current Ubuntu releases: oe 20.04=focal (latest LTS) |
| 168 | * this minimal rootfs is still missing some core packages and configuration before it can be booted. These steps are taken care of in a 2nd stage install within a chroot shell |
| 169 | * the chroot shell below will provide network support (inherited from the host) |
| 170 | |
| 171 | 3. We now have a minimal Ubuntu rootfs - chroot to it and perform the 2nd stage install: |
| 172 | {{{ |
| 173 | #!bash |
| 174 | sudo chroot $target |
| 175 | # now we are in the chroot - setup env matching the distro above |
| 176 | distro=focal |
| 177 | export LANG=C |
| 178 | # setup second stage |
| 179 | /debootstrap/debootstrap --second-stage |
| 180 | }}} |
| 181 | * this is the most minimal rootfs we would recommend |
| 182 | |
| 183 | 4. (optional) add additional apt package repos: |
| 184 | {{{ |
| 185 | #!bash |
| 186 | cat <<EOT > /etc/apt/sources.list |
| 187 | deb http://ports.ubuntu.com/ubuntu-ports $distro main restricted universe multiverse |
| 188 | deb http://ports.ubuntu.com/ubuntu-ports $distro-updates main restricted universe multiverse |
| 189 | deb http://ports.ubuntu.com/ubuntu-ports $distro-security main restricted universe multiverse |
| 190 | EOT |
| 191 | }}} |
| 192 | * you may want to customize the above list, depending on your needs. See [#packages below] for more detail on Ubuntu package feeds |
| 193 | |
| 194 | 5. (optional) update package database and setup locales (do not skip this step if you are needing to install any packages for the steps below or otherwise) |
| 195 | {{{ |
| 196 | #!bash |
| 197 | apt-get update |
| 198 | apt-get -f install # fixup missing package dependencies |
| 199 | apt-get install locales dialog |
| 200 | dpkg-reconfigure locales |
| 201 | }}} |
| 202 | |
| 203 | 6. Set hostname: |
| 204 | {{{ |
| 205 | #!bash |
| 206 | echo ${distro}-$(uname -m) > /etc/hostname |
| 207 | }}} |
| 208 | |
| 209 | 7. set a root passwd so you can login |
| 210 | {{{ |
| 211 | #!bash |
| 212 | passwd |
| 213 | }}} |
| 214 | - or consider adding a user via {{{adduser}}}: |
| 215 | {{{ |
| 216 | #!bash |
| 217 | adduser myuser |
| 218 | usermod -a -G tty myuser # add to tty group for tty access |
| 219 | usermod -a -G dialout myuser # add to dialout group for UART access |
| 220 | usermod -a -G sudo myuser # add to sudo group for root access |
| 221 | }}} |
| 222 | |
| 223 | 8. (optional) configure networking: |
| 224 | - wired ethernet with DHCP on eth0 |
| 225 | {{{#!bash |
| 226 | apt-get install net-tools ifupdown |
| 227 | cat <<EOF >> /etc/network/interfaces |
| 228 | allow-hotplug eth0 |
| 229 | auto eth0 |
| 230 | iface eth0 inet dhcp |
| 231 | |
| 232 | EOF |
| 233 | }}} |
| 234 | - or static IP: |
| 235 | {{{#!bash |
| 236 | apt-get install net-tools ifupdown |
| 237 | cat <<EOF >> /etc/network/interfaces |
| 238 | allow-hotplug eth0 |
| 239 | auto eth0 |
| 240 | iface eth0 inet static |
| 241 | address 192.168.1.1 |
| 242 | netmask 255.255.255.0 |
| 243 | gateway 192.168.1.254 |
| 244 | |
| 245 | EOF |
| 246 | }}} |
| 247 | - or wireless (requires ~3MB of additional packages): |
| 248 | {{{ |
| 249 | #!bash |
| 250 | apt-get install wpasupplicant iw |
| 251 | cat << EOF >> /etc/network/interfaces |
| 252 | # Wireless interface |
| 253 | auto wlan0 |
| 254 | iface wlan0 inet dhcp |
| 255 | wireless_mode managed |
| 256 | wireless_essid any |
| 257 | wpa-driver nl80211 |
| 258 | wpa-conf /etc/wpa_supplicant.conf |
| 259 | |
| 260 | EOF |
| 261 | wpa_passphrase <myssid> <mypass> >> /etc/wpa_supplicant.conf |
| 262 | }}} |
| 263 | |
| 264 | 9. (optional) install some useful packages |
| 265 | {{{ |
| 266 | #!bash |
| 267 | apt-get install openssh-server # ssh server for remote access |
| 268 | apt-get install can-utils i2c-tools usbutils pciutils # cmdline tools for various hardware support |
| 269 | }}} |
| 270 | * Note that by default root ssh access is disabled for security. See [wiki:/ubuntu#SSHServer This link] for info on enabling it |
| 271 | |
| 272 | 10. Exit the chroot shell and remove files we no longer need |
| 273 | {{{ |
| 274 | #!bash |
| 275 | exit |
| 276 | sudo rm $target/usr/bin/qemu-$qemu_arch-static |
| 277 | }}} |
| 278 | |
| 279 | At this point you have a directory containing a root filesystem (without kernel) and likely want to install it onto removable storage or the on-board FLASH of a target board. Some intermediate formats that are useful to keep around would be a tarball, perhaps an ext4 filesystem image, or a compressed disk image suitable for flashing in the U-Boot bootloader. |
| 280 | |
| 281 | To create a tarball which is the most flexible storage format and can be used for a variety of future installation uses: |
| 282 | {{{#!bash |
| 283 | sudo tar --keep-directory-symlink -cvJf focal-venice.tar.xz -C rootfs/ . |
| 284 | }}} |
| 285 | * '--numeric-owner' is required to store user/group as a number instead of a name, your specific use case may require this switch. |
| 286 | * the '-C rootfs/' is required to eliminate the rootfs directory prefix |
| 287 | * the sudo is needed to be able to read the root owned files |
| 288 | * '--keep-directory-symlink' will preserve symbolic links |
| 289 | |
| 290 | To create an ext4 filesystem from the directory or tarball requires you choose a size for the filesystem. This size can be increased at runtime using {{{resize2fs}}} as long as the partition table has room for it to grow. The advantage of using an as small as possible size is that the time necessary to flash it onto storage is reduced to a minimum (when flashing you have to write the entire ext4 fs but when formatting or resizing it only has to write periodic markers to FLASH). |
| 291 | |
| 292 | For a given size (see SIZEMB variable below) you can create a rootfs with: |
| 293 | {{{#!bash |
| 294 | SIZEMB=1536 # 1.5GB - expandable later with resize2fs |
| 295 | OUT=rootfs.ext4 |
| 296 | # create a file of specific size |
| 297 | truncate -s ${SIZEMB}M ${OUT} |
| 298 | # format it as an ext4 filesystem |
| 299 | mkfs.ext4 -q -F -L rootfs ${OUT} |
| 300 | # mount it to a temporary mount point |
| 301 | tmp_mnt=$(mktemp -d -p/tmp) |
| 302 | mount ${OUT} ${tmp_mnt} |
| 303 | # copy files to it |
| 304 | cp -rup rootfs/* ${tmp_mnt} |
| 305 | # and/or extract files from a tarball |
| 306 | tar -C ${tmp_mnt} -xf linux-venice.tar.xz --keep-directory-symlink |
| 307 | # unmount temporary mount point |
| 308 | umount ${tmp_mnt} |
| 309 | sync |
| 310 | # compress it |
| 311 | gzip -k -f ${OUT} |
| 312 | }}} |