From 1c03efc01485e2f0ba0a8c7eaa94b1bbbf393251 Mon Sep 17 00:00:00 2001 From: Maximilian Brune Date: Wed, 23 Oct 2024 15:19:44 +0200 Subject: acpi: x86: Move SPCR and DBG2 into common code This moves the SPCR and DBG2 table generation into common code, so that they can be used by architectures other than x86. Signed-off-by: Maximilian Brune Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass Cc: Bin Meng --- include/acpi/acpi_table.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 15fd61a51d7..a3724354920 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -913,6 +913,17 @@ static inline int acpi_add_fadt(struct acpi_ctx *ctx, struct acpi_fadt *fadt) return 0; } +/** + * acpi_write_dbg2_pci_uart() - Write out a DBG2 table + * + * @ctx: Current ACPI context + * @dev: Debug UART device to describe + * @access_size: Access size for UART (e.g. ACPI_ACCESS_SIZE_DWORD_ACCESS) + * Return: 0 if OK, -ve on error + */ +int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev, + uint access_size); + /** * acpi_write_rsdp() - Write out an RSDP indicating where the ACPI tables are * -- cgit v1.3.1 From f5f7962091e453feb2e3f1bfe79dbace5e087c3e Mon Sep 17 00:00:00 2001 From: Maximilian Brune Date: Wed, 23 Oct 2024 15:19:45 +0200 Subject: acpi: x86: Write FADT in common code Write the FADT in common code since it's used on all architectures. Since the FADT is mandatory all SoCs or mainboards must implement the introduced function acpi_fill_fadt() and properly update the FADT. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass Cc: Bin Meng --- arch/sandbox/lib/acpi_table.c | 6 ++++++ arch/x86/cpu/apollolake/acpi.c | 20 ++++---------------- arch/x86/cpu/baytrail/acpi.c | 17 +---------------- arch/x86/cpu/quark/acpi.c | 19 +------------------ arch/x86/cpu/tangier/acpi.c | 25 ++----------------------- arch/x86/include/asm/acpi_table.h | 12 ------------ arch/x86/lib/acpi_table.c | 23 ----------------------- include/acpi/acpi_table.h | 9 +++++++++ lib/acpi/acpi_table.c | 38 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 61 insertions(+), 108 deletions(-) create mode 100644 arch/sandbox/lib/acpi_table.c (limited to 'include') diff --git a/arch/sandbox/lib/acpi_table.c b/arch/sandbox/lib/acpi_table.c new file mode 100644 index 00000000000..10b7ce441ae --- /dev/null +++ b/arch/sandbox/lib/acpi_table.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include + +void acpi_fill_fadt(struct acpi_fadt *fadt) +{ +} \ No newline at end of file diff --git a/arch/x86/cpu/apollolake/acpi.c b/arch/x86/cpu/apollolake/acpi.c index 76230aea837..93040e7bb37 100644 --- a/arch/x86/cpu/apollolake/acpi.c +++ b/arch/x86/cpu/apollolake/acpi.c @@ -128,8 +128,10 @@ int arch_madt_sci_irq_polarity(int sci) return MP_IRQ_POLARITY_LOW; } -void fill_fadt(struct acpi_fadt *fadt) +void acpi_fill_fadt(struct acpi_fadt *fadt) { + intel_acpi_fill_fadt(fadt); + fadt->pm_tmr_blk = IOMAP_ACPI_BASE + PM1_TMR; fadt->p_lvl2_lat = ACPI_FADT_C2_NOT_SUPPORTED; @@ -143,23 +145,9 @@ void fill_fadt(struct acpi_fadt *fadt) fadt->x_pm_tmr_blk.space_id = 1; fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8; fadt->x_pm_tmr_blk.addrl = IOMAP_ACPI_BASE + PM1_TMR; -} - -static int apl_write_fadt(struct acpi_ctx *ctx, const struct acpi_writer *entry) -{ - struct acpi_table_header *header; - struct acpi_fadt *fadt; - - fadt = ctx->current; - acpi_fadt_common(fadt, ctx->facs, ctx->dsdt); - intel_acpi_fill_fadt(fadt); - fill_fadt(fadt); - header = &fadt->header; - header->checksum = table_compute_checksum(fadt, header->length); - return acpi_add_fadt(ctx, fadt); + fadt->preferred_pm_profile = ACPI_PM_MOBILE; } -ACPI_WRITER(5fadt, "FADT", apl_write_fadt, 0); int apl_acpi_fill_dmar(struct acpi_ctx *ctx) { diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c index 7821964f1fc..7e1c2de3d39 100644 --- a/arch/x86/cpu/baytrail/acpi.c +++ b/arch/x86/cpu/baytrail/acpi.c @@ -15,20 +15,13 @@ #include #include -static int baytrail_write_fadt(struct acpi_ctx *ctx, - const struct acpi_writer *entry) +void acpi_fill_fadt(struct acpi_fadt *fadt) { struct acpi_table_header *header; - struct acpi_fadt *fadt; - fadt = ctx->current; header = &fadt->header; u16 pmbase = ACPI_BASE_ADDRESS; - memset(fadt, '\0', sizeof(struct acpi_fadt)); - - acpi_fill_header(header, "FACP"); - header->length = sizeof(struct acpi_fadt); header->revision = 4; fadt->preferred_pm_profile = ACPI_PM_MOBILE; @@ -77,9 +70,6 @@ static int baytrail_write_fadt(struct acpi_ctx *ctx, fadt->reset_reg.addrh = 0; fadt->reset_value = SYS_RST | RST_CPU | FULL_RST; - fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); - fadt->x_dsdt = map_to_sysmem(ctx->dsdt); - fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; fadt->x_pm1a_evt_blk.bit_offset = 0; @@ -135,12 +125,7 @@ static int baytrail_write_fadt(struct acpi_ctx *ctx, fadt->x_gpe1_blk.access_size = 0; fadt->x_gpe1_blk.addrl = 0x0; fadt->x_gpe1_blk.addrh = 0x0; - - header->checksum = table_compute_checksum(fadt, header->length); - - return acpi_add_fadt(ctx, fadt); } -ACPI_WRITER(5fadt, "FADT", baytrail_write_fadt, 0); int acpi_create_gnvs(struct acpi_global_nvs *gnvs) { diff --git a/arch/x86/cpu/quark/acpi.c b/arch/x86/cpu/quark/acpi.c index 80e94600fc5..0fe5f2bafb4 100644 --- a/arch/x86/cpu/quark/acpi.c +++ b/arch/x86/cpu/quark/acpi.c @@ -11,23 +11,14 @@ #include #include -static int quark_write_fadt(struct acpi_ctx *ctx, - const struct acpi_writer *entry) +void acpi_fill_fadt(struct acpi_fadt *fadt) { u16 pmbase = ACPI_PM1_BASE_ADDRESS; struct acpi_table_header *header; - struct acpi_fadt *fadt; - fadt = ctx->current; header = &fadt->header; - - memset(fadt, '\0', sizeof(struct acpi_fadt)); - - acpi_fill_header(header, "FACP"); - header->length = sizeof(struct acpi_fadt); header->revision = 4; - fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED; fadt->sci_int = 9; fadt->smi_cmd = 0; fadt->acpi_enable = 0; @@ -73,9 +64,6 @@ static int quark_write_fadt(struct acpi_ctx *ctx, fadt->reset_reg.addrh = 0; fadt->reset_value = SYS_RST | RST_CPU | FULL_RST; - fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); - fadt->x_dsdt = map_to_sysmem(ctx->dsdt); - fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; fadt->x_pm1a_evt_blk.bit_offset = 0; @@ -131,12 +119,7 @@ static int quark_write_fadt(struct acpi_ctx *ctx, fadt->x_gpe1_blk.access_size = 0; fadt->x_gpe1_blk.addrl = 0x0; fadt->x_gpe1_blk.addrh = 0x0; - - header->checksum = table_compute_checksum(fadt, header->length); - - return acpi_add_fadt(ctx, fadt); } -ACPI_WRITER(5fadt, "FADT", quark_write_fadt, 0); int acpi_create_gnvs(struct acpi_global_nvs *gnvs) { diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index d4d0ef6f855..1c73a9dbfec 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -16,21 +16,8 @@ #include #include -static int tangier_write_fadt(struct acpi_ctx *ctx, - const struct acpi_writer *entry) +void acpi_fill_fadt(struct acpi_fadt *fadt) { - struct acpi_table_header *header; - struct acpi_fadt *fadt; - - fadt = ctx->current; - header = &fadt->header; - - memset(fadt, '\0', sizeof(struct acpi_fadt)); - - acpi_fill_header(header, "FACP"); - header->length = sizeof(struct acpi_fadt); - header->revision = 6; - fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED; fadt->iapc_boot_arch = ACPI_FADT_VGA_NOT_PRESENT | @@ -40,17 +27,9 @@ static int tangier_write_fadt(struct acpi_ctx *ctx, ACPI_FADT_POWER_BUTTON | ACPI_FADT_SLEEP_BUTTON | ACPI_FADT_SEALED_CASE | ACPI_FADT_HEADLESS | ACPI_FADT_HW_REDUCED_ACPI; - + fadt->header.revision = 6; fadt->minor_revision = 2; - - fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); - fadt->x_dsdt = map_to_sysmem(ctx->dsdt); - - header->checksum = table_compute_checksum(fadt, header->length); - - return acpi_add_fadt(ctx, fadt); } -ACPI_WRITER(5fadt, "FADT", tangier_write_fadt, 0); u32 acpi_fill_madt(u32 current) { diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index e6175246173..3988898f66e 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -168,18 +168,6 @@ int acpi_create_dmar_ds_ioapic(struct acpi_ctx *ctx, uint enumeration_id, int acpi_create_dmar_ds_msi_hpet(struct acpi_ctx *ctx, uint enumeration_id, pci_dev_t bdf); -/** - * acpi_fadt_common() - Handle common parts of filling out an FADT - * - * This sets up the Fixed ACPI Description Table - * - * @fadt: Pointer to place to put FADT - * @facs: Pointer to the FACS - * @dsdt: Pointer to the DSDT - */ -void acpi_fadt_common(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt); - /** * intel_acpi_fill_fadt() - Set up the contents of the FADT * diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 08fd1e54ce3..ff02ce80d15 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -381,29 +381,6 @@ int acpi_write_hpet(struct acpi_ctx *ctx) return 0; } -void acpi_fadt_common(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt) -{ - struct acpi_table_header *header = &fadt->header; - - memset((void *)fadt, '\0', sizeof(struct acpi_fadt)); - - acpi_fill_header(header, "FACP"); - header->length = sizeof(struct acpi_fadt); - header->revision = 4; - memcpy(header->oem_id, OEM_ID, 6); - memcpy(header->oem_table_id, OEM_TABLE_ID, 8); - memcpy(header->creator_id, ASLC_ID, 4); - - fadt->x_firmware_ctrl = map_to_sysmem(facs); - fadt->x_dsdt = map_to_sysmem(dsdt); - - fadt->preferred_pm_profile = ACPI_PM_MOBILE; - - /* Use ACPI 3.0 revision */ - fadt->header.revision = 4; -} - void acpi_create_dmar_drhd(struct acpi_ctx *ctx, uint flags, uint segment, u64 bar) { diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index a3724354920..19cf0fb43ca 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -954,6 +954,15 @@ void acpi_fill_header(struct acpi_table_header *header, char *signature); */ int acpi_fill_csrt(struct acpi_ctx *ctx); +/** + * acpi_fill_fadt() - Fill out the body of the FADT + * + * Must be implemented in SoC specific code or in mainboard code. + * + * @fadt: Pointer to FADT to update + */ +void acpi_fill_fadt(struct acpi_fadt *fadt); + /** * acpi_get_rsdp_addr() - get ACPI RSDP table address * diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index c9ddcca8cb5..9eb0b507a09 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -202,6 +202,44 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) return 0; } +int acpi_write_fadt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *header; + struct acpi_fadt *fadt; + + fadt = ctx->current; + header = &fadt->header; + + memset((void *)fadt, '\0', sizeof(struct acpi_fadt)); + + acpi_fill_header(header, "FACP"); + header->length = sizeof(struct acpi_fadt); + header->revision = acpi_get_table_revision(ACPITAB_FADT); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, OEM_TABLE_ID, 8); + memcpy(header->creator_id, ASLC_ID, 4); + header->creator_revision = 1; + + fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); + fadt->x_dsdt = map_to_sysmem(ctx->dsdt); + + if (fadt->x_firmware_ctrl < 0x100000000ULL) + fadt->firmware_ctrl = fadt->x_firmware_ctrl; + + if (fadt->x_dsdt < 0x100000000ULL) + fadt->dsdt = fadt->x_dsdt; + + fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED; + + acpi_fill_fadt(fadt); + + header->checksum = table_compute_checksum(fadt, header->length); + + return acpi_add_fadt(ctx, fadt); +} + +ACPI_WRITER(5fadt, "FADT", acpi_write_fadt, 0); + void acpi_create_dbg2(struct acpi_dbg2_header *dbg2, int port_type, int port_subtype, struct acpi_gen_regaddr *address, u32 address_size, -- cgit v1.3.1 From 4a3fc0f525dabecaf7e84d4d9761b56c5aebf345 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:46 +0200 Subject: acpi: x86: Move MADT to common code Write MADT in common code and let the SoC fill out the body by calling acpi_fill_madt() which must be implemented at SoC level. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass Cc: Bin Meng --- arch/sandbox/lib/Makefile | 9 ++++---- arch/sandbox/lib/acpi_table.c | 7 +++++- arch/x86/cpu/intel_common/acpi.c | 22 ++++++++++++------ arch/x86/cpu/tangier/acpi.c | 8 ++++++- arch/x86/include/asm/acpi_table.h | 3 +-- arch/x86/lib/acpi_table.c | 48 ++++++++------------------------------- include/acpi/acpi_table.h | 11 +++++++++ lib/acpi/acpi_table.c | 31 +++++++++++++++++++++++++ 8 files changed, 86 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/arch/sandbox/lib/Makefile b/arch/sandbox/lib/Makefile index d7d15a50bb6..5d7beb250cd 100644 --- a/arch/sandbox/lib/Makefile +++ b/arch/sandbox/lib/Makefile @@ -5,7 +5,8 @@ # (C) Copyright 2002-2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-y += fdt_fixup.o interrupts.o -obj-$(CONFIG_PCI) += pci_io.o -obj-$(CONFIG_CMD_BOOTM) += bootm.o -obj-$(CONFIG_CMD_BOOTZ) += bootm.o +obj-y += fdt_fixup.o interrupts.o +obj-$(CONFIG_PCI) += pci_io.o +obj-$(CONFIG_CMD_BOOTM) += bootm.o +obj-$(CONFIG_CMD_BOOTZ) += bootm.o +obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_table.o diff --git a/arch/sandbox/lib/acpi_table.c b/arch/sandbox/lib/acpi_table.c index 10b7ce441ae..90bb96d1e35 100644 --- a/arch/sandbox/lib/acpi_table.c +++ b/arch/sandbox/lib/acpi_table.c @@ -3,4 +3,9 @@ void acpi_fill_fadt(struct acpi_fadt *fadt) { -} \ No newline at end of file +} + +void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) +{ + return ctx->current; +} diff --git a/arch/x86/cpu/intel_common/acpi.c b/arch/x86/cpu/intel_common/acpi.c index 29676b4abfa..982149b394e 100644 --- a/arch/x86/cpu/intel_common/acpi.c +++ b/arch/x86/cpu/intel_common/acpi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -80,33 +81,40 @@ static int acpi_sci_irq(void) return sci_irq; } -static unsigned long acpi_madt_irq_overrides(unsigned long current) +static void *acpi_madt_irq_overrides(void *current) { int sci = acpi_sci_irq(); u16 flags = MP_IRQ_TRIGGER_LEVEL; - if (sci < 0) - return log_msg_ret("sci irq", sci); + if (sci < 0) { + log_err("sci irq %d", sci); + return current; + } /* INT_SRC_OVR */ - current += acpi_create_madt_irqoverride((void *)current, 0, 0, 2, 0); + current += acpi_create_madt_irqoverride(current, 0, 0, 2, 0); flags |= arch_madt_sci_irq_polarity(sci); /* SCI */ current += - acpi_create_madt_irqoverride((void *)current, 0, sci, sci, flags); + acpi_create_madt_irqoverride(current, 0, sci, sci, flags); return current; } -u32 acpi_fill_madt(u32 current) +void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) { + void *current = ctx->current; + + madt->lapic_addr = LAPIC_DEFAULT_BASE; + madt->flags = ACPI_MADT_PCAT_COMPAT; + /* Local APICs */ current += acpi_create_madt_lapics(current); /* IOAPIC */ - current += acpi_create_madt_ioapic((void *)current, 2, IO_APIC_ADDR, 0); + current += acpi_create_madt_ioapic(current, 2, IO_APIC_ADDR, 0); return acpi_madt_irq_overrides(current); } diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index 1c73a9dbfec..3d117878615 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,13 @@ void acpi_fill_fadt(struct acpi_fadt *fadt) fadt->minor_revision = 2; } -u32 acpi_fill_madt(u32 current) +void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) { + void *current = ctx->current; + + madt->lapic_addr = LAPIC_DEFAULT_BASE; + madt->flags = ACPI_MADT_PCAT_COMPAT; + current += acpi_create_madt_lapics(current); current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current, diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 3988898f66e..3254bbc380f 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -24,14 +24,13 @@ struct acpi_table_header; /* These can be used by the target port */ -int acpi_create_madt_lapics(u32 current); +int acpi_create_madt_lapics(void *current); int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id, u32 addr, u32 gsi_base); int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride, u8 bus, u8 source, u32 gsirq, u16 flags); int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, u8 cpu, u16 flags, u8 lint); -u32 acpi_fill_madt(u32 current); int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base, u16 seg_nr, u8 start, u8 end); diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index ff02ce80d15..3186e48d63b 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -40,7 +40,7 @@ static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic, return lapic->length; } -int acpi_create_madt_lapics(u32 current) +int acpi_create_madt_lapics(void *current) { struct udevice *dev; int total_length = 0; @@ -100,23 +100,28 @@ int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, return lapic_nmi->length; } -static int acpi_create_madt_irq_overrides(u32 current) +static int acpi_create_madt_irq_overrides(void *current) { struct acpi_madt_irqoverride *irqovr; u16 sci_flags = MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH; int length = 0; - irqovr = (void *)current; + irqovr = current; length += acpi_create_madt_irqoverride(irqovr, 0, 0, 2, 0); - irqovr = (void *)(current + length); + irqovr = current + length; length += acpi_create_madt_irqoverride(irqovr, 0, 9, 9, sci_flags); return length; } -__weak u32 acpi_fill_madt(u32 current) +__weak void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) { + void *current = ctx->current; + + madt->lapic_addr = LAPIC_DEFAULT_BASE; + madt->flags = ACPI_MADT_PCAT_COMPAT; + current += acpi_create_madt_lapics(current); current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current, @@ -127,39 +132,6 @@ __weak u32 acpi_fill_madt(u32 current) return current; } -int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry) -{ - struct acpi_table_header *header; - struct acpi_madt *madt; - u32 current; - - madt = ctx->current; - - memset(madt, '\0', sizeof(struct acpi_madt)); - header = &madt->header; - - /* Fill out header fields */ - acpi_fill_header(header, "APIC"); - header->length = sizeof(struct acpi_madt); - header->revision = ACPI_MADT_REV_ACPI_3_0; - - madt->lapic_addr = LAPIC_DEFAULT_BASE; - madt->flags = ACPI_MADT_PCAT_COMPAT; - - current = (u32)madt + sizeof(struct acpi_madt); - current = acpi_fill_madt(current); - - /* (Re)calculate length and checksum */ - header->length = current - (u32)madt; - - header->checksum = table_compute_checksum((void *)madt, header->length); - acpi_add_table(ctx, madt); - acpi_inc(ctx, madt->header.length); - - return 0; -} -ACPI_WRITER(5x86, NULL, acpi_write_madt, 0); - /** * acpi_create_tcpa() - Create a TCPA table * diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 19cf0fb43ca..c356df72bae 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -963,6 +963,17 @@ int acpi_fill_csrt(struct acpi_ctx *ctx); */ void acpi_fill_fadt(struct acpi_fadt *fadt); +/** + * acpi_fill_madt() - Fill out the body of the MADT + * + * Must be implemented in SoC specific code. + * + * @madt: The MADT to update + * @ctx: ACPI context to write MADT sub-tables to + * @return Pointer to the end of tables, where the next tables can be written + */ +void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx); + /** * acpi_get_rsdp_addr() - get ACPI RSDP table address * diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 9eb0b507a09..639d78125f6 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -240,6 +240,37 @@ int acpi_write_fadt(struct acpi_ctx *ctx, const struct acpi_writer *entry) ACPI_WRITER(5fadt, "FADT", acpi_write_fadt, 0); +int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *header; + struct acpi_madt *madt; + void *current; + + madt = ctx->current; + + memset(madt, '\0', sizeof(struct acpi_madt)); + header = &madt->header; + + /* Fill out header fields */ + acpi_fill_header(header, "APIC"); + header->length = sizeof(struct acpi_madt); + header->revision = ACPI_MADT_REV_ACPI_3_0; + + acpi_inc(ctx, sizeof(struct acpi_madt)); + current = acpi_fill_madt(madt, ctx); + + /* (Re)calculate length and checksum */ + header->length = (uintptr_t)current - (uintptr_t)madt; + + header->checksum = table_compute_checksum((void *)madt, header->length); + acpi_add_table(ctx, madt); + ctx->current = (void *)madt + madt->header.length; + + return 0; +} + +ACPI_WRITER(5madt, "MADT", acpi_write_madt, 0); + void acpi_create_dbg2(struct acpi_dbg2_header *dbg2, int port_type, int port_subtype, struct acpi_gen_regaddr *address, u32 address_size, -- cgit v1.3.1 From d016abb47f60e8630728fa8414db00c983d44d0b Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:47 +0200 Subject: acpi: Fix typo Rename ACPI tables MADR to MADT. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass --- include/acpi/acpi_table.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index c356df72bae..a1bdab73059 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -386,20 +386,20 @@ struct __packed acpi_madt_lapic_nmi { u8 lint; /* Local APIC LINT# */ }; -/* flags for acpi_madr_gicc flags word */ +/* flags for acpi_madt_gicc flags word */ enum { - ACPI_MADRF_ENABLED = BIT(0), - ACPI_MADRF_PERF = BIT(1), - ACPI_MADRF_VGIC = BIT(2), + ACPI_MADTF_ENABLED = BIT(0), + ACPI_MADTF_PERF = BIT(1), + ACPI_MADTF_VGIC = BIT(2), }; /** - * struct __packed acpi_madr_gicc - GIC CPU interface (type 0xb) + * struct __packed acpi_madt_gicc - GIC CPU interface (type 0xb) * * This holds information about the Generic Interrupt Controller (GIC) CPU * interface. See ACPI Spec v6.3 section 5.2.12.14 */ -struct acpi_madr_gicc { +struct acpi_madt_gicc { u8 type; u8 length; u16 reserved; @@ -421,12 +421,12 @@ struct acpi_madr_gicc { } __packed; /** - * struct __packed acpi_madr_gicc - GIC distributor (type 0xc) + * struct __packed acpi_madt_gicc - GIC distributor (type 0xc) * * This holds information about the Generic Interrupt Controller (GIC) * Distributor interface. See ACPI Spec v6.3 section 5.2.12.15 */ -struct acpi_madr_gicd { +struct acpi_madt_gicd { u8 type; u8 length; u16 reserved; -- cgit v1.3.1 From ea37e5064e20c63cbaa667cb8b81838d5bab65bb Mon Sep 17 00:00:00 2001 From: Maximilian Brune Date: Wed, 23 Oct 2024 15:19:48 +0200 Subject: serial: serial_pl01x: Implement .getinfo() for PL01 When ACPI is enabled on arm it will use the getinfo function to fill the SPCR ACPI table. Signed-off-by: Maximilian Brune Reviewed-by: Simon Glass Cc: Simon Glass Cc: Tom Rini Reviewed-by: Moritz Fischer --- boot/bootflow.c | 8 ++++++-- drivers/serial/serial_pl01x.c | 24 ++++++++++++++++++++++++ include/serial.h | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/boot/bootflow.c b/boot/bootflow.c index 59d77d2385f..d8807eb109d 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -936,11 +936,15 @@ int bootflow_cmdline_auto(struct bootflow *bflow, const char *arg) return ret; *buf = '\0'; - if (!strcmp("earlycon", arg)) { + if (!strcmp("earlycon", arg) && info.type == SERIAL_CHIP_16550_COMPATIBLE) { snprintf(buf, sizeof(buf), "uart8250,mmio32,%#lx,%dn8", info.addr, info.baudrate); - } else if (!strcmp("console", arg)) { + } else if (!strcmp("earlycon", arg) && info.type == SERIAL_CHIP_PL01X) { + snprintf(buf, sizeof(buf), + "pl011,mmio32,%#lx,%dn8", info.addr, + info.baudrate); + } else if (!strcmp("console", arg) && info.type == SERIAL_CHIP_16550_COMPATIBLE) { snprintf(buf, sizeof(buf), "ttyS0,%dn8", info.baudrate); } diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index 80c35963b8f..e6bf0c2935b 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -272,6 +273,28 @@ __weak struct serial_device *default_serial_console(void) return &pl01x_serial_drv; } #else + +static int pl01x_serial_getinfo(struct udevice *dev, + struct serial_device_info *info) +{ + struct pl01x_serial_plat *plat = dev_get_plat(dev); + + /* save code size */ + if (!not_xpl()) + return -ENOSYS; + + info->type = SERIAL_CHIP_PL01X; + info->addr_space = SERIAL_ADDRESS_SPACE_MEMORY; + info->addr = plat->base; + info->size = 0x1000; + info->reg_width = 4; + info->reg_shift = 2; + info->reg_offset = 0; + info->clock = plat->clock; + + return 0; +} + int pl01x_serial_setbrg(struct udevice *dev, int baudrate) { struct pl01x_serial_plat *plat = dev_get_plat(dev); @@ -341,6 +364,7 @@ static const struct dm_serial_ops pl01x_serial_ops = { .pending = pl01x_serial_pending, .getc = pl01x_serial_getc, .setbrg = pl01x_serial_setbrg, + .getinfo = pl01x_serial_getinfo, }; #if CONFIG_IS_ENABLED(OF_REAL) diff --git a/include/serial.h b/include/serial.h index d129dc3253c..d7a9a8cfb8b 100644 --- a/include/serial.h +++ b/include/serial.h @@ -124,6 +124,7 @@ enum serial_stop { enum serial_chip_type { SERIAL_CHIP_UNKNOWN = -1, SERIAL_CHIP_16550_COMPATIBLE, + SERIAL_CHIP_PL01X, }; enum adr_space_type { -- cgit v1.3.1 From 04001adce1fa93fee3dfd4563bc01b7b046e588c Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:49 +0200 Subject: acpi: Add define for GTDT Add the interrupt flag used in ACPI GTDT table as define. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass Reviewed-by: Moritz Fischer --- include/acpi/acpi_table.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index a1bdab73059..c6a3e44a952 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -707,6 +707,8 @@ struct acpi_gtdt { u32 virt_el2_flags; } __packed; +#define GTDT_FLAG_INT_ACTIVE_LOW BIT(1) + /** * struct acpi_bgrt - Boot Graphics Resource Table (BGRT) * -- cgit v1.3.1 From f36e29e8da2f1b4a776b157e5f6c8368546da803 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:50 +0200 Subject: arm: acpi: Add generic ACPI methods Add generic ACPI code to generate - MADT GICC - MADT GICD - MADT GICR - MADT GIC ITS - PPTT processor - PPTT cache as commonly used on arm platforms. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Tom Rini Cc: Simon Glass --- arch/arm/include/asm/acpi_table.h | 115 ++++++++++++++++++++++++++++++++++++++ arch/arm/lib/Makefile | 1 + arch/arm/lib/acpi_table.c | 114 +++++++++++++++++++++++++++++++++++++ include/acpi/acpi_table.h | 34 ++++++++++- 4 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 arch/arm/lib/acpi_table.c (limited to 'include') diff --git a/arch/arm/include/asm/acpi_table.h b/arch/arm/include/asm/acpi_table.h index e69de29bb2d..c65eabe8374 100644 --- a/arch/arm/include/asm/acpi_table.h +++ b/arch/arm/include/asm/acpi_table.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __ASM_ACPI_TABLE_H__ +#define __ASM_ACPI_TABLE_H__ + +#ifndef __ACPI__ + +#include + +/** + * acpi_write_madt_gicc() - Write out a MADT GICC sub-table + * + * Write out the GIC CPU Interface sub-table. + * + * @gicc: Pointer to place to put the sub-table + * @cpu_num: GIC's CPU Interface Number + * @perf_gsiv: The GSIV used for Performance Monitoring Interrupts + * @phys_base: Address at which the processor can access this + * GIC CPU Interface + * @gicv: Address of the GIC virtual CPU interface registers + * @gich: Address of the GIC virtual interface control block + * registers + * @vgic_maint_irq: GSIV for Virtual GIC maintenance interrupt + * @gicr_base: Physical address of the associated Redistributor + * @mpidr: MPIDR as defined by ARM architecture + * @efficiency: Describes the relative power efficiency + */ +void acpi_write_madt_gicc(struct acpi_madt_gicc *gicc, uint cpu_num, + uint perf_gsiv, ulong phys_base, ulong gicv, + ulong gich, uint vgic_maint_irq, u64 gicr_base, + ulong mpidr, uint efficiency); + +/** + * acpi_write_madt_gicd() - Write out a MADT GICD sub-table + * + * Write out the GIC Distributor sub-table. + * + * @gicd: Pointer to place to put the sub-table + * @gic_id: This GIC Distributor's hardware ID + * @phys_base: The 64-bit physical address for this Distributor + * @gic_version: GIC version + */ +void acpi_write_madt_gicd(struct acpi_madt_gicd *gicd, uint gic_id, + ulong phys_base, uint gic_version); + +/** + * acpi_write_madt_gicr() - Write out a MADT GICR sub-table + * + * Write out the GIC Redistributor sub-table. + * + * @gicr: Pointer to place to put the sub-table + * @discovery_range_base_address: Physical address of a page range + * containing all GIC Redistributors + * @discovery_range_length: Length of the GIC Redistributor + * Discovery page range + */ +void acpi_write_madt_gicr(struct acpi_madt_gicr *gicr, + u64 discovery_range_base_address, + u32 discovery_range_length); + +/** + * acpi_write_madt_its() - Write out a MADT ITS sub-table + * + * Write out the GIC Interrupt Translation Service sub-table. + * + * @its: Pointer to place to put the sub-table + * @its_id: Uniqueue GIC ITS ID + * @physical_base_address: Physical address for the Interrupt + * Translation Service + */ +void acpi_write_madt_its(struct acpi_madt_its *its, + u32 its_id, + u64 physical_base_address); + +/** + * acpi_pptt_add_proc() - Write out a PPTT processor sub-table + * + * Write out the Processor Properties Topology Table processor sub-table. + * + * @ctx: ACPI context pointer + * @flags: Processor Structure Flags + * @parent: Reference to parent processor + * @proc_id: ACPI processor ID as defined in MADT + * @num_resources: Number of resource structure references + * @resource_list: References to other PPTT structures + * Return: offset from start of PPTT in bytes + */ +int acpi_pptt_add_proc(struct acpi_ctx *ctx, const u32 flags, const u32 parent, + const u32 proc_id, const u32 num_resources, + const u32 *resource_list); + +/** + * acpi_pptt_add_cache() - Write out a PPTT cache sub-table + * + * Write out the Processor Properties Topology Table cache sub-table. + * + * @ctx: ACPI context pointer + * @flags: Cache Structure Flags + * @next_cache_level: Reference to next level of cache + * @size: Size of the cache in bytes + * @sets: Number of sets in the cache + * @assoc: Integer number of ways + * @attributes: Allocation type, Cache type, policy + * @line_size: Line size in bytes + * Return: offset from start of PPTT in bytes + */ +int acpi_pptt_add_cache(struct acpi_ctx *ctx, const u32 flags, + const u32 next_cache_level, const u32 size, + const u32 sets, const u8 assoc, const u8 attributes, + const u16 line_size); + +#endif /* !__ASSEMBLY__ */ +#endif /* !__ACPI__ */ + +#endif /* __ASM_ACPI_TABLE_H__ */ diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 67275fba616..a7efed6771d 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -86,6 +86,7 @@ obj-y += psci-dt.o obj-$(CONFIG_DEBUG_LL) += debug.o obj-$(CONFIG_BLOBLIST) += xferlist.o +obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o # For EABI conformant tool chains, provide eabi_compat() ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS))) diff --git a/arch/arm/lib/acpi_table.c b/arch/arm/lib/acpi_table.c new file mode 100644 index 00000000000..286ed7cecaa --- /dev/null +++ b/arch/arm/lib/acpi_table.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on acpi.c from coreboot + * + * Copyright (C) 2024 9elements GmbH + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include +#include +#include +#include + +void acpi_write_madt_gicc(struct acpi_madt_gicc *gicc, uint cpu_num, + uint perf_gsiv, ulong phys_base, ulong gicv, + ulong gich, uint vgic_maint_irq, u64 gicr_base, + ulong mpidr, uint efficiency) +{ + memset(gicc, '\0', sizeof(struct acpi_madt_gicc)); + gicc->type = ACPI_APIC_GICC; + gicc->length = sizeof(struct acpi_madt_gicc); + gicc->cpu_if_num = cpu_num; + gicc->processor_id = cpu_num; + gicc->flags = ACPI_MADTF_ENABLED; + gicc->perf_gsiv = perf_gsiv; + gicc->phys_base = phys_base; + gicc->gicv = gicv; + gicc->gich = gich; + gicc->vgic_maint_irq = vgic_maint_irq; + gicc->gicr_base = gicr_base; + gicc->mpidr = mpidr; + gicc->efficiency = efficiency; +} + +void acpi_write_madt_gicd(struct acpi_madt_gicd *gicd, uint gic_id, + ulong phys_base, uint gic_version) +{ + memset(gicd, '\0', sizeof(struct acpi_madt_gicd)); + gicd->type = ACPI_APIC_GICD; + gicd->length = sizeof(struct acpi_madt_gicd); + gicd->gic_id = gic_id; + gicd->phys_base = phys_base; + gicd->gic_version = gic_version; +} + +void acpi_write_madt_gicr(struct acpi_madt_gicr *gicr, + u64 discovery_range_base_address, + u32 discovery_range_length) +{ + memset(gicr, '\0', sizeof(struct acpi_madt_gicr)); + gicr->type = ACPI_APIC_GICR; + gicr->length = sizeof(struct acpi_madt_gicr); + gicr->discovery_range_base_address = discovery_range_base_address; + gicr->discovery_range_length = discovery_range_length; +} + +void acpi_write_madt_its(struct acpi_madt_its *its, + u32 its_id, + u64 physical_base_address) +{ + memset(its, '\0', sizeof(struct acpi_madt_its)); + its->type = ACPI_APIC_ITS; + its->length = sizeof(struct acpi_madt_its); + its->gic_its_id = its_id; + its->physical_base_address = physical_base_address; +} + +int acpi_pptt_add_proc(struct acpi_ctx *ctx, const u32 flags, const u32 parent, + const u32 proc_id, const u32 num_resources, + const u32 *resource_list) +{ + struct acpi_pptt_proc *proc = ctx->current; + int offset; + + offset = ctx->current - ctx->tab_start; + proc->hdr.type = ACPI_PPTT_TYPE_PROC; + proc->flags = flags; + proc->parent = parent; + proc->proc_id = proc_id; + proc->num_resources = num_resources; + proc->hdr.length = sizeof(struct acpi_pptt_proc) + + sizeof(u32) * num_resources; + + if (resource_list) + memcpy(proc + 1, resource_list, sizeof(u32) * num_resources); + + acpi_inc(ctx, proc->hdr.length); + + return offset; +} + +int acpi_pptt_add_cache(struct acpi_ctx *ctx, const u32 flags, + const u32 next_cache_level, const u32 size, + const u32 sets, const u8 assoc, const u8 attributes, + const u16 line_size) +{ + struct acpi_pptt_cache *cache = ctx->current; + int offset; + + offset = ctx->current - ctx->tab_start; + cache->hdr.type = ACPI_PPTT_TYPE_CACHE; + cache->hdr.length = sizeof(struct acpi_pptt_cache); + cache->flags = flags; + cache->next_cache_level = next_cache_level; + cache->size = size; + cache->sets = sets; + cache->assoc = assoc; + cache->attributes = attributes; + cache->line_size = line_size; + acpi_inc(ctx, cache->hdr.length); + + return offset; +} diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index c6a3e44a952..7d994a985fb 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -342,7 +342,10 @@ enum acpi_apic_types { ACPI_APIC_LX2APIC, /* Processor local x2APIC */ ACPI_APIC_LX2APIC_NMI, /* Local x2APIC NMI */ ACPI_APIC_GICC, /* Generic Interrupt Ctlr CPU i/f */ - ACPI_APIC_GICD /* Generic Interrupt Ctlr Distributor */ + ACPI_APIC_GICD, /* Generic Interrupt Ctlr Distributor */ + ACPI_APIC_MSI_FRAME, /* Generic Interrupt MSI Frame */ + ACPI_APIC_GICR, /* Generic Interrupt Ctlr Redistributor */ + ACPI_APIC_ITS, /* Interrupt Translation Service */ }; /* MADT: Processor Local APIC Structure */ @@ -437,6 +440,35 @@ struct acpi_madt_gicd { u8 reserved3[3]; } __packed; +/** + * struct __packed acpi_madt_gicr - GIC Redistributor (type 0xe) + * + * This holds information about the Generic Interrupt Controller (GIC) + * Redistributor interface. See ACPI Spec v6.3 section 5.2.12.17 + */ +struct acpi_madt_gicr { + u8 type; + u8 length; + u16 reserved; + u64 discovery_range_base_address; + u32 discovery_range_length; +} __packed; + +/** + * struct __packed acpi_madt_its - GIC Interrupt Translation Service (type 0xf) + * + * This holds information about the Interrupt Translation Service (ITS) + * Structure. See ACPI Spec v6.3 section 5.2.12.18 + */ +struct acpi_madt_its { + u8 type; + u8 length; + u16 reserved; + u32 gic_its_id; + u64 physical_base_address; + u32 reserved2; +} __packed; + /* MCFG (PCI Express MMIO config space BAR description table) */ struct acpi_mcfg { struct acpi_table_header header; -- cgit v1.3.1 From 763bad3e1cf202147fbddb74c9876d7426ddb26b Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:51 +0200 Subject: acpi: Add fill_madt to acpi_ops Add a new method to acpi_ops to let drivers fill out ACPI MADT. The code is unused for now until drivers implement the new ops. TEST: Booted on QEMU sbsa using driver model generated MADT. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass --- arch/arm/lib/acpi_table.c | 13 +++++++++++++ drivers/core/acpi.c | 16 ++++++++++++++++ include/dm/acpi.h | 26 ++++++++++++++++++++++++++ lib/acpi/acpi_table.c | 2 +- test/dm/acpi.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/lib/acpi_table.c b/arch/arm/lib/acpi_table.c index 286ed7cecaa..e0add049261 100644 --- a/arch/arm/lib/acpi_table.c +++ b/arch/arm/lib/acpi_table.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include void acpi_write_madt_gicc(struct acpi_madt_gicc *gicc, uint cpu_num, uint perf_gsiv, ulong phys_base, ulong gicv, @@ -112,3 +114,14 @@ int acpi_pptt_add_cache(struct acpi_ctx *ctx, const u32 flags, return offset; } + +void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) +{ + uclass_probe_all(UCLASS_CPU); + uclass_probe_all(UCLASS_IRQ); + + /* All SoCs must use the driver model */ + acpi_fill_madt_subtbl(ctx); + + return ctx->current; +} diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 9f784228921..4763963914b 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -48,6 +48,7 @@ enum method_t { METHOD_FILL_SSDT, METHOD_INJECT_DSDT, METHOD_SETUP_NHLT, + METHOD_FILL_MADT, }; /* Prototype for all methods */ @@ -282,6 +283,8 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) switch (method) { case METHOD_WRITE_TABLES: return aops->write_tables; + case METHOD_FILL_MADT: + return aops->fill_madt; case METHOD_FILL_SSDT: return aops->fill_ssdt; case METHOD_INJECT_DSDT: @@ -328,6 +331,19 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, return 0; } +int acpi_fill_madt_subtbl(struct acpi_ctx *ctx) +{ + int ret; + + log_debug("Writing MADT table\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_MADT, TYPE_NONE); + log_debug("Writing MADT finished, err=%d\n", ret); + if (ret) + return log_msg_ret("build", ret); + + return ret; +} + int acpi_fill_ssdt(struct acpi_ctx *ctx) { void *start = ctx->current; diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 3adfe217678..d6bc0c099ad 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -146,6 +146,22 @@ struct acpi_ops { */ int (*write_tables)(const struct udevice *dev, struct acpi_ctx *ctx); + /** + * fill_madt() - Generate MADT sub-tables for a device + * + * This is called to create the MADT table. The method should write out + * whatever sub-table is needed by this device. It will end up in the + * MADT table. + * + * Note that this is called 'fill' because the entire contents of the + * MADT is build by calling this method on all devices. + * + * @dev: Device to write + * @ctx: ACPI context to use + * @return 0 if OK, -ve on error + */ + int (*fill_madt)(const struct udevice *dev, struct acpi_ctx *ctx); + /** * fill_ssdt() - Generate SSDT code for a device * @@ -231,6 +247,16 @@ int acpi_copy_name(char *out_name, const char *name); */ int acpi_write_dev_tables(struct acpi_ctx *ctx); +/** + * acpi_fill_madt_subtbl() - Generate ACPI tables for MADT + * + * This is called to create the MADT sub-tables for all devices. + * + * @ctx: ACPI context to use + * Return: 0 if OK, -ve on error + */ +int acpi_fill_madt_subtbl(struct acpi_ctx *ctx); + /** * acpi_fill_ssdt() - Generate ACPI tables for SSDT * diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 639d78125f6..5b9b4d2f290 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -257,11 +257,11 @@ int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry) header->revision = ACPI_MADT_REV_ACPI_3_0; acpi_inc(ctx, sizeof(struct acpi_madt)); + /* TODO: Get rid of acpi_fill_madt and use driver model */ current = acpi_fill_madt(madt, ctx); /* (Re)calculate length and checksum */ header->length = (uintptr_t)current - (uintptr_t)madt; - header->checksum = table_compute_checksum((void *)madt, header->length); acpi_add_table(ctx, madt); ctx->current = (void *)madt + madt->header.length; diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 7ccd7f8d20a..39a26bbb492 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -95,6 +95,21 @@ static int testacpi_get_name(const struct udevice *dev, char *out_name) return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); } +static int testacpi_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + u64 *data = ctx->current; + + /* Only fill madt once */ + if (device_get_uclass_id(dev->parent) != UCLASS_TEST_ACPI) + return 0; + + *data = 0xdeadbeef; + + acpi_inc(ctx, sizeof(u64)); + + return 0; +} + static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) { const char *data; @@ -124,6 +139,7 @@ static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx) struct acpi_ops testacpi_ops = { .get_name = testacpi_get_name, .write_tables = testacpi_write_tables, + .fill_madt = testacpi_fill_madt, .fill_ssdt = testacpi_fill_ssdt, .inject_dsdt = testacpi_inject_dsdt, }; @@ -527,6 +543,33 @@ static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_fill_ssdt, UTF_SCAN_PDATA | UTF_SCAN_FDT); +/* Test acpi_fill_madt() */ +static int dm_test_acpi_fill_madt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u64 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + acpi_reset_items(); + ctx.current = buf; + buf[1] = 'z'; /* sentinel */ + ut_assertok(acpi_fill_madt_subtbl(&ctx)); + + /* + * These values come from acpi-test2's acpi-ssdt-test-data property. + * This device comes first because of u-boot,acpi-ssdt-order + */ + ut_asserteq(0xdeadbeef, buf[0]); + + ut_asserteq('z', buf[1]); + + return 0; +} + +DM_TEST(dm_test_acpi_fill_madt, UTF_SCAN_PDATA | UTF_SCAN_FDT); + /* Test acpi_inject_dsdt() */ static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts) { -- cgit v1.3.1 From 4b882f63d40e914558d9ffc4e76ae1115c8eb20e Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:52 +0200 Subject: acpi: acpi_table: Bump revisions The FADT structure found in U-Boot represents FADT revision 6 and the GICC and GICD structures defined in U-Boot are based on ACPI revision 6.3. Bump the table revision to fix FWTS failures seen on aarch64. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass Cc: Bin Meng --- arch/x86/cpu/tangier/acpi.c | 2 -- include/acpi/acpi_table.h | 3 ++- lib/acpi/acpi_table.c | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index 3d117878615..8ea764f7cc1 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -28,8 +28,6 @@ void acpi_fill_fadt(struct acpi_fadt *fadt) ACPI_FADT_POWER_BUTTON | ACPI_FADT_SLEEP_BUTTON | ACPI_FADT_SEALED_CASE | ACPI_FADT_HEADLESS | ACPI_FADT_HW_REDUCED_ACPI; - fadt->header.revision = 6; - fadt->minor_revision = 2; } void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 7d994a985fb..48f8ce248fd 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -290,7 +290,8 @@ struct __packed acpi_fadt { #define ACPI_MADT_REV_ACPI_3_0 2 #define ACPI_MADT_REV_ACPI_4_0 3 #define ACPI_MADT_REV_ACPI_5_0 3 -#define ACPI_MADT_REV_ACPI_6_0 5 +#define ACPI_MADT_REV_ACPI_6_2 4 +#define ACPI_MADT_REV_ACPI_6_3 5 #define ACPI_MCFG_REV_ACPI_3_0 1 diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 5b9b4d2f290..959cac9e2e3 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -63,9 +63,9 @@ int acpi_get_table_revision(enum acpi_tables table) { switch (table) { case ACPITAB_FADT: - return ACPI_FADT_REV_ACPI_3_0; + return ACPI_FADT_REV_ACPI_6_0; case ACPITAB_MADT: - return ACPI_MADT_REV_ACPI_3_0; + return ACPI_MADT_REV_ACPI_6_2; case ACPITAB_MCFG: return ACPI_MCFG_REV_ACPI_3_0; case ACPITAB_TCPA: @@ -219,6 +219,7 @@ int acpi_write_fadt(struct acpi_ctx *ctx, const struct acpi_writer *entry) memcpy(header->oem_table_id, OEM_TABLE_ID, 8); memcpy(header->creator_id, ASLC_ID, 4); header->creator_revision = 1; + fadt->minor_revision = 2; fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); fadt->x_dsdt = map_to_sysmem(ctx->dsdt); @@ -254,7 +255,7 @@ int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry) /* Fill out header fields */ acpi_fill_header(header, "APIC"); header->length = sizeof(struct acpi_madt); - header->revision = ACPI_MADT_REV_ACPI_3_0; + header->revision = acpi_get_table_revision(ACPITAB_MADT); acpi_inc(ctx, sizeof(struct acpi_madt)); /* TODO: Get rid of acpi_fill_madt and use driver model */ -- cgit v1.3.1 From 7f91bcac1eef8119a68a4e3a559f85728df1bbdc Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:53 +0200 Subject: acpi: Add ACPITAB for PPTT and GTDT Return the ACPI table revision in acpi_get_table_revision() for PPTT and GTDT. Match both to ACPI 6.2. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass --- include/acpi/acpi_table.h | 2 ++ lib/acpi/acpi_table.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 48f8ce248fd..7ee3248ff61 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -841,12 +841,14 @@ enum acpi_tables { ACPITAB_ECDT, ACPITAB_FACS, ACPITAB_FADT, + ACPITAB_GTDT, ACPITAB_HEST, ACPITAB_HPET, ACPITAB_IVRS, ACPITAB_MADT, ACPITAB_MCFG, ACPITAB_NHLT, + ACPITAB_PPTT, ACPITAB_RSDP, ACPITAB_RSDT, ACPITAB_SLIT, diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 959cac9e2e3..2dc7b998e22 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -107,6 +107,10 @@ int acpi_get_table_revision(enum acpi_tables table) return 1; case ACPITAB_SPCR: return 2; + case ACPITAB_PPTT: /* ACPI 6.2: 1 */ + return 1; + case ACPITAB_GTDT: /* ACPI 6.2: 2, ACPI 6.3: 3 */ + return 2; default: return -EINVAL; } -- cgit v1.3.1 From bf5d37662da52f3844fdfb245b135272da7774d5 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:54 +0200 Subject: acpi: acpi_table: Add IORT support The SoC can implement acpi_fill_iort to update the IORT table. Add a helper function to fill out the NAMED_COMPONENT node. TEST=Run FWTS V24.03.00 on RPi4 and round no problems. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass --- include/acpi/acpi_table.h | 213 ++++++++++++++++++++++++++++++++++++++++++++++ lib/acpi/acpi_table.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 426 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 7ee3248ff61..5a30465ad2a 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -832,6 +832,117 @@ struct acpi_pptt_cache { u16 line_size; } __packed; +/** IORT - IO Remapping Table revision 6 + * Document number: ARM DEN 0049E.e, Sep 2022 + */ +struct acpi_table_iort { + struct acpi_table_header header; + u32 node_count; + u32 node_offset; + u32 reserved; +} __packed; + +/* + * IORT subtables + */ +struct acpi_iort_node { + u8 type; + u16 length; + u8 revision; + u32 identifier; + u32 mapping_count; + u32 mapping_offset; + char node_data[]; +} __packed; + +/* Values for subtable Type above */ +enum acpi_iort_node_type { + ACPI_IORT_NODE_ITS_GROUP = 0x00, + ACPI_IORT_NODE_NAMED_COMPONENT = 0x01, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02, + ACPI_IORT_NODE_SMMU = 0x03, + ACPI_IORT_NODE_SMMU_V3 = 0x04, + ACPI_IORT_NODE_PMCG = 0x05, + ACPI_IORT_NODE_RMR = 0x06, +}; + +/* ITS Group revision 1 */ +struct acpi_iort_its_group { + u32 its_count; + u32 identifiers[]; /* GIC ITS identifier array */ +} __packed; + +/* PCI root complex node revision 2 */ +struct acpi_iort_rc { + u64 mem_access_properties; + u32 ats_attributes; + u32 pci_segment_number; + u8 memory_address_size_limit; + u8 reserved[3]; +} __packed; + +/* SMMUv3 revision 5 */ +struct acpi_iort_smmu_v3 { + u64 base_address; /* SMMUv3 base address */ + u32 flags; + u32 reserved; + u64 vatos_address; + u32 model; + u32 event_gsiv; + u32 pri_gsiv; + u32 gerr_gsiv; + u32 sync_gsiv; + u32 pxm; + u32 id_mapping_index; +} __packed; + +/* Masks for Flags field above */ +#define ACPI_IORT_SMMU_V3_COHACC_OVERRIDE (1) +#define ACPI_IORT_SMMU_V3_HTTU_OVERRIDE (3 << 1) +#define ACPI_IORT_SMMU_V3_PXM_VALID (1 << 3) +#define ACPI_IORT_SMMU_V3_DEVICEID_VALID (1 << 4) + +struct acpi_iort_id_mapping { + u32 input_base; /* Lowest value in input range */ + u32 id_count; /* Number of IDs */ + u32 output_base; /* Lowest value in output range */ + u32 output_reference; /* A reference to the output node */ + u32 flags; +} __packed; + +/* Masks for Flags field above for IORT subtable */ +#define ACPI_IORT_ID_SINGLE_MAPPING (1) + +/* Named Component revision 4 */ +struct acpi_iort_named_component { + u32 node_flags; + u64 memory_properties; /* Memory access properties */ + u8 memory_address_limit; /* Memory address size limit */ + char device_name[]; /* Path of namespace object */ +} __packed; + +/* Masks for Flags field above */ +#define ACPI_IORT_NC_STALL_SUPPORTED (1) +#define ACPI_IORT_NC_PASID_BITS (31 << 1) + +struct acpi_iort_root_complex { + u64 memory_properties; /* Memory access properties */ + u32 ats_attribute; + u32 pci_segment_number; + u8 memory_address_limit;/* Memory address size limit */ + u16 pasid_capabilities; /* PASID Capabilities */ + u8 reserved; /* Reserved, must be zero */ + u32 flags; /* Flags */ +} __packed; + +/* Masks for ats_attribute field above */ +#define ACPI_IORT_ATS_SUPPORTED (1) /* The root complex ATS support */ +#define ACPI_IORT_PRI_SUPPORTED (1 << 1) /* The root complex PRI support */ +#define ACPI_IORT_PASID_FWD_SUPPORTED (1 << 2) /* The root complex PASID forward support */ + +/* Masks for pasid_capabilities field above */ +#define ACPI_IORT_PASID_MAX_WIDTH (0x1F) /* Bits 0-4 */ + /* Tables defined/reserved by ACPI and generated by U-Boot */ enum acpi_tables { ACPITAB_BERT, @@ -1000,6 +1111,108 @@ int acpi_fill_csrt(struct acpi_ctx *ctx); */ void acpi_fill_fadt(struct acpi_fadt *fadt); +/** + * acpi_fill_iort() - Fill out the body of the IORT table + * + * Should be implemented in SoC specific code. + * + * @ctx: ACPI context to write to + * @offset: Offset from the start of the IORT + */ +int acpi_fill_iort(struct acpi_ctx *ctx); + +/** + * acpi_iort_add_its_group() - Add ITS group node to IORT table + * + * Called by SoC specific code within acpi_fill_iort(). + * + * @ctx: ACPI context to write to + * @its_count: Elements in identifiers + * @identifiers: The array of ITS identifiers. These IDs must match the value + * used in the Multiple APIC Description Table (MADT) GIC ITS + * structure for each relevant ITS unit. + * @return Offset of table within parent + */ +int acpi_iort_add_its_group(struct acpi_ctx *ctx, + const u32 its_count, + const u32 *identifiers); + +/** + * acpi_iort_add_named_component() - Add named component to IORT table + * + * Called by SoC specific code within acpi_fill_iort(). + * + * @ctx: ACPI context to write to + * @node_flags: Node flags + * @memory_properties: Memory properties + * @memory_address_limit: Memory address limit + * @device_name: ACPI device path + * @return Offset of table within parent + */ +int acpi_iort_add_named_component(struct acpi_ctx *ctx, + const u32 node_flags, + const u64 memory_properties, + const u8 memory_address_limit, + const char *device_name); + +/** + * acpi_iort_add_rc() - Add PCI root complex node to IORT table + * + * Called by SoC specific code within acpi_fill_iort(). + * + * @ctx: ACPI context to write to + * @mem_access_properties: Memory access properties + * @ats_attributes: Support for ATS and its ancillary feature + * @pci_segment_number: The PCI segment number, as in MCFG + * @memory_address_size_limit: The number of address bits, starting from LSB + * @num_mappings: Number of elements in map + * @map: ID mappings for this node + * @return Offset of table within parent + */ +int acpi_iort_add_rc(struct acpi_ctx *ctx, + const u64 mem_access_properties, + const u32 ats_attributes, + const u32 pci_segment_number, + const u8 memory_address_size_limit, + const int num_mappings, + const struct acpi_iort_id_mapping *map); + +/** + * acpi_iort_add_smmu_v3() - Add PCI root complex node to IORT table + * + * Called by SoC specific code within acpi_fill_iort(). + * + * @ctx: ACPI context to write to + * @base_address: Base address of SMMU + * @flags: SMMUv3 flags + * @vatos_address: Optional, set to zero if not supported + * @model: Model ID + * @event_gsiv: GSIV of the Event interrupt if SPI based + * @pri_gsiv: GSIV of the PRI interrupt if SPI based + * @gerr_gsiv: GSIV of the GERR interrupt if GSIV based + * @sync_gsiv: TGSIV of the Sync interrupt if GSIV based + * @pxm: Proximity Domain + * @id_mapping_index: If all the SMMU control interrupts are GSIV based, + * this field is ignored. Index into the array of ID + * mapping otherwise. + * @num_mappings: Number of elements in map + * @map: ID mappings for this node + * @return Offset of table within parent + */ +int acpi_iort_add_smmu_v3(struct acpi_ctx *ctx, + const u64 base_address, + const u32 flags, + const u64 vatos_address, + const u32 model, + const u32 event_gsiv, + const u32 pri_gsiv, + const u32 gerr_gsiv, + const u32 sync_gsiv, + const u32 pxm, + const u32 id_mapping_index, + const int num_mappings, + const struct acpi_iort_id_mapping *map); + /** * acpi_fill_madt() - Fill out the body of the MADT * diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 2dc7b998e22..9d9c8befe85 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -520,3 +520,216 @@ static int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry } ACPI_WRITER(5spcr, "SPCR", acpi_write_spcr, 0); + +__weak int acpi_fill_iort(struct acpi_ctx *ctx) +{ + return 0; +} + +int acpi_iort_add_its_group(struct acpi_ctx *ctx, + const u32 its_count, + const u32 *identifiers) +{ + struct acpi_iort_node *node; + struct acpi_iort_its_group *group; + int offset; + + offset = ctx->current - ctx->tab_start; + + node = ctx->current; + memset(node, '\0', sizeof(struct acpi_iort_node)); + + node->type = ACPI_IORT_NODE_ITS_GROUP; + node->revision = 1; + + node->length = sizeof(struct acpi_iort_node); + node->length += sizeof(struct acpi_iort_its_group); + node->length += sizeof(u32) * its_count; + + group = (struct acpi_iort_its_group *)node->node_data; + group->its_count = its_count; + memcpy(&group->identifiers, identifiers, sizeof(u32) * its_count); + + ctx->current += node->length; + + return offset; +} + +int acpi_iort_add_named_component(struct acpi_ctx *ctx, + const u32 node_flags, + const u64 memory_properties, + const u8 memory_address_limit, + const char *device_name) +{ + struct acpi_iort_node *node; + struct acpi_iort_named_component *comp; + int offset; + + offset = ctx->current - ctx->tab_start; + + node = ctx->current; + memset(node, '\0', sizeof(struct acpi_iort_node)); + + node->type = ACPI_IORT_NODE_NAMED_COMPONENT; + node->revision = 4; + node->length = sizeof(struct acpi_iort_node); + node->length += sizeof(struct acpi_iort_named_component); + node->length += strlen(device_name) + 1; + + comp = (struct acpi_iort_named_component *)node->node_data; + + comp->node_flags = node_flags; + comp->memory_properties = memory_properties; + comp->memory_address_limit = memory_address_limit; + memcpy(comp->device_name, device_name, strlen(device_name) + 1); + + ctx->current += node->length; + + return offset; +} + +int acpi_iort_add_rc(struct acpi_ctx *ctx, + const u64 mem_access_properties, + const u32 ats_attributes, + const u32 pci_segment_number, + const u8 memory_address_size_limit, + const int num_mappings, + const struct acpi_iort_id_mapping *map) +{ + struct acpi_iort_id_mapping *mapping; + struct acpi_iort_node *node; + struct acpi_iort_rc *rc; + int offset; + + offset = ctx->current - ctx->tab_start; + + node = ctx->current; + memset(node, '\0', sizeof(struct acpi_iort_node)); + + node->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX; + node->revision = 2; + + node->length = sizeof(struct acpi_iort_node); + node->length += sizeof(struct acpi_iort_rc); + node->length += sizeof(struct acpi_iort_id_mapping) * num_mappings; + + rc = (struct acpi_iort_rc *)node->node_data; + rc->mem_access_properties = mem_access_properties; + rc->ats_attributes = ats_attributes; + rc->pci_segment_number = pci_segment_number; + rc->memory_address_size_limit = memory_address_size_limit; + + mapping = (struct acpi_iort_id_mapping *)(rc + 1); + for (int i = 0; i < num_mappings; i++) { + memcpy(mapping, &map[i], sizeof(struct acpi_iort_id_mapping)); + mapping++; + } + + ctx->current += node->length; + + return offset; +} + +int acpi_iort_add_smmu_v3(struct acpi_ctx *ctx, + const u64 base_address, + const u32 flags, + const u64 vatos_address, + const u32 model, + const u32 event_gsiv, + const u32 pri_gsiv, + const u32 gerr_gsiv, + const u32 sync_gsiv, + const u32 pxm, + const u32 id_mapping_index, + const int num_mappings, + const struct acpi_iort_id_mapping *map) +{ + struct acpi_iort_node *node; + struct acpi_iort_smmu_v3 *smmu; + struct acpi_iort_id_mapping *mapping; + int offset; + + offset = ctx->current - ctx->tab_start; + + node = ctx->current; + memset(node, '\0', sizeof(struct acpi_iort_node)); + + node->type = ACPI_IORT_NODE_SMMU_V3; + node->revision = 5; + node->mapping_count = num_mappings; + node->mapping_offset = sizeof(struct acpi_iort_node) + sizeof(struct acpi_iort_smmu_v3); + + node->length = sizeof(struct acpi_iort_node); + node->length += sizeof(struct acpi_iort_smmu_v3); + node->length += sizeof(struct acpi_iort_id_mapping) * num_mappings; + + smmu = (struct acpi_iort_smmu_v3 *)node->node_data; + + smmu->base_address = base_address; + smmu->flags = flags; + smmu->vatos_address = vatos_address; + smmu->model = model; + smmu->event_gsiv = event_gsiv; + smmu->pri_gsiv = pri_gsiv; + smmu->gerr_gsiv = gerr_gsiv; + smmu->sync_gsiv = sync_gsiv; + smmu->pxm = pxm; + smmu->id_mapping_index = id_mapping_index; + + mapping = (struct acpi_iort_id_mapping *)(smmu + 1); + for (int i = 0; i < num_mappings; i++) { + memcpy(mapping, &map[i], sizeof(struct acpi_iort_id_mapping)); + mapping++; + } + + ctx->current += node->length; + + return offset; +} + +static int acpi_write_iort(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_iort *iort; + struct acpi_iort_node *node; + u32 offset; + int ret; + + iort = ctx->current; + ctx->tab_start = ctx->current; + memset(iort, '\0', sizeof(struct acpi_table_iort)); + + acpi_fill_header(&iort->header, "IORT"); + iort->header.revision = 1; + iort->header.creator_revision = 1; + iort->header.length = sizeof(struct acpi_table_iort); + iort->node_offset = sizeof(struct acpi_table_iort); + + acpi_inc(ctx, sizeof(struct acpi_table_iort)); + + offset = sizeof(struct acpi_table_iort); + ret = acpi_fill_iort(ctx); + if (ret) { + ctx->current = iort; + return log_msg_ret("fill", ret); + } + + /* Count nodes filled in */ + for (node = (void *)iort + iort->node_offset; + node->length > 0 && (void *)node < ctx->current; + node = (void *)node + node->length) + iort->node_count++; + + /* (Re)calculate length and checksum */ + iort->header.length = ctx->current - (void *)iort; + iort->header.checksum = table_compute_checksum((void *)iort, iort->header.length); + log_debug("IORT at %p, length %x\n", iort, iort->header.length); + + /* Drop the table if it is empty */ + if (iort->header.length == sizeof(struct acpi_table_iort)) + return log_msg_ret("fill", -ENOENT); + acpi_add_table(ctx, iort); + + return 0; +} + +ACPI_WRITER(5iort, "IORT", acpi_write_iort, 0); -- cgit v1.3.1 From 3451e03238fbd22240eee9dcee7bee857afd931a Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:55 +0200 Subject: acpi: Move function prototype Allow other architectures to use acpi_create_mcfg_mmconfig as well by moving the function prototype to common code. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass --- arch/x86/include/asm/acpi_table.h | 2 -- include/acpi/acpi_table.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 3254bbc380f..ff60384e620 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -31,8 +31,6 @@ int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride, u8 bus, u8 source, u32 gsirq, u16 flags); int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, u8 cpu, u16 flags, u8 lint); -int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base, - u16 seg_nr, u8 start, u8 end); /** * acpi_write_hpet() - Write out a HPET table diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 5a30465ad2a..07c5f65d753 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -994,6 +994,19 @@ int acpi_get_table_revision(enum acpi_tables table); */ int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags); +/** + * acpi_create_mcfg_mmconfig() - Create a MCFG table entry + * + * @mmconfig: Place to put the table + * @base: Base address of the ECAM space + * @seg_nr: PCI segment number + * @start: PCI bus start number + * @end: PCI bus end number + * Return: size of data written in bytes + */ +int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base, + u16 seg_nr, u8 start, u8 end); + /** * acpi_create_dbg2() - Create a DBG2 table * -- cgit v1.3.1 From 4ac655cd1a9108e0381e254afdeeffb607075096 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:19:58 +0200 Subject: acpi: Add processor device Add a new method to write the processor device identified by _HID ACPI0007, that is preferred over the Processor OpCode since ACPI 6.0. Fixes booting arm using ACPI only since the Processor OpCode isn't found valid by the Linux kernel. Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass --- include/acpi/acpigen.h | 12 ++++++++++++ lib/acpi/acpigen.c | 11 +++++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 3aa94d70b9c..16df85b9c1b 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -833,6 +833,7 @@ void acpigen_write_dsm_end(struct acpi_ctx *ctx); * * This emits a Processor package header with the required information. The * caller must complete the information and call acpigen_pop_len() at the end + * Deprecated since ACPI 6.0. * * @ctx: ACPI context pointer * @cpuindex: CPU number @@ -842,6 +843,17 @@ void acpigen_write_dsm_end(struct acpi_ctx *ctx); void acpigen_write_processor(struct acpi_ctx *ctx, uint cpuindex, u32 pblock_addr, uint pblock_len); +/** + * acpigen_write_processor_device() - Write a Processor device + * + * Write a device with _HID ACPI0007 identifying a processor. + * Replacement for the Processor OpCode. + * + * @ctx: ACPI context pointer + * @cpuindex: CPU number + */ +void acpigen_write_processor_device(struct acpi_ctx *ctx, uint cpuindex); + /** * acpigen_write_processor_package() - Write a package containing the processors * diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index ecff5a50d50..fa9dad32a3f 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -361,6 +361,17 @@ void acpigen_write_processor(struct acpi_ctx *ctx, uint cpuindex, acpigen_emit_byte(ctx, pblock_len); } +void acpigen_write_processor_device(struct acpi_ctx *ctx, uint cpuindex) +{ + char pscope[16]; + + snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, cpuindex); + acpigen_write_device(ctx, pscope); + acpigen_write_name_string(ctx, "_HID", "ACPI0007"); + acpigen_write_name_integer(ctx, "_UID", cpuindex); + acpigen_pop_len(ctx); /* Device */ +} + void acpigen_write_processor_package(struct acpi_ctx *ctx, const char *const name, const uint first_core, -- cgit v1.3.1 From f116feadea7be9afe412c25779dfcf241c121715 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:20:05 +0200 Subject: drivers: misc: irq-uclass: Update irq_get_by_index Support reading the "interrupts" property from the devicetree in case the "interrupts-extended" property isn't found. As the "interrupts" property is commonly used, this allows to parse all existing FDT and makes irq_get_by_index() more useful. The "interrupts" property doesn't contain a phandle as "interrupts-extended" does, so implement a new method to locate the interrupt-parent called irq_get_interrupt_parent(). TEST: Read the interrupts from the GIC node for ACPI MADT generation. Signed-off-by: Patrick Rudolph Reviewed-by: Moritz Fischer --- arch/sandbox/dts/test.dts | 3 +++ drivers/misc/irq-uclass.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++- include/irq.h | 14 ++++++++++ test/dm/irq.c | 15 +++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8412506c17a..9599a8db9c5 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -522,6 +522,9 @@ }; f-test { + #interrupt-cells = <2>; + interrupt-parent = <&irq>; + interrupts = <4 0>; compatible = "denx,u-boot-fdt-test"; }; diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index 79eb7c200dc..ac778593c96 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -62,6 +62,40 @@ int irq_read_and_clear(struct irq *irq) return ops->read_and_clear(irq); } +int irq_get_interrupt_parent(const struct udevice *dev, + struct udevice **interrupt_parent) +{ + struct ofnode_phandle_args phandle_args; + struct udevice *irq = NULL; + ofnode node; + int ret; + + if (!dev || !interrupt_parent) + return -EINVAL; + + *interrupt_parent = NULL; + + node = dev_ofnode(dev); + if (!ofnode_valid(node)) + return -EINVAL; + + while (ofnode_valid(node)) { + ret = ofnode_parse_phandle_with_args(node, "interrupt-parent", + NULL, 0, 0, &phandle_args); + if (!ret && !device_get_global_by_ofnode(phandle_args.node, &irq)) + break; + node = ofnode_get_parent(node); + } + + if (!irq) { + log_err("Cannot find an interrupt parent for device %s\n", dev->name); + return -ENODEV; + } + *interrupt_parent = irq; + + return 0; +} + #if CONFIG_IS_ENABLED(OF_PLATDATA) int irq_get_by_phandle(struct udevice *dev, const struct phandle_2_arg *cells, struct irq *irq) @@ -142,10 +176,40 @@ err: int irq_get_by_index(struct udevice *dev, int index, struct irq *irq) { struct ofnode_phandle_args args; - int ret; + struct udevice *interrupt_parent; + int ret, size, i; + const __be32 *list; + u32 count; ret = dev_read_phandle_with_args(dev, "interrupts-extended", "#interrupt-cells", 0, index, &args); + if (ret) { + list = dev_read_prop(dev, "interrupts", &size); + if (!list) + return -ENOENT; + + ret = irq_get_interrupt_parent(dev, &interrupt_parent); + if (ret) + return -ENODEV; + args.node = dev_ofnode(interrupt_parent); + + if (dev_read_u32(interrupt_parent, "#interrupt-cells", &count)) { + log_err("%s: could not get #interrupt-cells for %s\n", + __func__, dev->name); + return -ENOENT; + } + + if (index * count >= size / sizeof(*list)) + return -ENOENT; + if (count > OF_MAX_PHANDLE_ARGS) + count = OF_MAX_PHANDLE_ARGS; + args.args_count = count; + for (i = 0; i < count; i++) + args.args[i] = be32_to_cpup(&list[index * count + i]); + + return irq_get_by_index_tail(ret, dev_ofnode(dev), &args, + "interrupts", index, irq); + } return irq_get_by_index_tail(ret, dev_ofnode(dev), &args, "interrupts-extended", index > 0, irq); diff --git a/include/irq.h b/include/irq.h index 5638c10128e..0fbc1a5f485 100644 --- a/include/irq.h +++ b/include/irq.h @@ -200,6 +200,20 @@ int irq_restore_polarities(struct udevice *dev); */ int irq_read_and_clear(struct irq *irq); +/** + * irq_get_interrupt_parent() - returns the interrupt parent + * + * Walks the devicetree and returns the interrupt parent's ofnode + * for the specified device. + * + * @dev: device + * @interrupt_parent: The interrupt parent's ofnode' + * Return: 0 success, or error value + * + */ +int irq_get_interrupt_parent(const struct udevice *dev, + struct udevice **interrupt_parent); + struct phandle_2_arg; /** * irq_get_by_phandle() - Get an irq by its phandle information (of-platadata) diff --git a/test/dm/irq.c b/test/dm/irq.c index 836f2d82e71..ca3e1880656 100644 --- a/test/dm/irq.c +++ b/test/dm/irq.c @@ -76,6 +76,21 @@ static int dm_test_request(struct unit_test_state *uts) } DM_TEST(dm_test_request, UTF_SCAN_PDATA | UTF_SCAN_FDT); +/* Test of irq_get_by_index() */ +static int dm_test_irq_get_by_index(struct unit_test_state *uts) +{ + struct udevice *dev; + struct irq irq; + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "f-test", + &dev)); + ut_assertok(irq_get_by_index(dev, 0, &irq)); + ut_asserteq(4, irq.id); + + return 0; +} +DM_TEST(dm_test_irq_get_by_index, UTF_SCAN_PDATA | UTF_SCAN_FDT); + /* Test of irq_get_acpi() */ static int dm_test_irq_get_acpi(struct unit_test_state *uts) { -- cgit v1.3.1 From 6d722894fd4843b7b02051449a0444e5171e0429 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:20:08 +0200 Subject: board: emulation: Add QEMU sbsa support Add support for Arm sbsa [1] v0.3+ that is supported by QEMU [2]. Unlike other Arm based platforms the machine only provides a minimal FDT that contains number of CPUs, ammount of memory and machine-version. The boot firmware has to provide ACPI tables to the OS. Due to this design a full DTB is added here as well that allows U-Boot's driver to properly function. The DTB is appended at the end of the U-Boot image and will be merged with the QEMU provided DTB. In addition provide documentation how to use, enable binman to fabricate both ROMs that are required to boot and add ACPI tables to make it full compatible to the EDK2 reference implementation. The board was tested using Fedora 40 Aarch64 Workstation. It's able to boot from USB and AHCI or network. Tested and found working: - serial - PCI - xHCI - Bochs display - AHCI - network using e1000e - CPU init - Booting Fedora 40 1: Server Base System Architecture (SBSA) 2: https://www.qemu.org/docs/master/system/arm/sbsa.html Signed-off-by: Patrick Rudolph Cc: Peter Robinson Cc: Simon Glass Cc: Tom Rini --- arch/arm/Kconfig | 3 +- arch/arm/dts/qemu-sbsa.dts | 138 ++++++++ arch/arm/include/asm/arch-qemu-sbsa/boot0.h | 34 ++ arch/arm/mach-qemu/Kconfig | 36 ++- board/emulation/qemu-arm/MAINTAINERS | 2 + board/emulation/qemu-sbsa/Kconfig | 59 ++++ board/emulation/qemu-sbsa/Makefile | 8 + board/emulation/qemu-sbsa/acpi.c | 192 +++++++++++ board/emulation/qemu-sbsa/dsdt.asl | 483 ++++++++++++++++++++++++++++ board/emulation/qemu-sbsa/lowlevel_init.S | 22 ++ board/emulation/qemu-sbsa/qemu-sbsa.c | 273 ++++++++++++++++ board/emulation/qemu-sbsa/qemu-sbsa.env | 14 + board/emulation/qemu-sbsa/qemu-sbsa.h | 38 +++ board/emulation/qemu-sbsa/smc.c | 71 ++++ configs/qemu-arm-sbsa_defconfig | 12 + doc/board/emulation/index.rst | 1 + doc/board/emulation/qemu-sbsa.rst | 98 ++++++ doc/develop/driver-model/virtio.rst | 1 + include/configs/qemu-sbsa.h | 89 +++++ 19 files changed, 1567 insertions(+), 7 deletions(-) create mode 100644 arch/arm/dts/qemu-sbsa.dts create mode 100644 arch/arm/include/asm/arch-qemu-sbsa/boot0.h create mode 100644 board/emulation/qemu-sbsa/Kconfig create mode 100644 board/emulation/qemu-sbsa/Makefile create mode 100644 board/emulation/qemu-sbsa/acpi.c create mode 100644 board/emulation/qemu-sbsa/dsdt.asl create mode 100644 board/emulation/qemu-sbsa/lowlevel_init.S create mode 100644 board/emulation/qemu-sbsa/qemu-sbsa.c create mode 100644 board/emulation/qemu-sbsa/qemu-sbsa.env create mode 100644 board/emulation/qemu-sbsa/qemu-sbsa.h create mode 100644 board/emulation/qemu-sbsa/smc.c create mode 100644 configs/qemu-arm-sbsa_defconfig create mode 100644 doc/board/emulation/qemu-sbsa.rst create mode 100644 include/configs/qemu-sbsa.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 263f85b0d0f..0d0c731dd08 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1054,7 +1054,7 @@ config ARCH_QEMU imply DM_RNG imply DM_RTC imply RTC_PL031 - imply OF_HAS_PRIOR_STAGE + imply OF_HAS_PRIOR_STAGE if !TARGET_QEMU_ARM_SBSA imply VIDEO imply VIDEO_BOCHS imply SYS_WHITE_ON_BLACK @@ -2381,6 +2381,7 @@ source "board/broadcom/bcmns3/Kconfig" source "board/cavium/thunderx/Kconfig" source "board/eets/pdu001/Kconfig" source "board/emulation/qemu-arm/Kconfig" +source "board/emulation/qemu-sbsa/Kconfig" source "board/freescale/ls2080aqds/Kconfig" source "board/freescale/ls2080ardb/Kconfig" source "board/freescale/ls1088a/Kconfig" diff --git a/arch/arm/dts/qemu-sbsa.dts b/arch/arm/dts/qemu-sbsa.dts new file mode 100644 index 00000000000..ed00e501366 --- /dev/null +++ b/arch/arm/dts/qemu-sbsa.dts @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Devicetree with onboard devices for qemu_sbsa-ref for internal use only! + * DO NOT PASS TO THE OS! + * + * As QEMU provides only a minimal devicetree this one is merged with + * it and then fixed at runtime. + * + * Copyright 2024 9elements GmbH + */ +#include "configs/qemu-sbsa.h" +#include + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&intc>; + compatible = "linux,sbsa-ref"; + + binman: binman { + multiple-images; + }; + + cpus { + /* Filled by fdtdec_board_setup() */ + }; + + memory { + /* Filled by fdtdec_board_setup() */ + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cfi_flash { + compatible = "cfi-flash"; + reg = /bits/ 64 ; + status = "okay"; + }; + + uart0 { + compatible = "arm,pl011"; + status = "okay"; + reg = /bits/ 64 ; + }; + + ahci { + compatible = "generic-ahci"; + status = "okay"; + reg = /bits/ 64 <0x60100000 0x00010000>; + }; + + xhci { + compatible = "generic-xhci"; + status = "okay"; + reg = /bits/ 64 <0x60110000 0x00010000>; + }; + + pci { + #address-cells = <3>; + #size-cells = <2>; + compatible = "pci-host-ecam-generic"; + device_type = "pci"; + status = "okay"; + reg = /bits/ 64 <0xf0000000 0x10000000>; + bus-range = <0 0xff>; + ranges = /bits/ 32 <0x01000000>, + /bits/ 64 <0 + SBSA_PIO_BASE_ADDR + SBSA_PIO_LENGTH>, + /bits/ 32 <0x02000000>, + /bits/ 64 , + /bits/ 32 <0x43000000>, + /bits/ 64 ; + }; + }; + + intc: interrupt-controller { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + status = "okay"; + interrupt-controller; + interrupts = ; + reg = /bits/ 64 , + /bits/ 64 , + /bits/ 64 <0 0>, + /bits/ 64 , + /bits/ 64 ; + }; + + its { + compatible = "arm,gic-v3-its"; + status = "disabled"; + }; +}; + +&binman { + secure-world { + filename = "secure-world.rom"; + size = ; + + bl1 { + offset = <0x0>; + description = "ARM Trusted Firmware BL1"; + filename = "bl1.bin"; + type = "blob-ext"; + }; + + fip { + offset = <0x12000>; + description = "ARM Trusted Firmware FIP"; + filename = "fip.bin"; + type = "blob-ext"; + }; + }; + + unsecure-world { + filename = "unsecure-world.rom"; + size = ; + + u-boot { + }; + u-boot-dtb { + compress = "lz4"; + }; + }; +}; diff --git a/arch/arm/include/asm/arch-qemu-sbsa/boot0.h b/arch/arm/include/asm/arch-qemu-sbsa/boot0.h new file mode 100644 index 00000000000..4a1a254b923 --- /dev/null +++ b/arch/arm/include/asm/arch-qemu-sbsa/boot0.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * sbsa-ref starts U-Boot in XIP memory. Need to relocate U-Boot + * to DRAM which is already up. Instead of using SPL this simple loader + * is being used. + */ +relocate_check: + /* x0 contains the pointer to FDT provided by ATF */ + adr x1, _start /* x1 <- Runtime value of _start */ + ldr x2, _TEXT_BASE /* x2 <- Linked value of _start */ + subs x9, x1, x2 /* x9 <- Run-vs-link offset */ + beq reset + + adrp x1, __image_copy_start /* x2 <- address bits [31:12] */ + add x1, x1, :lo12:__image_copy_start/* x2 <- address bits [11:00] */ + adrp x3, __image_copy_end /* x3 <- address bits [31:12] */ + add x3, x3, :lo12:__image_copy_end /* x3 <- address bits [11:00] */ + add x3, x3, #0x100000 /* 1 MiB for the DTB found at _end */ + +copy_loop: + ldp x10, x11, [x1], #16 /* copy from source address [x1] */ + stp x10, x11, [x2], #16 /* copy to target address [x2] */ + cmp x1, x3 /* until source end address [x3] */ + b.lo copy_loop + + isb + ldr x2, _TEXT_BASE /* x2 <- Linked value of _start */ + br x2 /* Jump to linked address */ + /* Never reaches this point */ +1: + wfi + b 1b + +relocate_done: \ No newline at end of file diff --git a/arch/arm/mach-qemu/Kconfig b/arch/arm/mach-qemu/Kconfig index 186c3582ebf..9c06c6a3a5c 100644 --- a/arch/arm/mach-qemu/Kconfig +++ b/arch/arm/mach-qemu/Kconfig @@ -3,12 +3,6 @@ if ARCH_QEMU config SYS_VENDOR default "emulation" -config SYS_BOARD - default "qemu-arm" - -config SYS_CONFIG_NAME - default "qemu-arm" - choice prompt "QEMU ARM architecture" default TARGET_QEMU_ARM_64BIT @@ -25,6 +19,36 @@ config TARGET_QEMU_ARM_64BIT select ARM64 select BOARD_LATE_INIT +config TARGET_QEMU_ARM_SBSA + bool "SBSA Reference" + select ARM64 + select BINMAN + select BOARD_LATE_INIT + select ENABLE_ARM_SOC_BOOT0_HOOK + select MISC_INIT_R endchoice +if TARGET_QEMU_ARM_32BIT || TARGET_QEMU_ARM_64BIT + +config SYS_BOARD + default "qemu-arm" + +config SYS_CONFIG_NAME + default "qemu-arm" + +endif + +if TARGET_QEMU_ARM_SBSA + +config SYS_BOARD + default "qemu-sbsa" + +config SYS_CONFIG_NAME + default "qemu-sbsa" + +config SYS_SOC + default "qemu-sbsa" + +endif + endif diff --git a/board/emulation/qemu-arm/MAINTAINERS b/board/emulation/qemu-arm/MAINTAINERS index 5154262f29e..7bc0ee698c5 100644 --- a/board/emulation/qemu-arm/MAINTAINERS +++ b/board/emulation/qemu-arm/MAINTAINERS @@ -4,5 +4,7 @@ S: Maintained F: board/emulation/qemu-arm/ F: board/emulation/common/ F: include/configs/qemu-arm.h +F: include/configs/qemu-sbsa.h F: configs/qemu_arm_defconfig F: configs/qemu_arm64_defconfig +F: configs/qemu-arm-sbsa_defconfig diff --git a/board/emulation/qemu-sbsa/Kconfig b/board/emulation/qemu-sbsa/Kconfig new file mode 100644 index 00000000000..72c76b351fa --- /dev/null +++ b/board/emulation/qemu-sbsa/Kconfig @@ -0,0 +1,59 @@ +if TARGET_QEMU_ARM_SBSA + +config SYS_SOC + default "qemu-sbsa" + +config TEXT_BASE + default 0x10000100000 + +config SYS_LOAD_ADDR + default 0x10000100000 + +config PRE_CON_BUF_ADDR + default 0x100000FF000 + +config DEFAULT_DEVICE_TREE + default "qemu-sbsa" + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + select AHCI + select ACPIGEN + select ACPI + select BLOBLIST + select CPU + select CPU_ARMV8 + select DM + select DM_USB + select DM_MTD + select GENERATE_ACPI_TABLE + select HAS_ROM + select MTD + select OF_LIBFDT_OVERLAY + select OF_SEPARATE + select PCI + select PCIE_ECAM_GENERIC + select USB + select GIC_V3 + select GIC_V3_ITS + select SYS_FLASH_CFI_WIDTH_16BIT + imply AHCI_GENERIC + imply USB_XHCI_HCD + imply USB_XHCI_GENERIC + imply USB_STORAGE + imply E1000 + imply E1000_NO_NVM + imply NET_RANDOM_ETHADDR + imply VIDEO_BOCHS + imply CFI_FLASH + imply SYS_MTDPARTS_RUNTIME + imply SET_DFU_ALT_INFO + +if DEBUG_UART + +config DEBUG_UART_BASE + default 0x60000000 +endif + +source "board/emulation/common/Kconfig" +endif diff --git a/board/emulation/qemu-sbsa/Makefile b/board/emulation/qemu-sbsa/Makefile new file mode 100644 index 00000000000..bacae320e79 --- /dev/null +++ b/board/emulation/qemu-sbsa/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += qemu-sbsa.o +obj-y += lowlevel_init.o +obj-y += smc.o + +obj-$(CONFIG_GENERATE_ACPI_TABLE) += dsdt_generated.o +obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o diff --git a/board/emulation/qemu-sbsa/acpi.c b/board/emulation/qemu-sbsa/acpi.c new file mode 100644 index 00000000000..ba85e08fc7d --- /dev/null +++ b/board/emulation/qemu-sbsa/acpi.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024 9elements GmbH + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qemu-sbsa.h" + +#define SBSAQEMU_MADT_GIC_VBASE 0x2c020000 +#define SBSAQEMU_MADT_GIC_HBASE 0x2c010000 +#define SBSAQEMU_MADT_GIC_PMU_IRQ 23 + +#define SBSA_PLATFORM_WATCHDOG_COUNT 1 +#define SBSA_PLATFORM_TIMER_COUNT (SBSA_PLATFORM_WATCHDOG_COUNT) + +#define L2_ATTRIBUTES (ACPI_PPTT_READ_ALLOC | ACPI_PPTT_WRITE_ALLOC | \ + (ACPI_PPTT_CACHE_TYPE_UNIFIED << \ + ACPI_PPTT_CACHE_TYPE_SHIFT)) +#define L2_SIZE 0x80000 +#define L2_SETS 0x400 +#define L2_WAYS 8 + +#define L1D_ATTRIBUTES (ACPI_PPTT_READ_ALLOC | ACPI_PPTT_WRITE_ALLOC | \ + (ACPI_PPTT_CACHE_TYPE_DATA << \ + ACPI_PPTT_CACHE_TYPE_SHIFT)) +#define L1D_SIZE 0x8000 +#define L1D_SETS 0x100 +#define L1D_WAYS 2 + +#define L1I_ATTRIBUTES (ACPI_PPTT_READ_ALLOC | \ + (ACPI_PPTT_CACHE_TYPE_INSTR << \ + ACPI_PPTT_CACHE_TYPE_SHIFT)) +#define L1I_SIZE 0x8000 +#define L1I_SETS 0x100 +#define L1I_WAYS 2 + +int acpi_fill_iort(struct acpi_ctx *ctx) +{ + u32 its_offset, smmu_offset; + u64 gic_its_base = 0; + + smc_get_gic_its_base(&gic_its_base); + if (gic_its_base == 0) + return 0; + + u32 identifiers[] = { 0 }; + + its_offset = acpi_iort_add_its_group(ctx, ARRAY_SIZE(identifiers), + identifiers); + + struct acpi_iort_id_mapping map_smmu[] = {{ + 0, 0xffff, 0, its_offset, 0 + }}; + + smmu_offset = acpi_iort_add_smmu_v3(ctx, + SBSA_SMMU_BASE_ADDR, // Base address + ACPI_IORT_SMMU_V3_COHACC_OVERRIDE, // Flags + 0, // VATOS address + 0, // SMMUv3 Model + 74, // Event + 75, // Pri + 77, // Gerror + 76, // Sync + 0, // Proximity domain + 1, // DevIDMappingIndex + ARRAY_SIZE(map_smmu), + map_smmu); + + struct acpi_iort_id_mapping map_rc[] = {{ + 0, 0xffff, 0, smmu_offset, 0 + }}; + + acpi_iort_add_rc(ctx, + BIT(0) | BIT(56), // CacheCoherent + CPM + 0, // AtsAttribute + 0, // PciSegmentNumber + 64, // MemoryAddressSizeLimit + ARRAY_SIZE(map_rc), + map_rc); + return 0; +} + +void acpi_fill_fadt(struct acpi_fadt *fadt) +{ + fadt->flags = ACPI_FADT_HW_REDUCED_ACPI | ACPI_FADT_LOW_PWR_IDLE_S0; + fadt->preferred_pm_profile = ACPI_PM_PERFORMANCE_SERVER; + fadt->arm_boot_arch = ACPI_ARM_PSCI_COMPLIANT; +} + +int acpi_fill_mcfg(struct acpi_ctx *ctx) +{ + size_t size; + + /* PCI Segment Group 0, Start Bus Number 0, End Bus Number is 255 */ + size = acpi_create_mcfg_mmconfig((void *)ctx->current, + SBSA_PCIE_ECAM_BASE_ADDR, 0, 0, 255); + acpi_inc(ctx, size); + + return 0; +} + +static int sbsa_write_gtdt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *header; + struct acpi_gtdt *gtdt; + + gtdt = ctx->current; + header = >dt->header; + + memset(gtdt, '\0', sizeof(struct acpi_gtdt)); + + acpi_fill_header(header, "GTDT"); + header->length = sizeof(struct acpi_gtdt); + header->revision = acpi_get_table_revision(ACPITAB_GTDT); + + gtdt->cnt_ctrl_base = 0xFFFFFFFFFFFFFFFF; + gtdt->sec_el1_gsiv = 29; + gtdt->sec_el1_flags = GTDT_FLAG_INT_ACTIVE_LOW; + gtdt->el1_gsiv = 30; + gtdt->el1_flags = GTDT_FLAG_INT_ACTIVE_LOW; + gtdt->virt_el1_gsiv = 27; + gtdt->virt_el1_flags = GTDT_FLAG_INT_ACTIVE_LOW; + gtdt->el2_gsiv = 26; + gtdt->el2_flags = GTDT_FLAG_INT_ACTIVE_LOW; + gtdt->cnt_read_base = 0xffffffffffffffff; + + // FIXME: VirtualPL2Timer + header->checksum = table_compute_checksum(header, header->length); + + acpi_add_table(ctx, gtdt); + + acpi_inc(ctx, sizeof(struct acpi_gtdt)); + + return 0; +}; + +ACPI_WRITER(5gtdt, "GTDT", sbsa_write_gtdt, 0); + +static int acpi_write_pptt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *header; + int cluster_offset, l2_offset; + u32 offsets[2]; + + header = ctx->current; + ctx->tab_start = ctx->current; + + memset(header, '\0', sizeof(struct acpi_table_header)); + + acpi_fill_header(header, "PPTT"); + header->revision = acpi_get_table_revision(ACPITAB_PPTT); + acpi_inc(ctx, sizeof(*header)); + + cluster_offset = acpi_pptt_add_proc(ctx, ACPI_PPTT_PHYSICAL_PACKAGE | + ACPI_PPTT_CHILDREN_IDENTICAL, + 0, 0, 0, NULL); + + l2_offset = acpi_pptt_add_cache(ctx, ACPI_PPTT_ALL_VALID, 0, L2_SIZE, + L2_SETS, L2_WAYS, L2_ATTRIBUTES, 64); + + offsets[0] = acpi_pptt_add_cache(ctx, ACPI_PPTT_ALL_VALID, l2_offset, + L1D_SIZE, L1D_SETS, L1D_WAYS, + L1D_ATTRIBUTES, 64); + + offsets[1] = acpi_pptt_add_cache(ctx, ACPI_PPTT_ALL_BUT_WRITE_POL, + l2_offset, L1I_SIZE, L1I_SETS, + L1I_WAYS, L1I_ATTRIBUTES, 64); + + for (int i = 0; i < uclass_id_count(UCLASS_CPU); i++) { + acpi_pptt_add_proc(ctx, ACPI_PPTT_CHILDREN_IDENTICAL | + ACPI_PPTT_NODE_IS_LEAF | ACPI_PPTT_PROC_ID_VALID, + cluster_offset, i, 2, offsets); + } + + header->length = ctx->current - ctx->tab_start; + header->checksum = table_compute_checksum(header, header->length); + + acpi_inc(ctx, header->length); + acpi_add_table(ctx, header); + + return 0; +}; + +ACPI_WRITER(5pptt, "PPTT", acpi_write_pptt, 0); diff --git a/board/emulation/qemu-sbsa/dsdt.asl b/board/emulation/qemu-sbsa/dsdt.asl new file mode 100644 index 00000000000..f12cca04e2c --- /dev/null +++ b/board/emulation/qemu-sbsa/dsdt.asl @@ -0,0 +1,483 @@ +/** @file +* Differentiated System Description Table Fields (DSDT). +* +* Copyright (c) 2020, Linaro Ltd. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#define LINK_DEVICE(Uid, LinkName, Irq) \ + Device (LinkName) { \ + Name (_HID, EISAID("PNP0C0F")) \ + Name (_UID, Uid) \ + Name (_PRS, ResourceTemplate() { \ + Interrupt (ResourceProducer, Level, ActiveHigh, Exclusive) { Irq } \ + }) \ + Method (_STA) { \ + Return (0xF) \ + } \ + Method (_CRS, 0) { Return (_PRS) } \ + Method (_SRS, 1) { } \ + Method (_DIS) { } \ + } + +#define PRT_ENTRY(Address, Pin, Link) \ + Package (4) { \ + Address, Pin, Link, Zero \ + } + +DefinitionBlock ("Dsdt.aml", "DSDT", 2, "U-Boot", "SBSAQEMU", 2) { + Scope (_SB) { + // UART PL011 + Device (COM0) { + Name (_HID, "ARMH0011") + Name (_UID, Zero) + Name (_CRS, ResourceTemplate () { + Memory32Fixed (ReadWrite, + SBSA_UART_BASE_ADDR, + SBSA_UART_LENGTH) + Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 33 } + }) + Method (_STA) { + Return (0xF) + } + } + + // AHCI Host Controller + Device (AHC0) { + Name (_HID, "LNRO001E") + Name (_CLS, Package (3) { + 0x01, + 0x06, + 0x01, + }) + Name (_CCA, 1) + Name (_CRS, ResourceTemplate() { + Memory32Fixed (ReadWrite, + SBSA_AHCI_BASE_ADDR, + SBSA_AHCI_LENGTH) + Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 42 } + }) + Method (_STA) { + Return (0xF) + } + } + + + // USB XHCI Host Controller + Device (USB0) { + Name (_HID, "PNP0D10") // _HID: Hardware ID + Name (_UID, 0x00) // _UID: Unique ID + Name (_CCA, 0x01) // _CCA: Cache Coherency Attribute + Name (XHCI, 0xF) // will be set using AcpiLib + Method (_STA) { + Return (XHCI) + } + Name (_CRS, ResourceTemplate() { + Memory32Fixed (ReadWrite, + SBSA_XHCI_BASE_ADDR, + SBSA_XHCI_LENGTH) + Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 43 } + }) + + // Root Hub + Device (RHUB) { + Name (_ADR, 0x00000000) // Address of Root Hub should be 0 as per ACPI 5.0 spec + Method (_STA) { + Return (0xF) + } + + // Ports connected to Root Hub + Device (HUB1) { + Name (_ADR, 0x00000001) + Name (_UPC, Package() { + 0x00, // Port is NOT connectable + 0xFF, // Don't care + 0x00000000, // Reserved 0 must be zero + 0x00000000 // Reserved 1 must be zero + }) + Method (_STA) { + Return (0xF) + } + + Device (PRT1) { + Name (_ADR, 0x00000001) + Name (_UPC, Package() { + 0xFF, // Port is connectable + 0x00, // Port connector is A + 0x00000000, + 0x00000000 + }) + Name (_PLD, Package() { + Buffer(0x10) { + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } + }) + Method (_STA) { + Return (0xF) + } + } // USB0_RHUB_HUB1_PRT1 + Device (PRT2) { + Name (_ADR, 0x00000002) + Name (_UPC, Package() { + 0xFF, // Port is connectable + 0x00, // Port connector is A + 0x00000000, + 0x00000000 + }) + Name (_PLD, Package() { + Buffer(0x10) { + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } + }) + Method (_STA) { + Return (0xF) + } + } // USB0_RHUB_HUB1_PRT2 + + Device (PRT3) { + Name (_ADR, 0x00000003) + Name (_UPC, Package() { + 0xFF, // Port is connectable + 0x09, // Type C connector - USB2 and SS with Switch + 0x00000000, + 0x00000000 + }) + Name (_PLD, Package() { + Buffer (0x10) { + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } + }) + Method (_STA) { + Return (0xF) + } + } // USB0_RHUB_HUB1_PRT3 + + Device (PRT4) { + Name (_ADR, 0x00000004) + Name (_UPC, Package() { + 0xFF, // Port is connectable + 0x09, // Type C connector - USB2 and SS with Switch + 0x00000000, + 0x00000000 + }) + Name (_PLD, Package() { + Buffer (0x10){ + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } + }) + Method (_STA) { + Return (0xF) + } + } // USB0_RHUB_HUB1_PRT4 + } // USB0_RHUB_HUB1 + } // USB0_RHUB + } // USB0 + + Device (PCI0) + { + Name (_HID, EISAID ("PNP0A08")) // PCI Express Root Bridge + Name (_CID, EISAID ("PNP0A03")) // Compatible PCI Root Bridge + Name (_SEG, Zero) // PCI Segment Group number + Name (_BBN, Zero) // PCI Base Bus Number + Name (_UID, "PCI0") + Name (_CCA, One) // Initially mark the PCI coherent (for JunoR1) + + Method (_STA) { + Return (0xF) + } + + Method (_CBA, 0, NotSerialized) { + return (SBSA_PCIE_ECAM_BASE_ADDR) + } + + LINK_DEVICE(0, GSI0, 0x23) + LINK_DEVICE(1, GSI1, 0x24) + LINK_DEVICE(2, GSI2, 0x25) + LINK_DEVICE(3, GSI3, 0x26) + + Name (_PRT, Package () // _PRT: PCI Routing Table + { + PRT_ENTRY(0x0000FFFF, 0, GSI0), + PRT_ENTRY(0x0000FFFF, 0, GSI1), + PRT_ENTRY(0x0000FFFF, 0, GSI2), + PRT_ENTRY(0x0000FFFF, 0, GSI3), + + PRT_ENTRY(0x0001FFFF, 0, GSI1), + PRT_ENTRY(0x0001FFFF, 1, GSI2), + PRT_ENTRY(0x0001FFFF, 2, GSI3), + PRT_ENTRY(0x0001FFFF, 3, GSI0), + + PRT_ENTRY(0x0002FFFF, 0, GSI2), + PRT_ENTRY(0x0002FFFF, 1, GSI3), + PRT_ENTRY(0x0002FFFF, 2, GSI0), + PRT_ENTRY(0x0002FFFF, 3, GSI1), + + PRT_ENTRY(0x0003FFFF, 0, GSI3), + PRT_ENTRY(0x0003FFFF, 1, GSI0), + PRT_ENTRY(0x0003FFFF, 2, GSI1), + PRT_ENTRY(0x0003FFFF, 3, GSI2), + + PRT_ENTRY(0x0004FFFF, 0, GSI0), + PRT_ENTRY(0x0004FFFF, 1, GSI1), + PRT_ENTRY(0x0004FFFF, 2, GSI2), + PRT_ENTRY(0x0004FFFF, 3, GSI3), + + PRT_ENTRY(0x0005FFFF, 0, GSI1), + PRT_ENTRY(0x0005FFFF, 1, GSI2), + PRT_ENTRY(0x0005FFFF, 2, GSI3), + PRT_ENTRY(0x0005FFFF, 3, GSI0), + + PRT_ENTRY(0x0006FFFF, 0, GSI2), + PRT_ENTRY(0x0006FFFF, 1, GSI3), + PRT_ENTRY(0x0006FFFF, 2, GSI0), + PRT_ENTRY(0x0006FFFF, 3, GSI1), + + PRT_ENTRY(0x0007FFFF, 0, GSI3), + PRT_ENTRY(0x0007FFFF, 1, GSI0), + PRT_ENTRY(0x0007FFFF, 2, GSI1), + PRT_ENTRY(0x0007FFFF, 3, GSI2), + + PRT_ENTRY(0x0008FFFF, 0, GSI0), + PRT_ENTRY(0x0008FFFF, 1, GSI1), + PRT_ENTRY(0x0008FFFF, 2, GSI2), + PRT_ENTRY(0x0008FFFF, 3, GSI3), + + PRT_ENTRY(0x0009FFFF, 0, GSI1), + PRT_ENTRY(0x0009FFFF, 1, GSI2), + PRT_ENTRY(0x0009FFFF, 2, GSI3), + PRT_ENTRY(0x0009FFFF, 3, GSI0), + + PRT_ENTRY(0x000AFFFF, 0, GSI2), + PRT_ENTRY(0x000AFFFF, 1, GSI3), + PRT_ENTRY(0x000AFFFF, 2, GSI0), + PRT_ENTRY(0x000AFFFF, 3, GSI1), + + PRT_ENTRY(0x000BFFFF, 0, GSI3), + PRT_ENTRY(0x000BFFFF, 1, GSI0), + PRT_ENTRY(0x000BFFFF, 2, GSI1), + PRT_ENTRY(0x000BFFFF, 3, GSI2), + + PRT_ENTRY(0x000CFFFF, 0, GSI0), + PRT_ENTRY(0x000CFFFF, 1, GSI1), + PRT_ENTRY(0x000CFFFF, 2, GSI2), + PRT_ENTRY(0x000CFFFF, 3, GSI3), + + PRT_ENTRY(0x000DFFFF, 0, GSI1), + PRT_ENTRY(0x000DFFFF, 1, GSI2), + PRT_ENTRY(0x000DFFFF, 2, GSI3), + PRT_ENTRY(0x000DFFFF, 3, GSI0), + + PRT_ENTRY(0x000EFFFF, 0, GSI2), + PRT_ENTRY(0x000EFFFF, 1, GSI3), + PRT_ENTRY(0x000EFFFF, 2, GSI0), + PRT_ENTRY(0x000EFFFF, 3, GSI1), + + PRT_ENTRY(0x000FFFFF, 0, GSI3), + PRT_ENTRY(0x000FFFFF, 1, GSI0), + PRT_ENTRY(0x000FFFFF, 2, GSI1), + PRT_ENTRY(0x000FFFFF, 3, GSI2), + + PRT_ENTRY(0x0010FFFF, 0, GSI0), + PRT_ENTRY(0x0010FFFF, 1, GSI1), + PRT_ENTRY(0x0010FFFF, 2, GSI2), + PRT_ENTRY(0x0010FFFF, 3, GSI3), + + PRT_ENTRY(0x0011FFFF, 0, GSI1), + PRT_ENTRY(0x0011FFFF, 1, GSI2), + PRT_ENTRY(0x0011FFFF, 2, GSI3), + PRT_ENTRY(0x0011FFFF, 3, GSI0), + + PRT_ENTRY(0x0012FFFF, 0, GSI2), + PRT_ENTRY(0x0012FFFF, 1, GSI3), + PRT_ENTRY(0x0012FFFF, 2, GSI0), + PRT_ENTRY(0x0012FFFF, 3, GSI1), + + PRT_ENTRY(0x0013FFFF, 0, GSI3), + PRT_ENTRY(0x0013FFFF, 1, GSI0), + PRT_ENTRY(0x0013FFFF, 2, GSI1), + PRT_ENTRY(0x0013FFFF, 3, GSI2), + + PRT_ENTRY(0x0014FFFF, 0, GSI0), + PRT_ENTRY(0x0014FFFF, 1, GSI1), + PRT_ENTRY(0x0014FFFF, 2, GSI2), + PRT_ENTRY(0x0014FFFF, 3, GSI3), + + PRT_ENTRY(0x0015FFFF, 0, GSI1), + PRT_ENTRY(0x0015FFFF, 1, GSI2), + PRT_ENTRY(0x0015FFFF, 2, GSI3), + PRT_ENTRY(0x0015FFFF, 3, GSI0), + + PRT_ENTRY(0x0016FFFF, 0, GSI2), + PRT_ENTRY(0x0016FFFF, 1, GSI3), + PRT_ENTRY(0x0016FFFF, 2, GSI0), + PRT_ENTRY(0x0016FFFF, 3, GSI1), + + PRT_ENTRY(0x0017FFFF, 0, GSI3), + PRT_ENTRY(0x0017FFFF, 1, GSI0), + PRT_ENTRY(0x0017FFFF, 2, GSI1), + PRT_ENTRY(0x0017FFFF, 3, GSI2), + + PRT_ENTRY(0x0018FFFF, 0, GSI0), + PRT_ENTRY(0x0018FFFF, 1, GSI1), + PRT_ENTRY(0x0018FFFF, 2, GSI2), + PRT_ENTRY(0x0018FFFF, 3, GSI3), + + PRT_ENTRY(0x0019FFFF, 0, GSI1), + PRT_ENTRY(0x0019FFFF, 1, GSI2), + PRT_ENTRY(0x0019FFFF, 2, GSI3), + PRT_ENTRY(0x0019FFFF, 3, GSI0), + + PRT_ENTRY(0x001AFFFF, 0, GSI2), + PRT_ENTRY(0x001AFFFF, 1, GSI3), + PRT_ENTRY(0x001AFFFF, 2, GSI0), + PRT_ENTRY(0x001AFFFF, 3, GSI1), + + PRT_ENTRY(0x001BFFFF, 0, GSI3), + PRT_ENTRY(0x001BFFFF, 1, GSI0), + PRT_ENTRY(0x001BFFFF, 2, GSI1), + PRT_ENTRY(0x001BFFFF, 3, GSI2), + + PRT_ENTRY(0x001CFFFF, 0, GSI0), + PRT_ENTRY(0x001CFFFF, 1, GSI1), + PRT_ENTRY(0x001CFFFF, 2, GSI2), + PRT_ENTRY(0x001CFFFF, 3, GSI3), + + PRT_ENTRY(0x001DFFFF, 0, GSI1), + PRT_ENTRY(0x001DFFFF, 1, GSI2), + PRT_ENTRY(0x001DFFFF, 2, GSI3), + PRT_ENTRY(0x001DFFFF, 3, GSI0), + + PRT_ENTRY(0x001EFFFF, 0, GSI2), + PRT_ENTRY(0x001EFFFF, 1, GSI3), + PRT_ENTRY(0x001EFFFF, 2, GSI0), + PRT_ENTRY(0x001EFFFF, 3, GSI1), + + PRT_ENTRY(0x001FFFFF, 0, GSI3), + PRT_ENTRY(0x001FFFFF, 1, GSI0), + PRT_ENTRY(0x001FFFFF, 2, GSI1), + PRT_ENTRY(0x001FFFFF, 3, GSI2), + }) + + // Root complex resources + Name (_CRS, ResourceTemplate () { + WordBusNumber ( // Bus numbers assigned to this root + ResourceProducer, + MinFixed, MaxFixed, PosDecode, + 0, // AddressGranularity + 0, // AddressMinimum - Minimum Bus Number + 0xff,// AddressMaximum - Maximum Bus Number + 0, // AddressTranslation - Set to 0 + 256 // RangeLength - Number of Busses + ) + + // IO to mmio window + QWordIO ( + ResourceProducer, MinFixed, + MaxFixed, PosDecode, + EntireRange, + 0x00000000, // Granularity + 0x0000, // Min Base Address + 0xffff, // Max Base Address + SBSA_PIO_BASE_ADDR, // Translate + SBSA_PIO_LENGTH // Length + ) + + DWordMemory ( // 32-bit BAR Windows + ResourceProducer, PosDecode, + MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, // Granularity + SBSA_PCIE_MMIO_BASE_ADDR, // Min Base Address + SBSA_PCIE_MMIO_END, // Max Base Address + 0, // Translate + SBSA_PCIE_MMIO_LENGTH // Length + ) + + QWordMemory ( // 64-bit BAR Windows + ResourceProducer, PosDecode, + MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, // Granularity + SBSA_PCIE_MMIO_HIGH_BASE_ADDR, // Min Base Address + SBSA_PCIE_MMIO_HIGH_END, // Max Base Address + 0, // Translate + SBSA_PCIE_MMIO_HIGH_LENGTH // Length + ) + }) // Name(_CRS) + + Device (RES0) + { + Name (_HID, "PNP0C02" /* PNP Motherboard Resources */) // _HID: Hardware ID + Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings + { + QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x0000000000000000, // Granularity + SBSA_PCIE_ECAM_BASE_ADDR, // Range Minimum + SBSA_PCIE_ECAM_END, // Range Maximum + 0x0000000000000000, // Translation Offset + SBSA_PCIE_ECAM_LENGTH, // Length + ,, , AddressRangeMemory, TypeStatic) + }) + Method (_STA) { + Return (0xF) + } + } + + // OS Control Handoff + Name (SUPP, Zero) // PCI _OSC Support Field value + Name (CTRL, Zero) // PCI _OSC Control Field value + + /* + * See [1] 6.2.10, [2] 4.5 + */ + Method (_OSC,4) { + // Check for proper UUID + If (Arg0 == ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766")) { + // Create DWord-adressable fields from the Capabilities Buffer + CreateDWordField (Arg3,0,CDW1) + CreateDWordField (Arg3,4,CDW2) + CreateDWordField (Arg3,8,CDW3) + + // Save Capabilities DWord2 & 3 + Store (CDW2,SUPP) + Store (CDW3,CTRL) + + // Only allow native hot plug control if OS supports: + // * ASPM + // * Clock PM + // * MSI/MSI-X + If ((SUPP & 0x16) != 0x16) { + CTRL &= 0x1E // Mask bit 0 (and undefined bits) + } + + // Always allow native PME, AER (no dependencies) + + // Never allow SHPC (no SHPC controller in this system) + CTRL &= 0x1D + + If (Arg1 != One) { // Unknown revision + CDW1 |= 0x08 + } + + If (CDW3 != CTRL) { // Capabilities bits were masked + CDW1 |= 0x10 + } + + // Update DWORD3 in the buffer + Store (CTRL,CDW3) + Return (Arg3) + } Else { + CDW1 |= 4 // Unrecognized UUID + Return (Arg3) + } + } // End _OSC + } + } // Scope (_SB) +} diff --git a/board/emulation/qemu-sbsa/lowlevel_init.S b/board/emulation/qemu-sbsa/lowlevel_init.S new file mode 100644 index 00000000000..c997721af95 --- /dev/null +++ b/board/emulation/qemu-sbsa/lowlevel_init.S @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2016 + * Cédric Schieli + */ + +#include + +/* + * Routine: save_boot_params (called after reset from start.S) + * Description: save ATAG/FDT address provided by the firmware at boot time + */ + +.global save_boot_params +save_boot_params: + /* The firmware provided ATAG/FDT address can be found in r2/x0 */ + adr x8, fw_dtb_pointer + str x0, [x8] + + + /* Returns */ + b save_boot_params_ret diff --git a/board/emulation/qemu-sbsa/qemu-sbsa.c b/board/emulation/qemu-sbsa/qemu-sbsa.c new file mode 100644 index 00000000000..3943c924320 --- /dev/null +++ b/board/emulation/qemu-sbsa/qemu-sbsa.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2017 Tuomas Tynkkynen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qemu-sbsa.h" + +/* Assigned in lowlevel_init.S + * Push the variable into the .data section so that it + * does not get cleared later. + */ +unsigned long __section(".data") fw_dtb_pointer; + +static struct mm_region qemu_sbsa_mem_map[] = { + { + /* Secure flash */ + .virt = SBSA_SECURE_FLASH_BASE_ADDR, + .phys = SBSA_SECURE_FLASH_BASE_ADDR, + .size = SBSA_SECURE_FLASH_LENGTH, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_INNER_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* Flash */ + .virt = SBSA_FLASH_BASE_ADDR, + .phys = SBSA_FLASH_BASE_ADDR, + .size = SBSA_FLASH_LENGTH, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_INNER_SHARE + }, { + /* Lowmem peripherals */ + .virt = SBSA_PERIPH_BASE_ADDR, + .phys = SBSA_PERIPH_BASE_ADDR, + .size = SBSA_PCIE_MMIO_BASE_ADDR - SBSA_PERIPH_BASE_ADDR, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* 32-bit address PCIE MMIO space */ + .virt = SBSA_PCIE_MMIO_BASE_ADDR, + .phys = SBSA_PCIE_MMIO_BASE_ADDR, + .size = SBSA_PCIE_MMIO_LENGTH, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* PCI-E ECAM memory area */ + .virt = SBSA_PCIE_ECAM_BASE_ADDR, + .phys = SBSA_PCIE_ECAM_BASE_ADDR, + .size = SBSA_PCIE_ECAM_LENGTH, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* Highmem PCI-E MMIO memory area */ + .virt = SBSA_PCIE_MMIO_HIGH_BASE_ADDR, + .phys = SBSA_PCIE_MMIO_HIGH_BASE_ADDR, + .size = SBSA_PCIE_MMIO_HIGH_LENGTH, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* DRAM */ + .virt = SBSA_MEM_BASE_ADDR, + .phys = SBSA_MEM_BASE_ADDR, + .size = 0x800000000000ULL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = qemu_sbsa_mem_map; + +int board_late_init(void) +{ + /* start usb so that usb keyboard can be used as input device */ + if (CONFIG_IS_ENABLED(USB_KEYBOARD)) + usb_init(); + + return 0; +} + +int board_init(void) +{ + return 0; +} + +/** + * dtb_dt_qemu - Return the address of the QEMU provided FDT. + * + * @return: Pointer to FDT or NULL on failure + */ +static void *dtb_dt_qemu(void) +{ + /* FDT might be at start of DRAM */ + if (fdt_magic(SBSA_MEM_BASE_ADDR) == FDT_MAGIC) + return (void *)(u64)SBSA_MEM_BASE_ADDR; + + /* When ARM_LINUX_KERNEL_AS_BL33 is enabled in ATF, it's passed in x0 */ + if (fw_dtb_pointer >= SBSA_MEM_BASE_ADDR && + fdt_magic(fw_dtb_pointer) == FDT_MAGIC) { + return (void *)fw_dtb_pointer; + } + + return NULL; +} + +/* + * QEMU doesn't set compatible on cpus. + * Add them to make sure the U-Boot driver properly bind. + */ +static int fdtdec_fix_cpus(void *fdt_blob) +{ + int cpus_offset, off, ret; + u64 mpidr, i = 0; + + cpus_offset = fdt_path_offset(fdt_blob, "/cpus"); + if (cpus_offset < 0) { + puts("couldn't find /cpus node\n"); + return cpus_offset; + } + + fdt_for_each_subnode(off, fdt_blob, cpus_offset) { + if (strncmp(fdt_get_name(fdt_blob, off, NULL), "cpu@", 4)) + continue; + + mpidr = 0; + ret = smc_get_mpidr(i, &mpidr); + if (ret) { + log_warning("Failed to get MPIDR for processor %lld from SMC: %d\n", + i, ret); + mpidr = i; + } + + ret = fdt_setprop_string(fdt_blob, off, "compatible", "arm,armv8"); + if (ret < 0) + return ret; + + ret = fdt_setprop_string(fdt_blob, off, "device_type", "cpu"); + if (ret < 0) + return ret; + + ret = fdt_setprop_u64(fdt_blob, off, "reg", mpidr); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +/* + * Update the GIC node when necessary and add optional ITS when it has a + * non zero base-address. + */ +static int fdtdec_fix_gic(void *fdt) +{ + u64 gic_dist_base = SBSA_GIC_DIST_BASE_ADDR; + u64 gic_redist_base = SBSA_GIC_REDIST_BASE_ADDR; + u64 gic_its_base = 0; + int offs, ret; + u64 reg[10]; + + /* Invoke SMC to get real base-address */ + smc_get_gic_dist_base(&gic_dist_base); + smc_get_gic_redist_base(&gic_redist_base); + + if ((gic_dist_base != SBSA_GIC_DIST_BASE_ADDR) || + (gic_redist_base != SBSA_GIC_REDIST_BASE_ADDR)) { + offs = fdt_path_offset(fdt, "/interrupt-controller"); + if (offs < 0) { + puts("couldn't find /interrupt-controller node\n"); + return offs; + } + + reg[0] = cpu_to_fdt64(gic_dist_base); + reg[1] = cpu_to_fdt64((u64)SBSA_GIC_DIST_LENGTH); + reg[2] = cpu_to_fdt64(gic_redist_base); + reg[3] = cpu_to_fdt64((u64)SBSA_GIC_REDIST_LENGTH); + reg[4] = cpu_to_fdt64(0); + reg[5] = cpu_to_fdt64(0); + reg[6] = cpu_to_fdt64(SBSA_GIC_HBASE_ADDR); + reg[7] = cpu_to_fdt64((u64)SBSA_GIC_HBASE_LENGTH); + reg[8] = cpu_to_fdt64(SBSA_GIC_VBASE_ADDR); + reg[9] = cpu_to_fdt64((u64)SBSA_GIC_VBASE_LENGTH); + + ret = fdt_setprop_inplace(fdt, offs, "reg", reg, sizeof(reg)); + } + + smc_get_gic_its_base(&gic_its_base); + + if (gic_its_base != 0) { + offs = fdt_path_offset(fdt, "/its"); + if (offs < 0) + return offs; + + ret = fdt_setprop_string(fdt, offs, "status", "okay"); + if (ret < 0) + return ret; + + reg[0] = cpu_to_fdt64(gic_its_base); + reg[1] = 0; + + ret = fdt_setprop(fdt, offs, "reg", reg, sizeof(u64) * 2); + if (ret < 0) + return ret; + } + + return 0; +} + +int fdtdec_board_setup(const void *fdt_blob) +{ + void *qemu_fdt; + int ret; + + /* + * Locate the QEMU provided DTB that contains the CPUs and amount of DRAM. + */ + qemu_fdt = dtb_dt_qemu(); + if (!qemu_fdt) { + log_err("QEMU FDT not found\n"); + return -ENODEV; + } + + ret = fdt_increase_size((void *)fdt_blob, 1024 + fdt_totalsize(qemu_fdt)); + if (ret) + return -ENOMEM; + + /* + * Merge the QEMU DTB as overlay into the U-Boot provided DTB. + */ + ret = fdt_overlay_apply_node((void *)fdt_blob, 0, qemu_fdt, 0); + if (ret < 0) + log_err("Failed to apply overlay: %d\n", ret); + + /* Fix QEMU nodes to make sure U-Boot drivers are properly working */ + ret = fdtdec_fix_cpus((void *)fdt_blob); + if (ret < 0) + log_err("Failed to fix CPUs in FDT: %d\n", ret); + + ret = fdtdec_fix_gic((void *)fdt_blob); + if (ret < 0) + log_err("Failed to fix INTC in FDT: %d\n", ret); + + return 0; +} + +int misc_init_r(void) +{ + return env_set_hex("fdt_addr", (uintptr_t)gd->fdt_blob); +} + +void reset_cpu(void) +{ +} + +int dram_init(void) +{ + return fdtdec_setup_mem_size_base(); +} \ No newline at end of file diff --git a/board/emulation/qemu-sbsa/qemu-sbsa.env b/board/emulation/qemu-sbsa/qemu-sbsa.env new file mode 100644 index 00000000000..88fdb0ec1c5 --- /dev/null +++ b/board/emulation/qemu-sbsa/qemu-sbsa.env @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +/* environment for qemu-arm and qemu-arm64 */ + +stdin=serial,usbkbd +stdout=serial,vidconsole +stderr=serial,vidconsole +fdt_high=0xffffffffffffffff +initrd_high=0xffffffffffffffff +scriptaddr=0x100000300000 +pxefile_addr_r=0x10000400000 +kernel_addr_r=0x10000200000 +ramdisk_addr_r=0x10001000000 +boot_targets=qfw usb scsi virtio nvme dhcp diff --git a/board/emulation/qemu-sbsa/qemu-sbsa.h b/board/emulation/qemu-sbsa/qemu-sbsa.h new file mode 100644 index 00000000000..391a70bdc40 --- /dev/null +++ b/board/emulation/qemu-sbsa/qemu-sbsa.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2024 9elements GmbH + */ + +/** + * smc_get_mpidr() - Call into SMC and get the MPIDR for given CPU + * + * @id: CPU index + * @mpidr: Pointer where to place the MPIDR + * @return 0 if OK, other -ve on error + */ +int smc_get_mpidr(unsigned long id, u64 *mpidr); + +/** + * smc_get_gic_dist_base() - Call into SMC and get GIC dist base address + * + * @mpidr: Pointer where to place the base address + * @return 0 if OK, other -ve on error + */ +int smc_get_gic_dist_base(u64 *base); + +/** + * smc_get_gic_redist_base() - Call into SMC and get the GIC redistributor + * base address + * + * @mpidr: Pointer where to place the base address + * @return 0 if OK, other -ve on error + */ +int smc_get_gic_redist_base(u64 *base); + +/** + * smc_get_gic_its_base() - Call into SMC and get the ITS base address + * + * @mpidr: Pointer where to place the base address + * @return 0 if OK, other -ve on error + */ +int smc_get_gic_its_base(u64 *base); diff --git a/board/emulation/qemu-sbsa/smc.c b/board/emulation/qemu-sbsa/smc.c new file mode 100644 index 00000000000..9a2d091bea6 --- /dev/null +++ b/board/emulation/qemu-sbsa/smc.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024 9elements GmbH + */ + +#include +#include +#include +#include + +#define SMC_SIP_FUNCTION_ID(n) (0xC2000000 | (n)) + +#define SIP_SVC_VERSION SMC_SIP_FUNCTION_ID(1) +#define SIP_SVC_GET_GIC SMC_SIP_FUNCTION_ID(100) +#define SIP_SVC_GET_GIC_ITS SMC_SIP_FUNCTION_ID(101) +#define SIP_SVC_GET_CPU_COUNT SMC_SIP_FUNCTION_ID(200) +#define SIP_SVC_GET_CPU_NODE SMC_SIP_FUNCTION_ID(201) +#define SIP_SVC_GET_MEMORY_NODE_COUNT SMC_SIP_FUNCTION_ID(300) +#define SIP_SVC_GET_MEMORY_NODE SMC_SIP_FUNCTION_ID(301) + +int smc_get_mpidr(unsigned long id, u64 *mpidr) +{ + struct arm_smccc_res res; + + res.a0 = ~0; + arm_smccc_smc(SIP_SVC_GET_CPU_NODE, id, 0, 0, 0, 0, 0, 0, &res); + + if (!res.a0) + *mpidr = res.a2; + + return res.a0; +} + +int smc_get_gic_dist_base(u64 *base) +{ + struct arm_smccc_res res; + + res.a0 = ~0; + arm_smccc_smc(SIP_SVC_GET_GIC, 0, 0, 0, 0, 0, 0, 0, &res); + + if (!res.a0) + *base = res.a1; + + return res.a0; +} + +int smc_get_gic_redist_base(u64 *base) +{ + struct arm_smccc_res res; + + res.a0 = ~0; + arm_smccc_smc(SIP_SVC_GET_GIC, 0, 0, 0, 0, 0, 0, 0, &res); + + if (!res.a0) + *base = res.a2; + + return res.a0; +} + +int smc_get_gic_its_base(u64 *base) +{ + struct arm_smccc_res res; + + res.a0 = ~0; + arm_smccc_smc(SIP_SVC_GET_GIC_ITS, 0, 0, 0, 0, 0, 0, 0, &res); + + if (!res.a0) + *base = res.a1; + + return res.a0; +} diff --git a/configs/qemu-arm-sbsa_defconfig b/configs/qemu-arm-sbsa_defconfig new file mode 100644 index 00000000000..69195afeedf --- /dev/null +++ b/configs/qemu-arm-sbsa_defconfig @@ -0,0 +1,12 @@ +CONFIG_ARM=y +CONFIG_ARCH_QEMU=y +CONFIG_TARGET_QEMU_ARM_SBSA=y +CONFIG_USE_BOOTCOMMAND=y +CONFIG_BOOTCOMMAND="bootflow scan" +CONFIG_EFI_PARTITION=y +CONFIG_PARTITION_TYPE_GUID=y +CONFIG_EFI_MEDIA=y +CONFIG_FS_FAT=y +CONFIG_EFI_VARIABLE_NO_STORE=y +CONFIG_BLOBLIST_ALLOC=y +CONFIG_BLOBLIST_SIZE_RELOC=0x20000 diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst index 98a0b26ad24..0419d724150 100644 --- a/doc/board/emulation/index.rst +++ b/doc/board/emulation/index.rst @@ -13,5 +13,6 @@ Emulation qemu-mips qemu-ppce500 qemu-riscv + qemu-sbsa qemu-x86 qemu-xtensa diff --git a/doc/board/emulation/qemu-sbsa.rst b/doc/board/emulation/qemu-sbsa.rst new file mode 100644 index 00000000000..fe1dc3249e4 --- /dev/null +++ b/doc/board/emulation/qemu-sbsa.rst @@ -0,0 +1,98 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2024, Patrick Rudolph + +QEMU ARM SBSA +============= + +QEMU for ARM supports Arm Server Base System Architecture Reference board, +short 'sbsa-ref' that utilizes ACPI over FDT. This document describes how to run +U-Boot under it. Only AArch64 is supported. + +The 'sbsa' platform provides the following as the basic functionality: + + - A freely configurable amount of CPU cores + - U-Boot loaded and executing in the emulated flash at address 0x10000000 + - No device tree blob + - A freely configurable amount of RAM + - A PL011 serial port + - An ARMv7/ARMv8 architected timer + - PSCI for rebooting the system + - A generic ECAM-based PCI host controller + +Additionally, a number of optional peripherals can be added to the PCI bus. + +Compile ARM Trusted Firmware (ATF) +---------------------------------- + +Get and Build the ARM Trusted firmware +-------------------------------------- + +Note: srctree is U-Boot source directory +Get ATF from: https://github.com/ARM-software/arm-trusted-firmware + +.. code-block:: bash + + git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git tfa + cd tfa + make CROSS_COMPILE=aarch64-linux-gnu- all fip \ + ARM_LINUX_KERNEL_AS_BL33=1 DEBUG=1 PLAT=qemu_sbsa + +Copy the resulting FIP and BL1 binary + +.. code-block:: bash + + cp build/qemu_sbsa/debug/fip.bin ../ + cp build/qemu_sbsa/debug/bl1.bin ../ + +Building U-Boot +--------------- +Set the CROSS_COMPILE environment variable as usual, and run: + +.. code-block:: bash + + make qemu-arm-sbsa_defconfig + make + +Running U-Boot +-------------- +The minimal QEMU command line to get U-Boot up and running is: + +.. code-block:: bash + + qemu-system-aarch64 -machine sbsa-ref -nographic -cpu cortex-a57 \ + -pflash secure-world.rom \ + -pflash unsecure-world.rom + +Note that for some odd reason qemu-system-aarch64 needs to be explicitly +told to use a 64-bit CPU or it will boot in 32-bit mode. The -nographic argument +ensures that output appears on the terminal. Use Ctrl-A X to quit. + +Booting distros +--------------- + +It is possible to install and boot a standard Linux distribution using +sbsa by setting up a root disk:: + +.. code-block:: bash + + qemu-img create root.img 20G + +then using the installer to install. For example, with Debian 12:: + +.. code-block:: bash + + qemu-system-aarch64 \ + -machine sbsa-ref -cpu cortex-a57 -m 4G -smp 4 \ + -pflash secure-world.rom \ + -pflash unsecure-world.rom \ + -device virtio-rng-pci \ + -device usb-kbd -device usb-tablet \ + -cdrom debian-12.0.0-arm64-netinst.iso \ + -hda root.img + +Debug UART +---------- + +The debug UART on the ARM sbsa board uses these settings:: + + CONFIG_DEBUG_UART=y diff --git a/doc/develop/driver-model/virtio.rst b/doc/develop/driver-model/virtio.rst index 8ac9c94cafe..31b94d04675 100644 --- a/doc/develop/driver-model/virtio.rst +++ b/doc/develop/driver-model/virtio.rst @@ -34,6 +34,7 @@ The following QEMU targets are supported. - qemu_arm_defconfig - qemu_arm64_defconfig + - qemu-arm-sbsa_defconfig - qemu-riscv32_defconfig - qemu-riscv64_defconfig - qemu-x86_defconfig diff --git a/include/configs/qemu-sbsa.h b/include/configs/qemu-sbsa.h new file mode 100644 index 00000000000..aff78160e12 --- /dev/null +++ b/include/configs/qemu-sbsa.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2024 9elements GmbH + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +/* Physical memory map */ + +/* SECURE_FLASH */ +#define SBSA_SECURE_FLASH_BASE_ADDR 0x00000000 +#define SBSA_SECURE_FLASH_LENGTH 0x10000000 + +/* FLASH */ +#define SBSA_FLASH_BASE_ADDR 0x10000000 +#define SBSA_FLASH_LENGTH 0x10000000 + +/* PERIPH */ +#define SBSA_PERIPH_BASE_ADDR 0x40000000 + +/* GIC_DIST */ +#define SBSA_GIC_DIST_BASE_ADDR 0x40060000 +#define SBSA_GIC_DIST_LENGTH 0x00020000 + +#define SBSA_GIC_VBASE_ADDR 0x2c020000 +#define SBSA_GIC_VBASE_LENGTH 0x00010000 + +#define SBSA_GIC_HBASE_ADDR 0x2c010000 +#define SBSA_GIC_HBASE_LENGTH 0x00010000 + +/* GIC_REDIST */ +#define SBSA_GIC_REDIST_BASE_ADDR 0x40080000 +#define SBSA_GIC_REDIST_LENGTH 0x04000000 + +/* GIC_ITS */ +#define SBSA_GIC_ITS_BASE_ADDR 0x44081000 + +/* UART */ +#define SBSA_UART_BASE_ADDR 0x60000000 +#define SBSA_UART_LENGTH 0x00001000 + +/* SMMU */ +#define SBSA_SMMU_BASE_ADDR 0x60050000 + +/* SATA */ +#define SBSA_AHCI_BASE_ADDR 0x60100000 +#define SBSA_AHCI_LENGTH 0x00010000 + +/* xHCI */ +#define SBSA_XHCI_BASE_ADDR 0x60110000 +#define SBSA_XHCI_LENGTH 0x00010000 + +/* PIO */ +#define SBSA_PIO_BASE_ADDR 0x7fff0000 +#define SBSA_PIO_LENGTH 0x00010000 + +/* PCIE_MMIO */ +#define SBSA_PCIE_MMIO_BASE_ADDR 0x80000000 +#define SBSA_PCIE_MMIO_LENGTH 0x70000000 +#define SBSA_PCIE_MMIO_END 0xefffffff + +/* PCIE_ECAM */ +#define SBSA_PCIE_ECAM_BASE_ADDR 0xf0000000 +#define SBSA_PCIE_ECAM_LENGTH 0x10000000 +#define SBSA_PCIE_ECAM_END 0xffffffff + +/* PCIE_MMIO_HIGH */ +#ifdef __ACPI__ +#define SBSA_PCIE_MMIO_HIGH_BASE_ADDR 0x100000000 +#define SBSA_PCIE_MMIO_HIGH_LENGTH 0xFF00000000 +#define SBSA_PCIE_MMIO_HIGH_END 0xFFFFFFFFFF +#else +#define SBSA_PCIE_MMIO_HIGH_BASE_ADDR 0x100000000ULL +#define SBSA_PCIE_MMIO_HIGH_LENGTH 0xFF00000000ULL +#define SBSA_PCIE_MMIO_HIGH_END 0xFFFFFFFFFFULL +#endif + +/* MEM */ +#ifdef __ACPI__ +#define SBSA_MEM_BASE_ADDR 0x10000000000 +#else +#define SBSA_MEM_BASE_ADDR 0x10000000000ULL +#endif + +#define CFG_SYS_INIT_RAM_ADDR SBSA_MEM_BASE_ADDR +#define CFG_SYS_INIT_RAM_SIZE 0x1000000 + +#endif /* __CONFIG_H */ -- cgit v1.3.1 From 34bfe8eff895b864247d923ce37110a9053592ee Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Oct 2024 15:20:13 +0200 Subject: arm: cpu: Add ACPI parking protocol support On Arm platforms that use ACPI they cannot rely on the "spin-table" CPU bringup usually defined in the FDT. Thus implement the 'ACPI Multi-processor Startup for ARM Platforms', also referred to as 'ACPI parking protocol'. The ACPI parking protocol works similar to the spin-table mechanism, but the specification also covers lots of shortcomings of the spin-table implementations. Every CPU defined in the ACPI MADT table has it's own 4K page where the spinloop code and the OS mailbox resides. When selected the U-Boot board code must make sure that the secondary CPUs enter u-boot after relocation as well, so that they can enter the spinloop code residing in the ACPI parking protocol pages. The OS will then write to the mailbox and generate an IPI to release the CPUs from the spinloop code. For now it's only implemented on ARMv8, but can easily be extended to other platforms, like ARMv7. TEST: Boots all CPUs on qemu-system-aarch64 -machine raspi4b Signed-off-by: Patrick Rudolph Reviewed-by: Simon Glass Cc: Simon Glass Cc: Tom Rini --- arch/arm/cpu/armv8/Makefile | 1 + arch/arm/cpu/armv8/acpi_park_v8.S | 113 +++++++++++++++++++++++++++++ arch/arm/include/asm/acpi_table.h | 32 ++++++++ arch/arm/lib/acpi_table.c | 149 ++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_table.h | 10 +++ include/bloblist.h | 1 + lib/Kconfig | 16 ++++ lib/acpi/acpi_table.c | 4 + 8 files changed, 326 insertions(+) create mode 100644 arch/arm/cpu/armv8/acpi_park_v8.S (limited to 'include') diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile index bba4f570dbb..4f4368ff8c7 100644 --- a/arch/arm/cpu/armv8/Makefile +++ b/arch/arm/cpu/armv8/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_ARM_SMCCC) += smccc-call.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMV8_SPIN_TABLE) += spin_table.o spin_table_v8.o +obj-$(CONFIG_ACPI_PARKING_PROTOCOL) += acpi_park_v8.o else obj-$(CONFIG_ARCH_SUNXI) += fel_utils.o endif diff --git a/arch/arm/cpu/armv8/acpi_park_v8.S b/arch/arm/cpu/armv8/acpi_park_v8.S new file mode 100644 index 00000000000..0bc605d28df --- /dev/null +++ b/arch/arm/cpu/armv8/acpi_park_v8.S @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2024 9elements GmbH + * Author: Patrick Rudolph + * + * This file provides ARMv8 specific code for the generic part of the + * ACPI parking protocol implementation. It contains the spinning code + * that will be installed into the parking protocol and it points the + * secondary CPUs to their own parking protocol page once it has been + * set up by the generic part. + */ + +#include +#include + +/* Filled by C code */ +.global acpi_pp_tables +acpi_pp_tables: + .quad 0 + +.global acpi_pp_etables +acpi_pp_etables: + .quad 0 + +/* Read by C code */ +.global acpi_pp_code_size +acpi_pp_code_size: + .word __secondary_pp_code_end - __secondary_pp_code_start + +.global acpi_pp_secondary_jump +ENTRY(acpi_pp_secondary_jump) +0: + /* + * Cannot use atomic operations since the MMU and D-cache + * might be off. Use the MPIDR instead to find the spintable. + */ + + /* Check if parking protocol table is ready */ + ldr x1, =acpi_pp_tables + ldr x0, [x1] + cbnz x0, 0f + wfe + b 0b + +0: /* Get end of page tables in x3 */ + ldr x1, =acpi_pp_etables + ldr x3, [x1] + + /* Get own CPU ID in w2 */ + mrs x2, mpidr_el1 + lsr x9, x2, #32 + bfi x2, x9, #24, #8 /* w2 is aff3:aff2:aff1:aff0 */ + +0: /* Loop over all parking protocol pages */ + cmp x0, x3 + b.ge hlt + + /* Fetch CPU_ID from current page */ + ldr x1, [x0, #ACPI_PP_CPU_ID_OFFSET] + lsr x9, x1, #32 + bfi x1, x9, #24, #8 /* w1 is aff3:aff2:aff1:aff0 */ + + /* Compare CPU_IDs */ + cmp w1, w2 + b.eq 0f + + add x0, x0, #ACPI_PP_PAGE_SIZE + b 0b + +hlt: wfi + b hlt /* Should never happen. */ + +0: /* x0 points to the 4K-aligned, parking protocol page */ + add x2, x0, #ACPI_PP_CPU_CODE_OFFSET + + /* Jump to spin code in own parking protocol page */ + br x2 +ENDPROC(acpi_pp_secondary_jump) + +.align 8 +__secondary_pp_code_start: +.global acpi_pp_code_start +ENTRY(acpi_pp_code_start) + /* x0 points to the 4K-aligned, parking protocol page */ + + /* Prepare defines for spinning code */ + mov w3, #ACPI_PP_CPU_ID_INVALID + mov x2, #ACPI_PP_JMP_ADR_INVALID + + /* Mark parking protocol page as ready */ + str w3, [x0, #ACPI_PP_CPU_ID_OFFSET] + dsb sy + +0: wfe + ldr w1, [x0, #ACPI_PP_CPU_ID_OFFSET] + + /* Check CPU ID is valid */ + cmp w1, w3 + b.eq 0b + + /* Check jump address valid */ + ldr x1, [x0, #ACPI_PP_CPU_JMP_OFFSET] + cmp x1, x2 + b.eq 0b + + /* Clear jump address before jump */ + str x2, [x0, #ACPI_PP_CPU_JMP_OFFSET] + dsb sy + + br x1 +ENDPROC(acpi_pp_code_start) + /* Secondary Boot Code ends here */ +__secondary_pp_code_end: diff --git a/arch/arm/include/asm/acpi_table.h b/arch/arm/include/asm/acpi_table.h index c65eabe8374..7386f55ad34 100644 --- a/arch/arm/include/asm/acpi_table.h +++ b/arch/arm/include/asm/acpi_table.h @@ -4,6 +4,7 @@ #define __ASM_ACPI_TABLE_H__ #ifndef __ACPI__ +#ifndef __ASSEMBLY__ #include @@ -109,7 +110,38 @@ int acpi_pptt_add_cache(struct acpi_ctx *ctx, const u32 flags, const u32 sets, const u8 assoc, const u8 attributes, const u16 line_size); +/* Multi-processor Startup for ARM Platforms */ +/** + * struct acpi_pp_page - MP startup handshake mailbox + * + * Defines a 4096 byte memory region that is used for starting secondary CPUs on + * an Arm system that follows the "Multi-processor Startup for ARM Platforms" spec. + * + * @cpu_id: MPIDR as returned by the Multiprocessor Affinity Register. + * On 32bit Arm systems the upper bits are unused. + * @jumping_address: On 32bit Arm systems the address must be below 4 GiB + * @os_reserved: Reserved for OS use. Firmware must not access this memory. + * @spinning_code: Reserved for firmware use. OS must not access this memory. + * The spinning code will be installed by firmware and the secondary + * CPUs will enter it before the control is handed over to the OS. + */ +struct acpi_pp_page { + u64 cpu_id; + u64 jumping_address; + u8 os_reserved[2032]; + u8 spinning_code[2048]; +} __packed; + #endif /* !__ASSEMBLY__ */ #endif /* !__ACPI__ */ +/* Multi-processor Startup for ARM Platforms defines */ +#define ACPI_PP_CPU_ID_INVALID 0xffffffff +#define ACPI_PP_JMP_ADR_INVALID 0 +#define ACPI_PP_PAGE_SIZE 4096 +#define ACPI_PP_CPU_ID_OFFSET 0 +#define ACPI_PP_CPU_JMP_OFFSET 8 +#define ACPI_PP_CPU_CODE_OFFSET 2048 +#define ACPI_PP_VERSION 1 + #endif /* __ASM_ACPI_TABLE_H__ */ diff --git a/arch/arm/lib/acpi_table.c b/arch/arm/lib/acpi_table.c index e0add049261..f760b7fbce4 100644 --- a/arch/arm/lib/acpi_table.c +++ b/arch/arm/lib/acpi_table.c @@ -7,12 +7,48 @@ #define LOG_CATEGORY LOGC_ACPI +#include +#include +#include +#include #include +#include #include #include #include +#include #include #include +#include +#include + +/* defined in assembly file */ +/** + * acpi_pp_code_size - Spinloop code size * + */ +extern u16 acpi_pp_code_size; + +/** + * acpi_pp_tables - Start of ACPI PP tables. + */ +extern ulong acpi_pp_tables; + +/** + * acpi_pp_etables - End of ACPI PP tables. + */ +extern ulong acpi_pp_etables; + +/** + * acpi_pp_code_start() - Spinloop code + * + * Architectural spinloop code to be installed in each parking protocol + * page. The spinloop code must be less than 2048 bytes. + * + * The spinloop code will be entered after calling + * acpi_parking_protocol_install(). + * + */ +void acpi_pp_code_start(void); void acpi_write_madt_gicc(struct acpi_madt_gicc *gicc, uint cpu_num, uint perf_gsiv, ulong phys_base, ulong gicv, @@ -125,3 +161,116 @@ void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) return ctx->current; } + +/** + * acpi_write_pp_setup_one_page() - Fill out one page used by the PP + * + * Fill out the struct acpi_pp_page to contain the spin-loop + * code and the mailbox area. After this function the page is ready for + * the secondary core's to enter the spin-loop code. + * + * @page: Pointer to current parking protocol page + * @gicc: Pointer to corresponding GICC sub-table + */ +static void acpi_write_pp_setup_one_page(struct acpi_pp_page *page, + struct acpi_madt_gicc *gicc) +{ + void *reloc; + + /* Update GICC. Mark parking protocol as available. */ + gicc->parking_proto = ACPI_PP_VERSION; + gicc->parked_addr = virt_to_phys(page); + + /* Prepare parking protocol page */ + memset(page, '\0', sizeof(struct acpi_pp_page)); + + /* Init mailbox. Set MPIDR so core's will find their page. */ + page->cpu_id = gicc->mpidr; + page->jumping_address = ACPI_PP_JMP_ADR_INVALID; + + /* Relocate spinning code */ + reloc = &page->spinning_code[0]; + + log_debug("Relocating spin table from %lx to %lx (size %x)\n", + (ulong)&acpi_pp_code_start, (ulong)reloc, acpi_pp_code_size); + memcpy(reloc, &acpi_pp_code_start, acpi_pp_code_size); + + if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) + flush_dcache_range((unsigned long)page, + (unsigned long)(page + 1)); +} + +void acpi_write_park(struct acpi_madt *madt) +{ + struct acpi_pp_page *start, *page; + struct acpi_madt_gicc *gicc; + int ret, i, ncpus = 0; + + /* + * According to the "Multi-processor Startup for ARM Platforms": + * - Every CPU as specified by MADT GICC has it's own 4K page + * - Every page is divided into two sections: OS and FW reserved + * - Memory occupied by "Parking Protocol" must be marked 'Reserved' + * - Spinloop code should reside in FW reserved 2048 bytes + * - Spinloop code will check the mailbox in OS reserved area + */ + + if (acpi_pp_code_size > sizeof(page->spinning_code)) { + log_err("Spinning code too big to fit: %d\n", + acpi_pp_code_size); + return; + } + + /* Count all MADT GICCs including BSP */ + for (i = sizeof(struct acpi_madt); i < madt->header.length; + i += gicc->length) { + gicc = (struct acpi_madt_gicc *)((void *)madt + i); + if (gicc->type != ACPI_APIC_GICC) + continue; + ncpus++; + } + log_debug("Found %#x GICCs in MADT\n", ncpus); + + /* Allocate pages linearly due to assembly code requirements */ + start = bloblist_add(BLOBLISTT_ACPI_PP, ACPI_PP_PAGE_SIZE * ncpus, + ilog2(SZ_4K)); + if (!start) { + log_err("Failed to allocate memory for ACPI-parking-protocol pages\n"); + return; + } + log_debug("Allocated parking protocol at %p\n", start); + page = start; + + if (IS_ENABLED(CONFIG_EFI_LOADER)) { + /* Default mapping is 'BOOT CODE'. Mark as reserved instead. */ + ret = efi_add_memory_map((u64)(uintptr_t)start, + ncpus * ACPI_PP_PAGE_SIZE, + EFI_RESERVED_MEMORY_TYPE); + + if (ret) + log_err("Reserved memory mapping failed addr %p size %x\n", + start, ncpus * ACPI_PP_PAGE_SIZE); + } + + /* Prepare the parking protocol pages */ + for (i = sizeof(struct acpi_madt); i < madt->header.length; + i += gicc->length) { + gicc = (struct acpi_madt_gicc *)((void *)madt + i); + if (gicc->type != ACPI_APIC_GICC) + continue; + + acpi_write_pp_setup_one_page(page++, gicc); + } + + acpi_pp_etables = virt_to_phys(start) + + ACPI_PP_PAGE_SIZE * ncpus; + acpi_pp_tables = virt_to_phys(start); + + /* Make sure other cores see written value in memory */ + if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) + flush_dcache_all(); + + /* Send an event to wake up the secondary CPU. */ + asm("dsb ishst\n" + "sev"); +} diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 07c5f65d753..b8b1f1338c6 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -1237,6 +1237,16 @@ int acpi_iort_add_smmu_v3(struct acpi_ctx *ctx, */ void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx); +/** + * acpi_write_park() - Installs the ACPI parking protocol. + * + * Sets up the ACPI parking protocol and installs the spinning code for + * secondary CPUs. + * + * @madt: The MADT to update + */ +void acpi_write_park(struct acpi_madt *madt); + /** * acpi_get_rsdp_addr() - get ACPI RSDP table address * diff --git a/include/bloblist.h b/include/bloblist.h index b0706b5637d..ff32d3fecfd 100644 --- a/include/bloblist.h +++ b/include/bloblist.h @@ -110,6 +110,7 @@ enum bloblist_tag_t { BLOBLISTT_ACPI_TABLES = 4, BLOBLISTT_TPM_EVLOG = 5, BLOBLISTT_TPM_CRB_BASE = 6, + BLOBLISTT_ACPI_PP = 7, /* Standard area to allocate blobs used across firmware components */ BLOBLISTT_AREA_FIRMWARE = 0x10, diff --git a/lib/Kconfig b/lib/Kconfig index 2e0fc1bc8f5..6ad079a1284 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -315,6 +315,22 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring. +config ACPI_PARKING_PROTOCOL + bool "Support ACPI parking protocol method" + depends on GENERATE_ACPI_TABLE + depends on ARMV8_MULTIENTRY + depends on BLOBLIST_TABLES + default y if !SEC_FIRMWARE_ARMV8_PSCI && !ARMV8_PSCI + help + Say Y here to support "ACPI parking protocol" enable method + for booting Linux. + + To use this feature, you must do: + - Bring secondary CPUs into U-Boot proper in a board-specific + manner. This must be done *after* relocation. Otherwise, the + secondary CPUs will spin in unprotected memory-area because the + master CPU protects the relocated spin code. + config SPL_TINY_MEMSET bool "Use a very small memset() in SPL" depends on SPL diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index e6ebffcf1b0..6473d95c102 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -297,6 +297,10 @@ int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry) /* (Re)calculate length and checksum */ header->length = (uintptr_t)current - (uintptr_t)madt; + + if (IS_ENABLED(CONFIG_ACPI_PARKING_PROTOCOL)) + acpi_write_park(madt); + header->checksum = table_compute_checksum((void *)madt, header->length); acpi_add_table(ctx, madt); ctx->current = (void *)madt + madt->header.length; -- cgit v1.3.1