xref: /openbmc/qemu/tests/qtest/device-plug-test.c (revision 277ee172)
11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth  * QEMU device plug/unplug handling
31e8a1faeSThomas Huth  *
41e8a1faeSThomas Huth  * Copyright (C) 2019 Red Hat Inc.
51e8a1faeSThomas Huth  *
61e8a1faeSThomas Huth  * Authors:
71e8a1faeSThomas Huth  *  David Hildenbrand <david@redhat.com>
81e8a1faeSThomas Huth  *
91e8a1faeSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
101e8a1faeSThomas Huth  * See the COPYING file in the top-level directory.
111e8a1faeSThomas Huth  */
121e8a1faeSThomas Huth 
131e8a1faeSThomas Huth #include "qemu/osdep.h"
14907b5105SMarc-André Lureau #include "libqtest.h"
151e8a1faeSThomas Huth #include "qapi/qmp/qdict.h"
161e8a1faeSThomas Huth #include "qapi/qmp/qstring.h"
171e8a1faeSThomas Huth 
system_reset(QTestState * qtest)181e8a1faeSThomas Huth static void system_reset(QTestState *qtest)
191e8a1faeSThomas Huth {
201e8a1faeSThomas Huth     QDict *resp;
211e8a1faeSThomas Huth 
221e8a1faeSThomas Huth     resp = qtest_qmp(qtest, "{'execute': 'system_reset'}");
231e8a1faeSThomas Huth     g_assert(qdict_haskey(resp, "return"));
241e8a1faeSThomas Huth     qobject_unref(resp);
251e8a1faeSThomas Huth }
261e8a1faeSThomas Huth 
wait_device_deleted_event(QTestState * qtest,const char * id)271e8a1faeSThomas Huth static void wait_device_deleted_event(QTestState *qtest, const char *id)
281e8a1faeSThomas Huth {
291e8a1faeSThomas Huth     QDict *resp, *data;
301e8a1faeSThomas Huth     QString *qstr;
311e8a1faeSThomas Huth 
321e8a1faeSThomas Huth     /*
331e8a1faeSThomas Huth      * Other devices might get removed along with the removed device. Skip
341e8a1faeSThomas Huth      * these. The device of interest will be the last one.
351e8a1faeSThomas Huth      */
361e8a1faeSThomas Huth     for (;;) {
371e8a1faeSThomas Huth         resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED");
381e8a1faeSThomas Huth         data = qdict_get_qdict(resp, "data");
391e8a1faeSThomas Huth         if (!data || !qdict_get(data, "device")) {
401e8a1faeSThomas Huth             qobject_unref(resp);
411e8a1faeSThomas Huth             continue;
421e8a1faeSThomas Huth         }
431e8a1faeSThomas Huth         qstr = qobject_to(QString, qdict_get(data, "device"));
441e8a1faeSThomas Huth         g_assert(qstr);
451e8a1faeSThomas Huth         if (!strcmp(qstring_get_str(qstr), id)) {
461e8a1faeSThomas Huth             qobject_unref(resp);
471e8a1faeSThomas Huth             break;
481e8a1faeSThomas Huth         }
491e8a1faeSThomas Huth         qobject_unref(resp);
501e8a1faeSThomas Huth     }
511e8a1faeSThomas Huth }
521e8a1faeSThomas Huth 
process_device_remove(QTestState * qtest,const char * id)539bcc0f7dSMichael Labiuk static void process_device_remove(QTestState *qtest, const char *id)
549bcc0f7dSMichael Labiuk {
559bcc0f7dSMichael Labiuk     /*
569bcc0f7dSMichael Labiuk      * Request device removal. As the guest is not running, the request won't
579bcc0f7dSMichael Labiuk      * be processed. However during system reset, the removal will be
589bcc0f7dSMichael Labiuk      * handled, removing the device.
599bcc0f7dSMichael Labiuk      */
60ea42a6c4SMichael Labiuk     qtest_qmp_device_del_send(qtest, id);
619bcc0f7dSMichael Labiuk     system_reset(qtest);
629bcc0f7dSMichael Labiuk     wait_device_deleted_event(qtest, id);
639bcc0f7dSMichael Labiuk }
649bcc0f7dSMichael Labiuk 
test_pci_unplug_request(void)651e8a1faeSThomas Huth static void test_pci_unplug_request(void)
661e8a1faeSThomas Huth {
67ca7d9f5fSFabiano Rosas     QTestState *qtest;
687b172333SDr. David Alan Gilbert     const char *arch = qtest_get_arch();
697b172333SDr. David Alan Gilbert     const char *machine_addition = "";
707b172333SDr. David Alan Gilbert 
7145ec78beSFabiano Rosas     if (!qtest_has_device("virtio-mouse-pci")) {
7245ec78beSFabiano Rosas         g_test_skip("Device virtio-mouse-pci not available");
7345ec78beSFabiano Rosas         return;
7445ec78beSFabiano Rosas     }
7545ec78beSFabiano Rosas 
767b172333SDr. David Alan Gilbert     if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
777b172333SDr. David Alan Gilbert         machine_addition = "-machine pc";
787b172333SDr. David Alan Gilbert     }
797b172333SDr. David Alan Gilbert 
80ca7d9f5fSFabiano Rosas     qtest = qtest_initf("%s -device virtio-mouse-pci,id=dev0",
817b172333SDr. David Alan Gilbert                         machine_addition);
821e8a1faeSThomas Huth 
839bcc0f7dSMichael Labiuk     process_device_remove(qtest, "dev0");
841e8a1faeSThomas Huth 
851e8a1faeSThomas Huth     qtest_quit(qtest);
861e8a1faeSThomas Huth }
871e8a1faeSThomas Huth 
test_q35_pci_unplug_request(void)88a12f1a7eSMichael Labiuk static void test_q35_pci_unplug_request(void)
89a12f1a7eSMichael Labiuk {
9045ec78beSFabiano Rosas     QTestState *qtest;
91a12f1a7eSMichael Labiuk 
9245ec78beSFabiano Rosas     if (!qtest_has_device("virtio-mouse-pci")) {
9345ec78beSFabiano Rosas         g_test_skip("Device virtio-mouse-pci not available");
9445ec78beSFabiano Rosas         return;
9545ec78beSFabiano Rosas     }
9645ec78beSFabiano Rosas 
9745ec78beSFabiano Rosas     qtest = qtest_initf("-machine q35 "
98a12f1a7eSMichael Labiuk                         "-device pcie-root-port,id=p1 "
99a12f1a7eSMichael Labiuk                         "-device pcie-pci-bridge,bus=p1,id=b1 "
100a12f1a7eSMichael Labiuk                         "-device virtio-mouse-pci,bus=b1,id=dev0");
101a12f1a7eSMichael Labiuk 
102a12f1a7eSMichael Labiuk     process_device_remove(qtest, "dev0");
103a12f1a7eSMichael Labiuk 
104a12f1a7eSMichael Labiuk     qtest_quit(qtest);
105a12f1a7eSMichael Labiuk }
106a12f1a7eSMichael Labiuk 
test_pci_unplug_json_request(void)10764b4529aSDaniel P. Berrangé static void test_pci_unplug_json_request(void)
10864b4529aSDaniel P. Berrangé {
109ca7d9f5fSFabiano Rosas     QTestState *qtest;
1107b172333SDr. David Alan Gilbert     const char *arch = qtest_get_arch();
1117b172333SDr. David Alan Gilbert     const char *machine_addition = "";
1127b172333SDr. David Alan Gilbert 
11345ec78beSFabiano Rosas     if (!qtest_has_device("virtio-mouse-pci")) {
11445ec78beSFabiano Rosas         g_test_skip("Device virtio-mouse-pci not available");
11545ec78beSFabiano Rosas         return;
11645ec78beSFabiano Rosas     }
11745ec78beSFabiano Rosas 
1187b172333SDr. David Alan Gilbert     if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
1197b172333SDr. David Alan Gilbert         machine_addition = "-machine pc";
1207b172333SDr. David Alan Gilbert     }
1217b172333SDr. David Alan Gilbert 
122ca7d9f5fSFabiano Rosas     qtest = qtest_initf(
123fbde3ae8SBin Meng         "%s -device \"{'driver': 'virtio-mouse-pci', 'id': 'dev0'}\"",
1247b172333SDr. David Alan Gilbert         machine_addition);
12564b4529aSDaniel P. Berrangé 
1269bcc0f7dSMichael Labiuk     process_device_remove(qtest, "dev0");
12764b4529aSDaniel P. Berrangé 
12864b4529aSDaniel P. Berrangé     qtest_quit(qtest);
12964b4529aSDaniel P. Berrangé }
13064b4529aSDaniel P. Berrangé 
test_q35_pci_unplug_json_request(void)131a12f1a7eSMichael Labiuk static void test_q35_pci_unplug_json_request(void)
132a12f1a7eSMichael Labiuk {
13345ec78beSFabiano Rosas     QTestState *qtest;
134e4439e52SBin Meng     const char *port = "-device \"{'driver': 'pcie-root-port', "
135e4439e52SBin Meng                                   "'id': 'p1'}\"";
136a12f1a7eSMichael Labiuk 
137e4439e52SBin Meng     const char *bridge = "-device \"{'driver': 'pcie-pci-bridge', "
138e4439e52SBin Meng                                     "'id': 'b1', "
139e4439e52SBin Meng                                     "'bus': 'p1'}\"";
140a12f1a7eSMichael Labiuk 
141e4439e52SBin Meng     const char *device = "-device \"{'driver': 'virtio-mouse-pci', "
142e4439e52SBin Meng                                     "'bus': 'b1', "
143e4439e52SBin Meng                                     "'id': 'dev0'}\"";
144a12f1a7eSMichael Labiuk 
14545ec78beSFabiano Rosas     if (!qtest_has_device("virtio-mouse-pci")) {
14645ec78beSFabiano Rosas         g_test_skip("Device virtio-mouse-pci not available");
14745ec78beSFabiano Rosas         return;
14845ec78beSFabiano Rosas     }
14945ec78beSFabiano Rosas 
15045ec78beSFabiano Rosas     qtest = qtest_initf("-machine q35 %s %s %s", port, bridge, device);
151a12f1a7eSMichael Labiuk 
152a12f1a7eSMichael Labiuk     process_device_remove(qtest, "dev0");
153a12f1a7eSMichael Labiuk 
154a12f1a7eSMichael Labiuk     qtest_quit(qtest);
155a12f1a7eSMichael Labiuk }
156a12f1a7eSMichael Labiuk 
test_ccw_unplug(void)1571e8a1faeSThomas Huth static void test_ccw_unplug(void)
1581e8a1faeSThomas Huth {
15965331bf5SThomas Huth     QTestState *qtest;
16065331bf5SThomas Huth 
16165331bf5SThomas Huth     if (!qtest_has_device("virtio-balloon-ccw")) {
16265331bf5SThomas Huth         g_test_skip("Device virtio-balloon-ccw not available");
16365331bf5SThomas Huth         return;
16465331bf5SThomas Huth     }
16565331bf5SThomas Huth 
16665331bf5SThomas Huth     qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
1671e8a1faeSThomas Huth 
168ea42a6c4SMichael Labiuk     qtest_qmp_device_del_send(qtest, "dev0");
1691e8a1faeSThomas Huth     wait_device_deleted_event(qtest, "dev0");
1701e8a1faeSThomas Huth 
1711e8a1faeSThomas Huth     qtest_quit(qtest);
1721e8a1faeSThomas Huth }
1731e8a1faeSThomas Huth 
test_spapr_cpu_unplug_request(void)1741e8a1faeSThomas Huth static void test_spapr_cpu_unplug_request(void)
1751e8a1faeSThomas Huth {
1761e8a1faeSThomas Huth     QTestState *qtest;
1771e8a1faeSThomas Huth 
178*277ee172SNicholas Piggin     qtest = qtest_initf("-cpu power9_v2.2 -smp 1,maxcpus=2 "
179*277ee172SNicholas Piggin                         "-device power9_v2.2-spapr-cpu-core,core-id=1,id=dev0");
1801e8a1faeSThomas Huth 
1811e8a1faeSThomas Huth     /* similar to test_pci_unplug_request */
1829bcc0f7dSMichael Labiuk     process_device_remove(qtest, "dev0");
1831e8a1faeSThomas Huth 
1841e8a1faeSThomas Huth     qtest_quit(qtest);
1851e8a1faeSThomas Huth }
1861e8a1faeSThomas Huth 
test_spapr_memory_unplug_request(void)1871e8a1faeSThomas Huth static void test_spapr_memory_unplug_request(void)
1881e8a1faeSThomas Huth {
1891e8a1faeSThomas Huth     QTestState *qtest;
1901e8a1faeSThomas Huth 
1911e8a1faeSThomas Huth     qtest = qtest_initf("-m 256M,slots=1,maxmem=768M "
1921e8a1faeSThomas Huth                         "-object memory-backend-ram,id=mem0,size=512M "
1931e8a1faeSThomas Huth                         "-device pc-dimm,id=dev0,memdev=mem0");
1941e8a1faeSThomas Huth 
1951e8a1faeSThomas Huth     /* similar to test_pci_unplug_request */
1969bcc0f7dSMichael Labiuk     process_device_remove(qtest, "dev0");
1971e8a1faeSThomas Huth 
1981e8a1faeSThomas Huth     qtest_quit(qtest);
1991e8a1faeSThomas Huth }
2001e8a1faeSThomas Huth 
test_spapr_phb_unplug_request(void)2011e8a1faeSThomas Huth static void test_spapr_phb_unplug_request(void)
2021e8a1faeSThomas Huth {
2031e8a1faeSThomas Huth     QTestState *qtest;
2041e8a1faeSThomas Huth 
2051e8a1faeSThomas Huth     qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0");
2061e8a1faeSThomas Huth 
2071e8a1faeSThomas Huth     /* similar to test_pci_unplug_request */
2089bcc0f7dSMichael Labiuk     process_device_remove(qtest, "dev0");
2091e8a1faeSThomas Huth 
2101e8a1faeSThomas Huth     qtest_quit(qtest);
2111e8a1faeSThomas Huth }
2121e8a1faeSThomas Huth 
main(int argc,char ** argv)2131e8a1faeSThomas Huth int main(int argc, char **argv)
2141e8a1faeSThomas Huth {
2151e8a1faeSThomas Huth     const char *arch = qtest_get_arch();
2161e8a1faeSThomas Huth 
2171e8a1faeSThomas Huth     g_test_init(&argc, &argv, NULL);
2181e8a1faeSThomas Huth 
2191e8a1faeSThomas Huth     /*
2201e8a1faeSThomas Huth      * We need a system that will process unplug requests during system resets
2211e8a1faeSThomas Huth      * and does not do PCI surprise removal. This holds for x86 ACPI,
2221e8a1faeSThomas Huth      * s390x and spapr.
2231e8a1faeSThomas Huth      */
2241e8a1faeSThomas Huth     qtest_add_func("/device-plug/pci-unplug-request",
2251e8a1faeSThomas Huth                    test_pci_unplug_request);
22664b4529aSDaniel P. Berrangé     qtest_add_func("/device-plug/pci-unplug-json-request",
22764b4529aSDaniel P. Berrangé                    test_pci_unplug_json_request);
2281e8a1faeSThomas Huth 
2291e8a1faeSThomas Huth     if (!strcmp(arch, "s390x")) {
2301e8a1faeSThomas Huth         qtest_add_func("/device-plug/ccw-unplug",
2311e8a1faeSThomas Huth                        test_ccw_unplug);
2321e8a1faeSThomas Huth     }
2331e8a1faeSThomas Huth 
2341e8a1faeSThomas Huth     if (!strcmp(arch, "ppc64")) {
2351e8a1faeSThomas Huth         qtest_add_func("/device-plug/spapr-cpu-unplug-request",
2361e8a1faeSThomas Huth                        test_spapr_cpu_unplug_request);
2371e8a1faeSThomas Huth         qtest_add_func("/device-plug/spapr-memory-unplug-request",
2381e8a1faeSThomas Huth                        test_spapr_memory_unplug_request);
2391e8a1faeSThomas Huth         qtest_add_func("/device-plug/spapr-phb-unplug-request",
2401e8a1faeSThomas Huth                        test_spapr_phb_unplug_request);
2411e8a1faeSThomas Huth     }
2421e8a1faeSThomas Huth 
243a12f1a7eSMichael Labiuk     if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) {
244a12f1a7eSMichael Labiuk         qtest_add_func("/device-plug/q35-pci-unplug-request",
245a12f1a7eSMichael Labiuk                    test_q35_pci_unplug_request);
246a12f1a7eSMichael Labiuk         qtest_add_func("/device-plug/q35-pci-unplug-json-request",
247a12f1a7eSMichael Labiuk                    test_q35_pci_unplug_json_request);
248a12f1a7eSMichael Labiuk     }
249a12f1a7eSMichael Labiuk 
2501e8a1faeSThomas Huth     return g_test_run();
2511e8a1faeSThomas Huth }
252