Introduction
This blog post describes the progress of the first phase of enabling AMD Turin support in coreboot and porting Gigabyte MZ33-AR1 board. The project is funded by NLnet Foundation.
The project was inspired by AMD’s efforts to bring open-source firmware for their most recent CPUs. Couple months ago AMD published their CPU initialization code for AMD Turin server processor family on GitHub. The OpenSIL is a new initiative to unify the silicon initialization for AMD platform across multiple firmware frameworks, like EDK2 and coreboot. Following the successful integration of Genoa (Turin’s predecessor) Proof of Concept in coreboot, we are striving for a brand new Turin processor family.
The first phase of the project consisted of couple milestones:
-
Milestone a. Turin PSP firmware package
Extract APCB from the reference MZ33-AR1 image and add them to mainboard code in coreboot. Integrate support for stitching public Turin PSP blobs in coreboot.
-
Milestone b. Turin SoC skeleton in coreboot
Create soc/amd/turin_poc in coreboot, port minimal OpenSIL integration and make sure the tree compiles.
-
Milestone c. MZ33-AR1 mainboard skeleton
Add mainboard/gigabyte/mz33-ar1, wire it to the new SoC, enable serial console, and confirm the bootblock executes on hardware.
Let’s run through each of them and explain what was done to fulfill the goals.
Turin SoC skeleton in coreboot
SoC structure is the base of the “world” for building coreboot images for
board. It binds the mainboard code with silicon specific drivers and glues all
other pieces together. It is usually necessary to have a separate soc
directory with relevant source for each new microarchitecture/processor
family, as the differences between them may be too significant for reuse.
In our case we created the turin_poc
SoC based on genoa_poc
SoC, as it is
the closest SoC to Turin (architecture-wise) and it already integrates OpenSIL
drivers. So the easiest way is to simply copy the directory and rename genoa
to turin
everywhere.
The relevant patch can be found here.
Before it compiles though, we will need a couple more modifications in other places and a board target to build it. So let’s move forward to the next relevant patch, which is modification of the SoC structure to reflect the architectural changes introduce in Turin.
It is not trivial to describe what should be changed and how. One must simply
run through all the source file added in previous patch, all drivers selected
by src/soc/amd/turin_poc/Kconfig
file and compare it against Processor
Programming Reference from AMD. But let’s run through the modification in the
patch and briefly explain it.
- Turin CPU has less USB ports than Genoa, so the chip structure has to reflect that. The number of ports has been reduced to match the hardware capabilities.
src/soc/amd/turin_poc/early_fch.c
has been updated to match what other AMD SoCs do, e.g. Mendocino or Phoenix. it is very basic chipset initialization required for basic operation of coreboot in the very early stage. It set’s up mostly legacy ISA devices, eSPI, I/O decoding, UARTs, SMBus and SPI.- Small differences in registers bits for AOAC (Always on Always Connected) which are used to enable internal CPU devices not visible on PCI bus.
- Adjusted MMIO and I/O base addresses for CPU internal devices and ACPI. Proper MMIO is required to initialize the hardware properly.
src/soc/amd/turin_poc/root_complex.c
is the file I had most struggles with. It describes how PCI domains are laid out on the SoC. I had a long chat with Felix Held (fellow coreboot developer), who worked on Genoa POC, and helped me understand how the domain map to fabric IDs and the SMN (System Management Network) base addresses. As a result of this fruitful discussion a patch has been created. Thank you again Felix!src/soc/amd/turin_poc/chipset.cb
is the second file which gave me some trouble. The AMD’s Processor Programming Reference (PPR) for Turin is not so clear about the layout of the PCI devices. It has an enigmatic table describing which device is present on which domain, however these domains are divided into A,B,C,D without clear explanation which domain map to which IOHC (I/O Hub Controller). After a very long deep dives into PPR andlspci
logs from the actual Gigabyte MZ33-AR1 board, I figured out which devices should correspond to which domain.src/soc/amd/turin_poc/Makefile.mk
is updated to include non-volatile APOB (AGESA PSP Output Block) and microcode files in the PSP firmware structure. On most recent system microcode is loaded onto the CPU by PSP, so it has to be provided in the PSP-understandable way. APOBs are just pointers to the flash where the memory training results are stored. It can be used for fastboot purposes, like MRC cache on Intel platforms.
Once the SOC tree was ready, it was time to integrate the actual OpenSIL.
Again it was based on the genoa_poc
OpenSIL driver for easier integration.
Here is the relevant
patch. Unfortunately, the
OpenSIL did not build out of the box, so it was necessary to add a fork
temporarily, until the build
fixes are merged.
That concludes the effort for creating soc
structure for Turin processors.
Turin PSP firmware package
PSP (Platform Security Processor), nowadays known as ASP (AMD Security Processor) is a privileged coprocessor embedded into AMD CPUs, similar to Intel’s ME in Intel chipsets. It is responsible for early silicon and memory initialization before the BIOS/firmware runs.
Preparing PSP blobs for AMD platform can be divided into 2 steps:
- mainboard-agnostic blobs, specific for the CPU silicon
- mainboard-specific blobs.
These blobs are consumed by PSP (and other IP blocks) before the main CPU starts. We will be providing a more detailed analysis of these blobs in subsequent project phases.
The first group consists of blobs that are delivered by AMD to initialize the PSP and CPU before the BIOS/firmware kicks in and all boards should include them. These blobs have been published by AMD too on GitHub. So the task is pretty simple, add the repository as a submodule in coreboot, and hook these blobs into the build system. Here is the patch that accomplishes this.
The CPU-specific blobs are included in the build by defining a fw.cfg
file
and pointing to it with AMDFW_CONFIG_FILE
Kconfig option. The fw.cfg
file
is pretty simple, it points to the directory where blobs are located and
defines name of the SoC:
|
|
and then lists the file names (second column) to be included under specific blob type (first column):
|
|
It is worth noting that Genoa had less blobs than Turin. Some blobs are new
and not yet known to coreboot’s utility, amdfwtool
, that glues them
together. To get all those blobs stitched together, a modification amdfwtool
was necessary. The relevant changes can be found in this
patch. The patch is still
in work in progress state, because despite including all blobs, the board does
not boot with the image created by coreboot build system. There might be some
new requirements for stitching the PSP blobs for Turin, that was not present
earlier in Genoa. Solving this problem has been planned for the next project
phases.
Next are the board-specific blobs. There aren’t many of them, just one type: APCB (AGESA PSP Configuration Block). APCB blobs are configuration data blobs for PSP AGESA to configure memory for the board. They are board-specific and must be prepared for each board separately. They are needed to build a working coreboot image. The easiest way to obtain them is to extract them from vendor image. To do so we will need PSPTool, an utility to parse and dump PSP structures in AMD firmware images.
To quickly build the utility simply do:
|
|
We checking out zen5 branch, because Turin CPU is Zen5 architecture.
To know which blobs we have to extract or take we need to list the entries present in the images first:
|
|
The <image>
is a firmware dump taken from the board itself or the image
taken from vendor BIOS update. Below process uses vendors firmware image
R05_F04.
Once the update package is downloaded and unzipped, listing the image can be
done with
|
|
However, this command results in a failure. The current state of PSPTool does not parse the images properly yet. So to fix the problem, necessary modifications were made and a Pull Request uploaded. Rebuilding the utility using the modified code and listing the entries again results in success. A full output for reference is available here.
The linked pull request already fulfills half of work planned for the other milestone for this project. That is the
Upstream PSPTool parsing improvements
(task 7 milestone B). More changes to PSPTool will follow later, that expose even more information about the PSP firmware structures, like subprogram and instance fields, which are also useful to determine what blobs are applicable for given platform. The improved PSPTool will be also included in the Dasharo HCL reports to improve dumping data on AMD platforms (task 7 milestone A). We also have another tool incoming, which is similar to coreboot’s inteltool, that will help dumping AMD CPU registers relevant for coreboot porting (task 7, milestones C and D).
The image may have multiple ROMs inside it, 16MB each for modern platforms. Be careful to take the blobs from the right directory. For example Gigabyte MZ33-AR1 has a dual ROM with Genoa and Turin firmware:
|
|
Each ROM have two types of directories: PSP and BIOS. E.g.
|
|
Each of the directory types may have two levels of directories. Second level
directories are marked as $PL2
and $BL2
. The main difference is that the
main directories marked as $PSP
and $BHD
are considered recovery and have
a limited set of blobs in it. Also the APCB blobs may be configured
differently for recovery and normal boot. That is why it is important to
extract the APCBs from second level directory.
APCB blobs always live in the BIOS directories. For example, if we want to extract the APCB from Turin image, we will have to look at directory 3 in the second ROM:
|
|
We can see that there are 2 APCBs and 3 ACPB_COPY’s. We will need all of them. Since we located the APCBs we want, it is time to extract the blobs. Blobs can be extracted from the first ROM with the following command:
|
|
The command will extract all blobs to the
mb_bios_MZ33-AR1_R05_F04/SPI_UPD/image.bin_extracted
directory where the
image was located. However, we are interested in the second ROM with Turin
blobs, so we need to pass additional parameter:
|
|
The files will have a a prefixes consisting of dXX_eYY
where XX
is the
directory number in the ROM and YY
is the entry number in given directory.
So in our case we should look for d03_e01*
up to d03_e05*
. And these are
the files we will need when creating mainboard structure:
|
|
This concludes the PSP firmware package milestone.
MZ33-AR1 mainboard skeleton
Finally it is time for the last piece of the puzzle, the mainboard code. We only want the minimum required to run bootblock and have some signs of life on the serial console. The servers board often have the serial port exposed over network via BMC Serial over LAN (SOL) feature and sometimes as a physical DB9 connector for RS232 on rear panel.
But, let’s go back to the main topic. The patch adding initial board support can be found here. The current board’s code consists of a couple source files:
- Kconfigs (with the name and configuration options)
Makefile.mk
which adds mainboard source file to be compiledbootblock.c
the early board specific code that sets up the debug interfacemainboard.c
mainboard code for ramstage, currently has only interrupt configurationdsdt.asl
from which the DSDT ACPI table is builtdevicetree.cb
with the devices enabled and used by the board and board’s configuration*apcb
files, which we extracted just moments ago- And couple other necessary files not really relevant for this story
The bootblock.c
is very basic and does the following things:
- Sets up eSPI. eSPI is the interface used to communicate with BMC. It has to
be configured to route serial port access on port 0x3f8 and the BMC’s Super
I/O on port 0x2e/0x2f. Part of the configuration is done in
devicetree.cb
an eSPI interface GPIOs are set inbootblock.c
. - Configures BMC serial port. The BMC is AST2600, but the generic AST2050 and AST2400 driver will suffice here to set up the serial port for debugging. So we simply call the generic function that initialize serial port and that’s about it.
mainboard.c
and dsdt.asl
have pretty much similar content to Genoa POC
reference board, Onyx. not much will happen here, until later phases of the
project. The files add just enough source code to compile.
devicetree.cb
defines very basic configuration of the board and enables
crucial devices for the early booting phase, mainly the lpc_bridge
(eSPI)
with the ASPEED BMC Super I/O and TPM. Some additional settings are already
defined as well, like USB, SATA, but they are subject to change in later
phases.
Makefile.mk
mainly defines the APCB files that are going to be used by the
board. The APCB files should reside in mainboard directory and are defined as
follows:
|
|
The APCB files we extracted earlier map as follows:
APCB_SOURCES
- first APCB (type 0x60) from the second level BIOS directory with. In our case it will bed03_e04_APCB~0x60
file.APCB_SOURCES1
- second APCB (type 0x60) from the second level BIOS directory. In our case it will bed03_e05_APCB~0x60
file.APCB_SOURCES_RECOVERY
- first APCB_COPY (type 0x68) from the second level BIOS directory. In our case it will bed03_e01_APCB_COPY~0x68
file.APCB_SOURCES_RECOVERY1
- second APCB_COPY (type 0x68) from the second level BIOS directory. In our case it will bed03_e02_APCB_COPY~0x68
file.APCB_SOURCES_RECOVERY2
- third APCB_COPY (type 0x68) from the second level BIOS directory. In our case it will bed03_e03_APCB_COPY~0x68
file.
They are simply renamed to *.apcb
files to match the convention used in
coreboot.
The patch also adds a couple of configs to be used to build an image quickly:
configs/config.gigabyte_mz33-ar1
regular config file using PSP blobs and supposed to produce a full working imageconfigs/config.gigabyte_mz33-ar1_no_psp
- config file not using PSP blobs to workaround booting problem when public PSP blobs are used. I will explain why we have such config soon.
This concludes the mainboard code milestone. Time to build some images!
Building and running
To build a bootable coreboot image, we had to go for certain workarounds and omit stichting PSP blobs. Thankfully, the vendor image copies enough flash to memory for the BIOS to execute and this flash region is not compressed. Now, how did we discover it? Again, the PSPTool comes with the help together with UEFITool:
|
|
The 7th entry indicates where the early BIOS boot code resides in flash. It
says that it is 0x340000 bytes at offset 0x1cc0000. When the vendor image is
opened in the UEFITool
we can see that it point to uncompressed SEC and PEI
Firmware Volume:
So it means we can inject our coreboot image there. However, to make it work
properly, we will need to peek into the BIOS and APOB (AGESA PSP Output Block)
entry bytes with hex editor to obtain the destination address in DRAM where
the BIOS and APOB contents are copied. APOB is necessary for BIOS to be
consumed and parsed to obtain crucial information about memory configuration
from PSP. A quick peek into the hexdump of the image right at the beginning of
the BIOS directory ($BL2
marker) from which we extracted the APCBs:
|
|
The APOB entry is at offset 0x016620a0
and its destination address at
0x016620b0
(64bit address) and we see it is equal to 0x75bc0000
. Similarly
for BIOS entry is at offset 0x016620b8
, because each entry is 0x18 bytes and
BIOS is right after APOB. The destination address would the be at offset
0x016620c8
a is equal to 0x75cc0000
. Having this data we could fabricate
the same memory map for coreboot by defining the following in mainboard’s
Kconfig:
|
|
When user selects BUILD_WITHOUT_PSP_BLOBS
option, coreboot will configure
the memory map so that the bootblock is linked at the right address after PSP
copies it to DRAM from flash. PSP_APOB_DRAM_ADDRESS
is the APOB destination
address. The PSP_APOB_DRAM_SIZE
is simply the space between BIOS and APOB
destinations: 0x75cc0000 - 0x75bc0000 = 0x100000
. CBFS_SIZE
must be equal
to the BIOS region in flash that is being copied, so 0x340000
. At last
ROMSTAGE_ADDR
must be simply the first address after the BIOS flash region
that is copied: 0x75cc0000 + 0x340000 = 0x76000000
.
EARLY_RESERVED_DRAM_BASE
just needs to be lower then APOB with some space to
fit the CPU stack. A lot of hacking and maths, but it works.
And as a proof, let’s built the image without PSP blobs:
|
|
Assuming you have docker installed, run the container and start build process:
|
|
The resulting image will be present in build/coreboot.rom
. To flash it on
the board an external programmer is required. More convenient options are
probably available with BMC, but the methodology was not yet discovered.
Follow the instructions on Dasharo documentation to flash the image.
Once flashed, power on the board and observer the serial output. it can be done with USB to RS232 adapter and a DB9 null modem cable, or the BMC SOL feature.
Sample output:
|
|
This concludes the first phase of the project.
Summary
Turin OpenSIL is still in the Proof of Concept stage and is not intended for production use - proceed at your own risk. All current patches for Turin and Gigabyte MZ33-AR1 support are available under the turin_poc topic topic on coreboot’s Gerrit. The subsequent phases of the project will bring even more exciting developments, so stay tuned for updates.
Acknowledgements
We would like to thank the creators and contributors of PSPTool and UEFITool, whose excellent work played a key role in achieving the results presented here.
Vertical Application Roadmap
We’re also excited to share our longer‑term vision for vertical applications powered by the Dasharo Pro Package on the Gigabyte MZ33‑AR1 platform. As previewed in our Qubes OS Summit 2025 presentation, Qubes Air: Hardware, Firmware, and Architectural Foundations our roadmap includes secure integration of Dasharo firmware (coreboot+UEFI), AMD’s OpenSIL, and OpenBMC as a trusted root, aimed at delivering server‑grade Qubes OS deployments. In the follow‑up session, Qubes Air: Opinionated Value Proposition for Security‑Conscious Technical Professionals we expanded on this vision by highlighting vertical integration scenarios using Qubes OS with Dasharo, secure thin clients and servers, and advanced capabilities such as RemoteVM, attestation via TrenchBoot, and early Proof‑of‑Concepts tailored for highly sensitive technical workflows. Stay tuned: we’re working toward solutions that deliver secure, vertically integrated, real-world applications for privacy-focused environments using the Dasharo Pro Package.
If you plan to attend there are still some tickets to grab here.
For OEMs & ODMs
If you are an OEM or ODM and see the value in AMD OpenSIL support for your
products, our team can help make it a reality. Reach out to us via our contact
form or email us at
contact<at>3mdeb<dot>com
to start the conversation.
Stay Updated
If you’re following the Gigabyte MZ33-AR1 journey, we invite you to join our Dasharo Community Release mailing list for this platform. Subscribers will receive public announcements about project progress, including the Dasharo Product Package (DPP) release when it’s ready.
