Here I will justify spending unnecessary money

I’ve been wanting for a while to get a personal laptop and separate my work and personal life a little bit more. Currently I use my work laptop for everything - it’s a beast. But…

  1. I’m tired of Windows.
  2. I open Teams or Outlook off the clock far too often.
  3. See point 1.

I started the process of replacing my personal laptop in this post (you’ll notice there still isn’t a part two), but my frustration is that the X220 is from 2011, and I’ve already been through hell trying to replace the BIOS. It’s probably perfectly serviceable for my needs, realistically, but my work laptop has spoiled me. I also want it to support a dock (jury’s still out as to whether that works - I haven’t tested it).

Enter the T480. Crushingly, it has the chiclet keyboard, but everything else about it is wonderful. It meets the prerequisite of supporting coreboot or libreboot, has a full ethernet port, more than one USB port, is generally well-built (keyboard aside), new enough to have hefty specs and an updated CPU, but old enough that I don’t have to take out a mortgage to purchase one. The libreboot maintainer even sells these with libreboot pre-installed, which was the thing that pushed me over the line to get one.

After my package ended up in USPS purgatory I finally got my hands on it, and immediately proceeded to the install.

Deciphering the libreboot documentation

I experienced this back in November when I was trying to parse this same documentation, and I couldn’t quite put my finger on it. I think I figured something out this time…

In various places, the documentation says “read such and such other page before doing this”, so I would oblige and open said page in a new tab. A few iterations of this - maybe 4 or 5 tabs in - and I would be hopelessly lost. Between tabbing back and forth between my notes and stepping through some of the prerequisite steps, I’d lose track of which page was which. It’s hard to tell what information is a MUST do, what information is simply re-iterating something I’ve already been warned about in 10 other places, what are general disclaimers (the “eCryptfs file limit” specifically is on the build docs page and inserting vendor files page which led to me confusing the two). Changing the MAC address is emphasized as something that is highly recommended, but the damn nvmutil page that’s linked states directly that when injecting vendor files, the MAC address is set randomly automatically, making it a nothingburger. The thunderbolt portion of the T480 page goes on and on and on, and I eventually determined that I could skip it after verifying the laptop was still fast charging. (Maybe this will bite me later, not sure.) But then the flashing portion (arguably the most important bit) just has a few pictures of how to get to the flash chip, then ends. Moves right along to post-flashing. Earlier on the page, it directs you to how to inject vendor files and the page dedicated to external flashing, but says jack shit about the flashing process right there.

All this to say that the documentation is extremely non-linear and confounding. You as the reader must put in a good amount of intuition, flat-out guessing, and piecing together to get to the end result. This is why I used video guides for coreboot/libreboot previously…

Eventually, I got it - and I certainly wouldn’t have gotten close without the documentation (or obviously the libreboot project existing). All this is not to complain, or put Leah (libreboot maintainer) down or anything - I have mad respect. I just wanted to put my finger on why reading all this was such a head-scratcher for me. (As you can see, my documentation leaves MUCH more to be desired than do the libreboot docs.)

Cleaned up flash process

For posterity, here are the steps I would take, in order, if I were to do this again.

Step 0: check the laptop

  • Verify laptop is functional and all looks in order upon receipt - before doing anything else.
  • Verify the BIOS isn’t locked down with a password (I ran into this once - I didn’t check it this time, but I would have found out very quickly)
  • Verify specifically that fast-charging is working (this indicates that the Thunderbolt flash chip is not full - or at least I think it does)

Step 1: prepare files on build machine

On another system running Debian 12+ (or something equivalent - it needs to be updated)

Step 2: flash the Lenovo BIOS to a specific version

  • In the BIOS, disable “Secure Rollback Prevention”
  • Boot into the USB stick and follow the prompts to flash Lenovo firmware

Step 3: flash the device

(There’s almost certainly an easier way to do this than downloading the whole build system and installing ALL the dependencies again, but I couldn’t be bothered at this point in the process.)

  • Enable SPI on your raspi (if not already enabled) via raspi-config
  • Download and extract the build system (lbmk) to the raspi
  • scp the specific ROM from the build device to the raspi (there are several that get created as part of the ./mk inject command)
  • Run ./mk dependencies debian (literally just to install flashprog)
  • Connect the GPIO pins to your flash clip
  • Connect the flash clip to the ROM chip on the motherboard
  • Test your connection with sudo ./flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=32768
  • Dump the current contents of flash (3 times) with sudo ./flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=32768 -r stock01.bin
  • sha256sum all 3 dumped ROMs and verify that the hashes match
  • Make extra sure you haven’t fucked anything up
  • Flash the chip with sudo ./flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=32768 -w /path/to/libreboot.rom

Messy reality

Now here’s what I ACTUALLY did.

  • Boot into new T480 and verify things are working
  • Disassemble T480
  • Realize that I shouldn’t have disassembled yet
  • Reassemble T480
  • Check that fast charging is working
  • Disassemble T480 again
  • Read up on changing MAC address, realize that it’s unnecessary because the injected vendor files give you a random MAC
  • Download and extract the build system (lbmk)
  • Download and extract the T480 ROMs (then realize I shouldn’t have extracted them)
  • Comb through the flash instructions to make sure nothing major has changed
  • Read up on how to inject vendor files
  • Inject said vendor files with ./mk inject /path/to/tarball.tar.xz
  • Inject command fails, my version of cmake is too old
  • Realize the actual problem is my Debian WSL is still running Debian 11 (bullseye) and MS hasn’t pushed out an upgrade
  • Figure out how to upgrade to 12 (bookworm) manually
    • Edit /etc/apt/sources.list and replace all instances of bullseye with bookworm
    • sudo apt update
    • sudo apt upgrade
    • sudo apt full-upgrade
    • wsl --shutdown
  • Check cmake --version (3.25.1 yay!)
  • Realize that I need to downgrade the Lenovo BIOS to a specific version, which means I have to reassemble the damn thing again
  • Reassemble T480
  • Download Lenovo BIOS ROM
  • Prepare Lenovo image with geteltorito -o t480_bios_update.img /path/to/your/downloaded.iso
  • Prepare USB stick (got away with using Rufus on Windows)
  • Boot into BIOS configuration and disable “Secure Rollback Prevention”
  • Boot into the USB stick and flash specifically mentioned version of Lenovo BIOS
  • Review the Thunderbolt flashing section for like the 20th time to be sure…decide to skip it AMA
  • Boot up my trusty pi 4B
  • Realize that video output doesn’t work after a certain point in startup
  • Reflash raspbian lite onto a new SD card
  • Boot into the pi
  • Still in what looks like a boot loop (I think it was just not outputting video for some reason)
  • Fuck this, grab my pi 5 off the shelf
  • Run into the same issue (at this point it’s past midnight and still 90 degrees in our house (AC is out), I am increasingly flippant and pissed off…)
  • Remember the old static IP I had set, wire a point to point network, get SSH access to be able to change the static IP
  • Update everything because it’s been several months since I booted it up
  • Run into dependency issues (./mk script doesn’t account for the arm architecture packages) - figure out the equivalent packages and install
  • Enable spi in raspi-config
  • Ready to flash, realize I never injected the vendor files into the ROM tarball because of my version of cmake
  • Inject the vendor files into the tarball, scp over to the pi again
  • Write the flash chip. (this was the easiest part of the whole process, even though it’s the scariest.)

Despite what the above may imply, this was a fairly painless process. Most of my issues arose out of WSL being out of date and my raspis acting strange. It took me about 3 hours before I was greeted with the splash screen - not that long, all things considered.

The funniest part about this to me is that the laptop is still running the Windows 11 system that it shipped with. Really went for the low-hanging fruit here…

EOF