This is a resend of basically the same code as before, to get things rolling again.
-corey
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 | 32 ++++++++++++++++++++++++++++++++ src/ipmi.h | 39 +++++++++++++++++++++++++++++++++++++++ src/post.c | 2 ++ src/smbios.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/smbios.h | 12 ++++++++++++ 6 files changed, 141 insertions(+), 1 deletions(-) create mode 100644 src/ipmi.c create mode 100644 src/ipmi.h
diff --git a/Makefile b/Makefile index 5486f88..4559e5c 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..f4d29b1 --- /dev/null +++ b/src/ipmi.c @@ -0,0 +1,32 @@ +// 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 "byteorder.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;
On Tue, Sep 18, 2012 at 03:45:52PM -0500, minyard@acm.org wrote:
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.
[...]
+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;
[...]
+/* 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;
Hi Corey,
Sorry, but the above is still a problem for me.
In the recent q35 patches there was discussion of providing more of the acpi tables via qemu (in order to support more hardware variations). Perhaps these patches should take a similar approach.
-Kevin
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 4559e5c..cc14b74 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 6d239fa..c31122c 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -800,12 +800,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 f4d29b1..609e64b 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -7,9 +7,130 @@ #include "ipmi.h" #include "byteorder.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) {