Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/33255
Change subject: drivers/ipmi: Add chip ops ......................................................................
drivers/ipmi: Add chip ops
* Add SPMI ACPI code * Add chips ops for IPMI KCS. * Set firmware version over KCS. * Get IPMI version over KCS. * Generate ACPI SPMI for IPMI KCS. * Generate SMBIOS type 38 for IPMI KCS.
Change-Id: I73cbd2058ccdc5395baf244f31345a85eb0047d7 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/arch/x86/acpi.c M src/arch/x86/include/arch/acpi.h M src/drivers/ipmi/Makefile.inc A src/drivers/ipmi/chip.h M src/drivers/ipmi/ipmi_kcs.h A src/drivers/ipmi/ipmi_kcs_ops.c 6 files changed, 337 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/55/33255/1
diff --git a/src/arch/x86/acpi.c b/src/arch/x86/acpi.c index bf9813c..8938147 100644 --- a/src/arch/x86/acpi.c +++ b/src/arch/x86/acpi.c @@ -772,6 +772,53 @@ header->checksum = acpi_checksum((void *)vfct, header->length); }
+void acpi_create_ipmi(struct device *device, + struct acpi_spmi *spmi, + const u16 ipmi_revision, + const acpi_addr_t *addr, + const enum acpi_ipmi_interface_type type, + const s8 gpe_interrupt, + const u32 apic_interrupt) +{ + acpi_header_t *header = &(spmi->header); + memset((void *)spmi, 0, sizeof(struct acpi_spmi)); + + /* Fill out header fields. */ + memcpy(header->signature, "SPMI", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->asl_compiler_revision = asl_revision; + header->length = sizeof(struct acpi_spmi); + header->revision = get_acpi_table_revision(SPMI); + + if (device->path.type == DEVICE_PATH_PCI) { + spmi->pci_device_flag = 1; + spmi->pci_bus = device->bus->secondary; + spmi->pci_device = device->path.pci.devfn >> 3; + spmi->pci_function = device->path.pci.devfn & 0x7; + } + + spmi->base_address = *addr; + spmi->specification_revision = ipmi_revision; + + spmi->interface_type = type; + + if (gpe_interrupt >= 0 && gpe_interrupt < 32) { + spmi->gpe = gpe_interrupt; + spmi->interrupt_type |= 1; + } + if (apic_interrupt > 0) { + spmi->global_system_interrupt = apic_interrupt; + spmi->interrupt_type |= 2; + } + + /* (Re)calculate length and checksum. */ + header->length = sizeof(struct acpi_spmi); + header->checksum = acpi_checksum((void *)spmi, header->length); +} + void acpi_create_ivrs(acpi_ivrs_t *ivrs, unsigned long (*acpi_fill_ivrs)(acpi_ivrs_t *ivrs_struct, unsigned long current)) @@ -1490,6 +1537,8 @@ return 1; case SLIT: /* ACPI 2.0 upto 6.3: 1 */ return 1; + case SPMI: /* IMPI 2.0 */ + return 5; case HPET: /* Currently 1. Table added in ACPI 2.0. */ return 1; case VFCT: /* ACPI 2.0/3.0/4.0: 1 */ diff --git a/src/arch/x86/include/arch/acpi.h b/src/arch/x86/include/arch/acpi.h index dbf46a9..a864071 100644 --- a/src/arch/x86/include/arch/acpi.h +++ b/src/arch/x86/include/arch/acpi.h @@ -82,7 +82,7 @@ BERT, DBG2, DMAR, DSDT, FACS, FADT, HEST, HPET, IVRS, MADT, MCFG, RSDP, RSDT, SLIT, SRAT, SSDT, TCPA, TPM2, XSDT, ECDT, /* Additional proprietary tables used by coreboot */ - VFCT, NHLT + VFCT, NHLT, SPMI };
/* RSDP (Root System Description Pointer) */ @@ -782,6 +782,39 @@ UPC_TYPE_HUB };
+enum acpi_ipmi_interface_type { + IPMI_INTERFACE_RESERVED, + IPMI_INTERFACE_KCS, + IPMI_INTERFACE_SMIC, + IPMI_INTERFACE_BT, + IPMI_INTERFACE_SSIF, +}; + +/* ACPI IPMI 2.0 */ +struct acpi_spmi { + struct acpi_table_header header; + u8 interface_type; + u8 reserved; + u16 specification_revision; + u8 interrupt_type; + u8 gpe; + u8 reserved2; + u8 pci_device_flag; + + u32 global_system_interrupt; + acpi_addr_t base_address; + union { + struct { + u8 pci_segment_group; + u8 pci_bus; + u8 pci_device; + u8 pci_function; + }; + u8 uid[4]; + }; + u8 reserved3; +} __packed ; + unsigned long fw_cfg_acpi_tables(unsigned long start);
/* These are implemented by the target port or north/southbridge. */ @@ -834,6 +867,14 @@ struct acpi_vfct *vfct_struct, unsigned long current));
+void acpi_create_ipmi(struct device *device, + struct acpi_spmi *spmi, + const u16 ipmi_revision, + const acpi_addr_t *addr, + const enum acpi_ipmi_interface_type type, + const s8 gpe_interrupt, + const u32 apic_interrupt); + void acpi_create_ivrs(acpi_ivrs_t *ivrs, unsigned long (*acpi_fill_ivrs)(acpi_ivrs_t *ivrs_struct, unsigned long current)); diff --git a/src/drivers/ipmi/Makefile.inc b/src/drivers/ipmi/Makefile.inc index e9e7ff3..a29c2e2 100644 --- a/src/drivers/ipmi/Makefile.inc +++ b/src/drivers/ipmi/Makefile.inc @@ -1 +1,2 @@ ramstage-$(CONFIG_IPMI_KCS) += ipmi_kcs.c +ramstage-$(CONFIG_IPMI_KCS) += ipmi_kcs_ops.c diff --git a/src/drivers/ipmi/chip.h b/src/drivers/ipmi/chip.h new file mode 100644 index 0000000..eb8b4e6 --- /dev/null +++ b/src/drivers/ipmi/chip.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Patrick Rudolph siro@das-labor.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _IMPI_CHIP_H_ +#define _IPMI_CHIP_H_ + +struct drivers_ipmi_config { + u8 bmc_i2c_address; + u8 have_nv_storage; + u8 nv_storage_device_address; + u8 have_gpe; + u8 gpe_interrupt; + u8 have_apic; + u32 apic_interrupt; +}; + +#endif /* _IMPI_CHIP_H_ */ diff --git a/src/drivers/ipmi/ipmi_kcs.h b/src/drivers/ipmi/ipmi_kcs.h index 3cd7609..b43f043 100644 --- a/src/drivers/ipmi/ipmi_kcs.h +++ b/src/drivers/ipmi/ipmi_kcs.h @@ -26,7 +26,26 @@
#define IPMI_CMD_ACPI_POWERON 0x06
+#define IPMI_SET_SYS_INFO 0x58 +#define IPMI_GET_SYS_INFO 0x59 + +#define IPMI_BMC_GET_DEVICE_ID 0x01 + extern int ipmi_kcs_message(int port, int netfn, int lun, int cmd, const unsigned char *inmsg, int inlen, unsigned char *outmsg, int outlen); + +struct ipmi_devid_rsp { + uint8_t device_id; + uint8_t device_revision; + uint8_t fw_rev1; + uint8_t fw_rev2; + uint8_t ipmi_version; + uint8_t adtl_device_support; + uint8_t manufacturer_id[3]; + uint8_t product_id[2]; + uint8_t aux_fw_rev[4]; +} __packed; + + #endif diff --git a/src/drivers/ipmi/ipmi_kcs_ops.c b/src/drivers/ipmi/ipmi_kcs_ops.c new file mode 100644 index 0000000..3c394db --- /dev/null +++ b/src/drivers/ipmi/ipmi_kcs_ops.c @@ -0,0 +1,197 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 9elements Agency GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Place in devicetree.cb: + * + * chip drivers/ipmi + * device pnp 3ca.0 on end # IPMI KCS + * end + */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pnp.h> +#include <arch/acpi.h> +#include <smbios.h> +#include <version.h> +#include <delay.h> +#include "ipmi_kcs.h" +#include "chip.h" + +u16 ipmi_revision = 0x1500; + +static void ipmi_set_firmware_version(struct device *dev, const char *firmware) +{ + char msg[18]; + + if (!firmware || strlen(firmware) > 255) + return; + + msg[0] = 1; + msg[1] = 0; + msg[2] = 0; + msg[3] = strlen(firmware); + + memcpy(&msg[4], firmware, MIN(strlen(firmware), 12)); + firmware += MIN(strlen(firmware), 12); + + ipmi_kcs_message(dev->path.pnp.port, IPMI_NETFN_APPLICATION, 0, + IPMI_SET_SYS_INFO, (const u8 *)msg, sizeof(msg), + NULL, 0); + + do { + msg[1] ++; + memset(&msg[2], 0, 16); + + if (strlen(firmware)) { + memcpy(&msg[2], firmware, MIN(strlen(firmware), 16)); + firmware += MIN(strlen(firmware), 16); + } + + ipmi_kcs_message(dev->path.pnp.port, IPMI_NETFN_APPLICATION, 0, + IPMI_SET_SYS_INFO, (const u8 *)msg, + sizeof(msg), NULL, 0); + + } while (strlen(firmware)); +} + +static int ipmi_get_device_id(struct device *dev, struct ipmi_devid_rsp *rsp) +{ + if (ipmi_kcs_message(dev->path.pnp.port, IPMI_NETFN_APPLICATION, 0, + IPMI_BMC_GET_DEVICE_ID, NULL, 0, (u8 *)rsp, + sizeof(*rsp)) != sizeof(*rsp)) { + printk(BIOS_ERR, "IPMI: Failed to get device id\n"); + return 1; + } + return 0; +} + +static void ipmi_kcs_init(struct device *dev) +{ + struct ipmi_devid_rsp rsp; + + if (!dev->enabled) + return; + + /* Set firmware version string */ + ipmi_set_firmware_version(dev, coreboot_version); + + /* Get IPMI version for ACPI */ + if (!ipmi_get_device_id(dev, &rsp)) + ipmi_revision = rsp.ipmi_version; +} + +static void ipmi_set_resources(struct device *dev) +{ +} + +static void ipmi_enable_resources(struct device *dev) +{ +} + +#if CONFIG(HAVE_ACPI_TABLES) +static unsigned long +ipmi_write_acpi_tables(struct device *dev, unsigned long current, + struct acpi_rsdp *rsdp) +{ + struct drivers_ipmi_config *conf = NULL; + struct acpi_spmi *spmi; + s8 gpe_interrupt = -1; + u32 apic_interrupt = 0; + acpi_addr_t addr = { + .space_id = ACPI_ADDRESS_SPACE_IO, + .access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS, + .addrl = dev->path.pnp.port, + }; + + current = ALIGN(current, 8); + printk(BIOS_DEBUG, "ACPI: * SPMI at %lx\n", current); + spmi = (struct acpi_spmi *)current; + + if (dev->chip_info) + conf = dev->chip_info; + + if (conf) { + if (conf->have_gpe) + gpe_interrupt = conf->gpe_interrupt; + if (conf->have_apic) + apic_interrupt = conf->apic_interrupt; + } + acpi_create_ipmi(dev, spmi, ipmi_revision, &addr, IPMI_INTERFACE_KCS, + gpe_interrupt, apic_interrupt); + + acpi_add_table(rsdp, spmi); + + current += spmi->header.length; + + return current; +} +#endif + +#if CONFIG(GENERATE_SMBIOS_TABLES) +static int ipmi_smbios_data(struct device *dev, int *handle, + unsigned long *current) +{ + struct drivers_ipmi_config *conf = NULL; + u8 nv_storage = 0xff; + u8 i2c_address = 0; + int len = 0; + + if (dev->chip_info) + conf = dev->chip_info; + + if (conf) { + if (conf->have_nv_storage) + nv_storage = conf->nv_storage_device_address; + i2c_address = conf->bmc_i2c_address; + } + + // add IPMI Device Information + len += smbios_write_type38( + current, handle, + SMBIOS_BMC_INTERFACE_KCS, + ipmi_revision >> 4, // IPMI Version + i2c_address, // I2C address + nv_storage, // NV storage + dev->path.pnp.port | 1, // IO port interface address + 0, + 0); // no IRQ + + return len; +} +#endif + +static struct device_operations ops = { + .read_resources = pnp_read_resources, + .set_resources = ipmi_set_resources, + .enable_resources = ipmi_enable_resources, + .init = ipmi_kcs_init, +#if CONFIG(HAVE_ACPI_TABLES) + .write_acpi_tables = ipmi_write_acpi_tables, +#endif +#if CONFIG(GENERATE_SMBIOS_TABLES) + .get_smbios_data = ipmi_smbios_data, +#endif +}; + +static void enable_dev(struct device *dev) +{ + dev->ops = &ops; +} + +struct chip_operations drivers_ipmi_kcs_ops = { + CHIP_NAME("IPMI KCS") + .enable_dev = enable_dev, +};