Skip to content

Porting to other platforms

Michal Gorlas edited this page Jun 30, 2025 · 5 revisions

While LinuxBootSMM in its current form is rather platform independent, it heavily relies on coreboot providing register definitions for some of the higher level features. In its current state, LinuxBootSMM implements two higher level SMM specific features, enabling and disabling ACPI[1]. The ACPI related registers are platform dependent, and hence, provided by coreboot to the LinuxBootSMM SMI handler.

Currently supported platforms

At the time being, implemented higher level feature are working on QEMU Q35, and on most of the modern Intel boards - that is, all boards that make use of coreboot's common SMI handler for Intel. Adding support for the different board requires the following:

  • "hooking" up to the SoC specific SMI handler in coreboot to the LinuxBootSMM, taking soc/intel/common/block/smihandler.c as an example:
void smihandler_southbridge_apmc(
	const struct smm_save_state_ops *save_state_ops)
{
	uint8_t reg8;

	reg8 = apm_get_apmc();
	switch (reg8) {
	case APM_CNT_ACPI_DISABLE:
		pmc_disable_pm1_control(SCI_EN);
		break;
	case APM_CNT_ACPI_ENABLE:
		pmc_enable_pm1_control(SCI_EN);
		break;
	case APM_CNT_ELOG_GSMI:
		if (CONFIG(ELOG_GSMI))
			southbridge_smi_gsmi(save_state_ops);
		break;
	case APM_CNT_SMMSTORE:
		if (CONFIG(SMMSTORE)) {
			southbridge_smi_store(save_state_ops);
		}
		break;
	case APM_CNT_FINALIZE:
		finalize();
		break;
	}

	mainboard_smi_apmc(reg8);
}
void smihandler_southbridge_apmc(
	const struct smm_save_state_ops *save_state_ops)
{
	uint8_t reg8;
+	int mm_payload_ret;
+       context.pm1_cnt = pmc_read_pm1_control();

	reg8 = apm_get_apmc();
	switch (reg8) {
	case APM_CNT_ACPI_DISABLE:
+		context.command = PAYLOAD_MM_ACPI_DISABLE;
+		mm_payload_ret = payload_mm_call_entrypoint(context);
+		if (mm_payload_ret)
			pmc_disable_pm1_control(SCI_EN);
		break;
	case APM_CNT_ACPI_ENABLE:
+		context.command = PAYLOAD_MM_ACPI_ENABLE;
+		mm_payload_ret = payload_mm_call_entrypoint(context);
+		if (mm_payload_ret)
			pmc_enable_pm1_control(SCI_EN);
		break;
	case APM_CNT_ELOG_GSMI:
		if (CONFIG(ELOG_GSMI))
			southbridge_smi_gsmi(save_state_ops);
		break;
	case APM_CNT_SMMSTORE:
		if (CONFIG(SMMSTORE)) {
+			context.command = PAYLOAD_MM_SMM_STORE;
+			mm_payload_ret = payload_mm_call_entrypoint(context);
+			if (mm_payload_ret)
				southbridge_smi_store(save_state_ops);
		}
		break;
	case APM_CNT_FINALIZE:
		finalize();
		break;
	}

	mainboard_smi_apmc(reg8);
}

Essentially what happens here is that we redirect the handling of the higher level features to the payload's SMI handler, if it is already installed. This bring us to the next point.

  • providing payload with SoC specific definitions, this is done by providing the struct that payload's handler expect to receive, in SoC specific coreboot SMI handler. Again taking soc/intel/common/block/smihandler.c as an example:
static struct lb_entry_context context = {
	.command = 0, // To be set depending on the SMI subcommand (see snippet above)
	.pm1_cnt = 0, // To be set in *_*_apmc() (see snippet above)
	.acpi_base = ACPI_BASE_ADDRESS + PM1_CNT,
};

Please note that it payload's handler has no way to verify whether the provided definitions are valid - it simply has no idea on which platform it does run, neither during compile time, nor on the runtime. Hence it is important to verify whether the definitions are correct. Usually, the format in which bytes are written is pm1_cnt + 0/1 -> ACPI_BASE_ADDRESS + PM1_CNT (offset). This can be useful when debugging for other platforms.
Summarizing, when porting the support for the handler, we need to provide the PM1_CNT, which on most of the Intel SoCs obtained by reading from ACPI_BASE_ADDRESS + PM1_CNT (offset). The base address, and the offset has to be provided by the vendor. Taking RaptorLake as an example, we have base 0x1800 [2], and offset 0x04 (common for all Intel SoCs). Similarly, for AMD SoCs the situation is similar, we also need know base address and offset (0x62 for all AMD SoCs supported by coreboot).

Other higher level features to be implemented

Next step for LinuxBootSMM would be to implement variable store that could replace coreboot's SMMSTORE. This, however, should be done in C rather than in pure assembly. Purely theoretically, this can be done by including another binary in the mm_blob and calculating the entry point for the C function that is supposed to be called from the mm_entry_$(BITS). The binary with C code has to be compiled separately from assembly handler, assuming that the target for the kernel is x86_64. As the consequence, we can not directly link the C entry point with assembly binary. This could be potentially solved by including the binary made from C code into mm_blob and calculating the offset relative to the base address after the initial relocation (in a similar fashion to calculating offsets for assembly code entry points).

References

[1] Advanced Configuration and Power Interface (ACPI) Specification
[2] soc/intel/alderlake/include/soc/iomap.h

Clone this wiki locally