Thinkpad X220 – Shrink the ME Region

Despite vaguely reading the opposite, it is possible to truncate the ME region to the last byte that me_cleaner left untouched. This (on my image) freed about 4 MiB of flash – combined with the fact that coreboot’s default size is 1 MiB (when the region is 3 MiB wide) meant that I now have 7 meg of space to screw around with in CBFS.

Here’s how:

Preparation

  • Dump the rom
  • Run me_cleaner on the rom
  • Use ifdtool to extract the individual regions
    • ifdtool -x <image>

Truncation

Find the last relevant byte

Hexdump will automatically hide large sections of 0xFF – use hexdump on the ME region file:

  • hexdump <me_region>

Here you can see the last relevant line starts with 0x00dbef0 – therefore the last byte is 0x00dbeff.

Truncate the ME region file

We can use dd to truncate the file – but first we need to convert the hex value into a decimal number. Use your calculator of choice (google in my case). For 0x00dbeff it’s 900863

  • dd if=x220_me.bin of=x220_me_trunc.bin bs=1 count=900864

Just double check the size using ls:

  • -rw-r--r-- 1 nroach44 nroach44 900864 Jan 24 15:50 x220_me_trunc.bin

Adjust the Firmware Descriptor

We’ve shrunk the ME region, but we need to inform the chipset that the BIOS region can be bigger.

Extract the layout using ifdtool

  • ifdtool -f layout.txt <full image>

layout.txt will look like the following:

00000000:00000fff fd
00500000:007fffff bios
00003000:004fffff me
00001000:00002fff gbe

You need to edit it so that the ME region ends earlier. The file is simply

<Start address>:<End address> <region type>

So, we need to change the end address for the ME region, and the start address of the BIOS region.

The ME end address needs to be 0x3000 with 0xDBEFF added – so again using your calculator of choice, this comes to 0xDEEFF.

The BIOS start address needs to be one more than that – so 0xDEF00.

Note: The end addresses should end in 3 Fs, and starts should end in 3 0s – https://github.com/corna/me_cleaner/issues/20 – You’ll need to ensure that your addresses comply with this rule. This means the start address of the BIOS region should be one more than the new end address of the ME region

This gets me the following layout.txt:

00000000:00000fff fd
000df000:007fffff bios
00003000:000defff me
00001000:00002fff gbe

We then need to build the new descriptor with this layout file:

  • ifdtool -n layout.txt <full image>

IFDTool will spit out some info that you should sanity check, and also warn you that the ME region is shrinking, this is the whole point.

Then extract the new descriptor:

  • ifdtool -x <full image>

Build coreboot again

Use the new descriptor that you just extracted, and the truncate ME region file in your coreboot config.

You will also need to adjust the CBFS_SIZE option – this can be the size of the BIOS region – you shouldn’t need to do any maths here for padding or overhead. I simply just used 0x700000 as it was easier, but you should be able to just use the whole size. So that would be 0x7fffff – 0xDF000 = 0x720FFF

Flash the image and reboot – you shouldn’t have any issue with the ME forcing a reboot after 30 minutes.

Addendum

Using the 1.5MiB image from the coreboot wiki (thanks github/u/alegru here) it is possible to shrink the region down to ~300KiB:

nroach44@normandy:~/local/tmp/meshrink2$ ~/media/code/coreboot/me_cleaner-git/me_cleaner.py flashregion_2_intel_me15.bin
ME/TXE image detected
Found FPT header at 0x10
Found 11 partition(s)
Found FTPR header: FTPR partition spans from 0x37000 to 0xa1000
Removing extra partitions…
Removing extra partition entries in FPT…
Removing EFFS presence flag…
Correcting checksum (0x05)…
ME/TXE firmware version 7.1.80.1214
Reading FTPR modules list…
UPDATE           (LZMA   , 0x07ba31 – 0x07bac3): removed
BUP              (Huffman, fragmented data    ): NOT removed, essential
KERNEL           (Huffman, fragmented data    ): removed
POLICY           (Huffman, fragmented data    ): removed
HOSTCOMM         (LZMA   , 0x07bac3 – 0x08105a): removed
RSA              (LZMA   , 0x08105a – 0x085b07): removed
CLS              (LZMA   , 0x085b07 – 0x08a519): removed
TDT              (LZMA   , 0x08a519 – 0x0906c0): removed
FTCS             (Huffman, fragmented data    ): removed
The ME minimum size is 294912 bytes (0x48000 bytes)
Checking FTPR RSA signature… VALID
Done! Good luck!

nroach44@normandy:~/local/tmp/meshrink2$ cat layout
00000000:00000fff fd
0004b000:007fffff bios
00003000:0004afff me
00001000:00002fff gbe

nroach44@normandy:~/local/tmp/meshrink2$ ifdtool -n layout 02_shrunk.rom
File 02_shrunk.rom is 8388608 bytes
DANGER: Region Intel ME is shrinking.
The region will be truncated to fit.
This may result in an unusable image.
Copy Descriptor 0 (Flash Descriptor) (4096 bytes)
from 00000000+00000000:00000fff (      4096)
to 00000000+00000000:00000fff (      4096)
Copy Descriptor 1 (BIOS) (7475200 bytes)
from 000df000+00000000:007fffff (   7475200)
to 0004b000+00094000:007fffff (   8081408)
Copy Descriptor 2 (Intel ME) (294912 bytes)
from 00003000+00094000:000defff (    901120)
to 00003000+00000000:0004afff (    294912)
Copy Descriptor 3 (GbE) (8192 bytes)
from 00001000+00000000:00002fff (      8192)
to 00001000+00000000:00002fff (      8192)
Writing new image to 02_shrunk.rom.new

Thinkpad X220 – Coreboot and ME removal

Module preparation

See https://tylercipriani.com/blog/2016/11/13/coreboot-on-the-thinkpad-x220-with-a-raspberry-pi/

  1. Setup a RPi
  2. Build flashrom on it
  3. Connect RPi to BIOS chip
  4. Dump Firmware off chip
    1. Dump multiple times until you have multiple files with the same hashes
  5. Copy to workstation
  6. Use ifdtool (from the coreboot sources) to modify (unlock) descriptor (may not actually be necessary)
    1. ifdtool -u <firmware.bin>
  7. Extract partitions
    1. ifdtool -x <firmware.bin>
  8. Run me_cleaner over ME partition
    1. Get it from https://github.com/corna/me_cleaner
    2. python3 <me_cleaner_location>/me_cleaner.py flashregion_2_intel_me.bin

VGA BIOS extraction (optional)

This allows graphics in GRUB to work as intended, the text themes should work fine, SeaBIOS works fine without it, and the moment linux touches the GPU it all works anyway.

  1. Download UEFITool – get it from https://github.com/LongSoft/UEFITool
  2. Compile UEFITool  – run qmake uefitool.pro && make (requires QT5)
  3. Open whole firmware image
  4. Search “VGA Compatible BIOS”
  5. In the results in the bottom double click a (hopefully the only) result
    1. UEFITool with the module containing the VGA BIOS selected
  6. Right click “extract body”, save as vgabios.bin
  7. (Further optional) Double check the module extracted is correct:
    1. Install fcode-utils (debian)
    2. Run romheaders vgabios.bin
    3. Expect the following:
      1. romheaders output for the X220 VGA BIOS

 

Coreboot prep

Follow the usual setup instructions for coreboot – pull the git repo, initialise the submodules, build the toolchain etc.

Use the following as your .config:
CONFIG_USE_OPTION_TABLE=y
CONFIG_USE_BLOBS=y
CONFIG_VENDOR_LENOVO=y
CONFIG_ONBOARD_VGA_IS_PRIMARY=y
CONFIG_VGA_BIOS=y
CONFIG_VGA_BIOS_FILE="====YOUR==PATH==HERE====/vga-8086-126.bin"
CONFIG_HAVE_IFD_BIN=y
CONFIG_HAVE_ME_BIN=y
CONFIG_DRIVERS_PS2_KEYBOARD=y
CONFIG_CONSOLE_POST=y
CONFIG_HAVE_GBE_BIN=y
CONFIG_BOARD_LENOVO_X220=y
# CONFIG_CONSOLE_CBMEM is not set
CONFIG_ENABLE_VMX=y
CONFIG_IFD_BIN_PATH="====YOUR==PATH==HERE====/flashregion_0_flashdescriptor.bin"
CONFIG_ME_BIN_PATH="====YOUR==PATH==HERE====/flashregion_2_intel_me.bin"
CONFIG_GBE_BIN_PATH="====YOUR==PATH==HERE====/flashregion_3_gbe.bin"
CONFIG_DEFAULT_CONSOLE_LOGLEVEL_6=y
CONFIG_SEABIOS_MASTER=y
CONFIG_SEABIOS_THREAD_OPTIONROMS=y
CONFIG_PXE=y
CONFIG_BUILD_IPXE=y
CONFIG_PXE_ROM_ID="8086,1502"
CONFIG_MEMTEST_SECONDARY_PAYLOAD=y
CONFIG_NVRAMCUI_SECONDARY_PAYLOAD=y
CONFIG_TINT_SECONDARY_PAYLOAD=y

Run make like usual

Flash the image!

Pretty self explanatory, use the -w instead of -r flag on flashrom.

Notes:

  • CONFIG_USE_OPTION_TABLE=y Allows you to set things like the power management beeps using nvramtool, and the nvramcui payload
  • The other secondary payloads are optional too
  • CONFIG_SEABIOS_MASTER=y Seems to be required, using the numbered version may not work
  • If you don’t want to use the VGA rom, select “Devices/Use native graphics initialization” (MAINBOARD_DO_NATIVE_VGA_INIT)

DIS on a Mazda3 BKs2

Introduction

Background

I have a 2006 Mazda 3. It’s a nice car, but the more I learnt about the 3 I get ever so slightly more annoyed with Mazda Australia. There’s multiple things that the car “supports” but wasn’t sold with in Australia.

One of them (and the easiest of which to enable) is DIS – Driver Information System.

DIS lives on the left side of the LCD above the radio – it’s the clock by default. It can display the following:

  • Current Fuel Consumption (Averaged over the last few seconds)
  • Average Fuel Consumption
  • Average Speed
  • Remaining Distance (based on fuel in tank and average consumption)

There’s an official way to enable this – Hold down a few buttons, turn the key, and it’s on [1]! Unfortunately this only works for a few 3s, and not the base trims, or the later 2006 models (BKs2).

So if you can’t follow that guide, or are curious to see how it works I’ll explain how that button combination turns it on.

Theory

The Mazda 3 uses a CAN bus – this is a network not unlike a conventional computer network (think blue cables) but with a few differences:

  • It’s a bus – there’s no “Hub” or “Switch” – every device on the network is directly connected to each other via a common set of wires. Shorting this bus will stop any device from talking to another.
  • There’s no “Source” or “Destination” – only an ID, a flag or two, the data, and then a checksum.

The Mazda 3 has two of these buses – one is the “High Speed” bus for the Engine, ABS, Traction Control, Transmission, and Instrument Cluster computers, and the other is the “Medium Speed” bus in the cabin, for the LCD, Radio, Body Control (BCM – door locks, windows etc), Climate Control, and Instrument Cluster computers. The Instrument Cluster will pass some messages from the Engine network into the Cabin network, and may also do some passing the other way.

I’m pretty sure that the DIS is controlled either in the BCM or Instrument Cluster. Regardless, when you are holding down the Buttons, the Radio sends a message onto the Cabin network, and that somehow tells both the LCD and something else that DIS is now on. (from my testing this setting is not stored in the screen, as the screen does not seem to have any permanent memory and forgets everything on power loss, but I can’t see any other message responding to the button press)

Messages sent regarding DIS

From both a combination of reading Madox’s handy reference [2] and using my home made Arduino CAN tool [3] I was able to figure out the following messages relating to DIS and the screen:

  • 0x28F: A formatting and misc message for the screen, sets locations of colons and commas, does the CD/ST etc fixed segments on the screen. Also is the state of the buttons on the face of the Radio.
  • 0x290: The first 7 characters of the LCD
  • 0x291: The last 5 characters of the LCD (depending on the other values, it might be possible to implement scrolling)
  • 0x400: The trip computer data above. Funnily enough this data is present regardless of the state of DIS.
  • 0x401: Beeps emitted by either the Radio or LCD for the Radio to beep through the main speakers.

 

When you’re holding down CD and AM/FM, the expected combination of bits are flagged in 0x28F’s fifth byte (as in, bit-wise the correct bits are 1 while the buttons are being held). However, after the time out occurs, this byte switches to the DIS byte:

0x28F:128, 0, 0, 0, 32, 0, 0, 0 #Radio Face Idle
0x28F:128, 0, 0, 0, 160, 0, 0, 0 #DIS ENABLE

The Radio then writes the “DIS ON” message to the LCD, with 0x290 and 0x291

The LCD responds with an 0x401 message for the beep.

DIS is now enabled. As you can see, I’ve not found any evidence that the LCD and something else communicate during this process, so I presume that the LCD and BCM/Cluster both read the DIS ON message, and the BCM/Cluster remembers and tells the LCD when necessary.

The SET and CLOCK keys are similar with SET being 0x28F[4] = 40 (+8) and CLOCK being 0x28F[4] = 48 (+16)

The key is however, that the “Radio Face Idle” message MUST BE SENT AFTERWARDS – as these buttons are held to reset the clock/DIS, they only trigger off the release (falling edge) of the button.

Final Notes:

  • If the original Radio did not permit you to enable DIS through the button trick, the Radio will continue to block DIS usage until ACC power is turned off, and the Radio is removed. Probably something about the initial 0x28F message.
  • The MS CAN bus is easily accessible by pushing breadboard cables into purpose designed holes on the harness-side plug for the LCD.
  • There’s a nice little hole above/behind the centre vents that you can stash your micro, if you wish to leave it there permanently. You can run a USB cable down behind the LCD, Radio and HVAC controls, and then out through the cigarette ash holder to allow easy debugging.
  • You can switch the units used on DIS by using the following method:
    • Enter test mode on the Instrument Cluster (Hold the Trip reset button, and then switch the key to ON)
    • Press the trip button until the IC LCD is on page “60” and is showing “DIS”
    • Hold SET to switch the modes (It depends on which page you are on – just hold SET for ~5 seconds per page until the units switch)

[1] http://www.mazda3club.com/how-tos-96/mazda3-unlock-trip-computer-44448/

[2] http://www.madox.net/blog/projects/mazda-can-bus/

[3] https://github.com/nroach44/arduinocrap/tree/master/canscan