The Chromium OS project includes open source software for embedded controllers (EC) used in recent ARM and x86 based Chromebooks. This software includes a lightweight, multitasking OS with modules for power sequencing, keyboard control, thermal control, battery charging, and verified boot. The EC software is written in C and currently supports two different ARM Cortex based controllers. Intel based designs, such as the Chromebook Pixel use the TI Stellaris LM4F (Cortex M4) while the Samsung Chromebook (XE303C12) and HP Chromebook 11 use an ST-Micro STM32F100 (Cortex M3). Some STM32L variants are also supported. Support for additional embedded controllers is ongoing.
This document is a guide to help make you familiar with the EC code, current features, and the process for submitting code patches.
The code for the EC is open source and is included in the Chromium OS development environment (~/trunk/src/platform/ec/). See http://www.chromium.org/chromium-os/quick-start-guide for build setup instructions. If you want instant gratification, you can fetch the source code directly. However, you will need the tool-chain provided by the Chromium OS development environment to build a binary.
The source code can also be broswed on the web at:
The following is a quick overview of the top-level directories in the EC repository:
Each Chrome device has a firmware branch created when the read-only firmware is locked down prior to launch. This is done so that updates can be made to the read-write firmware with a minimal set of changes from the read-only. Some Chrome devices only have build targets on firmware branches and not on cros/master. Run “git branch -a | grep firmware” to locate the firmware branch for your board. Note that for devices still under development, the board configuration may be on the branch for the platform reference board.
To build EC firmware on a branch, just check it out and build it:
To make changes on a branch without creating a whole new development environment (chroot), create a local tracking branch:
(The --cbr means "upload to the current branch")
Here is a useful command to see commit differences between branches (change the branch1...branch2 as needed):
For example, to see the difference between cros/master and the HEAD of the current branch:
Note: Use three dots “...” or it won’t work!Note: The EC is normally built from within the Chromium OS development chroot to use the correct tool-chain. Building directly from the EC repository:
Building via emerge (the build file used when you build Chrome OS):
(optional) Run this command if you want to build from local source instead of the most recent stable version:
Build the EC binary:
Please be careful if doing both local “makes” and running emerge. The emerge can pick up build artifacts from the build sub-directory. It’s best to delete the build directory before running emerge with
The generated EC binary from emerge is found at:
The ebuild file used by Chromium OS is found here:
If you get an error, you may not have set up the dependencies for servo correctly. The EC (on current Chromebooks) must be powered either by external power or a charged battery for re-flashing to succeed. You can re-flash via servo even if your existing firmware is bad.
Note: This command will fail if write protect is enabled.
If you build your own EC firmware with the “make BOARD=<boardname>” command the firmware image will be at:
If you build Chrome OS with build_packages the firmware image will be at:
Specifying "--image" is optional. If you leave off the “--image” argument, the flash_ec script will first look for a locally built ec.bin followed by one generated by emerge.
Assuming your devices boots, you can flash it using the flashrom utility. Copy your binary to the device and run:
Note: “-p internal:bus=lpc” also works on x86 boards...but why would you want to remember and type all that?
A feature called “Software Sync” keeps a copy of the read-write (RW) EC firmware in the RW part of the system firmware image. At boot, if the RW EC firmware doesn't match the copy in the system firmware, the EC’s RW section is re-flashed. While this is great for normal use as it makes updating the EC and system firmware a unified operation, it can be a challenge for EC firmware development. To disable software sync a flag can be set in the system firmware. Run the following commands from a shell on the device to disable Software Sync and turn on other developer-friendly flags (note that write protect must be disabled for this to work):
This turns on the following flags:
The GBB (Google Binary Block) flags are defined in the vboot_reference source. A varying subset of these flags are implemented and/or relevant for any particular board.
The EC has an interactive serial console available only through the UART connected via servo. This console is essential to developing and debugging the EC.
Find the serial device of the ec console (on your workstation):
Connect to the console:
Useful EC console commands:
Most code run on the EC after initialization is run in the context of a task (with the rest in interrupt handlers). Each task has a fixed stack size and there is no heap (malloc). All variable storage must be explicitly declared at build-time. The EC (and system) will reboot if any task has a stack overflow. Tasks typically have a top-level loop with a call to task_wait_event() or usleep() to set a delay in uSec before continuing. A watchdog will trigger if a task runs for too long. The watchdog timeout varies by EC chip and the clock speed the EC is running at.
The list of tasks for a board is specified in ec.tasklist in the board/$BOARD/ sub-directory. Tasks are listed in priority order with the lowest priority task listed first. A task runs until it exits its main function or puts itself to sleep. The highest priority task that wants to run is scheduled next. Tasks can be preempted at any time by an interrupt and resumed after the handler is finished.
The console “taskinfo” command will print run-time stats on each task:
The “stack used” column reports the largest size the stack for each task grew since reset (or sysjump).
Hooks allow you to register a function to be run when specific events occur; such as the host suspending or external power being applied:
DECLARE_HOOK(HOOK_AC_CHANGE, ac_change_callback, HOOK_PRIO_DEFAULT);
Registered functions are run in the HOOKS task. Registered functions are called in priority order if more than one callback needs to be run. There are also hooks for running functions periodically: HOOK_TICK (fires every HOOK_TICK_INVERVAL mSec which varies by EC chip) and HOOK_SECOND. See hook_type in include/hooks.h for a complete list.
Deferred functions allow you to call a function after a delay specified in uSec without blocking. Deferred functions run in the HOOKS task. Here is an example of an interrupt handler. The deferred function allows the handler itself to be lightweight. Delaying the deferred call by 30 mSec also allows the interrupt to be debounced.
While there is no heap, there is a shared memory buffer that can be borrowed temporarily (ideally before a context switch). The size of the buffer depends on the EC chip being used. The buffer can only be used by one task at a time. See common/shared_mem.c for more information. At present (May 2014), this buffer is only used by debug commands.
If you see a bug or want to make an improvement to the EC code please file an issue at crbug.com/new. It's best to discuss the change you want to make first on an issue report to make sure the EC maintainers are on-board before digging into the fun part (writing code).
In general, make more, smaller changes that solve single problems rather than bigger changes that solve multiple problems. Smaller changes are easier and faster to review. When changing common code shared between boards along with board specific code, please split the shared code change into its own change list (CL). The board specific CL can depend on the shared code CL.
The EC code follows the Linux Kernel style guide (https://www.kernel.org/doc/Documentation/CodingStyle). Please adopt the same style used in the existing code. Use tabs, not spaces, < 80 column lines etc...
Other style notes:
Prior to uploading a new change for review, please run the EC unit tests with:
Pre-submit checks are run when you try to upload a change-list. If you wish to run these checks manually first, commit your change locally then run the following command from within the chroot and while in the src/platform/ec directory:
The pre-submit checks include checking the commit message. Commit messages must have a BUG, BRANCH, and TEST line along with “Signed-off-by: First Last <firstname.lastname@example.org>”. The signed-off-by line is a statement that you have written this code and it can be contributed under the terms of the LICENSE file.
Please refer to existing commits (git log) to see the proper format for the commit message. If you have configured git properly, running “git commit” with the “-s” argument will add the Signed-off-by line for you.
While adding printf statements can be handy, there are some other options for debugging problems during development.
There may already be a message on the serial console that indicates your problem. If you don’t have a servo connected, the "ectool console" command will show the current contents of the console buffer (the buffer’s size varies by EC chip). This log persists across warm resets of the host but is cleared if the EC resets. The “ectool console” command will only work when the EC is not write protected.
If you have interactive access to the serial console via servo, you can use the read word “rw” and write word “ww” commands to peek and poke the EC's RAM. You may need to refer to the datasheet for your EC chip or the disassembled code to find the memory address you need. There are other handy commands on the serial console to read temperatures, view the state of tasks (taskinfo) which may help. Type "help" for a list.
The EC may save panic data which persists across resets. Use the “ectool panicinfo” command or console “panicinfo” command to view the saved data:
The most interesting information are the program counter (pc) and the link register (return address, lr) as they give you an indication of what code the EC was running when the panic occurred. “HANDLER EXCEPTIONS” indicate the panic occurred while servicing an interrupt. “PROCESS EXCEPTIONS” occur in regular tasks. If you see “Imprecise data bus error” listed, the program counter value is incorrect as the panic occurred when flushing a write buffer. If using a cortex-m based EC, add “CONFIG_DEBUG_DISABLE_WRITE_BUFFER” to your board.h to disable write buffering (with a performance hit) to get a “Precise bus error” with an accurate program counter value.
If you have a program counter address you need to make sense of, you can generate the assembly code for the EC by checking out the code at the matching commit for your binary (“ectool version”) and running:
This outputs two files with assembly code:
which (in the case of the LM4 and STM32) are essentially the same, but the RW addresses are offset.
The EC has read-only (RO) and read-write (RW) firmware. Coming out of reset, the EC boots into its RO firmware. The RO firmware boots the host and asks it verify a hash of the RW firmware (software sync). If the RW firmware is invalid, it is updated from a copy in the hosts RW firmware. Once the EC RW firmware is valid, the EC jumps to it (without rebooting). The RO firmware is locked in the factory and is never changed. The RW firmware can be updated later by pushing a new system firmware containing an updated EC RW region.
Note that both the RO and RW firmware regions are normally protected once write protect has been turned on. The RW region is unprotected at EC boot until it has been verified by the host. The RW region is protected before the Linux kernel is loaded.
A hardware-based mechanism is used to prevent the RO firmware from being changed. The most common design is to have an input grounded by a screw. When the screw is inserted, hardware write protect is enabled. This grounded signal can be read by the host chipset and EC. It is also routed to the “write protect” pin on any SPI flash chips containing firmware.
Software-based write protect state stored in non-volatile memory. If hardware write protect is enabled, software write protect can be enabled but can’t be disabled. If hardware write protect is disabled, software write protect can be enabled or disabled (note that some implementations require an EC reset to disable software write protect).
The underlying mechanism implementing software write protect may differ between EC chips. However the common requirements are that software write protect can only be disabled when hardware write protect is off and that the RO firmware must be protected before jumping to RW firmware if protection is enabled.
Ectool includes commands to enable and disable software write protect.
ectool flashprotect - Print out current flash protection state.
ectool flashprotect enable - Set ro_at_boot flag. The next time the EC is reset it will protect the flash. Note that this requires a cold reset.
ectool flashprotect enable now - Set ro_at_boot ro_now all_now flags and immediately protect the flash. Note that this will fail if hardware write protect is disabled.
ectool flashprotect disable - Clear ro_at_boot flag. This can only be cleared if the EC booted without hardware write protect enabled.
Note that you must reset the EC to clear write protect after removing the screw. If the ro_at_boot flag set and the EC resets with the HW gpio disabled, the EC will leave the flash unprotected (ro_now and all_now flags are not set) but leave ro_at_boot flag set.
Flashrom can also be used to query and enable/disable EC flash protection.
View the current state of flash protection:
Enable protection. This is immediate. The protection range indicates the RO region of the firmware.
Disable protection. Disable can only be done with hardware write protect disabled.
Reboot with screw removed. Note that protection is still enabled but the protection range is zero.
The read-only and read-write sections of the EC firmware each have a version string. This string tells you the branch and last change at which the firmware was built. On a running machine, run "ectool version" from a shell to see version information:
You can also run the "version" command on the EC serial console for a similar output.
The format of the version string is:
If the version is: rambi_v1.6.68-a6608c8
The branch numbers (as of May 2014) are:
Hack command to check the branch tags:
Run “util/getversion.sh” to see the current version string. The board name is passed as an environment variable “BOARD”: