]>
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 | */ | |
0430891c | 24 | #include "qemu/osdep.h" |
da34e65c | 25 | #include "qapi/error.h" |
a9b74079 CM |
26 | #include "hw/hw.h" |
27 | #include "hw/ipmi/ipmi.h" | |
28 | #include "hw/isa/isa.h" | |
29 | #include "hw/i386/pc.h" | |
30 | ||
31 | /* Control register */ | |
32 | #define IPMI_BT_CLR_WR_BIT 0 | |
33 | #define IPMI_BT_CLR_RD_BIT 1 | |
34 | #define IPMI_BT_H2B_ATN_BIT 2 | |
35 | #define IPMI_BT_B2H_ATN_BIT 3 | |
36 | #define IPMI_BT_SMS_ATN_BIT 4 | |
37 | #define IPMI_BT_HBUSY_BIT 6 | |
38 | #define IPMI_BT_BBUSY_BIT 7 | |
39 | ||
a9b74079 | 40 | #define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) |
a9b74079 | 41 | |
a9b74079 | 42 | #define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) |
a9b74079 | 43 | |
a9b74079 | 44 | #define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) |
a9b74079 CM |
45 | |
46 | #define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) | |
47 | #define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) | |
cb9a05a4 CM |
48 | #define IPMI_BT_SET_B2H_ATN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ |
49 | (((v) & 1) << IPMI_BT_B2H_ATN_BIT))) | |
a9b74079 CM |
50 | |
51 | #define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) | |
52 | #define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) | |
cb9a05a4 CM |
53 | #define IPMI_BT_SET_SMS_ATN(d, v) ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ |
54 | (((v) & 1) << IPMI_BT_SMS_ATN_BIT))) | |
a9b74079 CM |
55 | |
56 | #define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) | |
57 | #define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) | |
cb9a05a4 CM |
58 | #define IPMI_BT_SET_HBUSY(d, v) ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ |
59 | (((v) & 1) << IPMI_BT_HBUSY_BIT))) | |
a9b74079 CM |
60 | |
61 | #define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) | |
cb9a05a4 CM |
62 | #define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ |
63 | (((v) & 1) << IPMI_BT_BBUSY_BIT))) | |
a9b74079 CM |
64 | |
65 | ||
66 | /* Mask register */ | |
67 | #define IPMI_BT_B2H_IRQ_EN_BIT 0 | |
68 | #define IPMI_BT_B2H_IRQ_BIT 1 | |
69 | ||
70 | #define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) | |
71 | #define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) | |
cb9a05a4 CM |
72 | #define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\ |
73 | (((v) & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) | |
a9b74079 CM |
74 | |
75 | #define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) | |
76 | #define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) | |
cb9a05a4 CM |
77 | #define IPMI_BT_SET_B2H_IRQ(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ |
78 | (((v) & 1) << IPMI_BT_B2H_IRQ_BIT))) | |
a9b74079 CM |
79 | |
80 | typedef struct IPMIBT { | |
81 | IPMIBmc *bmc; | |
82 | ||
83 | bool do_wake; | |
84 | ||
85 | qemu_irq irq; | |
86 | ||
87 | uint32_t io_base; | |
88 | unsigned long io_length; | |
89 | MemoryRegion io; | |
90 | ||
91 | bool obf_irq_set; | |
92 | bool atn_irq_set; | |
93 | bool use_irq; | |
94 | bool irqs_enabled; | |
95 | ||
96 | uint8_t outmsg[MAX_IPMI_MSG_SIZE]; | |
97 | uint32_t outpos; | |
98 | uint32_t outlen; | |
99 | ||
100 | uint8_t inmsg[MAX_IPMI_MSG_SIZE]; | |
101 | uint32_t inlen; | |
102 | ||
103 | uint8_t control_reg; | |
104 | uint8_t mask_reg; | |
105 | ||
106 | /* | |
107 | * This is a response number that we send with the command to make | |
108 | * sure that the response matches the command. | |
109 | */ | |
110 | uint8_t waiting_rsp; | |
111 | uint8_t waiting_seq; | |
112 | } IPMIBT; | |
113 | ||
114 | #define IPMI_CMD_GET_BT_INTF_CAP 0x36 | |
115 | ||
116 | static void ipmi_bt_handle_event(IPMIInterface *ii) | |
117 | { | |
118 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
119 | IPMIBT *ib = iic->get_backend_data(ii); | |
120 | ||
121 | if (ib->inlen < 4) { | |
122 | goto out; | |
123 | } | |
124 | /* Note that overruns are handled by handle_command */ | |
125 | if (ib->inmsg[0] != (ib->inlen - 1)) { | |
126 | /* Length mismatch, just ignore. */ | |
127 | IPMI_BT_SET_BBUSY(ib->control_reg, 1); | |
128 | ib->inlen = 0; | |
129 | goto out; | |
130 | } | |
131 | if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && | |
132 | (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { | |
133 | /* We handle this one ourselves. */ | |
134 | ib->outmsg[0] = 9; | |
135 | ib->outmsg[1] = ib->inmsg[1] | 0x04; | |
136 | ib->outmsg[2] = ib->inmsg[2]; | |
137 | ib->outmsg[3] = ib->inmsg[3]; | |
138 | ib->outmsg[4] = 0; | |
139 | ib->outmsg[5] = 1; /* Only support 1 outstanding request. */ | |
140 | if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */ | |
141 | ib->outmsg[6] = 0xff; | |
142 | } else { | |
143 | ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg); | |
144 | } | |
145 | if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */ | |
146 | ib->outmsg[7] = 0xff; | |
147 | } else { | |
148 | ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg); | |
149 | } | |
150 | ib->outmsg[8] = 10; /* Max request to response time */ | |
151 | ib->outmsg[9] = 0; /* Don't recommend retries */ | |
152 | ib->outlen = 10; | |
153 | IPMI_BT_SET_BBUSY(ib->control_reg, 0); | |
154 | IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); | |
155 | if (ib->use_irq && ib->irqs_enabled && | |
156 | !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && | |
157 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
158 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
159 | qemu_irq_raise(ib->irq); | |
160 | } | |
161 | goto out; | |
162 | } | |
163 | ib->waiting_seq = ib->inmsg[2]; | |
164 | ib->inmsg[2] = ib->inmsg[1]; | |
165 | { | |
166 | IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc); | |
167 | bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, | |
168 | sizeof(ib->inmsg), ib->waiting_rsp); | |
169 | } | |
170 | out: | |
171 | return; | |
172 | } | |
173 | ||
174 | static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, | |
175 | unsigned char *rsp, unsigned int rsp_len) | |
176 | { | |
177 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
178 | IPMIBT *ib = iic->get_backend_data(ii); | |
179 | ||
180 | if (ib->waiting_rsp == msg_id) { | |
181 | ib->waiting_rsp++; | |
182 | if (rsp_len > (sizeof(ib->outmsg) - 2)) { | |
183 | ib->outmsg[0] = 4; | |
184 | ib->outmsg[1] = rsp[0]; | |
185 | ib->outmsg[2] = ib->waiting_seq; | |
186 | ib->outmsg[3] = rsp[1]; | |
187 | ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; | |
188 | ib->outlen = 5; | |
189 | } else { | |
190 | ib->outmsg[0] = rsp_len + 1; | |
191 | ib->outmsg[1] = rsp[0]; | |
192 | ib->outmsg[2] = ib->waiting_seq; | |
193 | memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1); | |
194 | ib->outlen = rsp_len + 2; | |
195 | } | |
196 | IPMI_BT_SET_BBUSY(ib->control_reg, 0); | |
197 | IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); | |
198 | if (ib->use_irq && ib->irqs_enabled && | |
199 | !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && | |
200 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
201 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
202 | qemu_irq_raise(ib->irq); | |
203 | } | |
204 | } | |
205 | } | |
206 | ||
207 | ||
208 | static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size) | |
209 | { | |
210 | IPMIInterface *ii = opaque; | |
211 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
212 | IPMIBT *ib = iic->get_backend_data(ii); | |
213 | uint32_t ret = 0xff; | |
214 | ||
215 | switch (addr & 3) { | |
216 | case 0: | |
217 | ret = ib->control_reg; | |
218 | break; | |
219 | case 1: | |
220 | if (ib->outpos < ib->outlen) { | |
221 | ret = ib->outmsg[ib->outpos]; | |
222 | ib->outpos++; | |
223 | if (ib->outpos == ib->outlen) { | |
224 | ib->outpos = 0; | |
225 | ib->outlen = 0; | |
226 | } | |
227 | } else { | |
228 | ret = 0xff; | |
229 | } | |
230 | break; | |
231 | case 2: | |
232 | ret = ib->mask_reg; | |
233 | break; | |
234 | } | |
235 | return ret; | |
236 | } | |
237 | ||
238 | static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii) | |
239 | { | |
240 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
241 | ||
242 | ib->do_wake = 1; | |
243 | while (ib->do_wake) { | |
244 | ib->do_wake = 0; | |
245 | iic->handle_if_event(ii); | |
246 | } | |
247 | } | |
248 | ||
249 | static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val, | |
250 | unsigned size) | |
251 | { | |
252 | IPMIInterface *ii = opaque; | |
253 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
254 | IPMIBT *ib = iic->get_backend_data(ii); | |
255 | ||
256 | switch (addr & 3) { | |
257 | case 0: | |
258 | if (IPMI_BT_GET_CLR_WR(val)) { | |
259 | ib->inlen = 0; | |
260 | } | |
261 | if (IPMI_BT_GET_CLR_RD(val)) { | |
262 | ib->outpos = 0; | |
263 | } | |
264 | if (IPMI_BT_GET_B2H_ATN(val)) { | |
265 | IPMI_BT_SET_B2H_ATN(ib->control_reg, 0); | |
266 | } | |
267 | if (IPMI_BT_GET_SMS_ATN(val)) { | |
268 | IPMI_BT_SET_SMS_ATN(ib->control_reg, 0); | |
269 | } | |
270 | if (IPMI_BT_GET_HBUSY(val)) { | |
271 | /* Toggle */ | |
272 | IPMI_BT_SET_HBUSY(ib->control_reg, | |
273 | !IPMI_BT_GET_HBUSY(ib->control_reg)); | |
274 | } | |
275 | if (IPMI_BT_GET_H2B_ATN(val)) { | |
276 | IPMI_BT_SET_BBUSY(ib->control_reg, 1); | |
277 | ipmi_bt_signal(ib, ii); | |
278 | } | |
279 | break; | |
280 | ||
281 | case 1: | |
282 | if (ib->inlen < sizeof(ib->inmsg)) { | |
283 | ib->inmsg[ib->inlen] = val; | |
284 | } | |
285 | ib->inlen++; | |
286 | break; | |
287 | ||
288 | case 2: | |
289 | if (IPMI_BT_GET_B2H_IRQ_EN(val) != | |
290 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
291 | if (IPMI_BT_GET_B2H_IRQ_EN(val)) { | |
292 | if (IPMI_BT_GET_B2H_ATN(ib->control_reg) || | |
293 | IPMI_BT_GET_SMS_ATN(ib->control_reg)) { | |
294 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
295 | qemu_irq_raise(ib->irq); | |
296 | } | |
297 | IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1); | |
298 | } else { | |
299 | if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
300 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
301 | qemu_irq_lower(ib->irq); | |
302 | } | |
303 | IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); | |
304 | } | |
305 | } | |
306 | if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
307 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
308 | qemu_irq_lower(ib->irq); | |
309 | } | |
310 | break; | |
311 | } | |
312 | } | |
313 | ||
314 | static const MemoryRegionOps ipmi_bt_io_ops = { | |
315 | .read = ipmi_bt_ioport_read, | |
316 | .write = ipmi_bt_ioport_write, | |
317 | .impl = { | |
318 | .min_access_size = 1, | |
319 | .max_access_size = 1, | |
320 | }, | |
321 | .endianness = DEVICE_LITTLE_ENDIAN, | |
322 | }; | |
323 | ||
324 | static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq) | |
325 | { | |
326 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
327 | IPMIBT *ib = iic->get_backend_data(ii); | |
328 | ||
329 | if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) { | |
330 | return; | |
331 | } | |
332 | ||
333 | IPMI_BT_SET_SMS_ATN(ib->control_reg, val); | |
334 | if (val) { | |
335 | if (irq && ib->use_irq && ib->irqs_enabled && | |
336 | !IPMI_BT_GET_B2H_ATN(ib->control_reg) && | |
337 | IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { | |
338 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); | |
339 | qemu_irq_raise(ib->irq); | |
340 | } | |
341 | } else { | |
342 | if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) && | |
343 | IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
344 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
345 | qemu_irq_lower(ib->irq); | |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold) | |
351 | { | |
352 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
353 | IPMIBT *ib = iic->get_backend_data(ii); | |
354 | ||
355 | if (is_cold) { | |
356 | /* Disable the BT interrupt on reset */ | |
357 | if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { | |
358 | IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); | |
359 | qemu_irq_lower(ib->irq); | |
360 | } | |
361 | IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); | |
362 | } | |
363 | } | |
364 | ||
365 | static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val) | |
366 | { | |
367 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
368 | IPMIBT *ib = iic->get_backend_data(ii); | |
369 | ||
370 | ib->irqs_enabled = val; | |
371 | } | |
372 | ||
373 | static void ipmi_bt_init(IPMIInterface *ii, Error **errp) | |
374 | { | |
375 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
376 | IPMIBT *ib = iic->get_backend_data(ii); | |
377 | ||
378 | ib->io_length = 3; | |
379 | ||
380 | memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3); | |
381 | } | |
382 | ||
a9b74079 CM |
383 | |
384 | #define TYPE_ISA_IPMI_BT "isa-ipmi-bt" | |
385 | #define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \ | |
386 | TYPE_ISA_IPMI_BT) | |
387 | ||
388 | typedef struct ISAIPMIBTDevice { | |
389 | ISADevice dev; | |
f4014512 | 390 | int32_t isairq; |
a9b74079 | 391 | IPMIBT bt; |
15139b8e | 392 | uint32_t uuid; |
a9b74079 CM |
393 | } ISAIPMIBTDevice; |
394 | ||
15139b8e CM |
395 | static void ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) |
396 | { | |
397 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); | |
398 | ||
399 | info->interface_name = "bt"; | |
400 | info->interface_type = IPMI_SMBIOS_BT; | |
401 | info->ipmi_spec_major_revision = 2; | |
402 | info->ipmi_spec_minor_revision = 0; | |
403 | info->base_address = iib->bt.io_base; | |
404 | info->register_length = iib->bt.io_length; | |
405 | info->register_spacing = 1; | |
406 | info->memspace = IPMI_MEMSPACE_IO; | |
407 | info->irq_type = IPMI_LEVEL_IRQ; | |
408 | info->interrupt_number = iib->isairq; | |
409 | info->i2c_slave_address = iib->bt.bmc->slave_addr; | |
410 | info->uuid = iib->uuid; | |
411 | } | |
412 | ||
413 | static void ipmi_bt_class_init(IPMIInterfaceClass *iic) | |
414 | { | |
415 | iic->init = ipmi_bt_init; | |
416 | iic->set_atn = ipmi_bt_set_atn; | |
417 | iic->handle_rsp = ipmi_bt_handle_rsp; | |
418 | iic->handle_if_event = ipmi_bt_handle_event; | |
419 | iic->set_irq_enable = ipmi_bt_set_irq_enable; | |
420 | iic->reset = ipmi_bt_handle_reset; | |
421 | iic->get_fwinfo = ipmi_bt_get_fwinfo; | |
422 | } | |
423 | ||
a9b74079 CM |
424 | static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) |
425 | { | |
426 | ISADevice *isadev = ISA_DEVICE(dev); | |
427 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev); | |
428 | IPMIInterface *ii = IPMI_INTERFACE(dev); | |
429 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | |
430 | ||
431 | if (!iib->bt.bmc) { | |
432 | error_setg(errp, "IPMI device requires a bmc attribute to be set"); | |
433 | return; | |
434 | } | |
435 | ||
15139b8e CM |
436 | iib->uuid = ipmi_next_uuid(); |
437 | ||
a9b74079 CM |
438 | iib->bt.bmc->intf = ii; |
439 | ||
440 | iic->init(ii, errp); | |
441 | if (*errp) | |
442 | return; | |
443 | ||
444 | if (iib->isairq > 0) { | |
445 | isa_init_irq(isadev, &iib->bt.irq, iib->isairq); | |
446 | iib->bt.use_irq = 1; | |
447 | } | |
448 | ||
449 | qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); | |
450 | ||
451 | isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); | |
452 | } | |
453 | ||
bd66bcfc CM |
454 | static const VMStateDescription vmstate_ISAIPMIBTDevice = { |
455 | .name = TYPE_IPMI_INTERFACE, | |
456 | .version_id = 1, | |
457 | .minimum_version_id = 1, | |
458 | .fields = (VMStateField[]) { | |
459 | VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice), | |
460 | VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice), | |
461 | VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice), | |
462 | VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice), | |
463 | VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice), | |
59046ec2 HP |
464 | VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, bt.outlen), |
465 | VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, bt.inlen), | |
bd66bcfc CM |
466 | VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice), |
467 | VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice), | |
468 | VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice), | |
469 | VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice), | |
470 | VMSTATE_END_OF_LIST() | |
471 | } | |
472 | }; | |
473 | ||
a9b74079 CM |
474 | static void isa_ipmi_bt_init(Object *obj) |
475 | { | |
476 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); | |
477 | ||
478 | ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); | |
bd66bcfc CM |
479 | |
480 | vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); | |
a9b74079 CM |
481 | } |
482 | ||
483 | static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) | |
484 | { | |
485 | ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); | |
486 | ||
487 | return &iib->bt; | |
488 | } | |
489 | ||
490 | static Property ipmi_isa_properties[] = { | |
491 | DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), | |
492 | DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), | |
493 | DEFINE_PROP_END_OF_LIST(), | |
494 | }; | |
495 | ||
496 | static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) | |
497 | { | |
498 | DeviceClass *dc = DEVICE_CLASS(oc); | |
499 | IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); | |
500 | ||
501 | dc->realize = isa_ipmi_bt_realize; | |
502 | dc->props = ipmi_isa_properties; | |
503 | ||
504 | iic->get_backend_data = isa_ipmi_bt_get_backend_data; | |
505 | ipmi_bt_class_init(iic); | |
506 | } | |
507 | ||
508 | static const TypeInfo isa_ipmi_bt_info = { | |
509 | .name = TYPE_ISA_IPMI_BT, | |
510 | .parent = TYPE_ISA_DEVICE, | |
511 | .instance_size = sizeof(ISAIPMIBTDevice), | |
512 | .instance_init = isa_ipmi_bt_init, | |
513 | .class_init = isa_ipmi_bt_class_init, | |
514 | .interfaces = (InterfaceInfo[]) { | |
515 | { TYPE_IPMI_INTERFACE }, | |
516 | { } | |
517 | } | |
518 | }; | |
519 | ||
520 | static void ipmi_register_types(void) | |
521 | { | |
522 | type_register_static(&isa_ipmi_bt_info); | |
523 | } | |
524 | ||
525 | type_init(ipmi_register_types) |