I went ahead and kept the structure passing because I've added ACPI support. After thinking about it a while, I think if you have to pass anything to SMBIOS (like "IPMI is present") you might as well pass the whole structure, and making things fixed in the BIOS that can change in the hardware doesn't seem like a good idea.
Note that the acpi-element code might make building the SSDT table a little cleaner, if that is interesting.
From: Corey Minyard cminyard@mvista.com
An IPMI device is being added to the qemu code, and it has an SMBIOS entry to describe the interface characteristics. So add the SMBIOS entry to the BIOS so it can handle this.
Signed-off-by: Corey Minyard cminyard@mvista.com --- Makefile | 2 +- src/ipmi.c | 31 +++++++++++++++++++++++++++++++ src/ipmi.h | 39 +++++++++++++++++++++++++++++++++++++++ src/post.c | 2 ++ src/smbios.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/smbios.h | 12 ++++++++++++ src/util.h | 5 +++++ 7 files changed, 145 insertions(+), 1 deletions(-) create mode 100644 src/ipmi.c create mode 100644 src/ipmi.h
diff --git a/Makefile b/Makefile index 72ee152..e6f361c 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 + usb-uas.c lsi-scsi.c esp-scsi.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/ipmi.c b/src/ipmi.c new file mode 100644 index 0000000..8929173 --- /dev/null +++ b/src/ipmi.c @@ -0,0 +1,31 @@ +// 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 "ipmi.h" +#include "util.h" + +struct ipmi_info ipmi_info VAR16VISIBLE; + +void +ipmi_setup(void) +{ + u8 *data; + int size; + + data = romfile_loadfile("etc/ipmi", &size); + if (!data || (size < sizeof(ipmi_info))) { + ipmi_info.interface = 0; /* Disable */ + return; + } + + if (size > sizeof(ipmi_info)) + size = sizeof(ipmi_info); + + memcpy(&ipmi_info, data, size); + free(data); + + ipmi_info.base_addr = le64_to_cpu(ipmi_info.base_addr); +} diff --git a/src/ipmi.h b/src/ipmi.h new file mode 100644 index 0000000..72814ba --- /dev/null +++ b/src/ipmi.h @@ -0,0 +1,39 @@ +// 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 __IPMI_H +#define __IPMI_H + +#include "config.h" +#include "types.h" + +#define IPMI_MEM_SPACE 0 +#define IPMI_IO_SPACE 1 + +/* Specified in the SMBIOS spec. */ +#define IPMI_SMBIOS_KCS 0x01 +#define IPMI_SMBIOS_SMIC 0x02 +#define IPMI_SMBIOS_BT 0x03 +#define IPMI_SMBIOS_SSIF 0x04 + +struct ipmi_info { + u8 str_version; /* Version of this structure */ + u8 interface; + u8 reg_space; + u8 reg_spacing; + u8 slave_addr; + u8 irq; + u8 version; + u8 reserved1; + u64 base_addr; + u64 end_addr; +} PACKED; + +extern struct ipmi_info ipmi_info; + +void ipmi_setup(void); + +#endif diff --git a/src/post.c b/src/post.c index 924b311..e3156b4 100644 --- a/src/post.c +++ b/src/post.c @@ -29,6 +29,7 @@ #include "virtio-scsi.h" // virtio_scsi_setup #include "lsi-scsi.h" // lsi_scsi_setup #include "esp-scsi.h" // esp_scsi_setup +#include "ipmi.h" // ipmi_setup
/**************************************************************** @@ -257,6 +258,7 @@ maininit(void) pnp_setup(); kbd_setup(); mouse_setup(); + ipmi_setup(); init_bios_tables();
// Run vga option rom diff --git a/src/smbios.c b/src/smbios.c index fc84aad..f480a8d 100644 --- a/src/smbios.c +++ b/src/smbios.c @@ -8,6 +8,7 @@ #include "util.h" // dprintf #include "paravirt.h" // qemu_cfg_smbios_load_field #include "smbios.h" // struct smbios_entry_point +#include "ipmi.h" // struct ipmi_info
struct smbios_entry_point *SMBiosAddr;
@@ -422,6 +423,58 @@ smbios_init_type_32(void *start) return start+2; }
+/* Type 38 -- System Boot Information */ +static void * +smbios_init_type_38(void *start, struct ipmi_info *info) +{ + struct smbios_type_38 *p = (struct smbios_type_38 *)start; + + p->header.type = 38; + p->header.length = sizeof(struct smbios_type_38); + p->header.handle = 0x2100; + + p->interface_type = info->interface; + + p->base_addr = info->base_addr & ~1ULL; + /* Bit 0 goes into a special place */ + p->base_addr_mod_and_irq_info = (info->base_addr & 1) << 4; + + switch (info->reg_spacing) { + case 4: + p->base_addr_mod_and_irq_info |= (1 << 6); + break; + case 16: + p->base_addr_mod_and_irq_info |= (2 << 6); + break; + case 1: + default: + /* zero is the right value */ + break; + } + + if (info->reg_space) + p->base_addr |= 1; /* I/O space */ + + if (info->version) + p->ipmi_version = info->version; + else + p->ipmi_version = 0x15; + + if (info->slave_addr) + p->i2c_slave_addr = info->slave_addr; + else + p->i2c_slave_addr = 0x20; + + p->nv_storage_dev_addr = 0; + + p->interrupt_number = info->irq; + + start += sizeof(struct smbios_type_38); + *((u16 *)start) = 0; + + return start+2; +} + /* Type 127 -- End of Table */ static void * smbios_init_type_127(void *start) @@ -510,6 +563,8 @@ smbios_init(void) }
add_struct(32, p); + if (ipmi_info.interface) + add_struct(38, p, &ipmi_info); /* Add any remaining provided entries before the end marker */ for (i = 0; i < 256; i++) qemu_cfg_smbios_load_external(i, &p, &nr_structs, &max_struct_size, diff --git a/src/smbios.h b/src/smbios.h index 9d54e80..1935335 100644 --- a/src/smbios.h +++ b/src/smbios.h @@ -160,6 +160,18 @@ struct smbios_type_32 { u8 boot_status; } PACKED;
+/* SMBIOS type 38 - IPMI Information */ +struct smbios_type_38 { + struct smbios_structure_header header; + u8 interface_type; + u8 ipmi_version; + u8 i2c_slave_addr; + u8 nv_storage_dev_addr; + u64 base_addr; + u8 base_addr_mod_and_irq_info; + u8 interrupt_number; +} PACKED; + /* SMBIOS type 127 -- End-of-table */ struct smbios_type_127 { struct smbios_structure_header header; diff --git a/src/util.h b/src/util.h index 89e928c..7f7a4b2 100644 --- a/src/util.h +++ b/src/util.h @@ -134,6 +134,11 @@ static inline u32 le32_to_cpu(u32 x) return x; }
+static inline u64 le64_to_cpu(u64 x) +{ + return x; +} + static inline u32 getesp(void) { u32 esp; asm("movl %%esp, %0" : "=rm"(esp));
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) {
On 08/08/2012 11:11 AM, minyard@acm.org wrote:
I went ahead and kept the structure passing because I've added ACPI support. After thinking about it a while, I think if you have to pass anything to SMBIOS (like "IPMI is present") you might as well pass the whole structure, and making things fixed in the BIOS that can change in the hardware doesn't seem like a good idea.
Note that the acpi-element code might make building the SSDT table a little cleaner, if that is interesting.
I haven't heard anything on this patch set. Any comments?
-corey
On Fri, Aug 10, 2012 at 08:48:58AM -0500, Corey Minyard wrote:
On 08/08/2012 11:11 AM, minyard@acm.org wrote:
I went ahead and kept the structure passing because I've added ACPI support. After thinking about it a while, I think if you have to pass anything to SMBIOS (like "IPMI is present") you might as well pass the whole structure, and making things fixed in the BIOS that can change in the hardware doesn't seem like a good idea.
Note that the acpi-element code might make building the SSDT table a little cleaner, if that is interesting.
I haven't heard anything on this patch set. Any comments?
Patch 1 has the same problem as the last set - it introduces a new struct to carry info from QEMU to SeaBIOS when a standard struct (smbios) already exists.
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
-Kevin
On 08/12/2012 07:49 PM, Kevin O'Connor wrote:
On Fri, Aug 10, 2012 at 08:48:58AM -0500, Corey Minyard wrote:
On 08/08/2012 11:11 AM, minyard@acm.org wrote:
I went ahead and kept the structure passing because I've added ACPI support. After thinking about it a while, I think if you have to pass anything to SMBIOS (like "IPMI is present") you might as well pass the whole structure, and making things fixed in the BIOS that can change in the hardware doesn't seem like a good idea.
Note that the acpi-element code might make building the SSDT table a little cleaner, if that is interesting.
I haven't heard anything on this patch set. Any comments?
Patch 1 has the same problem as the last set - it introduces a new struct to carry info from QEMU to SeaBIOS when a standard struct (smbios) already exists.
That structure is also used by the ACPI code to build the ACPI structure. The data in the SMBIOS structure is not sufficient to build the ACPI structure.
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
It is constructing an addition to the DSDT table that is tacked on to the end of that table if IPMI is present. It is complex, but building ACPI namespace data is complex, and the data is not fixed length.
-corey
On Sun, Aug 12, 2012 at 08:22:12PM -0500, Corey Minyard wrote:
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
It is constructing an addition to the DSDT table that is tacked on to the end of that table if IPMI is present. It is complex, but building ACPI namespace data is complex, and the data is not fixed length.
You do not need to construct IPMI device dynamically in DSDT. Write it in AML and have _STA method that tells OSPM if device is present or not.
-- Gleb.
On 08/13/2012 01:25 AM, Gleb Natapov wrote:
On Sun, Aug 12, 2012 at 08:22:12PM -0500, Corey Minyard wrote:
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
It is constructing an addition to the DSDT table that is tacked on to the end of that table if IPMI is present. It is complex, but building ACPI namespace data is complex, and the data is not fixed length.
You do not need to construct IPMI device dynamically in DSDT. Write it in AML and have _STA method that tells OSPM if device is present or not.
There are lots of different options for IPMI devices. There are three different interface types, with two string lengths. They can all appear at arbitrary places in I/O or memory space. They can have an interrupt or not. I would like to be able to represent all off the possibilities so users can simulate any arbitrary machine they want.
I considered writing it in AML 8 times and figuring the offsets to set the various values, but that seems rather messy to me.
If the real desire is to have a single IPMI device type at a single address with a single interrupt always on, we could do that. The BIOS would still need a way to know that the device was present or not, so something will have to be passed. I'm not sure that reading from the standard address to detect the device is reliable enough, but that could be done, too.
-corey
On Mon, Aug 13, 2012 at 09:47:50AM -0500, Corey Minyard wrote:
On 08/13/2012 01:25 AM, Gleb Natapov wrote:
On Sun, Aug 12, 2012 at 08:22:12PM -0500, Corey Minyard wrote:
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
It is constructing an addition to the DSDT table that is tacked on to the end of that table if IPMI is present. It is complex, but building ACPI namespace data is complex, and the data is not fixed length.
You do not need to construct IPMI device dynamically in DSDT. Write it in AML and have _STA method that tells OSPM if device is present or not.
There are lots of different options for IPMI devices. There are three different interface types, with two string lengths. They can all appear at arbitrary places in I/O or memory space. They can have an interrupt or not. I would like to be able to represent all off the possibilities so users can simulate any arbitrary machine they want.
I considered writing it in AML 8 times and figuring the offsets to set the various values, but that seems rather messy to me.
How different are they. Can you give human readable example?
If the real desire is to have a single IPMI device type at a single address with a single interrupt always on, we could do that. The BIOS would still need a way to know that the device was present or not, so something will have to be passed. I'm not sure that reading from the standard address to detect the device is reliable enough, but that could be done, too.
-corey
-- Gleb.
On 08/13/2012 10:15 AM, Gleb Natapov wrote:
On Mon, Aug 13, 2012 at 09:47:50AM -0500, Corey Minyard wrote:
On 08/13/2012 01:25 AM, Gleb Natapov wrote:
On Sun, Aug 12, 2012 at 08:22:12PM -0500, Corey Minyard wrote:
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
It is constructing an addition to the DSDT table that is tacked on to the end of that table if IPMI is present. It is complex, but building ACPI namespace data is complex, and the data is not fixed length.
You do not need to construct IPMI device dynamically in DSDT. Write it in AML and have _STA method that tells OSPM if device is present or not.
There are lots of different options for IPMI devices. There are three different interface types, with two string lengths. They can all appear at arbitrary places in I/O or memory space. They can have an interrupt or not. I would like to be able to represent all off the possibilities so users can simulate any arbitrary machine they want.
I considered writing it in AML 8 times and figuring the offsets to set the various values, but that seems rather messy to me.
How different are they. Can you give human readable example?
Here are the examples from the IPMI spec. I lied a little bit, there are actually four standard interfaces (one can be on an SMBus), but it's a different thing to manage, I think.
-corey
Device(MI0) { Name(_HID, EISAID("IPI0001")) Name(_STR, Unicode("IPMI_SMIC")) Name(_UID, 0) // UID for the primary IPMI system interface in the system Name(_CRS, ResourceTemplate() { IO(Decode16, 0xCA9, 0, 3) // Ports 0xCA9, 0xCAA & 0xCAB } ) Method(_IFT) { Return(0x02) // IPMI SMIC } Method(_SRV) { Return(0x0100) // IPMI Specification Revision 1.0 } //This interface does not support interrupt }
Device(MI0) { Name(_HID, EISAID("IPI0001")) Name(_STR, Unicode("IPMI_KCS")) Name(_UID, 0) Name(_CRS, ResourceTemplate() { QWordMemory( ResourceConsumer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0xFFFFFFFFFFFFFFFF, // _GRA, Address granularity. 0x80000FFFFC020CA2, // _MIN, Address range minimum 0x80000FFFFC020CA4, // _MAX, Address range max 0x0000000000000000, // _TRA, Translation. 0x0000000000000002, // _LEN, Address range length , // Resource Source Index , // Resource Source Name , // A name to refer back to this resource , // _MTP, Nothing=>AddressRangeMemory , // _TTP, Translation. Nothing=>TypeStatic ) } ) Method(_IFT) { Return(0x01) // IPMI KCS } Method(_SRV) { Return(0x0100) // IPMI Specification Revision 1.0 } // This interface does not support interrupt }
Device(MI0) { Name(_HID, EISAID("IPI0001")) Name(_STR, Unicode("IPMI_BT")) Name(_UID, 0) Name(_CRS, ResourceTemplate() { IO(Decode16, 0x0E4, 0, 3) // Ports 0xE4h:E6h Interrupt(ResourceProducer,...){20} // GSI is 20 } ) // Returns the interface type Method(_IFT) { Return(0x03) // IPMI BT } // Returns the interface specification revision Method(_SRV) { Return(0x0150) // IPMI Specification Revision 1.5 } }
Device (SMB0) // example SMBus host controller { Name(_HID, "<Vendor-Specific HID>") // Vendor-Specific HID Name(_UID, 0) // Unique ID of particular host controller : : Device (SSIF) { Name(_HID,"IPI0001") // IPMI system interface Name(_UID, 0) // Unique device identifier Name(_STR, Unicode("IPMI_SSIF")) // Returns the interface type Method _IFT { Return(0x04) } // Returns the SSIF slave address Method _ADR { Return(0x10) } Method(_SRV) { Return(0x0200) // IPMI Specification Version 2.0 } } // end Device SSIF } // end Device SMB0
On Mon, Aug 13, 2012 at 02:30:52PM -0500, Corey Minyard wrote:
On 08/13/2012 10:15 AM, Gleb Natapov wrote:
On Mon, Aug 13, 2012 at 09:47:50AM -0500, Corey Minyard wrote:
On 08/13/2012 01:25 AM, Gleb Natapov wrote:
On Sun, Aug 12, 2012 at 08:22:12PM -0500, Corey Minyard wrote:
Patch 2 is complex and I don't fully understand what it is doing. A quick scan leads me to believe it is constructing a dynamic SSDT - though it's not clear why a dynamic SSDT is needed and why the existing mechanism (see build_ssdt()) for generating dynamic SSDTs is not used.
It is constructing an addition to the DSDT table that is tacked on to the end of that table if IPMI is present. It is complex, but building ACPI namespace data is complex, and the data is not fixed length.
You do not need to construct IPMI device dynamically in DSDT. Write it in AML and have _STA method that tells OSPM if device is present or not.
There are lots of different options for IPMI devices. There are three different interface types, with two string lengths. They can all appear at arbitrary places in I/O or memory space. They can have an interrupt or not. I would like to be able to represent all off the possibilities so users can simulate any arbitrary machine they want.
I considered writing it in AML 8 times and figuring the offsets to set the various values, but that seems rather messy to me.
How different are they. Can you give human readable example?
Here are the examples from the IPMI spec. I lied a little bit, there are actually four standard interfaces (one can be on an SMBus), but it's a different thing to manage, I think.
Does your patch produce all of the four variants you've identified? What fields (if any) are dynamic within the variants?
-Kevin
On 08/13/2012 05:49 PM, Kevin O'Connor wrote:
On
How different are they. Can you give human readable example?
Here are the examples from the IPMI spec. I lied a little bit, there are actually four standard interfaces (one can be on an SMBus), but it's a different thing to manage, I think.
Does your patch produce all of the four variants you've identified? What fields (if any) are dynamic within the variants?
-Kevin
My patch does not do the SMBus variant, as that would need to go into an SMBus device, and I haven't worked at all on an IPMI device to do this.
The fields that are dynamic are:
_STR - A string identifying the interface type (optional, but recommended)
_CRS - This can be a I/O or a memory address, and an interrupt may or may not be here. I have not done the work to add the memory bus, as my qemu patch does not support that yet.
_IFT - An integer that identifies the interface type.
_SRV - Identifies the IPMI spec version the interface complies to. It's generally best to use the actual version; if you used a newer version then an old driver may not work with it.
If more than one interface is added, then the _UID field becomes important, but there's no support for that at the moment.
-corey
On Mon, Aug 13, 2012 at 07:07:06PM -0500, Corey Minyard wrote:
On 08/13/2012 05:49 PM, Kevin O'Connor wrote:
How different are they. Can you give human readable example?
Here are the examples from the IPMI spec. I lied a little bit, there are actually four standard interfaces (one can be on an SMBus), but it's a different thing to manage, I think.
Does your patch produce all of the four variants you've identified? What fields (if any) are dynamic within the variants?
My patch does not do the SMBus variant, as that would need to go into an SMBus device, and I haven't worked at all on an IPMI device to do this.
So, the patch supports two variants - IPMI_SMIC and IPMI_BT?
The fields that are dynamic are:
_STR - A string identifying the interface type (optional, but recommended)
But this isn't dynamic within a variant, correct? (That is, if you're using an IPMI_SMIC type device then the _STR will always be "IPMI_SMIC"?)
_CRS - This can be a I/O or a memory address, and an interrupt may or may not be here. I have not done the work to add the memory bus, as my qemu patch does not support that yet.
Will the irq and ioports be different within a given variant?
_IFT - An integer that identifies the interface type.
Again, static within a variant, right?
_SRV - Identifies the IPMI spec version the interface complies to. It's generally best to use the actual version; if you used a newer version then an old driver may not work with it.
Again, static within a variant, right?
If more than one interface is added, then the _UID field becomes important, but there's no support for that at the moment.
-Kevin
On 08/13/2012 07:25 PM, Kevin O'Connor wrote:
On Mon, Aug 13, 2012 at 07:07:06PM -0500, Corey Minyard wrote:
On 08/13/2012 05:49 PM, Kevin O'Connor wrote:
How different are they. Can you give human readable example?
Here are the examples from the IPMI spec. I lied a little bit, there are actually four standard interfaces (one can be on an SMBus), but it's a different thing to manage, I think.
Does your patch produce all of the four variants you've identified? What fields (if any) are dynamic within the variants?
My patch does not do the SMBus variant, as that would need to go into an SMBus device, and I haven't worked at all on an IPMI device to do this.
So, the patch supports two variants - IPMI_SMIC and IPMI_BT?
IPMI_SMIC, IPMI_BT, and IPMI_KCS. So three variants.
The fields that are dynamic are:
_STR - A string identifying the interface type (optional, but recommended)
But this isn't dynamic within a variant, correct? (That is, if you're using an IPMI_SMIC type device then the _STR will always be "IPMI_SMIC"?)
Yes, that is correct.
_CRS - This can be a I/O or a memory address, and an interrupt may or may not be here. I have not done the work to add the memory bus, as my qemu patch does not support that yet.
Will the irq and ioports be different within a given variant?
The I/O ports are somewhat standard. There are spec-recommended places for all of them, and most (but not all) systems use those. Most systems do not support interrupts, but there is no standard for the ones that do.
System with the device in memory are all over the place.
_IFT - An integer that identifies the interface type.
Again, static within a variant, right?
Yes, this is what defines the variant, I guess.
_SRV - Identifies the IPMI spec version the interface complies to. It's generally best to use the actual version; if you used a newer version then an old driver may not work with it.
Again, static within a variant, right?
Yes.
-corey
If more than one interface is added, then the _UID field becomes important, but there's no support for that at the moment.
-Kevin