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 | */ |
24 | |
25 | #include "qemu/osdep.h" |
26 | #include "qemu/log.h" |
27 | #include "qemu/module.h" |
28 | #include "qapi/error.h" |
29 | #include "hw/ipmi/ipmi.h" |
30 | #include "hw/irq.h" |
31 | #include "hw/isa/isa.h" |
32 | #include "hw/qdev-properties.h" |
33 | #include "migration/vmstate.h" |
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 | |
44 | #define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) |
45 | |
46 | #define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) |
47 | |
48 | #define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) |
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) |
52 | #define IPMI_BT_SET_B2H_ATN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ |
53 | (!!(v) << IPMI_BT_B2H_ATN_BIT))) |
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) |
57 | #define IPMI_BT_SET_SMS_ATN(d, v) ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ |
58 | (!!(v) << IPMI_BT_SMS_ATN_BIT))) |
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) |
62 | #define IPMI_BT_SET_HBUSY(d, v) ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ |
63 | (!!(v) << IPMI_BT_HBUSY_BIT))) |
64 | |
65 | #define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) |
66 | #define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ |
67 | (!!(v) << IPMI_BT_BBUSY_BIT))) |
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) |
76 | #define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\ |
77 | (!!(v) << IPMI_BT_B2H_IRQ_EN_BIT))) |
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) |
81 | #define IPMI_BT_SET_B2H_IRQ(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ |
82 | (!!(v) << IPMI_BT_B2H_IRQ_BIT))) |
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 | |
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; |
394 | int32_t isairq; |
395 | IPMIBT bt; |
396 | uint32_t uuid; |
397 | } ISAIPMIBTDevice; |
398 | |
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 | |
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 | |
440 | iib->uuid = ipmi_next_uuid(); |
441 | |
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 | |
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" , |
484 | .version_id = 1, |
485 | .minimum_version_id = 1, |
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 | */ |
513 | .fields = (VMStateField[]) { |
514 | VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), |
515 | VMSTATE_END_OF_LIST() |
516 | } |
517 | }; |
518 | |
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); |
524 | |
525 | vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); |
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) |
571 | |