Intel i5000 northbridge code commited

I’ve started that port in November 2011, and made it finally working in January. Well, at least working on my Supermicro X7DB8 Board. Compared to the vendor BIOS which takes about 30 seconds from power-on to grub, coreboot finishes the same task in 3s. Unfortunately the VGA BIOS takes about 2 seconds to program the register to do 80×25 resolution, so eventually we end up with 5s. If you don’t need a VGA console, and prefer a serial console, you can save even these two seconds.

Actually i didn’t expect the port progressing so fast. One tool that was a great help was serialice. I used it to watch the original BIOS initializing memory. To get serialice running, all you have to do is writing a few lines of board specific C code, which initializes the serial port and some basic chipset parts required for accessing the serial port registers. On the host side, a patched QEMU is running, executing the vendor BIOS while redirecting HW accesses to the target computer. Which HW accesses are redirected can be configured by a small lua script. LUA can also be used to pretty print the output from serialice.

So with a bit of hacking i had the following output from serialice:

pci_mmmo_read_config32(PCI_ADDR(0, 16, 1, 0), MC) == 0x0040000;
pci_mmio_write_config32(PCI_ADDR(0, 16, 1, 0), MC, 00040000);
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDICMD0, EI);
pci_mmio_read_config16(PCI_ADDR(0, 21, 0, 0), FBDISTS0) == 0x1FFF;
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDHPC, STATE_INIT);
pci_mmio_read_config8(PCI_ADDR(0, 21, 0, 0), FBDST) == 0x10;
amb_smbus_write_config32(0, 1, 0, 1, AMB_FBDSBCFGNXT, 0x20b1b);
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDSBTXCFG0, 0x05);
pci_mmio_write_config32(PCI_ADDR(0, 21, 0, 0), FBD0IBTXPAT2EN, 00000000);
pci_mmio_write_config32(PCI_ADDR(0, 21, 0, 0), FBD0IBRXPAT2EN, 00000000);
pci_mmio_read_config32(PCI_ADDR(0, 21, 0, 0), FBD0IBPORTCTL) == 0x1000032;
pci_mmio_write_config32(PCI_ADDR(0, 21, 0, 0), FBD0IBPORTCTL, 00000012);
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDICMD0, TS0);
pci_mmio_read_config16(PCI_ADDR(0, 21, 0, 0), FBDISTS0) == 0x1FFF;
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDICMD0, TS1);
pci_mmio_read_config16(PCI_ADDR(0, 21, 0, 0), FBDISTS0) == 0x1FFF;
pci_mmio_read_config32(PCI_ADDR(0, 21, 0, 0), FBD0IBPORTCTL) == 0x0000016;
pci_mmio_read_config32(PCI_ADDR(0, 21, 0, 0), FBD0IBPORTCTL) == 0x0000016;
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDICMD0, TS2);
pci_mmio_read_config16(PCI_ADDR(0, 21, 0, 0), FBDISTS0) == 0x1FFF;
pci_mmio_read_config8(PCI_ADDR(0, 21, 0, 0), FBDLVL0) == 0x14;
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDICMD0, TS2);
pci_mmio_read_config16(PCI_ADDR(0, 21, 0, 0), FBDISTS0) == 0x1FFF;
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDICMD0, TS3);
pci_mmio_read_config16(PCI_ADDR(0, 21, 0, 0), FBDISTS0) == 0x1FFF;
pci_mmio_write_config8(PCI_ADDR(0, 21, 0, 0), FBDHPC, STATE_READY);
pci_mmio_read_config8(PCI_ADDR(0, 21, 0, 0), FBDST) == 0x20;
pci_mmio_write_config16(PCI_ADDR(0, 21, 0, 0), AMBPRESENT0, 0x8FFF);
pci_mmio_write_config16(PCI_ADDR(0, 21, 0, 0), AMBPRESENT0, 0x8FFF);
MEM:  readl AMB_ID => 0482111d *
MEM: writel AMB_FERR <= 00000009 *
MEM: writel AMB_NERR <= 00000009 *
pci_mmio_write_config16(PCI_ADDR(0, 21, 0, 0), AMBPRESENT0, 0x8001);
pci_mmio_read_config32(PCI_ADDR(0, 16, 1, 0), MC) == 0x0040000;
pci_mmio_write_config32(PCI_ADDR(0, 16, 1, 0), MC, 40040020);

As you can see, this almost looks like C code. Even if it looks like porting coreboot to this chipset should be simple with that level of output, there where some challenges.

First, i5000 uses FBDIMMs. Each of the memory modules contains an AMB (Advanced Memory Buffer) in addition to the memory chips. This AMB has to be initialized and trained during the Memory initalization sequence. So you have three things to setup right to get working memory:

  • The Northbridge (MCH in Intel’s terminologie)
  • The AMB
  • The memory chips on the module itself.

The training sequence is needed to account for the high bit rate used on this communication channel: stuffing 12 Bits into one Clock cycle at 166Mhz. But even if it makes initialization more complicated, the AMB can be really helpful during development. It allows to test the Memory chips without help from the MCH, so you can test if the memory module itself is working without having to rely on MCH setup. We’re (and the vendor BIOS does as well) are using it also for clearing the Memory during initialization. But we made one difference: The vendor BIOS clears the memory modules one after another, while the coreboot code sends the command to clear the memory to all modules at the same time, and collects the status response afterwards from all modules. This has the advantage of requiring only the time the slowest module needs, instead of the sum of all modules.

I spent several evenings investigating why Interrupts were not working, just to discover that i accidentally reset the Busmaster Flag on the Northbridge. This had the effect of having Interrupts in Virtual Wire mode, but not in APIC mode. After fixing this nasty bug, linux was booting properly, and i even had a working keyboard and mouse 🙂

Having i5000 code in coreboot, i’m aiming at supporting the following Boards in coreboot:

  • Supermicro X7DB8(+)
  • Asus DSBF-D12 (i have this board in my Desktop  Box)

There are also a lot of HP/Dell/FSC servers out there using this chipset, which might also be a nice target. Unfortunately i don’t have such hardware at home 😉