Chromebook EC Firmware

Hi,

I recently flashed MrChromebox’s coreboot+edk2 ROM on a Framework Chromebook so that I could run Linux on it. I also purchased a standard FW13 input cover, but I didn’t think about the fact that the key mappings are in the firmware and so are not updated when switching the keyboard.

Does anyone have advice for modifying the EC firmware and flashing an updated version? I assume all that needs to be done is to update the keyboard matrix in the code, but I haven’t worked a lot with EC firmware for things like keyboards before so I’m not sure. It’s also not clear to me what branch of the repo is appropriate for the chromebook variant, since it is not the same as the regular 12th gen Intel. All I really care about is making the caps lock key actually be caps lock/not registered as identical to the other super key so that my standard systemd service to remap it to control/esc will work.

For what it’s worth, I have a hardware programmer and have built/flashed coreboot on many other devices before, so I’m not really concerned about not being able to recover from a bad flash.

Thanks in advance for the help!

1 Like

Welcome to the forum

That’s a good question.

Some time ago, a community member posted a list of a few of the branch names and the platforms they correspond to. This doesn’t seem to be officially documented anywhere, so I’ve wanted to keep it updated. I forgot about the Chromebook, people don’t post about it much.

Out of the branch names available, maybe dogwood for the Chromebook.

Perhaps you could verify. The framework_tool will show the version numbers for all the Framework firmware currently in your laptop, including ther EC, github.com/FrameworkComputer/framework-system.
framework_tool --versions

result for FWL13 AMD Ryzen AI 300
> sudo framework_tool --versions
Place your finger on the fingerprint reader
Mainboard Hardware
  Type:           Laptop 13 (AMD Ryzen AI 300 Series)
  Revision:       MassProduction
UEFI BIOS
  Version:        03.03
  Release Date:   03/10/2025
EC Firmware
  Build version:  lilac-3.0.3-413f018 2025-03-06 05:45:28 marigold2@ip-172-26-3-226
  Current image:  RO
PD Controllers
  Right (01):       0.0.0B (MainFw)
  Left  (23):       0.0.0B (MainFw)
Laptop Webcam Module (2nd Gen)
  Firmware Version: 1.1.1
Touchpad
  Firmware Version: v0704

How to find out which EC-Tool-version my FW is on - #2 by basert


It looks like the ectool will also provide it, github.com/DHowett/framework-ec#ec-version-strings.

If I recall, the ECtool offers the ability to temporarily change keyboard key mapping. FWL13 keyboard matrix, howett.net/data/framework_matrix

For flashing the EC, howett.net. I also ran into instructions here, community.frame.work/t/tracking-battery-flipping-between-charging-and-discharging-draws-from-battery-even-on-ac/22484/234.

2 Likes

Thanks for the very helpful info. After digging more into the code, I don’t actually think any of the branches correspond directly to the FW Chromebook, though various versions of the code for it appear across branches. I think the EC code in this case can be taken directly from the upstream chromium EC code (which I am apparently not allowed to link - it can be found under boards/banshee in the Chromium EC repo on Google’s git).

The output of framework_tool –versions suggests this, as does looking in detail at MrChromebox’s coreboot fork:

❯ sudo framework_tool --versions
Mainboard Hardware
Type: Banshee
[ERROR] Invalid BaseBoard Version: rev3’
Revision: Unknown
UEFI BIOS
Version: MrChromebox-2503.0
Release Date: 04/27/2025
EC Firmware
Build version: banshee_v2.0.20911-c91ee7328e banshee_15357.0.23_04_23 2023-04-23 15:18:13 MrChromebox
RO Version: Unknown
RW Version: Unknown
Current image: Unknown
PD Controllers
Unknown
CSME
Firmware Version: 0:16.1.32.2562
Touchpad
Firmware Version: v0704

(Not sure what to make of the errors/unknown here! I have kernel param iomem=relaxed set so don’t think it’s a permissions issue.)

After comparing the banshee EC code to the standard hx30, I think that all that is necessary should be to recompile with a slightly changed keyboard customization file, and then flash that using the methods you linked above.

I’ll have more time to work on this over the weekend and will update/document if I am able to get positive results. Fixing this would be useful since it would mean the chromebook boards could run Linux with no real downsides, making them the only fully coreboot-capable Framework hardware.

2 Likes

Short update is that the first attempt was unsuccessful and I’m waiting for the correct probe for the EC WSON8 chip to arrive before I can externally flash and restore!

2 Likes

I’ve made the changes to the EC firmware (both on the ToT for chromiumos and on the appropriate firmware release branch), and have built ec.bin file for each. I’ve successfully flashed them, and I’ve got my custom versions in the EC RO slot, but the RW slot remains occupied by MrChromeBox’s EC firmware, and that ultimately gets used at runtime. I’ve checked the EC console during boot, and from what I can tell, my changes aren’t introducing a failure state, so I believe that the coreboot firmware must be altering the RW EC firmware on boot (which is completely reasonable, but I wasn’t expecting it). As a result, I’ll report back with hopefully useful patches after being able to test with a cycle that includes rebuilding coreboot after I understand the mechanism for loading the ec RW firmware better.

3 Likes

Well, this seems to have hit a snag. I got my WSON8 5x6 probe, but it seems that the Chromebook does not have the same EC as the other 12th gen Intel laptops. A previously linked post in this thread references 3 WSON8 chips on the underside of the mainboard, but there are only 2 on the Chromebook, and both seem to be devoted to USB controllers. From looking at the mainboard block diagrams for the chromebook

and for the standard 12th gen Intel

it seems to confirm that there is not a dedicated SPI ROM for the EC. So I am left wondering how exactly to recover from the bad EC firmware given that flashing it externally does not seem to be an option. Maybe the EC firmware is contained with the BIOS on the 32 MB chip on the topside of the mainboard? This could be in line with coreboot altering the EC firmware?

When working on a Chromebook, I use a SuzyQable for Closed Case Debugging to flash recovery firmware. See: hdctools: Chrome OS Hardware Debug & Control Tools - Closed Case Debug (CCD) for information regarding CCD.

I was able to build a version of coreboot that loads my RW firmware a this point, and that’s what was necessary. I believe that this is likely a function of the “Software Sync” capability included in coreboot for use with the chrome-ec firmware. Unfortunately, my changes weren’t good enough to get the keyboard working correctly (my initial naive attempt failed to get the function key working correctly, so I’m not happy with it), so I’m back to debugging what I need to do next.

Unfortunately, I’m a lot less well-versed with the hardware itself, so I’m not sure exactly where the EC firmware is stored physically. I have had very good luck recovering from bad flashes with a SuzyQable and CCD so far.

2 Likes

Short Update: I haven’t put much time into this this week, but I’ve learned a few things that may be worth sharing.

  1. EC firmware at HEAD works (results in a largely working as expected boot and keyboard)
  2. EC firmware on firmware-brya-14505.B-main is also working (as expected)
  3. The vivaldi keyboard definition used on banshee is more difficult than I anticipated to add things like function keys to (so this approach will take time)
  4. The Framework non-Chromebook uses an EC chip drastically different enough that GPIO pins are defined in a different way, and I suspect the i2c controller is different, meaning that attempting to build the hx30 keyboard variant with hacks for the banshee board is nontrivial (I tried this approach, and ended up with what seems to be a mapped keyboard, but clearly is missing one or two columns or rows, and has incorrect function key semantics, resulting in a less usable than Chromebook layout keyboard).

Hopefully I’ll be able to put more than a few minutes into this soon, but I figured since those approaches occurred to me, it’s worth documenting how well they work when done in the most niave way possible. I’m going to spend a chunk of time understanding the vivaldi keyboard definition to see if I can’t get some of the features from newer Chromebooks with more traditional keyboard layouts.

3 Likes

Thanks for documenting all of that very useful information.
I have a Suzy Qable board supposed to be delivered today so hopefully I will be back up and running and also working on this over the weekend. Update: Successfully recovered. Now back to trying to fix the keyboard layout.

I am also interested in seeing if it is possible to get the fingerprint reader to work, though this seems likely to be much more difficult than just fixing the keyboard layout. But having access to the EC console should make the whole process much easier.

The keyboard layout seems to be fixed for me. Vivaldi is indeed problematic; I removed line 43 of board.h in board/banshee to avoid using it:

#define CONFIG_KEYBOARD_VIVALDI

and then in keyboard_customization.c in board/banshee, replace the scancode_set2 matrix with the one from hx30:

uint16_t scancode_set2[KEYBOARD_COLS_MAX][KEYBOARD_ROWS] = {
		{0x0021, 0x007B, 0x0079, 0x0072, 0x007A, 0x0071, 0x0069, 0xe04A},
		{0xe071, 0xe070, 0x007D, 0xe01f, 0x006c, 0xe06c, 0xe07d, 0x0077},
		{0x0015, 0x0070, 0x00ff, 0x000D, 0x000E, 0x0016, 0x0067, 0x001c},
		{0xe011, 0x0011, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
		{0xe05a, 0x0029, 0x0024, 0x000c, 0x0058, 0x0026, 0x0004, 0xe07a},
		{0x0022, 0x001a, 0x0006, 0x0005, 0x001b, 0x001e, 0x001d, 0x0076},
		{0x002A, 0x0032, 0x0034, 0x002c, 0x002e, 0x0025, 0x002d, 0x002b},
		{0x003a, 0x0031, 0x0033, 0x0035, 0x0036, 0x003d, 0x003c, 0x003b},
		{0x0049, 0xe072, 0x005d, 0x0044, 0x0009, 0x0046, 0x0078, 0x004b},
		{0x0059, 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
		{0x0041, 0x007c, 0x0083, 0x000b, 0x0003, 0x003e, 0x0043, 0x0042},
		{0x0013, 0x0064, 0x0075, 0x0001, 0x0051, 0x0061, 0xe06b, 0xe02f},
		{0xe014, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
		{0x004a, 0xe075, 0x004e, 0x0007, 0x0045, 0x004d, 0x0054, 0x004c},
		{0x0052, 0x005a, 0xe03c, 0xe069, 0x0055, 0x0066, 0x005b, 0x0023},
		{0x006a, 0x000a, 0xe074, 0xe054, 0x0000, 0x006b, 0x0073, 0x0074},
};

The output of evtest seems to confirm this was at least partially succesful:

Event: time 1758404694.930867, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0
Event: time 1758404694.930867, -------------- SYN_REPORT ------------
Event: time 1758404695.944722, type 1 (EV_KEY), code 1 (KEY_ESC), value 1
Event: time 1758404695.944722, -------------- SYN_REPORT ------------
^[Event: time 1758404696.026767, type 1 (EV_KEY), code 1 (KEY_ESC), value 0
Event: time 1758404696.026767, -------------- SYN_REPORT ------------
Event: time 1758404696.325733, type 1 (EV_KEY), code 59 (KEY_F1), value 1
Event: time 1758404696.325733, -------------- SYN_REPORT ------------
^[OPEvent: time 1758404696.452880, type 1 (EV_KEY), code 59 (KEY_F1), value 0
Event: time 1758404696.452880, -------------- SYN_REPORT ------------
Event: time 1758404696.716804, type 1 (EV_KEY), code 60 (KEY_F2), value 1
Event: time 1758404696.716804, -------------- SYN_REPORT ------------
^[OQEvent: time 1758404696.795247, type 1 (EV_KEY), code 60 (KEY_F2), value 0
Event: time 1758404696.795247, -------------- SYN_REPORT ------------
Event: time 1758404697.086872, type 1 (EV_KEY), code 61 (KEY_F3), value 1
Event: time 1758404697.086872, -------------- SYN_REPORT ------------
^[OREvent: time 1758404697.181376, type 1 (EV_KEY), code 61 (KEY_F3), value 0
Event: time 1758404697.181376, -------------- SYN_REPORT ------------
Event: time 1758404697.402350, type 1 (EV_KEY), code 62 (KEY_F4), value 1
Event: time 1758404697.402350, -------------- SYN_REPORT ------------
^[OSEvent: time 1758404697.503945, type 1 (EV_KEY), code 62 (KEY_F4), value 0
Event: time 1758404697.503945, -------------- SYN_REPORT ------------
Event: time 1758404697.737995, type 1 (EV_KEY), code 63 (KEY_F5), value 1
Event: time 1758404697.737995, -------------- SYN_REPORT ------------
^[[15~Event: time 1758404697.802600, type 1 (EV_KEY), code 63 (KEY_F5), value0
Event: time 1758404697.802600, -------------- SYN_REPORT ------------
Event: time 1758404698.188745, type 1 (EV_KEY), code 64 (KEY_F6), value 1
Event: time 1758404698.188745, -------------- SYN_REPORT ------------
^[[17~Event: time 1758404698.290089, type 1 (EV_KEY), code 64 (KEY_F6), value0
Event: time 1758404698.290089, -------------- SYN_REPORT ------------
Event: time 1758404698.522865, type 1 (EV_KEY), code 65 (KEY_F7), value 1
Event: time 1758404698.522865, -------------- SYN_REPORT ------------
^[[18~Event: time 1758404698.612872, type 1 (EV_KEY), code 65 (KEY_F7), value0
Event: time 1758404698.612872, -------------- SYN_REPORT ------------
Event: time 1758404698.821763, type 1 (EV_KEY), code 66 (KEY_F8), value 1
Event: time 1758404698.821763, -------------- SYN_REPORT ------------
^[[19~Event: time 1758404698.896393, type 1 (EV_KEY), code 66 (KEY_F8), value0
Event: time 1758404698.896393, -------------- SYN_REPORT ------------
Event: time 1758404699.264084, type 1 (EV_KEY), code 67 (KEY_F9), value 1
Event: time 1758404699.264084, -------------- SYN_REPORT ------------
^[[20~Event: time 1758404699.350030, type 1 (EV_KEY), code 67 (KEY_F9), value0
Event: time 1758404699.350030, -------------- SYN_REPORT ------------
Event: time 1758404699.647561, type 1 (EV_KEY), code 68 (KEY_F10), value 1
Event: time 1758404699.647561, -------------- SYN_REPORT ------------
^[[21~Event: time 1758404699.756267, type 1 (EV_KEY), code 68 (KEY_F10), value 0
Event: time 1758404699.756267, -------------- SYN_REPORT ------------
Event: time 1758404700.163409, type 1 (EV_KEY), code 87 (KEY_F11), value 1
Event: time 1758404700.163409, -------------- SYN_REPORT ------------
^[[23~Event: time 1758404700.323213, type 1 (EV_KEY), code 87 (KEY_F11), value 0
Event: time 1758404700.323213, -------------- SYN_REPORT ------------
Event: time 1758404700.676415, type 1 (EV_KEY), code 88 (KEY_F12), value 1
Event: time 1758404700.676415, -------------- SYN_REPORT ------------
^[[24~Event: time 1758404700.792431, type 1 (EV_KEY), code 88 (KEY_F12), value 0
Event: time 1758404700.792431, -------------- SYN_REPORT ------------
Event: time 1758404702.243608, type 1 (EV_KEY), code 88 (KEY_F12), value 1
Event: time 1758404702.243608, -------------- SYN_REPORT ------------
^[[24~Event: time 1758404702.374220, type 1 (EV_KEY), code 88 (KEY_F12), value 0
Event: time 1758404702.374220, -------------- SYN_REPORT ------------
Event: time 1758404703.129257, type 1 (EV_KEY), code 111 (KEY_DELETE), value 1
Event: time 1758404703.129257, -------------- SYN_REPORT ------------
^[[3~Event: time 1758404703.230498, type 1 (EV_KEY), code 111 (KEY_DELETE), value 0
Event: time 1758404703.230498, -------------- SYN_REPORT ------------

Certainly remains to be seen if the removal of vivaldi will cause other issues. More work needs to be done to get the function key to work properly; it currently does not register key presses.

1 Like

I have made progress in getting the function key itself to work properly. The default is for the keys to act as the function indicated by the icon (volume mute, up, etc.) and now the function key acts as normal - it must be pressed to use F1-F12, and escape does work as function lock. It seems almost completed; however, for some reason, not all of the other controls work. Volume/media controls do, but the brightness controls are not working despite the fact that the scancode seems to exist. The relevant part of the keyboard_customization.c is here:

int hotkey_F1_F12(uint16_t *key_code, uint16_t fn, int8_t pressed)
{
	const uint16_t prss_key = *key_code;

	if (!(Fn_key & FN_LOCKED) && (fn & FN_PRESSED))
		return EC_SUCCESS;
	else if (Fn_key & FN_LOCKED && !(fn & FN_PRESSED) &&
		 !fn_key_table_media)
		return EC_SUCCESS;
	else if (!fn_key_table_media && !pressed)
		return EC_SUCCESS;

	switch (prss_key) {
	case SCANCODE_F1: /* SPEAKER_MUTE */
		if (fn_table_media_set(pressed, KB_FN_F1))
			*key_code = SCANCODE_VOLUME_MUTE;
		break;
	case SCANCODE_F2: /* VOLUME_DOWN */
		if (fn_table_media_set(pressed, KB_FN_F2))
			*key_code = SCANCODE_VOLUME_DOWN;
		break;
	case SCANCODE_F3: /* VOLUME_UP */
		if (fn_table_media_set(pressed, KB_FN_F3))
			*key_code = SCANCODE_VOLUME_UP;
		break;
	case SCANCODE_F4: /* PREVIOUS_TRACK */
		if (fn_table_media_set(pressed, KB_FN_F4))
			*key_code = SCANCODE_PREV_TRACK;
		break;
	case SCANCODE_F5: /* PLAY_PAUSE */
		if (fn_table_media_set(pressed, KB_FN_F5))
			*key_code = 0xe034;
		break;
	case SCANCODE_F6: /* NEXT_TRACK */
		if (fn_table_media_set(pressed, KB_FN_F6))
			*key_code = SCANCODE_NEXT_TRACK;
		break;
	case SCANCODE_F7: /* TODO: DIM_SCREEN */
		if (fn_table_media_set(pressed, KB_FN_F7)) {
			*key_code = SCANCODE_BRIGHTNESS_DOWN;
		}
		break;
	case SCANCODE_F8: /* TODO: BRIGHTEN_SCREEN */
		if (fn_table_media_set(pressed, KB_FN_F8)) {
			*key_code = SCANCODE_BRIGHTNESS_UP;
		}
		break;
	case SCANCODE_F9: /* EXTERNAL_DISPLAY */
		if (fn_table_media_set(pressed, KB_FN_F9)) {
			return EC_ERROR_UNIMPLEMENTED;
		}
		break;
	case SCANCODE_F10: /* FLIGHT_MODE */
		if (fn_table_media_set(pressed, KB_FN_F10)) {
			return EC_ERROR_UNIMPLEMENTED;
		}
		break;
	case SCANCODE_F11:
		/*
		 * TODO this might need an
		 * extra key combo of:
		 * 0xE012 0xE07C to simulate
		 * PRINT_SCREEN
		 */
		if (fn_table_media_set(pressed, KB_FN_F11))
			*key_code = 0xE07C;
		break;
	case SCANCODE_F12: /* TODO: FRAMEWORK */
		/* Media Select scan code */
		if (fn_table_media_set(pressed, KB_FN_F12))
			*key_code = 0xE050;
		break;
	default:
		return EC_SUCCESS;
	}
	return EC_SUCCESS;
}

I have made my full modified code available on Github: ewtodd/FrameworkEC (I can’t post a link!).

I’ll finally note that hx30 seemed to use a workaround function to control brightness, but since the scancode existed already here I had hoped it might just work. I’ve settled to just mapping brightness controls to F7/F8 at window manager level for now.

I was able to get the keyboard backlight to work as well.

1 Like