From: Corey Minyard cminyard@mvista.com
Take the IPMI firmware information and add an ACPI device entry in the DSDT table.
As part of this, add some basic structure to create the ACPI device entries without having to compile them. The elements required for IPMI are added.
Signed-off-by: Corey Minyard cminyard@mvista.com --- Makefile | 2 +- src/acpi-elements.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/acpi-elements.h | 61 +++++++++ src/acpi.c | 17 +++- src/acpi.h | 2 + src/ipmi.c | 121 ++++++++++++++++++ 6 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 src/acpi-elements.c create mode 100644 src/acpi-elements.h
diff --git a/Makefile b/Makefile index e6f361c..1ea848d 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SRCBOTH=misc.c stacks.c pmm.c output.c util.c block.c floppy.c ata.c mouse.c \ pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \ virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c \ - usb-uas.c lsi-scsi.c esp-scsi.c ipmi.c + usb-uas.c lsi-scsi.c esp-scsi.c acpi-elements.c ipmi.c SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ diff --git a/src/acpi-elements.c b/src/acpi-elements.c new file mode 100644 index 0000000..f5d5fb5 --- /dev/null +++ b/src/acpi-elements.c @@ -0,0 +1,336 @@ +// IPMI setup information +// +// Copyright (C) 2012 Corey Minyard cminyard@mvista.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "acpi-elements.h" +#include "util.h" + +#define acpi_add_byte(data, val) do { **data = val; (*data)++; } while(0) + +static int +acpi_add_Pkglen(char **data, int dlen, int length) +{ + int pkglen; + + /* + * The funny length values following are because we include the length + * bytes in the full length. + */ + if (length <= 0x3e) { + if (dlen > 0) { + acpi_add_byte(data, length + 1); + } + return 1; + } else if (length <= 0xffd) { + pkglen = 2; + } else if (length <= 0xffffc) { + pkglen = 3; + } else if (length <= 0xffffffb) { + pkglen = 4; + } else { + return -1; + } + length += pkglen; + if (pkglen <= dlen) { + acpi_add_byte(data, ((pkglen - 1) << 6) | (length & 0xf)); + length >>= 4; + pkglen--; + while (pkglen > 0) { + acpi_add_byte(data, length & 0xff); + length >>= 8; + pkglen--; + } + } + return pkglen; +} + +static int +acpi_add_NameString(char **data, int dlen, char *name) +{ + int length = strlen(name); + int i; + + if (dlen >= 4) { + i = 0; + while ((i < 4) && (i < length)) { + acpi_add_byte(data, *name++); + i++; + } + while (i < 4) { + acpi_add_byte(data, '_'); + i++; + } + } + return 4; +} + +int +acpi_add_Device(char **data, int dlen, char *name, + contained_acpi_elem e, void *opaque) +{ + int length, plen, totlen; + + length = e(NULL, 0, opaque); + if (length < 0) + return length; + + plen = acpi_add_Pkglen(NULL, 0, length); + if (plen < 0) + return plen; + totlen = length + plen + 6; + if (dlen >= totlen) { + acpi_add_byte(data, 0x5b); + acpi_add_byte(data, 0x82); + dlen -= 2; + dlen -= acpi_add_Pkglen(data, dlen, length); + dlen -= acpi_add_NameString(data, dlen, name); + dlen -= e(data, dlen, opaque); + } + + return totlen; +} + +int +acpi_add_Name(char **data, int dlen, char *name, + contained_acpi_elem e, void *opaque) +{ + int rv; + + if (dlen >= 5) { + acpi_add_byte(data, 0x8); + dlen -= 1; + dlen -= acpi_add_NameString(data, dlen, name); + } + rv = e(data, dlen, opaque); + if (rv < 0) + return rv; + return rv + 5; +} + +int +acpi_add_Method(char **data, int dlen, char *name, u8 flags, + contained_acpi_elem e, void *opaque) +{ + int elen, plen; + + elen = e(NULL, 0, opaque); + if (elen < 0) + return elen; + + plen = acpi_add_Pkglen(NULL, 0, elen + 5); + if (plen < 0) + return plen; + if (plen + elen + 6 <= dlen) { + acpi_add_byte(data, 0x14); + dlen -= 1; + dlen -= acpi_add_Pkglen(data, dlen, elen + 5); + dlen -= acpi_add_NameString(data, dlen, name); + acpi_add_byte(data, flags); + dlen -= 1; + dlen -= e(data, dlen, opaque); + } + return plen + elen + 6; +} + +int +acpi_add_Integer(char **data, int dlen, void *vval) +{ + u64 val = *((u64 *) vval); + int length, i; + unsigned char op; + + if ((val == 0) || (val == 1)) { + /* ZeroOp or OneOp */ + if (dlen > 0) { + acpi_add_byte(data, val); + } + return 1; + } if (val <= 0xff) { + length = 1; + op = 0x0a; + } else if (val <= 0xffff) { + length = 2; + op = 0x0b; + } else if (val <= 0xffffffff) { + length = 4; + op = 0x0c; + } else { + length = 8; + op = 0x0e; + } + + if (dlen >= length + 1) { + acpi_add_byte(data, op); + for (i = 0; i < length; i++) { + acpi_add_byte(data, val & 0xff); + val >>= 8; + } + } + return length + 1; +} + +/* + * A compressed EISA ID has the top bit reserved, the next 15 bits as + * compressed ASCII upper case letters, and the bottom 16 bits as four + * hex digits. + */ +int +acpi_add_EISAID(char **data, int dlen, void *val) +{ + char *str = val; + u32 ival = 0; + int i; + + if (dlen >= 5) { + acpi_add_byte(data, 0xc); /* dword */ + if (strlen(val) != 7) + return -1; + for (i = 0; i < 3; i++) { + if (str[i] < 'A' || str[i] > 'Z') + return -1; + ival = (ival << 5) | (str[i] - 0x40); + } + for (; i < 7; i++) { + int v; + if (str[i] >= '0' && str[i] <= '9') + v = str[i] - '0'; + else if (str[i] >= 'A' && str[i] <= 'F') + v = str[i] - 'A' + 10; + else + return -1; + ival = (ival << 4) | v; + } + /* Note that for some reason this is big endian */ + for (i = 0; i < 4; i++) { + acpi_add_byte(data, (ival >> 24) & 0xff); + ival <<= 8; + } + } + + return 5; +} + +int +acpi_add_BufferOp(char **data, int dlen, + contained_acpi_elem e, void *opaque) +{ + int blen, slen, plen, tlen; + u64 val; + + blen = e(NULL, 0, opaque); + if (blen < 0) + return blen; + + val = blen; + slen = acpi_add_Integer(NULL, 0, &val); + if (slen < 0) + return slen; + plen = acpi_add_Pkglen(NULL, 0, slen + blen); + if (plen < 0) + return plen; + tlen = blen + slen + plen + 1; + if (tlen <= dlen) { + acpi_add_byte(data, 0x11); + dlen--; + dlen -= acpi_add_Pkglen(data, dlen, slen + blen); + dlen -= acpi_add_Integer(data, dlen, &val); + dlen -= e(data, dlen, opaque); + } + return tlen; +} + +int +acpi_add_Return(char **data, int dlen, void *val) +{ + int blen; + + blen = acpi_add_Integer(NULL, 0, val); + if (blen + 1 <= dlen) { + acpi_add_byte(data, 0xa4); + dlen -= 1; + dlen -= acpi_add_Integer(data, dlen, val); + } + return blen + 1; +} + +/* + * Note that str is void*, not char*, so it can be passed as a + * contained element. + */ +static int +unicode_helper(char **data, int dlen, void *vstr) +{ + char *str = vstr; + int len = strlen(str) + 1; + + if (len * 2 <= dlen) { + while (*str) { + acpi_add_byte(data, *str++); + acpi_add_byte(data, 0); + } + acpi_add_byte(data, 0); + acpi_add_byte(data, 0); + } + return len * 2; +} +int +acpi_add_Unicode(char **data, int dlen, void *vstr) +{ + int len; + + len = acpi_add_BufferOp(NULL, 0, unicode_helper, vstr); + if (len < 0) + return len; + if (len <= dlen) { + acpi_add_BufferOp(data, dlen, unicode_helper, vstr); + } + return len; +} + +int +acpi_add_IO16(char **data, int dlen, + u16 minaddr, u16 maxaddr, u8 align, u8 range) +{ + if (dlen >= 8) { + acpi_add_byte(data, 0x47); + acpi_add_byte(data, 1); + acpi_add_byte(data, minaddr & 0xff); + acpi_add_byte(data, minaddr >> 8); + acpi_add_byte(data, maxaddr & 0xff); + acpi_add_byte(data, maxaddr >> 8); + acpi_add_byte(data, align); + acpi_add_byte(data, range); + } + return 8; +} + +int +acpi_add_Interrupt(char **data, int dlen, u32 irq, + int consumer, int mode, int polarity, int sharing) +{ + if (dlen >= 9) { + acpi_add_byte(data, 0x89); + acpi_add_byte(data, 6); + acpi_add_byte(data, 0); + acpi_add_byte(data, consumer | (mode << 1) | (polarity << 2) | (sharing << 3)); + acpi_add_byte(data, 1); /* Only 1 irq */ + acpi_add_byte(data, irq); + acpi_add_byte(data, 0); + acpi_add_byte(data, 0); + acpi_add_byte(data, 0); + } + return 9; +} + +int +acpi_add_EndResource(char **data, int dlen) +{ + if (dlen >= 2) { + acpi_add_byte(data, 0x79); + acpi_add_byte(data, 0); + } + return 2; +} diff --git a/src/acpi-elements.h b/src/acpi-elements.h new file mode 100644 index 0000000..0df146f --- /dev/null +++ b/src/acpi-elements.h @@ -0,0 +1,61 @@ +// IPMI setup information +// +// Copyright (C) 2012 Corey Minyard cminyard@mvista.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#ifndef __ACPI_ELEMENTS_H +#define __ACPI_ELEMENTS_H + +#include "types.h" + +typedef int (*contained_acpi_elem)(char **data, int length, + void *opaque); + +int acpi_add_Device(char **data, int dlen, char *name, + contained_acpi_elem e, void *opaque); + +int acpi_add_Name(char **data, int dlen, char *name, + contained_acpi_elem e, void *opaque); + +int acpi_add_Method(char **data, int dlen, char *name, u8 flags, + contained_acpi_elem e, void *opaque); + +/* Pass in a pointer to a u64 */ +int acpi_add_Integer(char **data, int dlen, void *val); + +/* Pass in a pointer to a string */ +int acpi_add_EISAID(char **data, int dlen, void *val); + +int acpi_add_BufferOp(char **data, int dlen, + contained_acpi_elem e, void *opaque); + +/* Pass in a pointer to a u64 */ +int acpi_add_Return(char **data, int dlen, void *); + +/* + * Note that str is void*, not char*, so it can be passed as a + * contained element. + */ +int acpi_add_Unicode(char **data, int dlen, void *vstr); + +int acpi_add_IO16(char **data, int dlen, + u16 minaddr, u16 maxaddr, u8 align, u8 range); + +#define ACPI_RESOURCE_PRODUCER 0 +#define ACPI_RESOURCE_CONSUMER 1 +#define ACPI_INTERRUPT_MODE_LEVEL 0 +#define ACPI_INTERRUPT_MODE_EDGE 1 +#define ACPI_INTERRUPT_POLARITY_ACTIVE_HIGH 0 +#define ACPI_INTERRUPT_POLARITY_ACTIVE_LOW 1 +#define ACPI_INTERRUPT_EXCLUSIVE 0 +#define ACPI_INTERRUPT_SHARED 1 +#define ACPI_INTERRUPT_EXCLUSIVE_WAKE 2 +#define ACPI_INTERRUPT_SHARED_WAKE 3 + +int acpi_add_Interrupt(char **data, int dlen, u32 irq, + int consumer, int mode, int polarity, int sharing); + +int acpi_add_EndResource(char **data, int dlen); + +#endif diff --git a/src/acpi.c b/src/acpi.c index 39b7172..ed3f69f 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -777,12 +777,27 @@ acpi_bios_init(void) } if (fadt && !fadt->dsdt) { /* default DSDT */ - void *dsdt = malloc_high(sizeof(AmlCode)); + void *dsdt; + char *extrap; + int extra = ipmi_encode_acpi(NULL, 0); + u32 length; + struct acpi_table_header *h; + if (extra < 0) + extra = 0; + dsdt = malloc_high(sizeof(AmlCode) + extra); if (!dsdt) { warn_noalloc(); return; } memcpy(dsdt, AmlCode, sizeof(AmlCode)); + extrap = ((char *) dsdt) + sizeof(AmlCode); + ipmi_encode_acpi(&extrap, extra); + h = (struct acpi_table_header *) dsdt; + length = le32_to_cpu(h->length); + length += extra; + h->length = cpu_to_le32(length); + h->checksum = 0; + h->checksum -= checksum(h, length); fill_dsdt(fadt, dsdt); }
diff --git a/src/acpi.h b/src/acpi.h index cb21561..ae09097 100644 --- a/src/acpi.h +++ b/src/acpi.h @@ -107,4 +107,6 @@ struct bfld { u64 p1l; /* pci window 1 (above 4g) - length */ } PACKED;
+int ipmi_encode_acpi(char **data, int dlen); + #endif // acpi.h diff --git a/src/ipmi.c b/src/ipmi.c index 8929173..c4069f1 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -6,9 +6,130 @@
#include "ipmi.h" #include "util.h" +#include "acpi.h" +#include "acpi-elements.h"
struct ipmi_info ipmi_info VAR16VISIBLE;
+static int +acpi_ipmi_crs_ops(char **data, int dlen, void *opaque) +{ + struct ipmi_info *info = opaque; + int len, rv; + u8 regspacing = info->reg_spacing; + + if (info->reg_space != IPMI_IO_SPACE) + return -1; + + if (regspacing == 1) + regspacing = 0; + /* IO(Decode16, x, y, z, c) */ + len = acpi_add_IO16(data, dlen, info->base_addr, info->end_addr, + regspacing, info->end_addr - info->base_addr + 1); + if (len < 0) + return len; + if (info->irq) { + /* Interrupt(ResourceConsumer,Level,ActiveHigh,Exclusive) {n} */ + rv = acpi_add_Interrupt(data, dlen, info->irq, + ACPI_RESOURCE_CONSUMER, + ACPI_INTERRUPT_MODE_LEVEL, + ACPI_INTERRUPT_POLARITY_ACTIVE_HIGH, + ACPI_INTERRUPT_EXCLUSIVE); + if (rv < 0) + return rv; + len += rv; + } + rv = acpi_add_EndResource(data, dlen); + if (rv < 0) + return rv; + len += rv; + return len; +} + +static int +acpi_ipmi_crs(char **data, int dlen, void *opaque) +{ + struct ipmi_info *info = opaque; + int len; + + len = acpi_add_BufferOp(NULL, 0, acpi_ipmi_crs_ops, info); + if (len < 0) + return len; + if (len <= dlen) { + acpi_add_BufferOp(data, dlen, acpi_ipmi_crs_ops, info); + } + return len; +} + +static int +acpi_ipmi_dev(char **data, int dlen, void *opaque) +{ + struct ipmi_info *info = opaque; + int len, rv; + char *name; + u64 val; + + switch (info->interface) { + case IPMI_SMBIOS_KCS: + name = "IPMI_KCS"; + break; + case IPMI_SMBIOS_SMIC: + name = "IPMI_SMIC"; + break; + case IPMI_SMBIOS_BT: + name = "IPMI_BT"; + break; + case IPMI_SMBIOS_SSIF: + name = "IPMI_SSIF"; + break; + default: + return -1; + } + /* Name(_HID, EISAID("IPI0001")) */ + len = acpi_add_Name(data, dlen, "_HID", acpi_add_EISAID, "IPI0001"); + if (len < 0) + return len; + /* Name(_STR, Unicode("IPMI_SMIC")) */ + rv = acpi_add_Name(data, dlen, "_STR", acpi_add_Unicode, name); + if (rv < 0) + return rv; + len += rv; + val = 0; + /* Name(_UID, 0) */ + rv = acpi_add_Name(data, dlen, "_UID", acpi_add_Integer, &val); + if (rv < 0) + return rv; + len += rv; + /* Name(_CRS, ResourceTemplate() { */ + rv = acpi_add_Name(data, dlen, "_CRS", acpi_ipmi_crs, info); + if (rv < 0) + return rv; + len += rv; + val = info->interface; + /* Method(_IFT) { Return(i) } */ + rv = acpi_add_Method(data, dlen, "_IFT", 0, acpi_add_Return, &val); + if (rv < 0) + return rv; + len += rv; + val = ((info->version & 0xf0) << 4) | (info->version & 0x0f); + /* Method(_SRV) { Return(version) } */ + rv = acpi_add_Method(data, dlen, "_SRV", 0, acpi_add_Return, &val); + if (rv < 0) + return rv; + len += rv; + return len; +} + +int +ipmi_encode_acpi(char **data, int dlen) +{ + if (!ipmi_info.interface) + return 0; + /* Device(MI0) { */ + return acpi_add_Device(data, dlen, "MI0", acpi_ipmi_dev, &ipmi_info); + /* } */ +} + void ipmi_setup(void) {