]>
Commit | Line | Data |
---|---|---|
45dcdb9d PA |
1 | /* |
2 | * QEMU ICH9 TCO emulation tests | |
3 | * | |
4 | * Copyright (c) 2015 Paulo Alcantara <[email protected]> | |
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 | */ | |
681c28a3 | 9 | #include "qemu/osdep.h" |
45dcdb9d PA |
10 | |
11 | #include "libqtest.h" | |
12 | #include "libqos/pci.h" | |
13 | #include "libqos/pci-pc.h" | |
14 | #include "hw/pci/pci_regs.h" | |
15 | #include "hw/i386/ich9.h" | |
16 | #include "hw/acpi/ich9.h" | |
17 | #include "hw/acpi/tco.h" | |
18 | ||
19 | #define RCBA_BASE_ADDR 0xfed1c000 | |
20 | #define PM_IO_BASE_ADDR 0xb000 | |
21 | ||
22 | enum { | |
23 | TCO_RLD_DEFAULT = 0x0000, | |
24 | TCO_DAT_IN_DEFAULT = 0x00, | |
25 | TCO_DAT_OUT_DEFAULT = 0x00, | |
26 | TCO1_STS_DEFAULT = 0x0000, | |
27 | TCO2_STS_DEFAULT = 0x0000, | |
28 | TCO1_CNT_DEFAULT = 0x0000, | |
29 | TCO2_CNT_DEFAULT = 0x0008, | |
30 | TCO_MESSAGE1_DEFAULT = 0x00, | |
31 | TCO_MESSAGE2_DEFAULT = 0x00, | |
32 | TCO_WDCNT_DEFAULT = 0x00, | |
33 | TCO_TMR_DEFAULT = 0x0004, | |
34 | SW_IRQ_GEN_DEFAULT = 0x03, | |
35 | }; | |
36 | ||
37 | #define TCO_SECS_TO_TICKS(secs) (((secs) * 10) / 6) | |
38 | #define TCO_TICKS_TO_SECS(ticks) (((ticks) * 6) / 10) | |
39 | ||
40 | typedef struct { | |
41 | const char *args; | |
5add35be | 42 | bool noreboot; |
45dcdb9d | 43 | QPCIDevice *dev; |
b4ba67d9 | 44 | QPCIBar tco_io_bar; |
34779e8c | 45 | QPCIBus *bus; |
45dcdb9d PA |
46 | } TestData; |
47 | ||
34779e8c MAL |
48 | static void test_end(TestData *d) |
49 | { | |
50 | g_free(d->dev); | |
51 | qpci_free_pc(d->bus); | |
52 | qtest_end(); | |
53 | } | |
54 | ||
45dcdb9d PA |
55 | static void test_init(TestData *d) |
56 | { | |
45dcdb9d | 57 | QTestState *qs; |
45dcdb9d | 58 | |
78b27bad EB |
59 | qs = qtest_startf("-machine q35 %s %s", |
60 | d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", | |
61 | !d->args ? "" : d->args); | |
62 | global_qtest = qs; | |
45dcdb9d | 63 | qtest_irq_intercept_in(qs, "ioapic"); |
45dcdb9d | 64 | |
34779e8c MAL |
65 | d->bus = qpci_init_pc(NULL); |
66 | d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00)); | |
45dcdb9d PA |
67 | g_assert(d->dev != NULL); |
68 | ||
45dcdb9d PA |
69 | qpci_device_enable(d->dev); |
70 | ||
45dcdb9d | 71 | /* set ACPI PM I/O space base address */ |
c4fc82bf | 72 | qpci_config_writel(d->dev, ICH9_LPC_PMBASE, PM_IO_BASE_ADDR | 0x1); |
45dcdb9d | 73 | /* enable ACPI I/O */ |
c4fc82bf | 74 | qpci_config_writeb(d->dev, ICH9_LPC_ACPI_CTRL, 0x80); |
45dcdb9d | 75 | /* set Root Complex BAR */ |
c4fc82bf | 76 | qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1); |
45dcdb9d | 77 | |
b4ba67d9 | 78 | d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60); |
45dcdb9d PA |
79 | } |
80 | ||
81 | static void stop_tco(const TestData *d) | |
82 | { | |
83 | uint32_t val; | |
84 | ||
b4ba67d9 | 85 | val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); |
45dcdb9d | 86 | val |= TCO_TMR_HLT; |
b4ba67d9 | 87 | qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); |
45dcdb9d PA |
88 | } |
89 | ||
90 | static void start_tco(const TestData *d) | |
91 | { | |
92 | uint32_t val; | |
93 | ||
b4ba67d9 | 94 | val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); |
45dcdb9d | 95 | val &= ~TCO_TMR_HLT; |
b4ba67d9 | 96 | qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); |
45dcdb9d PA |
97 | } |
98 | ||
99 | static void load_tco(const TestData *d) | |
100 | { | |
b4ba67d9 | 101 | qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4); |
45dcdb9d PA |
102 | } |
103 | ||
104 | static void set_tco_timeout(const TestData *d, uint16_t ticks) | |
105 | { | |
b4ba67d9 | 106 | qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks); |
45dcdb9d PA |
107 | } |
108 | ||
109 | static void clear_tco_status(const TestData *d) | |
110 | { | |
b4ba67d9 DG |
111 | qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008); |
112 | qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002); | |
113 | qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004); | |
45dcdb9d PA |
114 | } |
115 | ||
116 | static void reset_on_second_timeout(bool enable) | |
117 | { | |
118 | uint32_t val; | |
119 | ||
120 | val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS); | |
121 | if (enable) { | |
122 | val &= ~ICH9_CC_GCS_NO_REBOOT; | |
123 | } else { | |
124 | val |= ICH9_CC_GCS_NO_REBOOT; | |
125 | } | |
126 | writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val); | |
127 | } | |
128 | ||
129 | static void test_tco_defaults(void) | |
130 | { | |
131 | TestData d; | |
132 | ||
133 | d.args = NULL; | |
5add35be | 134 | d.noreboot = true; |
45dcdb9d | 135 | test_init(&d); |
b4ba67d9 | 136 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==, |
45dcdb9d PA |
137 | TCO_RLD_DEFAULT); |
138 | /* TCO_DAT_IN & TCO_DAT_OUT */ | |
b4ba67d9 | 139 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==, |
45dcdb9d PA |
140 | (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT); |
141 | /* TCO1_STS & TCO2_STS */ | |
b4ba67d9 | 142 | g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==, |
45dcdb9d PA |
143 | (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT); |
144 | /* TCO1_CNT & TCO2_CNT */ | |
b4ba67d9 | 145 | g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==, |
45dcdb9d PA |
146 | (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT); |
147 | /* TCO_MESSAGE1 & TCO_MESSAGE2 */ | |
b4ba67d9 | 148 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==, |
45dcdb9d | 149 | (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT); |
b4ba67d9 | 150 | g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==, |
45dcdb9d | 151 | TCO_WDCNT_DEFAULT); |
b4ba67d9 | 152 | g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==, |
45dcdb9d | 153 | SW_IRQ_GEN_DEFAULT); |
b4ba67d9 | 154 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==, |
45dcdb9d | 155 | TCO_TMR_DEFAULT); |
34779e8c | 156 | test_end(&d); |
45dcdb9d PA |
157 | } |
158 | ||
159 | static void test_tco_timeout(void) | |
160 | { | |
161 | TestData d; | |
162 | const uint16_t ticks = TCO_SECS_TO_TICKS(4); | |
163 | uint32_t val; | |
164 | int ret; | |
165 | ||
166 | d.args = NULL; | |
5add35be | 167 | d.noreboot = true; |
45dcdb9d PA |
168 | test_init(&d); |
169 | ||
170 | stop_tco(&d); | |
171 | clear_tco_status(&d); | |
172 | reset_on_second_timeout(false); | |
173 | set_tco_timeout(&d, ticks); | |
174 | load_tco(&d); | |
175 | start_tco(&d); | |
176 | clock_step(ticks * TCO_TICK_NSEC); | |
177 | ||
178 | /* test first timeout */ | |
b4ba67d9 | 179 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); |
45dcdb9d PA |
180 | ret = val & TCO_TIMEOUT ? 1 : 0; |
181 | g_assert(ret == 1); | |
182 | ||
183 | /* test clearing timeout bit */ | |
184 | val |= TCO_TIMEOUT; | |
b4ba67d9 DG |
185 | qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); |
186 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); | |
45dcdb9d PA |
187 | ret = val & TCO_TIMEOUT ? 1 : 0; |
188 | g_assert(ret == 0); | |
189 | ||
190 | /* test second timeout */ | |
191 | clock_step(ticks * TCO_TICK_NSEC); | |
b4ba67d9 | 192 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); |
45dcdb9d PA |
193 | ret = val & TCO_TIMEOUT ? 1 : 0; |
194 | g_assert(ret == 1); | |
b4ba67d9 | 195 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); |
45dcdb9d PA |
196 | ret = val & TCO_SECOND_TO_STS ? 1 : 0; |
197 | g_assert(ret == 1); | |
198 | ||
199 | stop_tco(&d); | |
34779e8c | 200 | test_end(&d); |
45dcdb9d PA |
201 | } |
202 | ||
203 | static void test_tco_max_timeout(void) | |
204 | { | |
205 | TestData d; | |
206 | const uint16_t ticks = 0xffff; | |
207 | uint32_t val; | |
208 | int ret; | |
209 | ||
210 | d.args = NULL; | |
5add35be | 211 | d.noreboot = true; |
45dcdb9d PA |
212 | test_init(&d); |
213 | ||
214 | stop_tco(&d); | |
215 | clear_tco_status(&d); | |
216 | reset_on_second_timeout(false); | |
217 | set_tco_timeout(&d, ticks); | |
218 | load_tco(&d); | |
219 | start_tco(&d); | |
220 | clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); | |
221 | ||
b4ba67d9 | 222 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD); |
45dcdb9d | 223 | g_assert_cmpint(val & TCO_RLD_MASK, ==, 1); |
b4ba67d9 | 224 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); |
45dcdb9d PA |
225 | ret = val & TCO_TIMEOUT ? 1 : 0; |
226 | g_assert(ret == 0); | |
227 | clock_step(TCO_TICK_NSEC); | |
b4ba67d9 | 228 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); |
45dcdb9d PA |
229 | ret = val & TCO_TIMEOUT ? 1 : 0; |
230 | g_assert(ret == 1); | |
231 | ||
232 | stop_tco(&d); | |
34779e8c | 233 | test_end(&d); |
45dcdb9d PA |
234 | } |
235 | ||
236 | static QDict *get_watchdog_action(void) | |
237 | { | |
238 | QDict *ev = qmp(""); | |
239 | QDict *data; | |
240 | g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG")); | |
241 | ||
242 | data = qdict_get_qdict(ev, "data"); | |
243 | QINCREF(data); | |
244 | QDECREF(ev); | |
245 | return data; | |
246 | } | |
247 | ||
248 | static void test_tco_second_timeout_pause(void) | |
249 | { | |
250 | TestData td; | |
251 | const uint16_t ticks = TCO_SECS_TO_TICKS(32); | |
252 | QDict *ad; | |
253 | ||
254 | td.args = "-watchdog-action pause"; | |
5add35be | 255 | td.noreboot = false; |
45dcdb9d PA |
256 | test_init(&td); |
257 | ||
258 | stop_tco(&td); | |
259 | clear_tco_status(&td); | |
260 | reset_on_second_timeout(true); | |
261 | set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); | |
262 | load_tco(&td); | |
263 | start_tco(&td); | |
264 | clock_step(ticks * TCO_TICK_NSEC * 2); | |
265 | ad = get_watchdog_action(); | |
266 | g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); | |
267 | QDECREF(ad); | |
268 | ||
269 | stop_tco(&td); | |
34779e8c | 270 | test_end(&td); |
45dcdb9d PA |
271 | } |
272 | ||
273 | static void test_tco_second_timeout_reset(void) | |
274 | { | |
275 | TestData td; | |
276 | const uint16_t ticks = TCO_SECS_TO_TICKS(16); | |
277 | QDict *ad; | |
278 | ||
279 | td.args = "-watchdog-action reset"; | |
5add35be | 280 | td.noreboot = false; |
45dcdb9d PA |
281 | test_init(&td); |
282 | ||
283 | stop_tco(&td); | |
284 | clear_tco_status(&td); | |
285 | reset_on_second_timeout(true); | |
286 | set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); | |
287 | load_tco(&td); | |
288 | start_tco(&td); | |
289 | clock_step(ticks * TCO_TICK_NSEC * 2); | |
290 | ad = get_watchdog_action(); | |
291 | g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); | |
292 | QDECREF(ad); | |
293 | ||
294 | stop_tco(&td); | |
34779e8c | 295 | test_end(&td); |
45dcdb9d PA |
296 | } |
297 | ||
298 | static void test_tco_second_timeout_shutdown(void) | |
299 | { | |
300 | TestData td; | |
301 | const uint16_t ticks = TCO_SECS_TO_TICKS(128); | |
302 | QDict *ad; | |
303 | ||
304 | td.args = "-watchdog-action shutdown"; | |
5add35be | 305 | td.noreboot = false; |
45dcdb9d PA |
306 | test_init(&td); |
307 | ||
308 | stop_tco(&td); | |
309 | clear_tco_status(&td); | |
310 | reset_on_second_timeout(true); | |
311 | set_tco_timeout(&td, ticks); | |
312 | load_tco(&td); | |
313 | start_tco(&td); | |
314 | clock_step(ticks * TCO_TICK_NSEC * 2); | |
315 | ad = get_watchdog_action(); | |
316 | g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); | |
317 | QDECREF(ad); | |
318 | ||
319 | stop_tco(&td); | |
34779e8c | 320 | test_end(&td); |
45dcdb9d PA |
321 | } |
322 | ||
323 | static void test_tco_second_timeout_none(void) | |
324 | { | |
325 | TestData td; | |
326 | const uint16_t ticks = TCO_SECS_TO_TICKS(256); | |
327 | QDict *ad; | |
328 | ||
329 | td.args = "-watchdog-action none"; | |
5add35be | 330 | td.noreboot = false; |
45dcdb9d PA |
331 | test_init(&td); |
332 | ||
333 | stop_tco(&td); | |
334 | clear_tco_status(&td); | |
335 | reset_on_second_timeout(true); | |
336 | set_tco_timeout(&td, ticks); | |
337 | load_tco(&td); | |
338 | start_tco(&td); | |
339 | clock_step(ticks * TCO_TICK_NSEC * 2); | |
340 | ad = get_watchdog_action(); | |
341 | g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); | |
342 | QDECREF(ad); | |
343 | ||
344 | stop_tco(&td); | |
34779e8c | 345 | test_end(&td); |
45dcdb9d PA |
346 | } |
347 | ||
348 | static void test_tco_ticks_counter(void) | |
349 | { | |
350 | TestData d; | |
351 | uint16_t ticks = TCO_SECS_TO_TICKS(8); | |
352 | uint16_t rld; | |
353 | ||
354 | d.args = NULL; | |
5add35be | 355 | d.noreboot = true; |
45dcdb9d PA |
356 | test_init(&d); |
357 | ||
358 | stop_tco(&d); | |
359 | clear_tco_status(&d); | |
360 | reset_on_second_timeout(false); | |
361 | set_tco_timeout(&d, ticks); | |
362 | load_tco(&d); | |
363 | start_tco(&d); | |
364 | ||
365 | do { | |
b4ba67d9 | 366 | rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK; |
45dcdb9d PA |
367 | g_assert_cmpint(rld, ==, ticks); |
368 | clock_step(TCO_TICK_NSEC); | |
369 | ticks--; | |
b4ba67d9 | 370 | } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT)); |
45dcdb9d PA |
371 | |
372 | stop_tco(&d); | |
34779e8c | 373 | test_end(&d); |
45dcdb9d PA |
374 | } |
375 | ||
376 | static void test_tco1_control_bits(void) | |
377 | { | |
378 | TestData d; | |
379 | uint16_t val; | |
380 | ||
381 | d.args = NULL; | |
5add35be | 382 | d.noreboot = true; |
45dcdb9d PA |
383 | test_init(&d); |
384 | ||
385 | val = TCO_LOCK; | |
b4ba67d9 | 386 | qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); |
45dcdb9d | 387 | val &= ~TCO_LOCK; |
b4ba67d9 DG |
388 | qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); |
389 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==, | |
45dcdb9d | 390 | TCO_LOCK); |
34779e8c | 391 | test_end(&d); |
45dcdb9d PA |
392 | } |
393 | ||
394 | static void test_tco1_status_bits(void) | |
395 | { | |
396 | TestData d; | |
397 | uint16_t ticks = 8; | |
398 | uint16_t val; | |
399 | int ret; | |
400 | ||
401 | d.args = NULL; | |
5add35be | 402 | d.noreboot = true; |
45dcdb9d PA |
403 | test_init(&d); |
404 | ||
405 | stop_tco(&d); | |
406 | clear_tco_status(&d); | |
407 | reset_on_second_timeout(false); | |
408 | set_tco_timeout(&d, ticks); | |
409 | load_tco(&d); | |
410 | start_tco(&d); | |
411 | clock_step(ticks * TCO_TICK_NSEC); | |
412 | ||
b4ba67d9 DG |
413 | qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0); |
414 | qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0); | |
415 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); | |
45dcdb9d PA |
416 | ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0; |
417 | g_assert(ret == 1); | |
b4ba67d9 DG |
418 | qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); |
419 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0); | |
34779e8c | 420 | test_end(&d); |
45dcdb9d PA |
421 | } |
422 | ||
423 | static void test_tco2_status_bits(void) | |
424 | { | |
425 | TestData d; | |
426 | uint16_t ticks = 8; | |
427 | uint16_t val; | |
428 | int ret; | |
429 | ||
5add35be PA |
430 | d.args = NULL; |
431 | d.noreboot = true; | |
45dcdb9d PA |
432 | test_init(&d); |
433 | ||
434 | stop_tco(&d); | |
435 | clear_tco_status(&d); | |
436 | reset_on_second_timeout(true); | |
437 | set_tco_timeout(&d, ticks); | |
438 | load_tco(&d); | |
439 | start_tco(&d); | |
440 | clock_step(ticks * TCO_TICK_NSEC * 2); | |
441 | ||
b4ba67d9 | 442 | val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); |
45dcdb9d PA |
443 | ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0; |
444 | g_assert(ret == 1); | |
b4ba67d9 DG |
445 | qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val); |
446 | g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0); | |
34779e8c | 447 | test_end(&d); |
45dcdb9d PA |
448 | } |
449 | ||
450 | int main(int argc, char **argv) | |
451 | { | |
452 | g_test_init(&argc, &argv, NULL); | |
453 | ||
454 | qtest_add_func("tco/defaults", test_tco_defaults); | |
455 | qtest_add_func("tco/timeout/no_action", test_tco_timeout); | |
456 | qtest_add_func("tco/timeout/no_action/max", test_tco_max_timeout); | |
457 | qtest_add_func("tco/second_timeout/pause", test_tco_second_timeout_pause); | |
458 | qtest_add_func("tco/second_timeout/reset", test_tco_second_timeout_reset); | |
459 | qtest_add_func("tco/second_timeout/shutdown", | |
460 | test_tco_second_timeout_shutdown); | |
461 | qtest_add_func("tco/second_timeout/none", test_tco_second_timeout_none); | |
462 | qtest_add_func("tco/counter", test_tco_ticks_counter); | |
463 | qtest_add_func("tco/tco1_control/bits", test_tco1_control_bits); | |
464 | qtest_add_func("tco/tco1_status/bits", test_tco1_status_bits); | |
465 | qtest_add_func("tco/tco2_status/bits", test_tco2_status_bits); | |
466 | return g_test_run(); | |
467 | } |