FSP1.1 Braswell TempRaminit problems

Coreboot is migrating platforms from a romcc bootblock to C_ENVIRONMENT_BOOTBLOCK in which Cache-as-Ram is set up in the bootblock. When migrating Braswell, chromebooks featuring this SOC did not boot anymore while other boards did. Google uses a different FSP binary than the one present in the Intel Github FSP repository.

Previously the romcc bootblock set up caching of the ROM, located microcodes and performed the update before calling FSP TempRaminit.

Given that you pass on a pointer and a size of the microcode updates to the FSP TempRaminit the assumption was made that loading microcode updates before loading FSP was not needed. This assumption was correct for the public FSP release.

The google FSP contains a bug however. Let's see what is going on.

Often your system can feature multiple CPU revisions and including different microcode for these revision is needed. Intel microcode updates have a simple format. They have a header specifying the CPU target + the size and a data section holding the actual update. What is done is to append microcode updates to each other in one file and loop through them until the correct one is found.

The FSP does the following:

  • Loop through microcode
  • Check for revision, flags, ...
  • If a match is found, check if it is not the same revision as currently installed (it should check for > current revision!)
  • go the the next one by parsing the header for the size
  • When the end is reached, check for the current installed revision. If it's 0 bail out (and reboot!)

The bug of the Google FSP release lies in incrementing the register holding the microcode address. At some point the intend is to increment it with 0x14 and compare the revision to the CPUID. Instead of incrementing it with $0x14 it increments it to 0x14. This means that not 0x14 is additioned but whatever the content of address 0x14 holds, which is unkown since memory is not initialized at this point.

So what happens is that the Google FSP binary cannot load the update microcode if the first microcode is not a match, which will have the FSP fail and reboot (loop).

The Intel FSP release has this bug fixed.

The google FSP version for Braswell has a bug in the microcode updater.

Given the nature of the microcode update mechanism of the FSP and the check it does at the end. The best solution seems to be to provide the FSP with a fake MCU and do the updating ourselves before FSP is called. This reduces dependency on FSP doing the right thing (which in this case it clearly doesn't) and also removes the need to specifiy the MCU location at build-time.

About CAR

A gentle introduction to Cache-as-Ram on X86

This explains a bit of history on CAR in coreboot and how it works.


  • CPU cache: CPU cache is a piece of fast memory used by the CPU to cache access to things accessed in the CPU's linear memory space. This includes for DRAM, PCI BARs, the boot flash.
  • Cache-as-RAM/CAR: Using not memory mapped CPU cache as execution environment.
  • XIP: Execute in place on a memory mapped (read only) medium.
  • SRAM: Static Random Access Memory, here: memory mapped memory that needs no initialization.
  • ROMCC: A C compiler using only the CPU registers.

Cache-as-Ram, the basics

When an X86 platforms boot, it starts in a pretty bare environment. The first instruction is fetched from the top of the boot medium, which on most x86 platforms is memory mapped below 4G (in 32bit protected mode). Not only do they start in 16bit 'real' mode, most of the hardware starts uninitialized. This includes the dram controller. Another thing about X86 platforms is that they generally don't feature SRAM, static RAM. SRAM for this purpose of this text is a small region of ram that is available without any initialization. Without RAM this leaves the system with just CPU registers and a memory mapped boot medium of which code can be executed in place (XIP) to initialize the hardware.

For simple dram controllers that environment is just fine and initializing the dram controller in assembly code without using a stack is very doable. Modern systems however require complex initialization procedures and writing that in assembly would be either too hard or simply impossible. To overcome this limitation a technique called Cache-as-Ram exists. It allows to use the CPU's Cache-as-Ram. This in turn makes it possible to write the early initialization code in a higher level programming language compiled by a regular compiler, e.g. GCC. Typically only the stack is placed in Cache-as-Ram and the code is still executed in place on the boot medium, but copying code in the cache as ram and executing from there is also possible.

For coreboot this means that with only a very small amount of assembly code to set up CAR, all the other code can written in regular ANSI C, which compared to writing assembly tremendously increases development speed and is much less error prone.

A bit of history on Cache-as-Ram in coreboot

I was not around so this part could be inaccurate. It should however paint an picture of the challenges of early code on x86 platforms.

Linux as a BIOS? The invention of romstage

When LinuxBIOS, later coreboot, started out, the first naive attempt to run the Linux kernel instead of the legacy BIOS interface, was to jump straight to loading the kernel. This did not work at all, since dram was not working at that point. LinuxBIOS v1 therefore initialized the memory in assembly before it was able to load the kernel. This executed in place and would later become romstage. It turned that Linux needed other devices, like enumerated and have it's resources allocated, so yet another stage, ramstage, was called into life, but that's another story.

coreboot v2: romcc

In 2003 AMD's K8 CPU's came out. Those CPU's featured an on die memory controller and also a new point to point BUS for CPU <-> CPU and CPU <-> IO controller (northbridge) devices, called Hypertransport. Writing support for these in assembly became difficult and a better solution was needed. This is where ROMCC came to the rescue. ROMCC is a 25K lines of code C compiler, written by Eric Biederman, that does not use a stack but uses CPU registers instead. Besides the 8 general purpose registers on x86 the MMX and SSE registers, respectively %mm0-%mm7 and %xmm0-%xmm7, can be used to store data. ROMCC also converts all function calls to static inline functions. This has the disadvantage that a ROMCC compiled stage must be compiled in one go, linking is not possible. In practice you will see a lot of '#include _somefile.c' which is quite unusual in C, because it makes the order of inclusion of files fragile. Given that limited amount 'register-stack', it also imposes restrictions on how many levels of nested function can be achieved. One last issue with ROMCC is that no-one really dared touching that code, due to its size and complexity besides its original author.

coreboot used ROMCC in the following way: a bootblock would use assembly to enter 32bit protected mode after which it would use ROMCC compiled code to either jump to 'normal/romstage' or 'fallback/romstage' which in turn was also compiled with ROMCC.


While ROMCC was eventually used for early initialization, prior to that development attempts were made to use the CPU's cache as RAM. This would allow regular GCC compiled and linked code to be used. The idea is to set up the CPU's cache to Write Back to a fixed region, while disable cacheline filling or simply hope that no cache eviction or invalidation happens. Eric Biederman initially got CAR working, but with the advent of Intel Hyperthreading on the Pentium 4, on which CAR setup proved to be a little more difficult and he developed ROMCC instead.

Around 2006 a lot was learned from coreboot v2 with its ROMCC usage and development on coreboot v3 was started. The decision was mode to not use ROMCC anymore and use Cache-as-Ram for early stages. The results were good as init object code size is reduced by factor of four. So the code was smaller and faster at the same time.

coreboot v4 sort of merged v2 and v3, so it was a mix of ROMCC compiled romstage and romstage with CAR.

In 2014 Support for ROMCC compiled romstage was dropped.

Intel Apollolake, a new era: POSTCAR_STAGE and C_ENVIRONMENT_BOOTBLOCK

In 2016 Intel released the ApolloLake architecture. This architecture is a weird duck in the X86 pool. It is the first Intel CPU to feature MMC as a boot medium and it does not memory map it. It also features to the main CPU read only SRAM that is mapped right below 4G. Before the main CPU is released from reset the bootblock is copied to SRAM and executed. Given that the boot medium is not (always) memory mapped XIP is not an option. The solution is to set up CAR in the bootblock and copy the romstage in CAR. We call this C_ENVIRONMENT_BOOTBLOCK, because it runs GCC compiled code. Granted XIP is still possible on ApolloLake when using SPI flash as bootmedium, but coreboot has to use Intel's blob, FSP-M, and it is linked to run in CAR, so a blob actually forced a nice feature in coreboot!

Another issue arises with this setup. With romstage running from the read only boot medium, you can continue executing code (albeit without a stack) to tear down the CAR and start executing code from 'real' RAM. On ApolloLake with romstage executing from CAR, tearing down CAR in there, would shooting in ones own foot as our code would be gone in doing so. The solution was to tear down CAR in a separate stage, named 'postcar' stage. This provides a clean separation of programs and results in needing less linker scripts hacks in romstage. This solution was therefore adopted by many other platforms even though they did XIP.

AMD Zen, 2019

On AMD Zen the first CPU to come out of reset is the PSP and it initializes the dram controller before pulling out the main CPU out of reset. CAR won't be needed nor used on this platform.

The future?

For coreboot release 4.11, scheduled for october 2019, support for ROMCC in the bootblock will be dropped as will support for the messy tearing down of CAR in romstage as opposed to doing that in a separate stage.


2007, The Open Source BIOS is Ten An interview with the coreboot developers
A Framework for Using Processor Cache-as-Ram (CAR)
CAR: Using Cache-as-Ram in LinuxBIOS

It’s back! Running a server of a single board computer.


After about 6 months this blog is back online. The reason for it being offline is that that my Bananapi, featuring an allwinner A20, crashed. More specifically the SD card of which most of the software was run had fatally entered a state in which it started to have bad blocks that are marked read only. I previously also had this on a raspberry pi after 2 years of use.

Don't use SD cards for stuff you rely on! Now only the bootloader, u-boot, and a boot partition is on the SD card. The bulk of the OS is one small disk scavenged from a laptop hooked up via SATA and a larger HDD via USB to serve files around the house.

Why use a Single board computer to run a server? Electricity is expensive in Belgium, so devices running 24-7 better use little power.

Hopefully this setup will last longer. I'll make database backups for sure this time.

OLD: How to flash coreboot to the thinkpad x60 the proper way

What is so special about the x60 when running vendor bios?

Vendor BIOS write protects its bootblock, which means the lowest 64K of the flash can’t be modified

This is a problem since the first code that runs on the CPU comes from there and if we ever want to run coreboot the cpu must start with coreboot code. This write protection is set in the PBR (protect bios range) registers on the southbridge, there is currently no known way to change them back once they are locked by setting the SPI Configuration Lock-Down bit.

FLASH (2M)  
    +--------------------+ -> 0x200000
    |                    | 
    |  bootblock_0       | ---> Only read allowed
    |                    | 
    +--------------------+ -> 0x1F0000
    |                    |  ---> Read and write: ok
    Z                    Z
    Z                    Z
    |                    |
    +--------------------+ -> 0x000000

Vendor BIOS prohibits many flash operations

Naturally flash chips have many different operation, for example multiple read, write, erase and ID probe operation. The vendor bios prohibits a lot of those, which is why stock flashrom will fail to work properly.

How to solve these issue without needing external or hardware flashing

The write protection problem

Luckily the chipset provides a feature called BUC.TS (back up control top swap). In this mode the way the flash gets decoded (read by the cpu) changes. In this mode not the lowest 64K get read by the CPU first but the 64K above those. This is great since we can now put our coreboot bootblock in that 64K region above and that will execute first on the CPU instead of the vendor BIOS bootblock!

This ascii art tries to explain it.

    +-------------+ -> 0x200000         +-------------+ -> 0xFFFFFFFF       +-------------+ -> 0xFFFFFFFF
    |             |                     |             |                     |             |
    | bootblock_0 |                     | bootblock_0 |                     | bootblock_1 |
    |             |                     |             |                     |             |
    +-------------+ -> 0x1F0000         +-------------+ -> 0xFFFF0000       +-------------+ -> 0xFFFF0000
    |             |                     |             |                     |             |
    | bootblock_1 |                     | bootblock_1 |                     | bootblock_0 |
    |             |                     |             |                     |             |
    +-------------+ -> 0x1E0000         +-------------+ -> 0xFFFE0000       +-------------+ -> 0xFFFE0000
    |             |                     |             |                     |             |
    |             |                     |             |                     |             |
    Z             Z                     Z             Z                     Z             Z
    Z             Z                     Z             Z                     Z             Z
    |             |                     |             |                     |             |
    |             |                     |             |                     |             |
    FLASH (2M)                          Memory Map BUC.TS=0               Memory Map BUC.TS=1

The flash operations restriction

The only real issue will be that the flash cannot be detected with default flashrom ID operation. Luckily the flash has backup operation for probing the ID so we can patch flashrom to use those.

There is some WIP to probe chips with multiple method which would obsolete the hack we need here.

Some preparations

Because of all of this some tools will be needed


To swap the BUC.TS bit we will need a tool called buc.ts. Go to the directory where the code is located and build it

cd util/bucts/


Flashrom needs some patching to be able to probe the flashchips and write to them. On the X60 2 different flashchips can occur so we need to patch flashrom for two of these.

First download flashrom (best outside the coreboot tree to keep everything tidy):

wget https://download.flashrom.org/releases/flashrom-1.1.tar.bz2
tar xvf flashrom-1.1.tar.bz2

Go inside the flashrom dir, fetch the patches and apply them:

cd flashrom
wget https://notabug.org/libreboot/libreboot/raw/master/resources/flashrom/patch/lenovobios_sst.diff
wget https://notabug.org/libreboot/libreboot/raw/master/resources/flashrom/patch/lenovobios_macronix.diff
patch -p0 < lenovobios_sst.diff
patch -p0 < lenovobios_macronix.diff

For reference here are them:

--- flashchips.c    2014-12-30 01:59:49.846383043 +0000
+++ flashchips.c.i945lenovobios_sst 2014-12-30 02:03:51.367580645 +0000
@@ -10886,12 +10886,12 @@
        .name       = "SST25VF016B",
        .bustype    = BUS_SPI,
        .manufacture_id = SST_ID,
-       .model_id   = SST_SST25VF016B,
+       .model_id   = 0x41,
        .total_size = 2048,
        .page_size  = 256,
        .feature_bits   = FEATURE_WRSR_EITHER,
        .tested     = TEST_OK_PREW,
-       .probe      = probe_spi_rdid,
+       .probe      = probe_spi_res2,
        .probe_timing   = TIMING_ZERO,
        .block_erasers  =
@@ -10914,7 +10914,7 @@
        .printlock  = spi_prettyprint_status_register_sst25vf016,
        .unlock     = spi_disable_blockprotect,
-       .write      = spi_aai_write,
+       .write      = spi_chip_write_1,
        .read       = spi_chip_read,
        .voltage    = {2700, 3600},
--- flashchips.c    2014-12-30 01:59:49.846383043 +0000
+++ flashchips.c.i945lenovobios_macronix    2014-12-30 02:05:16.060000647 +0000
@@ -6605,12 +6605,12 @@
        .name       = "MX25L1605D/MX25L1608D/MX25L1673E",
        .bustype    = BUS_SPI,
        .manufacture_id = MACRONIX_ID,
-       .model_id   = MACRONIX_MX25L1605,
+       .model_id   = 0x14,
        .total_size = 2048,
        .page_size  = 256,
        .feature_bits   = FEATURE_WRSR_WREN,
        .tested     = TEST_OK_PREW,
-       .probe      = probe_spi_rdid,
+       .probe      = probe_spi_res1,
        .probe_timing   = TIMING_ZERO,
        .block_erasers  =
@@ -6630,7 +6630,7 @@
        .printlock  = spi_prettyprint_status_register_bp3_srwd, /* bit6: Continuously Program (CP) mode */
        .unlock     = spi_disable_blockprotect,
-       .write      = spi_chip_write_256,
+       .write      = spi_chip_write_1,
        .read       = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
        .voltage    = {2700, 3600},

Now our flashrom is ready to use (remember to use this patched flashrom build and not the one from your distro).


We need a second bootblock at a 64K offset of the bottom.

In the menuconfig select:


and make sure the bootblock size remains to the default 0x10000:


Coreboot set BILD (BIOS Interface Lock-Down) register, which prevents changes to BUC.TS. Since we only set BUC.TS to flash coreboot once but want to unset it later on we want


which prevents coreboot from locking down this register.

Now we are ready to build the image which will be covered in the next section.

Building the coreboot image

This section assumes you have got the coreboot toolchain built.

Now it’s just a matter selecting some options and compiling the rom we will flash.

The defaults are ok (SeaBIOS with native graphic init) so the only thing we will need to select are the binary repository for microcode select _Allow use of binary-only repository\/(CONFIG_USEBLOBS) in make menuconfig under general the options from the previous section and build it:


make sure at the end of the build process you see the following line at the end:

bootblock                      0x1dfdc0   bootblock      131072 none

The bootblock file must be exactly! 0x131072 (128K) large. It contains both the regular and the bucts bootblock.

Flashing the coreboot rom

This is the exciting part where we will actually flash coreboot to the motherboard.

So our rom is now inside the coreboot tree sitting at build/coreboot.rom flashrom is in whichever place you downloaded, patched and build flashrom and bucts is under util/bucts/bucts

Let’s get started. First backup the vendor bios in case something goes wrong. For this we need to use our special flashrom:

./path/to/flashrom/flashrom --programmer internal -r backup.rom

We want to be absolutely sure we got a proper backup so let’s verify it against the flash.

./path/to/flashrom/flashrom --programmer internal --verify backup.rom

Now put that backup.rom somewhere safe like on an USB drive.

Now we want to flip BUC.TS

util/bucts/bucts --set

Now the scary/fun part: flashing the actual rom. Because the bottom part of the flash is write protected we want to skip writing to it because that will have flashrom print out some scary messages. To do this we will be using a layout. Create a file, x60_layout with the following content:

0x00000000:0x001effff RW
0x001f0000:0x001fffff RO

And let’s flash it using this layout

./path/to/flashrom/flashrom --programmer internal --write build/coreboot.rm --layout x60_layout --image RW

Flashrom will try some erase function (that the vendor BIOS prohibits using) but will succeed in the end. Note that it can take some time because slow write operations have have to be used.

One thing you still want to do before rebooting is setting coreboot settings, located in the rtc nvram or cmos to their default values: start in the coreboot tree

cd util/nvramtool
./nvramtool -y ../../src/mainboard/lenovo/x60/cmos.layout -p ../../src/mainboard/lenovo/x60/cmos.default

And done!

NOTE: If flashrom complains, then don’t reboot and report to #coreboot chat on freenode irc!

Well almost. If BUC.TS gets 0 again (which happens if you pull the RTC CMOS ‘yellow’ battery) it will start the vendor bootblock again and we have a brick. So we need to flash coreboot again which is now possible because coreboot does not write protect the flash/bootblock like vendor does it.

So reboot from first flash, assuming flashrom did not complain previous. Simply flash the rom again and we are done (for real now) and set bucts to 0 again:

flashrom -p internal -w build/coreboot.rom
util/bucts/bucts --unset

It is really important to set BUC.TS to zero again. When you build coreboot normally it won’t have a bucts bootblock and will therefore fail to boot (although pulling the cmos battery will do the trick too).

Note that you can use your distro’s flashrom now. When building a new coreboot rom, you now don’t need to select CONFIG_INTEL_ADD_TOP_SWAP_BOOTBLOCK=y anymore.

NOTE: you might want to re-flash a new build with CONFIG_INTEL_CHIPSET_LOCKDOWN=y set since locking down the system is a desirable security feature.

Was originally published here.


Add this to the coreboot documentation

OLD + UPDATE:Porting the Intel DG41WV and Intel 4 series DDR3 raminit to coreboot

This post will explain a bit how the Intel 4 series DDR3 raminit came to be and will introduce a new board that can make use of this code, namely the Intel DG41WV.

DDR3 raminit

In the past I have worked quite on a bit on the coreboot code that support the Intel 4 series desktop chipset (those chipsets go by the name of G41, G43, G45, Q43, Q45, P41, P43, P45, B43 with each having a somewhat different feature set). In the past I have made quite a few improvements to the coreboot code for this platform:

  • Improve the dram compatibility
  • Rework how the SPD’s get decoded
  • Rework the DQS receive enable calibration
  • Add support for using external graphic devices in the PEG slottart of the dram initialization and it will be the firmware's job to find out good values for those.
  • Add resume from S3 support
  • Make properly rebooting possible
  • Getting variants with the ICH10 southbridge to work

Given the work I did on this and other northbridges/raminit code I was ready for something more challenging and I thought adding DDR3 support would be something nice (previously. DDR3 is quite a different beast however.

DDR3 fly-by topology

To increase the clock frequency beyond DDR2 frequencies something had to be done to the topology of dram DIMMs. To increase the signal integrity a fly by topology had to be adopted.

The data signals follow a shorter path and come from below, but where the clock signal previously followed a T-branch path and arrived more or less at the same time in the chips, DDR3 technology adopted a fly-by topology. The clock signal arrives at a different time for each data signal pair, DQ (data) and DQS (data strobe).
Memory controllers naturally can delay some of its signals using DLL's (delay locked loop). The clock signal will generally be fixed at the start of the dram initialization and it will be the firmware's job to find out good values for the delays on the other signal lines. The delays are therefore always delays with respect to this master's clock signal.

Write leveling

A first training that is done on DDR3 dimms is called write leveling. This is a special mode of operation on DDR3 dimm's which allow you to sample back the DQS on the DQ signal in order to find a rising edge with respect to the clock:

Read and Write training

After that you need to adjust the delays such that the skew between DQ and DQS is fine for both read and write operations. The algorithms for these are rather straightforward.

First you start with write training. You sample all delays starting from the DQS delays obtained in the write leveling (the DQ delays are always with respect to the strobe DQS) and save where write start working and start failing. After that you pick the centered value.

DQS delay value (start)
      ^                             middle value
      |  write to dram              ideal DQ delay     write to dram
      |  don't read back                  ^            don't read back
      |         |                         |                  |
                                   write to dram
                                   read back

Something analogous is done for DQS read delay but instead of trying to write back something you loop over all possible read DQS delays (starting from 0) and try to read back something.

S3 support and faster boot times

All these trainings are destroy the the dram's content. This is fine if you boot for the first time but when you are trying to resume from S3 (suspend to ram) this is not an option. To keep the ram content in place you need to save the result from those trainings and restore those values when resuming from S3. Previously the RTC nvram (cmos battery backed) was used to store a few things but its size is severely limited (128 bytes) so another non volatile medium was needed. The other easily accessible non volatile medium is the flash chip itself which is usually at least 1MiB large. So what we do is to reserve a small space (64KiB) on that flash where we can store the results of our dram training.
Typically users don't change DIMM's on their system that often. Fetching information from these DIMM's that the dram controller needs is quite slow (reading a full SPD chips takes at least 50ms for each DIMM depending the protocol used). The SPI flash cache we previously used to save the training results can come in handy here to cache that information too (we only need to check if DIMM's have changed which can be done by just reading the ID bytes on the SPD chip). The end result is that with this cache the boot times are noticeably reduced. On the first boot it takes at least 500ms to initialize the dram but on subsequent boots it only takes ~20ms since everything is cached on a fast medium (SPI flash).

Intel DG41WV

The board I chose to develop the DDR3 raminit on was an Intel DG41WV. I chose it for a few reasons:

  • It was cheap
  • It had a socket (DIP8) spi flash which makes reflashing easy and avoid unexpected electrical difficulties which might require desoldering it.
  • It has a SuperIO supported by coreboot
  • It has a serial port

Obviously getting the ram to work was by far the largest part of this port. The board port part is usually really small and with some experience it can usually be done in a few hours. The DG41WV was no different in that aspect. One peculiarity was that the on the VGA output the display was garbled. I had this problem on other boards before and is typically an issue with the default clockgen configuration having a wrong clock frequency for the VGA output. Publicly available documentation for CK505 clockgens is scarce so one solution is to program whatever vendor has set.
First figure out on which number Linux maps the SMBUS:

modprobe i2c-dev
i2cdetect -l

The result will look like:

i2c-3   i2c             i915 gmbus dpc                          I2C adapter
i2c-1   i2c             i915 gmbus vga                          I2C adapter
i2c-8   i2c             DPDDC-D                                 I2C adapter
i2c-6   i2c             DPDDC-B                                 I2C adapter
i2c-4   i2c             i915 gmbus dpb                          I2C adapter
i2c-2   i2c             i915 gmbus panel                        I2C adapter
i2c-0   i2c             i915 gmbus ssc                          I2C adapter
i2c-9   smbus           SMBus I801 adapter at 0400              SMBus adapter (this is the one)
i2c-7   i2c             DPDDC-C                                 I2C adapter
i2c-5   i2c             i915 gmbus dpd                          I2C adapter

Now we can read back to ck505 configuration while running vendor firmware using smbus block read operation:

i2cdump -y 9 0x69 s

Now you put this information in the devicetree in coreboot

            device pci 1f.3 on      # SMbus
                subsystemid 0x8086 0x5756
                    chip drivers/i2c/ck505
                    register "mask" = "{ 0xff, 0xff, 0xff,
                         0xff, 0xff, 0xff, 0xff, 0xff,
                         0xff, 0xff, 0xff, 0xff, 0xff,
                         0xff, 0xff, 0xff, 0xff, 0xff,
                         0xff, 0xff, 0xff }"
                    register "regs" = "{ 0x41, 0x99, 0xff,
                         0xff, 0xff, 0x00, 0x00, 0x06,
                         0x03, 0x65, 0x83, 0x80, 0x15,
                         0xc0, 0x09, 0x00, 0x00, 0x00,
                         0x06, 0x00, 0xea }"
                    device i2c 69 on end

What works on this board?

  • 2 x 4GB DDR3 DIMMs (using dual rank DIMMs)
  • Core 2 CPU's (duo and quad) LGA775 CPU's (including modded LGA771 CPU's)
  • PCI, PCIe, USB, PS2, Serial
  • Native graphic init with VGA output
  • S3 resume

TD;DL how do I use coreboot on the Intel DG41WV

Build a basic but working image with SeaBIOS for this board is really easy.
Run the following in the coreboot directory:

echo CONFIG_VENDOR_INTEL=y > .config
echo CONFIG_BOARD_INTEL_DG41WV=y >> .config
make olddefconfig

Now you can flash this to the flash chip. This can be done internally since the vendor firmware does not lock the SPI flash.

flashrom -p internal -w build/coreboot.rom

Feel free to ask any questions!


This board broke on me. Luckily I have equivalent board from other vendors.

OLD: Hacking ath9k wifi device adventures

So here is where it all started. I have this apple branded atheros AR5BXB92, with an ar9280 chipset, which is supported by the ath9k Linux drivers. This chipset is supposed to support both 5GHz and 2.4GHz frequencies, but using the wavemon tool 5GHz SSID never show up even when I'm next to a device that emits one.

iw list shows the following:

Band 2:
    Capabilities: 0x11ce
        SM Power Save disabled
        RX HT40 SGI
        TX STBC
        RX STBC 1-stream
        Max AMSDU length: 3839 bytes
        DSSS/CCK HT40
    Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
    Minimum RX AMPDU time spacing: 8 usec (0x06)
    HT TX/RX MCS rate indexes supported: 0-15
    Bitrates (non-HT):
        * 6.0 Mbps
        * 9.0 Mbps
        * 12.0 Mbps
        * 18.0 Mbps
        * 24.0 Mbps
        * 36.0 Mbps
        * 48.0 Mbps
        * 54.0 Mbps
        * 5180 MHz [36] (16.0 dBm) (no IR)
        * 5200 MHz [40] (16.0 dBm) (no IR)
        * 5220 MHz [44] (16.0 dBm) (no IR)
        * 5240 MHz [48] (16.0 dBm) (no IR)
        * 5260 MHz [52] (17.0 dBm) (no IR, radar detection)
        * 5280 MHz [56] (16.0 dBm) (no IR, radar detection)
        * 5300 MHz [60] (16.0 dBm) (no IR, radar detection)
        * 5320 MHz [64] (16.0 dBm) (no IR, radar detection)
        * 5500 MHz [100] (disabled)
        * 5520 MHz [104] (disabled)
        * 5540 MHz [108] (disabled)
        * 5560 MHz [112] (disabled)
        * 5580 MHz [116] (disabled)
        * 5600 MHz [120] (disabled)
        * 5620 MHz [124] (disabled)
        * 5640 MHz [128] (disabled)
        * 5660 MHz [132] (disabled)
        * 5680 MHz [136] (disabled)
        * 5700 MHz [140] (disabled)
        * 5745 MHz [149] (disabled)
        * 5765 MHz [153] (disabled)
        * 5785 MHz [157] (disabled)
        * 5805 MHz [161] (disabled)
        * 5825 MHz [165] (disabled)

So most of the 5GHz channels which are avaible in my country were disabled. Changing the reg domain

iw reg set BE

did not change this.
On the web I found some articles about modifying the reg domain in the eeprom that is present on that wifi card. One of these is the following guide. The link to the iwleeprom code is dead but can be found on the following github repo. Doing so I found out that my region code is 0x65. I tried to modify that eeprom content but that didn't seem to work properly for some reason.
I knew that almost everything is done by the kernel driver so I looked for the possibilty to override this regional setting an I found it! By changing the reg_rules for my 0x65 domain device and recompiling the ath9k module I got those 5GHz appearing in wavemon and listed as supported in iw list!

diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index e25bfdf78c2e..e4cbf366b4e3 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -74,12 +74,12 @@ static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
 /* Can be used by 0x63 and 0x65 */
 static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
-   .n_reg_rules = 4,
+   .n_reg_rules = 5,
    .alpha2 =  "99",
    .reg_rules = {
-       ATH9K_2GHZ_CH01_11,
-       ATH9K_2GHZ_CH12_13,
+       ATH9K_2GHZ_ALL,
+       ATH9K_5GHZ_ALL,

Not wanting to recompile this module each time I get a kernel update, I looked into modifying the eeprom again to change it to a value where those channel were permanently enabled (0x60 for instance).
So I looked at the the dump iweeprom provided me

./iwleeprom -o mydump.bin

The dump looked like this

00000000: 5AA5 0000 0300 0060 8C16 2900 0860 0100  Z......`..)..`..
00000010: 8002 2C60 6B10 8F00 0050 8C16 2A00 0850  ..,`k....P..*..P
00000020: 0100 8002 2C50 6B10 8F00 6450 C00C 0405  ....,Pk...dP....
00000030: 6C50 1138 0300 0440 3B07 4000 7440 0300  lP.8...@;.@.t@..
00000040: 0000 0040 0150 C2FF 1440 0004 003A 3460  ...@.P...@...:4`
00000050: 4400 0000 FFFF FFFF FFFF FFFF FFFF FFFF  D...............
00000060: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000070: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000080: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000090: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000000a0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000000b0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000000c0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000000d0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000000e0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000000f0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000100: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000110: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000120: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000130: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000140: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000150: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000160: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000170: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000180: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000190: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000001a0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000001b0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000001c0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000001d0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000001e0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
000001f0: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF  ................
00000200: B80C AB15 16E0 0302 6500 1F00 D49A 2060  ........e..... `
00000210: 06C6 0303 0000 0000 0000 0004 0900 0501  ................
00000220: 01FF 0200 0001 0000 00FB 0000 0000 0000  ................
00000230: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000240: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000250: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000260: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000270: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000280: 4001 0000 4001 0000 0000 0000 1002 0000  @...@...........
00000290: 0000 002C 2020 0B00 000B E200 0E0E 0E00  ...,  ..........
000002a0: 020E 1CCA CACA 0301 0000 0000 0000 0604  ................
000002b0: 0400 0000 0E0E 0215 1500 1F1F 002C 0000  .............,..
000002c0: 0000 0000 0404 1D09 0000 0000 0000 8E8C  ................
000002d0: 888E 8C88 0080 0000 0000 0000 0000 0000  ................
000002e0: 0000 0000 0000 0000 1000 0000 1000 0000  ................
000002f0: 0000 0000 1002 0000 0000 002D 2020 0B00  ...........-  ..
00000300: 0010 E200 0D0D 0D00 020E 1CFF FFFF 0301  ................


From the previously linked blogpost it looks like the country code resides at offset 520 or 0x208 and indeed that one contains 0x65. So one needs to change that to 0x60. (one still needs to change the checksum but later more about that)
When trying to write this back to the eeprom, it does not seem to do anything. A first clue comes for the iwleeprom output.

ath9k short eeprom base: 0  size: 512

The things I changed reside at addresses larger than byte 512. So I changed the iwleeprom to do just that.

diff --git a/iwleeprom/ath9kio.c b/iwleeprom/ath9kio.c
index c57062d..83187e3 100644
--- a/iwleeprom/ath9kio.c
+++ b/iwleeprom/ath9kio.c
@@ -799,8 +799,8 @@ static bool ath9k_eeprom_check(struct pcidev *dev)
        goto ssize_ok;
    if (dev->ops->eeprom_read16(dev, 512, &data) && (3256 == data)) {
-       short_eeprom_base =  0;
-       short_eeprom_size = 512;
+       short_eeprom_base =  512;
+       short_eeprom_size = 1024 * 4;
        goto ssize_ok;
    if (dev->ops->eeprom_read16(dev, 256, &data) && (727 == data)) {

Using this I was able to write back my changes to the eeprom. But Linux won't be happy about this since the eeprom's checksum still needs to be fixes. Looking at the linux source it looks like the checksum is computed by XORing each word of the eeprom and expects the result to be 0xffff. The bytes to fix this checksum result are bytes 514-515.
After some trial and error I found that the checksum needs to be computed from byte 512 to byte 4096, which the iwleeprom tool will correctly provide with the patch mentioned above.

Supported devices detected: 
  [1] 0000:02:00.0 [RW] AR928X Wireless Adapter (PCI-E) (168c:002a, 106b:008f)
Select device [1-1] (or 0 to quit): 1
Using device 0000:02:00.0 [RW] AR928X Wireless Adapter (PCI-E) 
IO driver: ath9k
HW: AR9280 (PCI-E) rev 0002
RF: integrated
Checking NVM size...
ath9k short eeprom base: 512  size: 4096
MAC address : d4:9a:20:60:06:c6
Reg. domain : 0065
Capabilities: 0203
       Bands:  5GHz 2.4GHz
       HT 2G:  HT20 HT40
       HT 5G:  HT20 HT40
CRC (stored): 15ab
Calculating EEPROM CRC...............................
CRC (eval)  : 15ab

What needs to be programmed at byte 0x202-0x203 is computed in the CRC (eval) entry. Use your favorite hexeditor to change this and write it back. (make a backup of the file you want to edit first) Reload the ath9k module.

./iwleeprom -i modified.bin
rmmod ath9k; modprobe ath9k

and finally we get

    * 5180 MHz [36] (20.0 dBm) (no IR)
    * 5200 MHz [40] (20.0 dBm) (no IR)
    * 5220 MHz [44] (20.0 dBm) (no IR)
    * 5240 MHz [48] (20.0 dBm) (no IR)
    * 5260 MHz [52] (20.0 dBm) (no IR, radar detection)
    * 5280 MHz [56] (20.0 dBm) (no IR, radar detection)
    * 5300 MHz [60] (20.0 dBm) (no IR, radar detection)
    * 5320 MHz [64] (20.0 dBm) (no IR, radar detection)
    * 5500 MHz [100] (27.0 dBm) (no IR, radar detection)
    * 5520 MHz [104] (27.0 dBm) (no IR, radar detection)
    * 5540 MHz [108] (27.0 dBm) (no IR, radar detection)
    * 5560 MHz [112] (27.0 dBm) (no IR, radar detection)
    * 5580 MHz [116] (27.0 dBm) (no IR, radar detection)
    * 5600 MHz [120] (27.0 dBm) (no IR, radar detection)
    * 5620 MHz [124] (27.0 dBm) (no IR, radar detection)
    * 5640 MHz [128] (27.0 dBm) (no IR, radar detection)
    * 5660 MHz [132] (27.0 dBm) (no IR, radar detection)
    * 5680 MHz [136] (27.0 dBm) (no IR, radar detection)
    * 5700 MHz [140] (27.0 dBm) (no IR, radar detection)
    * 5745 MHz [149] (disabled)
    * 5765 MHz [153] (disabled)
    * 5785 MHz [157] (disabled)
    * 5805 MHz [161] (disabled)
    * 5825 MHz [165] (disabled)