1 | /* |
2 | * IPMI SMBIOS firmware handling |
3 | * |
4 | * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC |
5 | * |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
7 | * See the COPYING file in the top-level directory. |
8 | */ |
9 | |
10 | #include "qemu/osdep.h" |
11 | #include "hw/ipmi/ipmi.h" |
12 | #include "hw/firmware/smbios.h" |
13 | #include "qemu/error-report.h" |
14 | #include "smbios_build.h" |
15 | |
16 | /* SMBIOS type 38 - IPMI */ |
17 | struct smbios_type_38 { |
18 | struct smbios_structure_header ; |
19 | uint8_t interface_type; |
20 | uint8_t ipmi_spec_revision; |
21 | uint8_t i2c_slave_address; |
22 | uint8_t nv_storage_device_address; |
23 | uint64_t base_address; |
24 | uint8_t base_address_modifier; |
25 | uint8_t interrupt_number; |
26 | } QEMU_PACKED; |
27 | |
28 | static void smbios_build_one_type_38(IPMIFwInfo *info) |
29 | { |
30 | uint64_t baseaddr = info->base_address; |
31 | SMBIOS_BUILD_TABLE_PRE(38, 0x3000, true); |
32 | |
33 | t->interface_type = info->interface_type; |
34 | t->ipmi_spec_revision = ((info->ipmi_spec_major_revision << 4) |
35 | | info->ipmi_spec_minor_revision); |
36 | t->i2c_slave_address = info->i2c_slave_address; |
37 | t->nv_storage_device_address = 0; |
38 | |
39 | assert(info->ipmi_spec_minor_revision <= 15); |
40 | assert(info->ipmi_spec_major_revision <= 15); |
41 | |
42 | /* or 1 to set it to I/O space */ |
43 | switch (info->memspace) { |
44 | case IPMI_MEMSPACE_IO: |
45 | baseaddr |= 1; |
46 | break; |
47 | case IPMI_MEMSPACE_MEM32: |
48 | case IPMI_MEMSPACE_MEM64: |
49 | break; |
50 | case IPMI_MEMSPACE_SMBUS: |
51 | baseaddr <<= 1; |
52 | break; |
53 | } |
54 | |
55 | t->base_address = cpu_to_le64(baseaddr); |
56 | |
57 | t->base_address_modifier = 0; |
58 | if (info->irq_type == IPMI_LEVEL_IRQ) { |
59 | t->base_address_modifier |= 1; |
60 | } |
61 | switch (info->register_spacing) { |
62 | case 1: |
63 | break; |
64 | case 4: |
65 | t->base_address_modifier |= 1 << 6; |
66 | break; |
67 | case 16: |
68 | t->base_address_modifier |= 2 << 6; |
69 | break; |
70 | default: |
71 | error_report("IPMI register spacing %d is not compatible with" |
72 | " SMBIOS, ignoring this entry." , info->register_spacing); |
73 | return; |
74 | } |
75 | t->interrupt_number = info->interrupt_number; |
76 | |
77 | SMBIOS_BUILD_TABLE_POST; |
78 | } |
79 | |
80 | static void smbios_add_ipmi_devices(BusState *bus) |
81 | { |
82 | BusChild *kid; |
83 | |
84 | QTAILQ_FOREACH(kid, &bus->children, sibling) { |
85 | DeviceState *dev = kid->child; |
86 | Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_IPMI_INTERFACE); |
87 | BusState *childbus; |
88 | |
89 | if (obj) { |
90 | IPMIInterface *ii; |
91 | IPMIInterfaceClass *iic; |
92 | IPMIFwInfo info; |
93 | |
94 | ii = IPMI_INTERFACE(obj); |
95 | iic = IPMI_INTERFACE_GET_CLASS(obj); |
96 | memset(&info, 0, sizeof(info)); |
97 | iic->get_fwinfo(ii, &info); |
98 | smbios_build_one_type_38(&info); |
99 | continue; |
100 | } |
101 | |
102 | QLIST_FOREACH(childbus, &dev->child_bus, sibling) { |
103 | smbios_add_ipmi_devices(childbus); |
104 | } |
105 | } |
106 | } |
107 | |
108 | void smbios_build_type_38_table(void) |
109 | { |
110 | BusState *bus; |
111 | |
112 | bus = sysbus_get_default(); |
113 | if (bus) { |
114 | smbios_add_ipmi_devices(bus); |
115 | } |
116 | } |
117 | |