Mauro Morales

software developer

Tag: Ubuntu

  • How does a Raspberry Pi5 boot an image?

    When the Raspberry Pi5 is turned on, it will check on which device it is configured to boot. By default, this is the SD card, but you can change it to boot from an NVMe or USB drive while still fallback to SD. In my case, I’m using a USB SSD. Let’s take a look at how the disk is partitioned.

    For this article, I will be making reference to the Ubuntu 24.04 server image because its configuration is easier to understand than the Raspbian one, which uses implicit defaults. I mounted the image as a loop device, hence the dev/loop44 in the examples, but if you burned it to an SSD or SD card, you could get the same results from /dev/sdX and /dev/mmcblkY.

    root@zeno:~# lsblk -f /dev/loop44
    NAME       FSTYPE FSVER LABEL       UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
    loop44
    ├─loop44p1 vfat   FAT32 system-boot F526-0340                             419.3M    17% /media/mauro/system-boot
    └─loop44p2 ext4   1.0   writable    1305c13b-200a-49e8-8083-80cd01552617  781.9M    66% /media/mauro/writable

    From the labels, we can assume the system-boot partition will be the one booting the system, but how does the system know this is the case. From the documentation, I was able to find this:

    Partition numbers start at 1 and the MBR partitions are 1 to 4. Specifying partition 0 means boot from the default partition which is the first bootable FAT partition.

    Bootable partitions must be formatted as FAT12, FAT16 or FAT32 and contain a start.elf file (or config.txt file on Raspberry Pi 5) in order to be classed as be bootable by the bootloader

    Looking at the output of the previous command, only the system-boot partition has the right format, so let’s look into that one first.

    # ls -1 /media/mauro/system-boot/
    README
    bcm2710-rpi-2-b.dtb
    bcm2710-rpi-3-b-plus.dtb
    bcm2710-rpi-3-b.dtb
    bcm2710-rpi-cm3.dtb
    bcm2710-rpi-zero-2-w.dtb
    bcm2710-rpi-zero-2.dtb
    bcm2711-rpi-4-b.dtb
    bcm2711-rpi-400.dtb
    bcm2711-rpi-cm4.dtb
    bcm2711-rpi-cm4s.dtb
    bcm2712-rpi-5-b.dtb
    bcm2712-rpi-cm5-cm4io.dtb
    bcm2712-rpi-cm5-cm5io.dtb
    bcm2712d0-rpi-5-b.dtb
    boot.scr
    bootcode.bin
    cmdline.txt
    config.txt
    fixup.dat
    fixup4.dat
    fixup4cd.dat
    fixup4db.dat
    fixup4x.dat
    fixup_cd.dat
    fixup_db.dat
    fixup_x.dat
    hat_map.dtb
    initrd.img
    meta-data
    network-config
    overlays
    start.elf
    start4.elf
    start4cd.elf
    start4db.elf
    start4x.elf
    start_cd.elf
    start_db.elf
    start_x.elf
    uboot_rpi_3.bin
    uboot_rpi_4.bin
    uboot_rpi_arm64.bin
    user-data
    vmlinuz
    

    We can see that the expected config.txt. Let’s take a look at its contents.

    root@zeno:~# cat /media/mauro/system-boot/config.txt
    [all]
    kernel=vmlinuz
    cmdline=cmdline.txt
    initramfs initrd.img followkernel
    
    [pi4]
    max_framebuffers=2
    arm_boost=1
    
    [all]
    # Enable the audio output, I2C and SPI interfaces on the GPIO header. As these
    # parameters related to the base device-tree they must appear *before* any
    # other dtoverlay= specification
    dtparam=audio=on
    dtparam=i2c_arm=on
    dtparam=spi=on
    
    # Comment out the following line if the edges of the desktop appear outside
    # the edges of your display
    disable_overscan=1
    
    # If you have issues with audio, you may try uncommenting the following line
    # which forces the HDMI output into HDMI mode instead of DVI (which doesn't
    # support audio output)
    #hdmi_drive=2
    
    # Enable the serial pins
    enable_uart=1
    
    # Autoload overlays for any recognized cameras or displays that are attached
    # to the CSI/DSI ports. Please note this is for libcamera support, *not* for
    # the legacy camera stack
    camera_auto_detect=1
    display_auto_detect=1
    
    # Config settings specific to arm64
    arm_64bit=1
    dtoverlay=dwc2
    
    # Enable the KMS ("full" KMS) graphics overlay, leaving GPU memory as the
    # default (the kernel is in control of graphics memory with full KMS)
    dtoverlay=vc4-kms-v3d
    disable_fw_kms_setup=1
    
    [pi3+]
    # Use a smaller contiguous memory area, specifically on the 3A+ to avoid an
    # OOM oops on boot. The 3B+ is also affected by this section, but it shouldn't
    # cause any issues on that board
    dtoverlay=vc4-kms-v3d,cma-128
    
    [pi02]
    # The Zero 2W is another 512MB board which is occasionally affected by the same
    # OOM oops on boot.
    dtoverlay=vc4-kms-v3d,cma-128
    
    [all]
    
    [cm4]
    # Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into
    # such a board)
    dtoverlay=dwc2,dr_mode=host
    
    [all]

    I’m only interested in the first 5 lines.

    • [all]: makes reference to the board, or in this case, all boards
    • kernel: defines the kernel file to be read, in this case vmlinuz which was present on the files list
    • cmdline: defines the file with the cmdline used to boot the kernel. In this case cmdline.txt which is also there
    • initramfs: defines the initrd file to be loaded, in this case initrd.img, also there. And the followkernel stanza loads the initrd file in memory right after the kernel. Pay attention that this instruction, different from all others, doesn’t use the assignment =.

    Now we can take a look into cmdline.txt

    # cat /media/mauro/system-boot/cmdline.txt
    console=serial0,115200 multipath=off dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc
    

    This tells us that the root of the system is a partition with the label writable. This matches with the output of our very first command. Listing everything in writable we find:

    root@zeno:~# ls -1 /media/mauro/writable/
    bin
    bin.usr-is-merged
    boot
    dev
    etc
    home
    lib
    lib.usr-is-merged
    lost+found
    media
    mnt
    opt
    proc
    root
    run
    sbin
    sbin.usr-is-merged
    snap
    srv
    sys
    tmp
    usr
    var

    This looks like a common root directory for an Ubuntu system, so I will not go deeper into it.

    From a Linux installation on a PC the bootloader, but it turns out that for the Pi 5 it is already part of the EEPROM. So we can trust that it’s present and following the instructions from config.txt.

    An important part of this process is the Device Tree (.dtb files), which is also read by the bootloader. The Device Tree describes the hardware present on the board, ensuring that the kernel knows how to interact with all connected peripherals.

    To summarize it all. When the Raspberry Pi 5 powers up, the EEPROM will look for the first bootable partition, where it will read the config.txt file. The configuration file tells the bootloader which kernel, initramfs and cmdline params to load. After that, it’s the kernel’s job to decide how to proceed, in this case after the kernel and initramfs are running in memory, it will pivot to the system living in the writable partition. Last and out of the scope of this article, the init system will finalize the system.

    Raspbian

    Keep in mind that the Raspbian image doesn’t define all these details, since it uses defaults:

    The Raspberry Pi 5 firmware defaults to loading kernel_2712.img because this image contains optimizations specific to Raspberry Pi 5 (e.g. 16K page-size). If this file is not present, then the common 64-bit kernel (kernel8.img) will be loaded instead.

    And I assume that for initramfs, if not defined, it will also look within the directory and load either initramfs8 or initramfs_2712 by default since those files are present in the raspbian image.

  • Revived my Dell XPS 9350

    My work laptop has been giving me some trouble since I first installed openSUSE Tumbleweed. At first, it was just small annoyances, like not properly syncing the time. But installing the OS again is a bit of a hassle, as much as I enjoy doing it, so I found a workaround to reset it whenever it broke. However, last week it started freezing multiple times during the day, the workaround was to hard shut down the machine, which was very annoying, but I was hoping would get fixed in a next upgrade. Tumbleweed has weekly upgrades, so waiting wasn’t that big of a deal. But with the latest update, my Docker setup stopped working with Earthly, which is my bread and butter for Kairos, so I decided to try a different distro.

    Switching distros can be a big deal, and I didn’t want to learn a new package manager and special configurations, so I went with something I’ve already used before, Ubuntu 23.04. But even then, I first waned to give it a try, just in case there was any red flag. So, I dusted off my personal Dell XPS 9350, an 8-year-old laptop, to test it out. So far, everything seems to be working well, much slower than the workstation but still good enough and way more portable, so I’m probably going to start leaving my workstation at the desk.

  • Added ARM/RPI support for Ubuntu on Kairos

    Work this week came with a few challenges, but with enough patience and some help I was able to get the Ubuntu flavor for Kairos working on the Raspberry Pi.

    https://github.com/kairos-io/kairos/pull/1170

    Feedback loops when working on ARM are very slow because I’m cross compiling and because I have to burn the images on the SD cards. Switching contexts between tickets is not easy, but it’s also ok for working on chores, learning other things or playing with new technologies. I was mostly playing with ChatGPT, Copilot and LocalAI which was a lot of fun.

    Most of the lost time was because of a misconfiguration of the serial console. The issue was caused by some files which were not copied in the right directory, but it was not so obvious and without video it was very tricky to debug. Thankfully, after looking into some errors in dmesg and a recommendation from a colleague, I was able to sort it out. Reminder that it’s always good to review one’s own code and to get away from the code occasionally and look at it with a fresh pair of eyes.

  • Installing SQL Developer on Ubuntu 9.04

    One of the mayor reasons why I still use my Windows box is because I havent found a subtitute for TOAD. I know I could make it work some how using wine but I just didn’t feel like it. Since Oracle is so Linux supportive I looked for something on their website and for my surprise I found SQLdeveloper. So far, so good! I like it and I am going to start using it for work. Here are the steps I followed to make it work in my Ubuntu 9.04 box:

    1. Install Java JDK sudo apt-get install sun-java6-jdk
    2. Download Oracle SQL Developer for other platforms from Oracle’s website.
    3. Unzipped the package in my /home/{user}/Programs/sqldeveloper
    4. Run the .shsudo sh /home/{user}/Programs/sqldeveloper/sqldeveloper.sh
    5. When asked for my Java path wrote the following (be sure about your java version):/usr/lib/jvm/java-6-sun-1.6.0.14
    6. Enjoy!

    Since I enjoy launching commands from my Applications menu this is what I did:

    1. System > Preferences > Main Menu
    2. Go to the Programming tab
    3. New Item
    4. Name: SQLdeveloper
    5. Command: sh /home/{user}/Programs/sqldeveloper/sqldeveloper.sh
    6. OK

    Now I can go to my Applications > Programming and click on my SQLdeveloper icon.

    If you have any questions please comment about it or feel free to contact me.

    This post was originally published on my Tumblr blog