1 /* 2 * Inter-VM Shared Memory Flat Device 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * Copyright (c) 2023 Linaro Ltd. 6 * Authors: 7 * Gustavo Romero 8 * 9 */ 10 11 #include "qemu/osdep.h" 12 #include "qemu/units.h" 13 #include "qemu/error-report.h" 14 #include "qemu/module.h" 15 #include "qapi/error.h" 16 #include "hw/irq.h" 17 #include "hw/qdev-properties-system.h" 18 #include "hw/sysbus.h" 19 #include "chardev/char-fe.h" 20 #include "system/address-spaces.h" 21 #include "trace.h" 22 23 #include "hw/misc/ivshmem-flat.h" 24 25 static int64_t ivshmem_flat_recv_msg(IvshmemFTState *s, int *pfd) 26 { 27 int64_t msg; 28 int n, ret; 29 30 n = 0; 31 do { 32 ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, 33 sizeof(msg) - n); 34 if (ret < 0) { 35 if (ret == -EINTR) { 36 continue; 37 } 38 exit(1); 39 } 40 n += ret; 41 } while (n < sizeof(msg)); 42 43 if (pfd) { 44 *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); 45 } 46 return le64_to_cpu(msg); 47 } 48 49 static void ivshmem_flat_irq_handler(void *opaque) 50 { 51 VectorInfo *vi = opaque; 52 EventNotifier *e = &vi->event_notifier; 53 uint16_t vector_id; 54 const VectorInfo (*v)[64]; 55 56 assert(e->initialized); 57 58 vector_id = vi->id; 59 60 /* 61 * The vector info struct is passed to the handler via the 'opaque' pointer. 62 * This struct pointer allows the retrieval of the vector ID and its 63 * associated event notifier. However, for triggering an interrupt using 64 * qemu_set_irq, it's necessary to also have a pointer to the device state, 65 * i.e., a pointer to the IvshmemFTState struct. Since the vector info 66 * struct is contained within the IvshmemFTState struct, its pointer can be 67 * used to obtain the pointer to IvshmemFTState through simple pointer math. 68 */ 69 v = (void *)(vi - vector_id); /* v = &IvshmemPeer->vector[0] */ 70 IvshmemPeer *own_peer = container_of(v, IvshmemPeer, vector); 71 IvshmemFTState *s = container_of(own_peer, IvshmemFTState, own); 72 73 /* Clear event */ 74 if (!event_notifier_test_and_clear(e)) { 75 return; 76 } 77 78 trace_ivshmem_flat_irq_handler(vector_id); 79 80 /* 81 * Toggle device's output line, which is connected to interrupt controller, 82 * generating an interrupt request to the CPU. 83 */ 84 qemu_irq_pulse(s->irq); 85 } 86 87 static IvshmemPeer *ivshmem_flat_find_peer(IvshmemFTState *s, uint16_t peer_id) 88 { 89 IvshmemPeer *peer; 90 91 /* Own ID */ 92 if (s->own.id == peer_id) { 93 return &s->own; 94 } 95 96 /* Peer ID */ 97 QTAILQ_FOREACH(peer, &s->peer, next) { 98 if (peer->id == peer_id) { 99 return peer; 100 } 101 } 102 103 return NULL; 104 } 105 106 static IvshmemPeer *ivshmem_flat_add_peer(IvshmemFTState *s, uint16_t peer_id) 107 { 108 IvshmemPeer *new_peer; 109 110 new_peer = g_malloc0(sizeof(*new_peer)); 111 new_peer->id = peer_id; 112 new_peer->vector_counter = 0; 113 114 QTAILQ_INSERT_TAIL(&s->peer, new_peer, next); 115 116 trace_ivshmem_flat_new_peer(peer_id); 117 118 return new_peer; 119 } 120 121 static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id) 122 { 123 IvshmemPeer *peer; 124 125 peer = ivshmem_flat_find_peer(s, peer_id); 126 assert(peer); 127 128 QTAILQ_REMOVE(&s->peer, peer, next); 129 for (int n = 0; n < peer->vector_counter; n++) { 130 int efd; 131 efd = event_notifier_get_fd(&(peer->vector[n].event_notifier)); 132 close(efd); 133 } 134 135 g_free(peer); 136 } 137 138 static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, 139 int vector_fd) 140 { 141 if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) { 142 trace_ivshmem_flat_add_vector_failure(peer->vector_counter, 143 vector_fd, peer->id); 144 close(vector_fd); 145 146 return; 147 } 148 149 trace_ivshmem_flat_add_vector_success(peer->vector_counter, 150 vector_fd, peer->id); 151 152 /* 153 * Set vector ID and its associated eventfd notifier and add them to the 154 * peer. 155 */ 156 peer->vector[peer->vector_counter].id = peer->vector_counter; 157 g_unix_set_fd_nonblocking(vector_fd, true, NULL); 158 event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, 159 vector_fd); 160 161 /* 162 * If it's the device's own ID, register also the handler for the eventfd 163 * so the device can be notified by the other peers. 164 */ 165 if (peer == &s->own) { 166 qemu_set_fd_handler(vector_fd, ivshmem_flat_irq_handler, NULL, 167 &peer->vector); 168 } 169 170 peer->vector_counter++; 171 } 172 173 static void ivshmem_flat_process_msg(IvshmemFTState *s, uint64_t msg, int fd) 174 { 175 uint16_t peer_id; 176 IvshmemPeer *peer; 177 178 peer_id = msg & 0xFFFF; 179 peer = ivshmem_flat_find_peer(s, peer_id); 180 181 if (!peer) { 182 peer = ivshmem_flat_add_peer(s, peer_id); 183 } 184 185 if (fd >= 0) { 186 ivshmem_flat_add_vector(s, peer, fd); 187 } else { /* fd == -1, which is received when peers disconnect. */ 188 ivshmem_flat_remove_peer(s, peer_id); 189 } 190 } 191 192 static int ivshmem_flat_can_receive_data(void *opaque) 193 { 194 IvshmemFTState *s = opaque; 195 196 assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); 197 return sizeof(s->msg_buf) - s->msg_buffered_bytes; 198 } 199 200 static void ivshmem_flat_read_msg(void *opaque, const uint8_t *buf, int size) 201 { 202 IvshmemFTState *s = opaque; 203 int fd; 204 int64_t msg; 205 206 assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); 207 memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); 208 s->msg_buffered_bytes += size; 209 if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { 210 return; 211 } 212 msg = le64_to_cpu(s->msg_buf); 213 s->msg_buffered_bytes = 0; 214 215 fd = qemu_chr_fe_get_msgfd(&s->server_chr); 216 217 ivshmem_flat_process_msg(s, msg, fd); 218 } 219 220 static uint64_t ivshmem_flat_iomem_read(void *opaque, 221 hwaddr offset, unsigned size) 222 { 223 IvshmemFTState *s = opaque; 224 uint32_t ret; 225 226 trace_ivshmem_flat_read_mmr(offset); 227 228 switch (offset) { 229 case INTMASK: 230 ret = 0; /* Ignore read since all bits are reserved in rev 1. */ 231 break; 232 case INTSTATUS: 233 ret = 0; /* Ignore read since all bits are reserved in rev 1. */ 234 break; 235 case IVPOSITION: 236 ret = s->own.id; 237 break; 238 case DOORBELL: 239 trace_ivshmem_flat_read_mmr_doorbell(); /* DOORBELL is write-only */ 240 ret = 0; 241 break; 242 default: 243 /* Should never reach out here due to iomem map range being exact */ 244 trace_ivshmem_flat_read_write_mmr_invalid(offset); 245 ret = 0; 246 } 247 248 return ret; 249 } 250 251 static int ivshmem_flat_interrupt_peer(IvshmemFTState *s, 252 uint16_t peer_id, uint16_t vector_id) 253 { 254 IvshmemPeer *peer; 255 256 peer = ivshmem_flat_find_peer(s, peer_id); 257 if (!peer) { 258 trace_ivshmem_flat_interrupt_invalid_peer(peer_id); 259 return 1; 260 } 261 262 event_notifier_set(&(peer->vector[vector_id].event_notifier)); 263 264 return 0; 265 } 266 267 static void ivshmem_flat_iomem_write(void *opaque, hwaddr offset, 268 uint64_t value, unsigned size) 269 { 270 IvshmemFTState *s = opaque; 271 uint16_t peer_id = (value >> 16) & 0xFFFF; 272 uint16_t vector_id = value & 0xFFFF; 273 274 trace_ivshmem_flat_write_mmr(offset); 275 276 switch (offset) { 277 case INTMASK: 278 break; 279 case INTSTATUS: 280 break; 281 case IVPOSITION: 282 break; 283 case DOORBELL: 284 trace_ivshmem_flat_interrupt_peer(peer_id, vector_id); 285 ivshmem_flat_interrupt_peer(s, peer_id, vector_id); 286 break; 287 default: 288 /* Should never reach out here due to iomem map range being exact. */ 289 trace_ivshmem_flat_read_write_mmr_invalid(offset); 290 break; 291 } 292 293 return; 294 } 295 296 static const MemoryRegionOps ivshmem_flat_ops = { 297 .read = ivshmem_flat_iomem_read, 298 .write = ivshmem_flat_iomem_write, 299 .endianness = DEVICE_LITTLE_ENDIAN, 300 .impl = { /* Read/write aligned at 32 bits. */ 301 .min_access_size = 4, 302 .max_access_size = 4, 303 }, 304 }; 305 306 static void ivshmem_flat_instance_init(Object *obj) 307 { 308 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 309 IvshmemFTState *s = IVSHMEM_FLAT(obj); 310 311 /* 312 * Init mem region for 4 MMRs (ivshmem_registers), 313 * 32 bits each => 16 bytes (0x10). 314 */ 315 memory_region_init_io(&s->iomem, obj, &ivshmem_flat_ops, s, 316 "ivshmem-mmio", 0x10); 317 sysbus_init_mmio(sbd, &s->iomem); 318 319 /* 320 * Create one output IRQ that will be connect to the 321 * machine's interrupt controller. 322 */ 323 sysbus_init_irq(sbd, &s->irq); 324 325 QTAILQ_INIT(&s->peer); 326 } 327 328 static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp) 329 { 330 IvshmemFTState *s = IVSHMEM_FLAT(dev); 331 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 332 int64_t protocol_version, msg; 333 int shmem_fd; 334 uint16_t peer_id; 335 struct stat fdstat; 336 337 /* Check ivshmem server connection. */ 338 if (!qemu_chr_fe_backend_connected(&s->server_chr)) { 339 error_setg(errp, "ivshmem server socket not specified or incorret." 340 " Can't create device."); 341 return false; 342 } 343 344 /* 345 * Message sequence from server on new connection: 346 * _____________________________________ 347 * |STEP| uint64_t msg | int fd | 348 * ------------------------------------- 349 * 350 * 0 PROTOCOL -1 \ 351 * 1 OWN PEER ID -1 |-- Header/Greeting 352 * 2 -1 shmem fd / 353 * 354 * 3 PEER IDx Other peer's Vector 0 eventfd 355 * 4 PEER IDx Other peer's Vector 1 eventfd 356 * . . 357 * . . 358 * . . 359 * N PEER IDy Other peer's Vector 0 eventfd 360 * N+1 PEER IDy Other peer's Vector 1 eventfd 361 * . . 362 * . . 363 * . . 364 * 365 * ivshmem_flat_recv_msg() calls return 'msg' and 'fd'. 366 * 367 * See ./docs/specs/ivshmem-spec.txt for details on the protocol. 368 */ 369 370 /* Step 0 */ 371 protocol_version = ivshmem_flat_recv_msg(s, NULL); 372 373 /* Step 1 */ 374 msg = ivshmem_flat_recv_msg(s, NULL); 375 peer_id = 0xFFFF & msg; 376 s->own.id = peer_id; 377 s->own.vector_counter = 0; 378 379 trace_ivshmem_flat_proto_ver_own_id(protocol_version, s->own.id); 380 381 /* Step 2 */ 382 msg = ivshmem_flat_recv_msg(s, &shmem_fd); 383 /* Map shmem fd and MMRs into memory regions. */ 384 if (msg != -1 || shmem_fd < 0) { 385 error_setg(errp, "Could not receive valid shmem fd." 386 " Can't create device!"); 387 return false; 388 } 389 390 if (fstat(shmem_fd, &fdstat) != 0) { 391 error_setg(errp, "Could not determine shmem fd size." 392 " Can't create device!"); 393 return false; 394 } 395 trace_ivshmem_flat_shmem_size(shmem_fd, fdstat.st_size); 396 397 /* 398 * Shmem size provided by the ivshmem server must be equal to 399 * device's shmem size. 400 */ 401 if (fdstat.st_size != s->shmem_size) { 402 error_setg(errp, "Can't map shmem fd: shmem size different" 403 " from device size!"); 404 return false; 405 } 406 407 /* 408 * Beyond step 2 ivshmem_process_msg, called by ivshmem_flat_read_msg 409 * handler -- when data is available on the server socket -- will handle 410 * the additional messages that will be generated by the server as peers 411 * connect or disconnect. 412 */ 413 qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_flat_can_receive_data, 414 ivshmem_flat_read_msg, NULL, NULL, s, NULL, true); 415 416 memory_region_init_ram_from_fd(&s->shmem, OBJECT(s), 417 "ivshmem-shmem", s->shmem_size, 418 RAM_SHARED, shmem_fd, 0, NULL); 419 sysbus_init_mmio(sbd, &s->shmem); 420 421 return true; 422 } 423 424 static void ivshmem_flat_realize(DeviceState *dev, Error **errp) 425 { 426 if (!ivshmem_flat_connect_server(dev, errp)) { 427 return; 428 } 429 } 430 431 static const Property ivshmem_flat_props[] = { 432 DEFINE_PROP_CHR("chardev", IvshmemFTState, server_chr), 433 DEFINE_PROP_UINT32("shmem-size", IvshmemFTState, shmem_size, 4 * MiB), 434 }; 435 436 static void ivshmem_flat_class_init(ObjectClass *klass, void *data) 437 { 438 DeviceClass *dc = DEVICE_CLASS(klass); 439 440 dc->hotpluggable = true; 441 dc->realize = ivshmem_flat_realize; 442 443 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 444 device_class_set_props(dc, ivshmem_flat_props); 445 446 /* Reason: Must be wired up in code (sysbus MRs and IRQ) */ 447 dc->user_creatable = false; 448 } 449 450 static const TypeInfo ivshmem_flat_types[] = { 451 { 452 .name = TYPE_IVSHMEM_FLAT, 453 .parent = TYPE_SYS_BUS_DEVICE, 454 .instance_size = sizeof(IvshmemFTState), 455 .instance_init = ivshmem_flat_instance_init, 456 .class_init = ivshmem_flat_class_init, 457 }, 458 }; 459 460 DEFINE_TYPES(ivshmem_flat_types) 461