]>
Commit | Line | Data |
---|---|---|
a9b74079 CM |
1 | /* |
2 | * QEMU ISA IPMI BT emulation | |
3 | * | |
4 | * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
0b8fa32f | 24 | |
0430891c | 25 | #include "qemu/osdep.h" |
efbb649d | 26 | #include "qemu/log.h" |
0b8fa32f | 27 | #include "qemu/module.h" |
da34e65c | 28 | #include "qapi/error.h" |
a9b74079 | 29 | #include "hw/ipmi/ipmi.h" |
64552b6b | 30 | #include "hw/irq.h" |
a9b74079 | 31 | #include "hw/isa/isa.h" |
a27bd6c7 | 32 | #include "hw/qdev-properties.h" |
d6454270 | 33 | #include "migration/vmstate.h" |
a9b74079 CM |
34 | |
35 | /* Control register */ | |
36 | #define IPMI_BT_CLR_WR_BIT 0 | |
37 | #define IPMI_BT_CLR_RD_BIT 1 | |
38 | #define IPMI_BT_H2B_ATN_BIT 2 | |
39 | #define IPMI_BT_B2H_ATN_BIT 3 | |
40 | #define IPMI_BT_SMS_ATN_BIT 4 | |
41 | #define IPMI_BT_HBUSY_BIT 6 | |
42 | #define IPMI_BT_BBUSY_BIT 7 | |
43 | ||
a9b74079 | 44 | #define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) |
a9b74079 | 45 | |
a9b74079 | 46 | #define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) |
a9b74079 | 47 | |
a9b74079 | 48 | #define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) |
a9b74079 CM |
49 | |
50 | #define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) | |
51 | #define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) | |
cb9a05a4 | 52 | #define IPMI_BT_SET_B2H_ATN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ |
c9c47229 | 53 | (!!(v) << IPMI_BT_B2H_ATN_BIT))) |
a9b74079 CM |
54 | |
55 | #define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) | |
56 | #define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) | |
cb9a05a4 | 57 | #define IPMI_BT_SET_SMS_ATN(d, v) ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ |
c9c47229 | 58 | (!!(v) << IPMI_BT_SMS_ATN_BIT))) |
a9b74079 CM |
59 | |
60 | #define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) | |
61 | #define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) | |
cb9a05a4 | 62 | #define IPMI_BT_SET_HBUSY(d, v) ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ |
c9c47229 | 63 | (!!(v) << IPMI_BT_HBUSY_BIT))) |
a9b74079 CM |
64 | |
65 | #define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) | |
cb9a05a4 | 66 | #define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ |
c9c47229 | 67 | (!!(v) << IPMI_BT_BBUSY_BIT))) |
a9b74079 CM |
68 | |
69 | ||
70 | /* Mask register */ | |
71 | #define IPMI_BT_B2H_IRQ_EN_BIT 0 | |
72 | #define IPMI_BT_B2H_IRQ_BIT 1 | |
73 | ||
74 | #define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) | |
75 | #define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) | |
cb9a05a4 | 76 | #define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\ |
c9c47229 | 77 | (!!(v) << IPMI_BT_B2H_IRQ_EN_BIT))) |
a9b74079 CM |
78 | |
79 | #define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) | |
80 | #define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) | |
cb9a05a4 | 81 | #define IPMI_BT_SET_B2H_IRQ(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ |
c9c47229 | 82 | (!!(v) << IPMI_BT_B2H_IRQ_BIT))) |
a9b74079 CM |
83 | |
84 | typedef struct IPMIBT { | |
85 | IPMIBmc *bmc; | |
86 | ||
87 | bool do_wake; | |
88 | ||
89 | qemu_irq irq; | |
90 | ||
91 | uint32_t io_base; | |
92 | unsigned long io_length; | |
93 | MemoryRegion io; | |
94 | ||
95 | bool obf_irq_set; | |
96 | bool atn_irq_set; | |
97 | bool use_irq; | |
98 | bool irqs_enabled; | |
99 | ||
100 | uint8_t outmsg[MAX_IPMI_MSG_SIZE]; | |
101 | uint32_t outpos; | |
102 | uint32_t outlen; | |
103 | ||
104 | uint8_t inmsg[MAX_IPMI_MSG_SIZE]; | |
105 | uint32_t inlen; | |
106 | ||
107 | uint8_t control_reg; | |
108 | uint8_t mask_reg; | |
109 | ||
110 | /* | |
111 | * This is a response number that we send with the command to make | |
112 | * sure that the response matches the command. | |
113 | */ | |
114 | uint8_t waiting_rsp; | |
115 | uint8_t waiting_seq; | |
116 | } IPMIBT; | |
117 | ||
118 | #define IPMI_CMD_GET_BT_INTF_CAP 0x36 | |
119 | ||
120 | static void ipmi_bt_handle_event(IPMIInterface *ii) | |
121 | { | |
122 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
123 | IPMIBT *ib = iic->get_backend_data(ii); | |
124 | ||
125 | if (ib->inlen < 4) { | |
126 | goto out; | |
127 | } | |
128 | /* Note that overruns are handled by handle_command */ | |
129 | if (ib->inmsg[0] != (ib->inlen - 1)) { | |
130 | /* Length mismatch, just ignore. */ | |
131 | IPMI_BT_SET_BBUSY(ib->control_reg, 1); | |
132 | ib->inlen = 0; | |
133 | goto out; | |
134 | } | |
135 | if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && | |
136 | (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { | |
137 | /* We handle this one ourselves. */ | |
138 | ib->outmsg[0] = 9; | |
139 | ib->outmsg[1] = ib->inmsg[1] | 0x04; | |
140 | ib->outmsg[2] = ib->inmsg[2]; | |
141 | ib->outmsg[3] = ib->inmsg[3]; | |
142 | ib->outmsg[4] = 0; | |
143 | ib->outmsg[5] = 1; /* Only support 1 outstanding request. */ | |
144 | if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */ | |
145 | ib->outmsg[6] = 0xff; | |
146 | } else { | |
147 | ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg); | |
148 | } | |
149 | if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */ | |
150 | ib->outmsg[7] = 0xff; | |
151 | } else { | |
152 | ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg); | |
153 | } | |
154 | ib->outmsg[8] = 10; /* Max request to response time */ | |
155 | ib->outmsg[9] = 0; /* Don't recommend retries */ | |
156 | ib->outlen = 10; | |
157 | IPMI_BT_SET_BBUSY(ib->control_reg, 0); | |
158 | IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); | |
159 | if (ib->use_irq && ib->irqs_enabled && | |
160 | !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && | |
161 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
162 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
163 | qemu_irq_raise(ib->irq); | |
164 | } | |
165 | goto out; | |
166 | } | |
167 | ib->waiting_seq = ib->inmsg[2]; | |
168 | ib->inmsg[2] = ib->inmsg[1]; | |
169 | { | |
170 | IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc); | |
171 | bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, | |
172 | sizeof(ib->inmsg), ib->waiting_rsp); | |
173 | } | |
174 | out: | |
175 | return; | |
176 | } | |
177 | ||
178 | static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, | |
179 | unsigned char *rsp, unsigned int rsp_len) | |
180 | { | |
181 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
182 | IPMIBT *ib = iic->get_backend_data(ii); | |
183 | ||
184 | if (ib->waiting_rsp == msg_id) { | |
185 | ib->waiting_rsp++; | |
186 | if (rsp_len > (sizeof(ib->outmsg) - 2)) { | |
187 | ib->outmsg[0] = 4; | |
188 | ib->outmsg[1] = rsp[0]; | |
189 | ib->outmsg[2] = ib->waiting_seq; | |
190 | ib->outmsg[3] = rsp[1]; | |
191 | ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; | |
192 | ib->outlen = 5; | |
193 | } else { | |
194 | ib->outmsg[0] = rsp_len + 1; | |
195 | ib->outmsg[1] = rsp[0]; | |
196 | ib->outmsg[2] = ib->waiting_seq; | |
197 | memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1); | |
198 | ib->outlen = rsp_len + 2; | |
199 | } | |
200 | IPMI_BT_SET_BBUSY(ib->control_reg, 0); | |
201 | IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); | |
202 | if (ib->use_irq && ib->irqs_enabled && | |
203 | !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && | |
204 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
205 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
206 | qemu_irq_raise(ib->irq); | |
207 | } | |
208 | } | |
209 | } | |
210 | ||
211 | ||
212 | static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size) | |
213 | { | |
214 | IPMIInterface *ii = opaque; | |
215 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
216 | IPMIBT *ib = iic->get_backend_data(ii); | |
217 | uint32_t ret = 0xff; | |
218 | ||
219 | switch (addr & 3) { | |
220 | case 0: | |
221 | ret = ib->control_reg; | |
222 | break; | |
223 | case 1: | |
224 | if (ib->outpos < ib->outlen) { | |
225 | ret = ib->outmsg[ib->outpos]; | |
226 | ib->outpos++; | |
227 | if (ib->outpos == ib->outlen) { | |
228 | ib->outpos = 0; | |
229 | ib->outlen = 0; | |
230 | } | |
231 | } else { | |
232 | ret = 0xff; | |
233 | } | |
234 | break; | |
235 | case 2: | |
236 | ret = ib->mask_reg; | |
237 | break; | |
238 | } | |
239 | return ret; | |
240 | } | |
241 | ||
242 | static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii) | |
243 | { | |
244 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
245 | ||
246 | ib->do_wake = 1; | |
247 | while (ib->do_wake) { | |
248 | ib->do_wake = 0; | |
249 | iic->handle_if_event(ii); | |
250 | } | |
251 | } | |
252 | ||
253 | static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val, | |
254 | unsigned size) | |
255 | { | |
256 | IPMIInterface *ii = opaque; | |
257 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
258 | IPMIBT *ib = iic->get_backend_data(ii); | |
259 | ||
260 | switch (addr & 3) { | |
261 | case 0: | |
262 | if (IPMI_BT_GET_CLR_WR(val)) { | |
263 | ib->inlen = 0; | |
264 | } | |
265 | if (IPMI_BT_GET_CLR_RD(val)) { | |
266 | ib->outpos = 0; | |
267 | } | |
268 | if (IPMI_BT_GET_B2H_ATN(val)) { | |
269 | IPMI_BT_SET_B2H_ATN(ib->control_reg, 0); | |
270 | } | |
271 | if (IPMI_BT_GET_SMS_ATN(val)) { | |
272 | IPMI_BT_SET_SMS_ATN(ib->control_reg, 0); | |
273 | } | |
274 | if (IPMI_BT_GET_HBUSY(val)) { | |
275 | /* Toggle */ | |
276 | IPMI_BT_SET_HBUSY(ib->control_reg, | |
277 | !IPMI_BT_GET_HBUSY(ib->control_reg)); | |
278 | } | |
279 | if (IPMI_BT_GET_H2B_ATN(val)) { | |
280 | IPMI_BT_SET_BBUSY(ib->control_reg, 1); | |
281 | ipmi_bt_signal(ib, ii); | |
282 | } | |
283 | break; | |
284 | ||
285 | case 1: | |
286 | if (ib->inlen < sizeof(ib->inmsg)) { | |
287 | ib->inmsg[ib->inlen] = val; | |
288 | } | |
289 | ib->inlen++; | |
290 | break; | |
291 | ||
292 | case 2: | |
293 | if (IPMI_BT_GET_B2H_IRQ_EN(val) != | |
294 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
295 | if (IPMI_BT_GET_B2H_IRQ_EN(val)) { | |
296 | if (IPMI_BT_GET_B2H_ATN(ib->control_reg) || | |
297 | IPMI_BT_GET_SMS_ATN(ib->control_reg)) { | |
298 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
299 | qemu_irq_raise(ib->irq); | |
300 | } | |
301 | IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1); | |
302 | } else { | |
303 | if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
304 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
305 | qemu_irq_lower(ib->irq); | |
306 | } | |
307 | IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); | |
308 | } | |
309 | } | |
310 | if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
311 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
312 | qemu_irq_lower(ib->irq); | |
313 | } | |
314 | break; | |
315 | } | |
316 | } | |
317 | ||
318 | static const MemoryRegionOps ipmi_bt_io_ops = { | |
319 | .read = ipmi_bt_ioport_read, | |
320 | .write = ipmi_bt_ioport_write, | |
321 | .impl = { | |
322 | .min_access_size = 1, | |
323 | .max_access_size = 1, | |
324 | }, | |
325 | .endianness = DEVICE_LITTLE_ENDIAN, | |
326 | }; | |
327 | ||
328 | static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq) | |
329 | { | |
330 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
331 | IPMIBT *ib = iic->get_backend_data(ii); | |
332 | ||
333 | if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) { | |
334 | return; | |
335 | } | |
336 | ||
337 | IPMI_BT_SET_SMS_ATN(ib->control_reg, val); | |
338 | if (val) { | |
339 | if (irq && ib->use_irq && ib->irqs_enabled && | |
340 | !IPMI_BT_GET_B2H_ATN(ib->control_reg) && | |
341 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
342 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
343 | qemu_irq_raise(ib->irq); | |
344 | } | |
345 | } else { | |
346 | if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) && | |
347 | IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
348 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
349 | qemu_irq_lower(ib->irq); | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
354 | static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold) | |
355 | { | |
356 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
357 | IPMIBT *ib = iic->get_backend_data(ii); | |
358 | ||
359 | if (is_cold) { | |
360 | /* Disable the BT interrupt on reset */ | |
361 | if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
362 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
363 | qemu_irq_lower(ib->irq); | |
364 | } | |
365 | IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); | |
366 | } | |
367 | } | |
368 | ||
369 | static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val) | |
370 | { | |
371 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
372 | IPMIBT *ib = iic->get_backend_data(ii); | |
373 | ||
374 | ib->irqs_enabled = val; | |
375 | } | |
376 | ||
377 | static void ipmi_bt_init(IPMIInterface *ii, Error **errp) | |
378 | { | |
379 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
380 | IPMIBT *ib = iic->get_backend_data(ii); | |
381 | ||
382 | ib->io_length = 3; | |
383 | ||
384 | memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3); | |
385 | } | |
386 | ||
a9b74079 CM |
387 | |
388 | #define TYPE_ISA_IPMI_BT "isa-ipmi-bt" | |
389 | #define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \ | |
390 | TYPE_ISA_IPMI_BT) | |
391 | ||
392 | typedef struct ISAIPMIBTDevice { | |
393 | ISADevice dev; | |
f4014512 | 394 | int32_t isairq; |
a9b74079 | 395 | IPMIBT bt; |
15139b8e | 396 | uint32_t uuid; |
a9b74079 CM |
397 | } ISAIPMIBTDevice; |
398 | ||
15139b8e CM |
399 | static void ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) |
400 | { | |
401 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); | |
402 | ||
403 | info->interface_name = "bt"; | |
404 | info->interface_type = IPMI_SMBIOS_BT; | |
405 | info->ipmi_spec_major_revision = 2; | |
406 | info->ipmi_spec_minor_revision = 0; | |
407 | info->base_address = iib->bt.io_base; | |
408 | info->register_length = iib->bt.io_length; | |
409 | info->register_spacing = 1; | |
410 | info->memspace = IPMI_MEMSPACE_IO; | |
411 | info->irq_type = IPMI_LEVEL_IRQ; | |
412 | info->interrupt_number = iib->isairq; | |
413 | info->i2c_slave_address = iib->bt.bmc->slave_addr; | |
414 | info->uuid = iib->uuid; | |
415 | } | |
416 | ||
417 | static void ipmi_bt_class_init(IPMIInterfaceClass *iic) | |
418 | { | |
419 | iic->init = ipmi_bt_init; | |
420 | iic->set_atn = ipmi_bt_set_atn; | |
421 | iic->handle_rsp = ipmi_bt_handle_rsp; | |
422 | iic->handle_if_event = ipmi_bt_handle_event; | |
423 | iic->set_irq_enable = ipmi_bt_set_irq_enable; | |
424 | iic->reset = ipmi_bt_handle_reset; | |
425 | iic->get_fwinfo = ipmi_bt_get_fwinfo; | |
426 | } | |
427 | ||
a9b74079 CM |
428 | static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) |
429 | { | |
430 | ISADevice *isadev = ISA_DEVICE(dev); | |
431 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev); | |
432 | IPMIInterface *ii = IPMI_INTERFACE(dev); | |
433 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
434 | ||
435 | if (!iib->bt.bmc) { | |
436 | error_setg(errp, "IPMI device requires a bmc attribute to be set"); | |
437 | return; | |
438 | } | |
439 | ||
15139b8e CM |
440 | iib->uuid = ipmi_next_uuid(); |
441 | ||
a9b74079 CM |
442 | iib->bt.bmc->intf = ii; |
443 | ||
444 | iic->init(ii, errp); | |
445 | if (*errp) | |
446 | return; | |
447 | ||
448 | if (iib->isairq > 0) { | |
449 | isa_init_irq(isadev, &iib->bt.irq, iib->isairq); | |
450 | iib->bt.use_irq = 1; | |
451 | } | |
452 | ||
453 | qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); | |
454 | ||
455 | isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); | |
456 | } | |
457 | ||
efbb649d CM |
458 | static int ipmi_bt_vmstate_post_load(void *opaque, int version) |
459 | { | |
460 | IPMIBT *ib = opaque; | |
461 | ||
462 | /* Make sure all the values are sane. */ | |
463 | if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE || | |
464 | ib->outpos >= ib->outlen) { | |
465 | qemu_log_mask(LOG_GUEST_ERROR, | |
466 | "ipmi:bt: vmstate transfer received bad out values: %d %d\n", | |
467 | ib->outpos, ib->outlen); | |
468 | ib->outpos = 0; | |
469 | ib->outlen = 0; | |
470 | } | |
471 | ||
472 | if (ib->inlen >= MAX_IPMI_MSG_SIZE) { | |
473 | qemu_log_mask(LOG_GUEST_ERROR, | |
474 | "ipmi:bt: vmstate transfer received bad in value: %d\n", | |
475 | ib->inlen); | |
476 | ib->inlen = 0; | |
477 | } | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
482 | const VMStateDescription vmstate_IPMIBT = { | |
483 | .name = TYPE_IPMI_INTERFACE_PREFIX "bt", | |
bd66bcfc CM |
484 | .version_id = 1, |
485 | .minimum_version_id = 1, | |
efbb649d CM |
486 | .post_load = ipmi_bt_vmstate_post_load, |
487 | .fields = (VMStateField[]) { | |
488 | VMSTATE_BOOL(obf_irq_set, IPMIBT), | |
489 | VMSTATE_BOOL(atn_irq_set, IPMIBT), | |
490 | VMSTATE_BOOL(irqs_enabled, IPMIBT), | |
491 | VMSTATE_UINT32(outpos, IPMIBT), | |
492 | VMSTATE_UINT32(outlen, IPMIBT), | |
493 | VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE), | |
494 | VMSTATE_UINT32(inlen, IPMIBT), | |
495 | VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE), | |
496 | VMSTATE_UINT8(control_reg, IPMIBT), | |
497 | VMSTATE_UINT8(mask_reg, IPMIBT), | |
498 | VMSTATE_UINT8(waiting_rsp, IPMIBT), | |
499 | VMSTATE_UINT8(waiting_seq, IPMIBT), | |
500 | VMSTATE_END_OF_LIST() | |
501 | } | |
502 | }; | |
503 | ||
504 | static const VMStateDescription vmstate_ISAIPMIBTDevice = { | |
505 | .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", | |
506 | .version_id = 2, | |
507 | .minimum_version_id = 2, | |
508 | /* | |
509 | * Version 1 had messed up the array transfer, it's not even usable | |
510 | * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer | |
511 | * the buffer length, so random things would happen. | |
512 | */ | |
bd66bcfc | 513 | .fields = (VMStateField[]) { |
efbb649d | 514 | VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), |
bd66bcfc CM |
515 | VMSTATE_END_OF_LIST() |
516 | } | |
517 | }; | |
518 | ||
a9b74079 CM |
519 | static void isa_ipmi_bt_init(Object *obj) |
520 | { | |
521 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); | |
522 | ||
523 | ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); | |
bd66bcfc CM |
524 | |
525 | vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); | |
a9b74079 CM |
526 | } |
527 | ||
528 | static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) | |
529 | { | |
530 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); | |
531 | ||
532 | return &iib->bt; | |
533 | } | |
534 | ||
535 | static Property ipmi_isa_properties[] = { | |
536 | DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), | |
537 | DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), | |
538 | DEFINE_PROP_END_OF_LIST(), | |
539 | }; | |
540 | ||
541 | static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) | |
542 | { | |
543 | DeviceClass *dc = DEVICE_CLASS(oc); | |
544 | IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); | |
545 | ||
546 | dc->realize = isa_ipmi_bt_realize; | |
547 | dc->props = ipmi_isa_properties; | |
548 | ||
549 | iic->get_backend_data = isa_ipmi_bt_get_backend_data; | |
550 | ipmi_bt_class_init(iic); | |
551 | } | |
552 | ||
553 | static const TypeInfo isa_ipmi_bt_info = { | |
554 | .name = TYPE_ISA_IPMI_BT, | |
555 | .parent = TYPE_ISA_DEVICE, | |
556 | .instance_size = sizeof(ISAIPMIBTDevice), | |
557 | .instance_init = isa_ipmi_bt_init, | |
558 | .class_init = isa_ipmi_bt_class_init, | |
559 | .interfaces = (InterfaceInfo[]) { | |
560 | { TYPE_IPMI_INTERFACE }, | |
561 | { } | |
562 | } | |
563 | }; | |
564 | ||
565 | static void ipmi_register_types(void) | |
566 | { | |
567 | type_register_static(&isa_ipmi_bt_info); | |
568 | } | |
569 | ||
570 | type_init(ipmi_register_types) |