1 /* 2 * libqos driver framework 3 * 4 * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License version 2.1 as published by the Free Software Foundation. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, see <http://www.gnu.org/licenses/> 17 */ 18 19 /* 20 * Not so fast! You might want to read the 9p developer docs first: 21 * https://wiki.qemu.org/Documentation/9p 22 */ 23 24 #include "qemu/osdep.h" 25 #include "libqtest.h" 26 #include "qemu/module.h" 27 #include "standard-headers/linux/virtio_ids.h" 28 #include "virtio-9p.h" 29 #include "qgraph.h" 30 31 static QGuestAllocator *alloc; 32 static char *local_test_path; 33 34 /* Concatenates the passed 2 pathes. Returned result must be freed. */ 35 static char *concat_path(const char* a, const char* b) 36 { 37 return g_build_filename(a, b, NULL); 38 } 39 40 void virtio_9p_create_local_test_dir(void) 41 { 42 g_assert(local_test_path == NULL); 43 struct stat st; 44 g_autofree char *pwd = g_get_current_dir(); 45 /* 46 * template gets cached into local_test_path and freed in 47 * virtio_9p_remove_local_test_dir(). 48 */ 49 char *template = concat_path(pwd, "qtest-9p-local-XXXXXX"); 50 51 local_test_path = mkdtemp(template); 52 if (!local_test_path) { 53 g_test_message("mkdtemp('%s') failed: %s", template, strerror(errno)); 54 } 55 56 g_assert(local_test_path != NULL); 57 58 /* ensure test directory exists now ... */ 59 g_assert(stat(local_test_path, &st) == 0); 60 /* ... and is actually a directory */ 61 g_assert((st.st_mode & S_IFMT) == S_IFDIR); 62 } 63 64 void virtio_9p_remove_local_test_dir(void) 65 { 66 g_assert(local_test_path != NULL); 67 g_autofree char *cmd = g_strdup_printf("rm -fr '%s'\n", local_test_path); 68 int res = system(cmd); 69 if (res < 0) { 70 /* ignore error, dummy check to prevent compiler error */ 71 } 72 g_free(local_test_path); 73 local_test_path = NULL; 74 } 75 76 char *virtio_9p_test_path(const char *path) 77 { 78 g_assert(local_test_path); 79 return concat_path(local_test_path, path); 80 } 81 82 static void virtio_9p_cleanup(QVirtio9P *interface) 83 { 84 qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc); 85 } 86 87 static void virtio_9p_setup(QVirtio9P *interface) 88 { 89 uint64_t features; 90 91 features = qvirtio_get_features(interface->vdev); 92 features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX)); 93 qvirtio_set_features(interface->vdev, features); 94 95 interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0); 96 qvirtio_set_driver_ok(interface->vdev); 97 } 98 99 /* virtio-9p-device */ 100 static void virtio_9p_device_destructor(QOSGraphObject *obj) 101 { 102 QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj; 103 QVirtio9P *v9p = &v_9p->v9p; 104 105 virtio_9p_cleanup(v9p); 106 } 107 108 static void virtio_9p_device_start_hw(QOSGraphObject *obj) 109 { 110 QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj; 111 QVirtio9P *v9p = &v_9p->v9p; 112 113 virtio_9p_setup(v9p); 114 } 115 116 static void *virtio_9p_get_driver(QVirtio9P *v_9p, 117 const char *interface) 118 { 119 if (!g_strcmp0(interface, "virtio-9p")) { 120 return v_9p; 121 } 122 if (!g_strcmp0(interface, "virtio")) { 123 return v_9p->vdev; 124 } 125 126 fprintf(stderr, "%s not present in virtio-9p-device\n", interface); 127 g_assert_not_reached(); 128 } 129 130 static void *virtio_9p_device_get_driver(void *object, const char *interface) 131 { 132 QVirtio9PDevice *v_9p = object; 133 return virtio_9p_get_driver(&v_9p->v9p, interface); 134 } 135 136 static void *virtio_9p_device_create(void *virtio_dev, 137 QGuestAllocator *t_alloc, 138 void *addr) 139 { 140 QVirtio9PDevice *virtio_device = g_new0(QVirtio9PDevice, 1); 141 QVirtio9P *interface = &virtio_device->v9p; 142 143 interface->vdev = virtio_dev; 144 alloc = t_alloc; 145 146 virtio_device->obj.destructor = virtio_9p_device_destructor; 147 virtio_device->obj.get_driver = virtio_9p_device_get_driver; 148 virtio_device->obj.start_hw = virtio_9p_device_start_hw; 149 150 return &virtio_device->obj; 151 } 152 153 /* virtio-9p-pci */ 154 static void virtio_9p_pci_destructor(QOSGraphObject *obj) 155 { 156 QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj; 157 QVirtio9P *interface = &v9_pci->v9p; 158 QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj; 159 160 virtio_9p_cleanup(interface); 161 qvirtio_pci_destructor(pci_vobj); 162 } 163 164 static void virtio_9p_pci_start_hw(QOSGraphObject *obj) 165 { 166 QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj; 167 QVirtio9P *interface = &v9_pci->v9p; 168 QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj; 169 170 qvirtio_pci_start_hw(pci_vobj); 171 virtio_9p_setup(interface); 172 } 173 174 static void *virtio_9p_pci_get_driver(void *object, const char *interface) 175 { 176 QVirtio9PPCI *v_9p = object; 177 if (!g_strcmp0(interface, "pci-device")) { 178 return v_9p->pci_vdev.pdev; 179 } 180 return virtio_9p_get_driver(&v_9p->v9p, interface); 181 } 182 183 static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc, 184 void *addr) 185 { 186 QVirtio9PPCI *v9_pci = g_new0(QVirtio9PPCI, 1); 187 QVirtio9P *interface = &v9_pci->v9p; 188 QOSGraphObject *obj = &v9_pci->pci_vdev.obj; 189 190 virtio_pci_init(&v9_pci->pci_vdev, pci_bus, addr); 191 interface->vdev = &v9_pci->pci_vdev.vdev; 192 alloc = t_alloc; 193 194 g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_9P); 195 196 obj->destructor = virtio_9p_pci_destructor; 197 obj->start_hw = virtio_9p_pci_start_hw; 198 obj->get_driver = virtio_9p_pci_get_driver; 199 200 return obj; 201 } 202 203 /** 204 * Performs regular expression based search and replace on @a haystack. 205 * 206 * @param haystack - input string to be parsed, result of replacement is 207 * stored back to @a haystack 208 * @param pattern - the regular expression pattern for scanning @a haystack 209 * @param replace_fmt - matches of supplied @a pattern are replaced by this, 210 * if necessary glib printf format can be used to add 211 * variable arguments of this function to this 212 * replacement string 213 */ 214 static void regex_replace(GString *haystack, const char *pattern, 215 const char *replace_fmt, ...) 216 { 217 g_autoptr(GRegex) regex = NULL; 218 g_autofree char *replace = NULL, *s = NULL; 219 va_list argp; 220 221 va_start(argp, replace_fmt); 222 replace = g_strdup_vprintf(replace_fmt, argp); 223 va_end(argp); 224 225 regex = g_regex_new(pattern, 0, 0, NULL); 226 s = g_regex_replace(regex, haystack->str, -1, 0, replace, 0, NULL); 227 g_string_assign(haystack, s); 228 } 229 230 void virtio_9p_assign_local_driver(GString *cmd_line, const char *args) 231 { 232 g_assert_nonnull(local_test_path); 233 234 /* replace 'synth' driver by 'local' driver */ 235 regex_replace(cmd_line, "-fsdev synth,", "-fsdev local,"); 236 237 /* append 'path=...' to '-fsdev ...' group */ 238 regex_replace(cmd_line, "(-fsdev \\w[^ ]*)", "\\1,path='%s'", 239 local_test_path); 240 241 if (!args) { 242 return; 243 } 244 245 /* append passed args to '-fsdev ...' group */ 246 regex_replace(cmd_line, "(-fsdev \\w[^ ]*)", "\\1,%s", args); 247 } 248 249 static void virtio_9p_register_nodes(void) 250 { 251 const char *str_simple = "fsdev=fsdev0,mount_tag=" MOUNT_TAG; 252 const char *str_addr = "fsdev=fsdev0,addr=04.0,mount_tag=" MOUNT_TAG; 253 254 QPCIAddress addr = { 255 .devfn = QPCI_DEVFN(4, 0), 256 }; 257 258 QOSGraphEdgeOptions opts = { 259 .before_cmd_line = "-fsdev synth,id=fsdev0", 260 }; 261 262 /* virtio-9p-device */ 263 opts.extra_device_opts = str_simple, 264 qos_node_create_driver("virtio-9p-device", virtio_9p_device_create); 265 qos_node_consumes("virtio-9p-device", "virtio-bus", &opts); 266 qos_node_produces("virtio-9p-device", "virtio"); 267 qos_node_produces("virtio-9p-device", "virtio-9p"); 268 269 /* virtio-9p-pci */ 270 opts.extra_device_opts = str_addr; 271 add_qpci_address(&opts, &addr); 272 qos_node_create_driver("virtio-9p-pci", virtio_9p_pci_create); 273 qos_node_consumes("virtio-9p-pci", "pci-bus", &opts); 274 qos_node_produces("virtio-9p-pci", "pci-device"); 275 qos_node_produces("virtio-9p-pci", "virtio"); 276 qos_node_produces("virtio-9p-pci", "virtio-9p"); 277 278 } 279 280 libqos_init(virtio_9p_register_nodes); 281