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