1299976b0SCésar Belley /* 2299976b0SCésar Belley * U2F USB Passthru device. 3299976b0SCésar Belley * 4299976b0SCésar Belley * Copyright (c) 2020 César Belley <cesar.belley@lse.epita.fr> 5299976b0SCésar Belley * Written by César Belley <cesar.belley@lse.epita.fr> 6299976b0SCésar Belley * 7299976b0SCésar Belley * Permission is hereby granted, free of charge, to any person obtaining a copy 8299976b0SCésar Belley * of this software and associated documentation files (the "Software"), to deal 9299976b0SCésar Belley * in the Software without restriction, including without limitation the rights 10299976b0SCésar Belley * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11299976b0SCésar Belley * copies of the Software, and to permit persons to whom the Software is 12299976b0SCésar Belley * furnished to do so, subject to the following conditions: 13299976b0SCésar Belley * 14299976b0SCésar Belley * The above copyright notice and this permission notice shall be included in 15299976b0SCésar Belley * all copies or substantial portions of the Software. 16299976b0SCésar Belley * 17299976b0SCésar Belley * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18299976b0SCésar Belley * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19299976b0SCésar Belley * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20299976b0SCésar Belley * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21299976b0SCésar Belley * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22299976b0SCésar Belley * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23299976b0SCésar Belley * THE SOFTWARE. 24299976b0SCésar Belley */ 25299976b0SCésar Belley 26299976b0SCésar Belley #include "qemu/osdep.h" 27299976b0SCésar Belley #include "qemu/module.h" 28299976b0SCésar Belley #include "qemu/main-loop.h" 29299976b0SCésar Belley #include "qemu/error-report.h" 30299976b0SCésar Belley #include "qapi/error.h" 31299976b0SCésar Belley #include "hw/qdev-properties.h" 32299976b0SCésar Belley #include "hw/usb.h" 33299976b0SCésar Belley #include "migration/vmstate.h" 34299976b0SCésar Belley 35299976b0SCésar Belley #include "u2f.h" 36299976b0SCésar Belley 37*4ee40a6bSCésar Belley #ifdef CONFIG_LIBUDEV 38*4ee40a6bSCésar Belley #include <libudev.h> 39*4ee40a6bSCésar Belley #endif 40*4ee40a6bSCésar Belley #include <linux/hidraw.h> 41*4ee40a6bSCésar Belley #include <sys/ioctl.h> 42*4ee40a6bSCésar Belley 43299976b0SCésar Belley #define NONCE_SIZE 8 44299976b0SCésar Belley #define BROADCAST_CID 0xFFFFFFFF 45299976b0SCésar Belley #define TRANSACTION_TIMEOUT 120000 46299976b0SCésar Belley 47299976b0SCésar Belley struct transaction { 48299976b0SCésar Belley uint32_t cid; 49299976b0SCésar Belley uint16_t resp_bcnt; 50299976b0SCésar Belley uint16_t resp_size; 51299976b0SCésar Belley 52299976b0SCésar Belley /* Nonce for broadcast isolation */ 53299976b0SCésar Belley uint8_t nonce[NONCE_SIZE]; 54299976b0SCésar Belley }; 55299976b0SCésar Belley 56299976b0SCésar Belley typedef struct U2FPassthruState U2FPassthruState; 57299976b0SCésar Belley 58299976b0SCésar Belley #define CURRENT_TRANSACTIONS_NUM 4 59299976b0SCésar Belley 60299976b0SCésar Belley struct U2FPassthruState { 61299976b0SCésar Belley U2FKeyState base; 62299976b0SCésar Belley 63299976b0SCésar Belley /* Host device */ 64299976b0SCésar Belley char *hidraw; 65299976b0SCésar Belley int hidraw_fd; 66299976b0SCésar Belley 67299976b0SCésar Belley /* Current Transactions */ 68299976b0SCésar Belley struct transaction current_transactions[CURRENT_TRANSACTIONS_NUM]; 69299976b0SCésar Belley uint8_t current_transactions_start; 70299976b0SCésar Belley uint8_t current_transactions_end; 71299976b0SCésar Belley uint8_t current_transactions_num; 72299976b0SCésar Belley 73299976b0SCésar Belley /* Transaction time checking */ 74299976b0SCésar Belley int64_t last_transaction_time; 75299976b0SCésar Belley QEMUTimer timer; 76299976b0SCésar Belley }; 77299976b0SCésar Belley 78299976b0SCésar Belley #define TYPE_U2F_PASSTHRU "u2f-passthru" 79299976b0SCésar Belley #define PASSTHRU_U2F_KEY(obj) \ 80299976b0SCésar Belley OBJECT_CHECK(U2FPassthruState, (obj), TYPE_U2F_PASSTHRU) 81299976b0SCésar Belley 82299976b0SCésar Belley /* Init packet sizes */ 83299976b0SCésar Belley #define PACKET_INIT_HEADER_SIZE 7 84299976b0SCésar Belley #define PACKET_INIT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_INIT_HEADER_SIZE) 85299976b0SCésar Belley 86299976b0SCésar Belley /* Cont packet sizes */ 87299976b0SCésar Belley #define PACKET_CONT_HEADER_SIZE 5 88299976b0SCésar Belley #define PACKET_CONT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_CONT_HEADER_SIZE) 89299976b0SCésar Belley 90299976b0SCésar Belley struct packet_init { 91299976b0SCésar Belley uint32_t cid; 92299976b0SCésar Belley uint8_t cmd; 93299976b0SCésar Belley uint8_t bcnth; 94299976b0SCésar Belley uint8_t bcntl; 95299976b0SCésar Belley uint8_t data[PACKET_INIT_DATA_SIZE]; 96299976b0SCésar Belley } QEMU_PACKED; 97299976b0SCésar Belley 98299976b0SCésar Belley static inline uint32_t packet_get_cid(const void *packet) 99299976b0SCésar Belley { 100299976b0SCésar Belley return *((uint32_t *)packet); 101299976b0SCésar Belley } 102299976b0SCésar Belley 103299976b0SCésar Belley static inline bool packet_is_init(const void *packet) 104299976b0SCésar Belley { 105299976b0SCésar Belley return ((uint8_t *)packet)[4] & (1 << 7); 106299976b0SCésar Belley } 107299976b0SCésar Belley 108299976b0SCésar Belley static inline uint16_t packet_init_get_bcnt( 109299976b0SCésar Belley const struct packet_init *packet_init) 110299976b0SCésar Belley { 111299976b0SCésar Belley uint16_t bcnt = 0; 112299976b0SCésar Belley bcnt |= packet_init->bcnth << 8; 113299976b0SCésar Belley bcnt |= packet_init->bcntl; 114299976b0SCésar Belley 115299976b0SCésar Belley return bcnt; 116299976b0SCésar Belley } 117299976b0SCésar Belley 118299976b0SCésar Belley static void u2f_passthru_reset(U2FPassthruState *key) 119299976b0SCésar Belley { 120299976b0SCésar Belley timer_del(&key->timer); 121299976b0SCésar Belley qemu_set_fd_handler(key->hidraw_fd, NULL, NULL, key); 122299976b0SCésar Belley key->last_transaction_time = 0; 123299976b0SCésar Belley key->current_transactions_start = 0; 124299976b0SCésar Belley key->current_transactions_end = 0; 125299976b0SCésar Belley key->current_transactions_num = 0; 126299976b0SCésar Belley } 127299976b0SCésar Belley 128299976b0SCésar Belley static void u2f_timeout_check(void *opaque) 129299976b0SCésar Belley { 130299976b0SCésar Belley U2FPassthruState *key = opaque; 131299976b0SCésar Belley int64_t time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); 132299976b0SCésar Belley 133299976b0SCésar Belley if (time > key->last_transaction_time + TRANSACTION_TIMEOUT) { 134299976b0SCésar Belley u2f_passthru_reset(key); 135299976b0SCésar Belley } else { 136299976b0SCésar Belley timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4); 137299976b0SCésar Belley } 138299976b0SCésar Belley } 139299976b0SCésar Belley 140299976b0SCésar Belley static int u2f_transaction_get_index(U2FPassthruState *key, uint32_t cid) 141299976b0SCésar Belley { 142299976b0SCésar Belley for (int i = 0; i < key->current_transactions_num; ++i) { 143299976b0SCésar Belley int index = (key->current_transactions_start + i) 144299976b0SCésar Belley % CURRENT_TRANSACTIONS_NUM; 145299976b0SCésar Belley if (cid == key->current_transactions[index].cid) { 146299976b0SCésar Belley return index; 147299976b0SCésar Belley } 148299976b0SCésar Belley } 149299976b0SCésar Belley return -1; 150299976b0SCésar Belley } 151299976b0SCésar Belley 152299976b0SCésar Belley static struct transaction *u2f_transaction_get(U2FPassthruState *key, 153299976b0SCésar Belley uint32_t cid) 154299976b0SCésar Belley { 155299976b0SCésar Belley int index = u2f_transaction_get_index(key, cid); 156299976b0SCésar Belley if (index < 0) { 157299976b0SCésar Belley return NULL; 158299976b0SCésar Belley } 159299976b0SCésar Belley return &key->current_transactions[index]; 160299976b0SCésar Belley } 161299976b0SCésar Belley 162299976b0SCésar Belley static struct transaction *u2f_transaction_get_from_nonce(U2FPassthruState *key, 163299976b0SCésar Belley const uint8_t nonce[NONCE_SIZE]) 164299976b0SCésar Belley { 165299976b0SCésar Belley for (int i = 0; i < key->current_transactions_num; ++i) { 166299976b0SCésar Belley int index = (key->current_transactions_start + i) 167299976b0SCésar Belley % CURRENT_TRANSACTIONS_NUM; 168299976b0SCésar Belley if (key->current_transactions[index].cid == BROADCAST_CID 169299976b0SCésar Belley && memcmp(nonce, key->current_transactions[index].nonce, 170299976b0SCésar Belley NONCE_SIZE) == 0) { 171299976b0SCésar Belley return &key->current_transactions[index]; 172299976b0SCésar Belley } 173299976b0SCésar Belley } 174299976b0SCésar Belley return NULL; 175299976b0SCésar Belley } 176299976b0SCésar Belley 177299976b0SCésar Belley static void u2f_transaction_close(U2FPassthruState *key, uint32_t cid) 178299976b0SCésar Belley { 179299976b0SCésar Belley int index, next_index; 180299976b0SCésar Belley index = u2f_transaction_get_index(key, cid); 181299976b0SCésar Belley if (index < 0) { 182299976b0SCésar Belley return; 183299976b0SCésar Belley } 184299976b0SCésar Belley next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM; 185299976b0SCésar Belley 186299976b0SCésar Belley /* Rearrange to ensure the oldest is at the start position */ 187299976b0SCésar Belley while (next_index != key->current_transactions_end) { 188299976b0SCésar Belley memcpy(&key->current_transactions[index], 189299976b0SCésar Belley &key->current_transactions[next_index], 190299976b0SCésar Belley sizeof(struct transaction)); 191299976b0SCésar Belley 192299976b0SCésar Belley index = next_index; 193299976b0SCésar Belley next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM; 194299976b0SCésar Belley } 195299976b0SCésar Belley 196299976b0SCésar Belley key->current_transactions_end = index; 197299976b0SCésar Belley --key->current_transactions_num; 198299976b0SCésar Belley 199299976b0SCésar Belley if (key->current_transactions_num == 0) { 200299976b0SCésar Belley u2f_passthru_reset(key); 201299976b0SCésar Belley } 202299976b0SCésar Belley } 203299976b0SCésar Belley 204299976b0SCésar Belley static void u2f_transaction_add(U2FPassthruState *key, uint32_t cid, 205299976b0SCésar Belley const uint8_t nonce[NONCE_SIZE]) 206299976b0SCésar Belley { 207299976b0SCésar Belley uint8_t index; 208299976b0SCésar Belley struct transaction *transaction; 209299976b0SCésar Belley 210299976b0SCésar Belley if (key->current_transactions_num >= CURRENT_TRANSACTIONS_NUM) { 211299976b0SCésar Belley /* Close the oldest transaction */ 212299976b0SCésar Belley index = key->current_transactions_start; 213299976b0SCésar Belley transaction = &key->current_transactions[index]; 214299976b0SCésar Belley u2f_transaction_close(key, transaction->cid); 215299976b0SCésar Belley } 216299976b0SCésar Belley 217299976b0SCésar Belley /* Index */ 218299976b0SCésar Belley index = key->current_transactions_end; 219299976b0SCésar Belley key->current_transactions_end = (index + 1) % CURRENT_TRANSACTIONS_NUM; 220299976b0SCésar Belley ++key->current_transactions_num; 221299976b0SCésar Belley 222299976b0SCésar Belley /* Transaction */ 223299976b0SCésar Belley transaction = &key->current_transactions[index]; 224299976b0SCésar Belley transaction->cid = cid; 225299976b0SCésar Belley transaction->resp_bcnt = 0; 226299976b0SCésar Belley transaction->resp_size = 0; 227299976b0SCésar Belley 228299976b0SCésar Belley /* Nonce */ 229299976b0SCésar Belley if (nonce != NULL) { 230299976b0SCésar Belley memcpy(transaction->nonce, nonce, NONCE_SIZE); 231299976b0SCésar Belley } 232299976b0SCésar Belley } 233299976b0SCésar Belley 234299976b0SCésar Belley static void u2f_passthru_read(void *opaque); 235299976b0SCésar Belley 236299976b0SCésar Belley static void u2f_transaction_start(U2FPassthruState *key, 237299976b0SCésar Belley const struct packet_init *packet_init) 238299976b0SCésar Belley { 239299976b0SCésar Belley int64_t time; 240299976b0SCésar Belley 241299976b0SCésar Belley /* Transaction */ 242299976b0SCésar Belley if (packet_init->cid == BROADCAST_CID) { 243299976b0SCésar Belley u2f_transaction_add(key, packet_init->cid, packet_init->data); 244299976b0SCésar Belley } else { 245299976b0SCésar Belley u2f_transaction_add(key, packet_init->cid, NULL); 246299976b0SCésar Belley } 247299976b0SCésar Belley 248299976b0SCésar Belley /* Time */ 249299976b0SCésar Belley time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); 250299976b0SCésar Belley if (key->last_transaction_time == 0) { 251299976b0SCésar Belley qemu_set_fd_handler(key->hidraw_fd, u2f_passthru_read, NULL, key); 252299976b0SCésar Belley timer_init_ms(&key->timer, QEMU_CLOCK_VIRTUAL, u2f_timeout_check, key); 253299976b0SCésar Belley timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4); 254299976b0SCésar Belley } 255299976b0SCésar Belley key->last_transaction_time = time; 256299976b0SCésar Belley } 257299976b0SCésar Belley 258299976b0SCésar Belley static void u2f_passthru_recv_from_host(U2FPassthruState *key, 259299976b0SCésar Belley const uint8_t packet[U2FHID_PACKET_SIZE]) 260299976b0SCésar Belley { 261299976b0SCésar Belley struct transaction *transaction; 262299976b0SCésar Belley uint32_t cid; 263299976b0SCésar Belley 264299976b0SCésar Belley /* Retrieve transaction */ 265299976b0SCésar Belley cid = packet_get_cid(packet); 266299976b0SCésar Belley if (cid == BROADCAST_CID) { 267299976b0SCésar Belley struct packet_init *packet_init; 268299976b0SCésar Belley if (!packet_is_init(packet)) { 269299976b0SCésar Belley return; 270299976b0SCésar Belley } 271299976b0SCésar Belley packet_init = (struct packet_init *)packet; 272299976b0SCésar Belley transaction = u2f_transaction_get_from_nonce(key, packet_init->data); 273299976b0SCésar Belley } else { 274299976b0SCésar Belley transaction = u2f_transaction_get(key, cid); 275299976b0SCésar Belley } 276299976b0SCésar Belley 277299976b0SCésar Belley /* Ignore no started transaction */ 278299976b0SCésar Belley if (transaction == NULL) { 279299976b0SCésar Belley return; 280299976b0SCésar Belley } 281299976b0SCésar Belley 282299976b0SCésar Belley if (packet_is_init(packet)) { 283299976b0SCésar Belley struct packet_init *packet_init = (struct packet_init *)packet; 284299976b0SCésar Belley transaction->resp_bcnt = packet_init_get_bcnt(packet_init); 285299976b0SCésar Belley transaction->resp_size = PACKET_INIT_DATA_SIZE; 286299976b0SCésar Belley 287299976b0SCésar Belley if (packet_init->cid == BROADCAST_CID) { 288299976b0SCésar Belley /* Nonce checking for legitimate response */ 289299976b0SCésar Belley if (memcmp(transaction->nonce, packet_init->data, NONCE_SIZE) 290299976b0SCésar Belley != 0) { 291299976b0SCésar Belley return; 292299976b0SCésar Belley } 293299976b0SCésar Belley } 294299976b0SCésar Belley } else { 295299976b0SCésar Belley transaction->resp_size += PACKET_CONT_DATA_SIZE; 296299976b0SCésar Belley } 297299976b0SCésar Belley 298299976b0SCésar Belley /* Transaction end check */ 299299976b0SCésar Belley if (transaction->resp_size >= transaction->resp_bcnt) { 300299976b0SCésar Belley u2f_transaction_close(key, cid); 301299976b0SCésar Belley } 302299976b0SCésar Belley u2f_send_to_guest(&key->base, packet); 303299976b0SCésar Belley } 304299976b0SCésar Belley 305299976b0SCésar Belley static void u2f_passthru_read(void *opaque) 306299976b0SCésar Belley { 307299976b0SCésar Belley U2FPassthruState *key = opaque; 308299976b0SCésar Belley U2FKeyState *base = &key->base; 309299976b0SCésar Belley uint8_t packet[2 * U2FHID_PACKET_SIZE]; 310299976b0SCésar Belley int ret; 311299976b0SCésar Belley 312299976b0SCésar Belley /* Full size base queue check */ 313299976b0SCésar Belley if (base->pending_in_num >= U2FHID_PENDING_IN_NUM) { 314299976b0SCésar Belley return; 315299976b0SCésar Belley } 316299976b0SCésar Belley 317299976b0SCésar Belley ret = read(key->hidraw_fd, packet, sizeof(packet)); 318299976b0SCésar Belley if (ret < 0) { 319299976b0SCésar Belley /* Detach */ 320299976b0SCésar Belley if (base->dev.attached) { 321299976b0SCésar Belley usb_device_detach(&base->dev); 322299976b0SCésar Belley u2f_passthru_reset(key); 323299976b0SCésar Belley } 324299976b0SCésar Belley return; 325299976b0SCésar Belley } 326299976b0SCésar Belley if (ret != U2FHID_PACKET_SIZE) { 327299976b0SCésar Belley return; 328299976b0SCésar Belley } 329299976b0SCésar Belley u2f_passthru_recv_from_host(key, packet); 330299976b0SCésar Belley } 331299976b0SCésar Belley 332299976b0SCésar Belley static void u2f_passthru_recv_from_guest(U2FKeyState *base, 333299976b0SCésar Belley const uint8_t packet[U2FHID_PACKET_SIZE]) 334299976b0SCésar Belley { 335299976b0SCésar Belley U2FPassthruState *key = PASSTHRU_U2F_KEY(base); 336299976b0SCésar Belley uint8_t host_packet[U2FHID_PACKET_SIZE + 1]; 337299976b0SCésar Belley ssize_t written; 338299976b0SCésar Belley 339299976b0SCésar Belley if (packet_is_init(packet)) { 340299976b0SCésar Belley u2f_transaction_start(key, (struct packet_init *)packet); 341299976b0SCésar Belley } 342299976b0SCésar Belley 343299976b0SCésar Belley host_packet[0] = 0; 344299976b0SCésar Belley memcpy(host_packet + 1, packet, U2FHID_PACKET_SIZE); 345299976b0SCésar Belley 346299976b0SCésar Belley written = write(key->hidraw_fd, host_packet, sizeof(host_packet)); 347299976b0SCésar Belley if (written != sizeof(host_packet)) { 348299976b0SCésar Belley error_report("%s: Bad written size (req 0x%zu, val 0x%zd)", 349299976b0SCésar Belley TYPE_U2F_PASSTHRU, sizeof(host_packet), written); 350299976b0SCésar Belley } 351299976b0SCésar Belley } 352299976b0SCésar Belley 353*4ee40a6bSCésar Belley static bool u2f_passthru_is_u2f_device(int fd) 354*4ee40a6bSCésar Belley { 355*4ee40a6bSCésar Belley int ret, rdesc_size; 356*4ee40a6bSCésar Belley struct hidraw_report_descriptor rdesc; 357*4ee40a6bSCésar Belley const uint8_t u2f_hid_report_desc_header[] = { 358*4ee40a6bSCésar Belley 0x06, 0xd0, 0xf1, /* Usage Page (FIDO) */ 359*4ee40a6bSCésar Belley 0x09, 0x01, /* Usage (FIDO) */ 360*4ee40a6bSCésar Belley }; 361*4ee40a6bSCésar Belley 362*4ee40a6bSCésar Belley /* Get report descriptor size */ 363*4ee40a6bSCésar Belley ret = ioctl(fd, HIDIOCGRDESCSIZE, &rdesc_size); 364*4ee40a6bSCésar Belley if (ret < 0 || rdesc_size < sizeof(u2f_hid_report_desc_header)) { 365*4ee40a6bSCésar Belley return false; 366*4ee40a6bSCésar Belley } 367*4ee40a6bSCésar Belley 368*4ee40a6bSCésar Belley /* Get report descriptor */ 369*4ee40a6bSCésar Belley memset(&rdesc, 0x0, sizeof(rdesc)); 370*4ee40a6bSCésar Belley rdesc.size = rdesc_size; 371*4ee40a6bSCésar Belley ret = ioctl(fd, HIDIOCGRDESC, &rdesc); 372*4ee40a6bSCésar Belley if (ret < 0) { 373*4ee40a6bSCésar Belley return false; 374*4ee40a6bSCésar Belley } 375*4ee40a6bSCésar Belley 376*4ee40a6bSCésar Belley /* Header bytes cover specific U2F rdesc values */ 377*4ee40a6bSCésar Belley return memcmp(u2f_hid_report_desc_header, rdesc.value, 378*4ee40a6bSCésar Belley sizeof(u2f_hid_report_desc_header)) == 0; 379*4ee40a6bSCésar Belley } 380*4ee40a6bSCésar Belley 381299976b0SCésar Belley static void u2f_passthru_unrealize(U2FKeyState *base) 382299976b0SCésar Belley { 383299976b0SCésar Belley U2FPassthruState *key = PASSTHRU_U2F_KEY(base); 384299976b0SCésar Belley 385299976b0SCésar Belley u2f_passthru_reset(key); 386299976b0SCésar Belley qemu_close(key->hidraw_fd); 387299976b0SCésar Belley } 388299976b0SCésar Belley 389299976b0SCésar Belley static void u2f_passthru_realize(U2FKeyState *base, Error **errp) 390299976b0SCésar Belley { 391299976b0SCésar Belley U2FPassthruState *key = PASSTHRU_U2F_KEY(base); 392299976b0SCésar Belley int fd; 393299976b0SCésar Belley 394299976b0SCésar Belley if (key->hidraw == NULL) { 395299976b0SCésar Belley error_setg(errp, "%s: Missing hidraw", TYPE_U2F_PASSTHRU); 396299976b0SCésar Belley return; 397299976b0SCésar Belley } 398299976b0SCésar Belley 399299976b0SCésar Belley fd = qemu_open(key->hidraw, O_RDWR); 400299976b0SCésar Belley if (fd < 0) { 401299976b0SCésar Belley error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU, 402299976b0SCésar Belley key->hidraw); 403299976b0SCésar Belley return; 404299976b0SCésar Belley } 405*4ee40a6bSCésar Belley 406*4ee40a6bSCésar Belley if (!u2f_passthru_is_u2f_device(fd)) { 407*4ee40a6bSCésar Belley qemu_close(fd); 408*4ee40a6bSCésar Belley error_setg(errp, "%s: Passed hidraw does not represent " 409*4ee40a6bSCésar Belley "a U2F HID device", TYPE_U2F_PASSTHRU); 410*4ee40a6bSCésar Belley return; 411*4ee40a6bSCésar Belley } 412299976b0SCésar Belley key->hidraw_fd = fd; 413299976b0SCésar Belley u2f_passthru_reset(key); 414299976b0SCésar Belley } 415299976b0SCésar Belley 416299976b0SCésar Belley static int u2f_passthru_post_load(void *opaque, int version_id) 417299976b0SCésar Belley { 418299976b0SCésar Belley U2FPassthruState *key = opaque; 419299976b0SCésar Belley u2f_passthru_reset(key); 420299976b0SCésar Belley return 0; 421299976b0SCésar Belley } 422299976b0SCésar Belley 423299976b0SCésar Belley static const VMStateDescription u2f_passthru_vmstate = { 424299976b0SCésar Belley .name = "u2f-key-passthru", 425299976b0SCésar Belley .version_id = 1, 426299976b0SCésar Belley .minimum_version_id = 1, 427299976b0SCésar Belley .post_load = u2f_passthru_post_load, 428299976b0SCésar Belley .fields = (VMStateField[]) { 429299976b0SCésar Belley VMSTATE_U2F_KEY(base, U2FPassthruState), 430299976b0SCésar Belley VMSTATE_END_OF_LIST() 431299976b0SCésar Belley } 432299976b0SCésar Belley }; 433299976b0SCésar Belley 434299976b0SCésar Belley static Property u2f_passthru_properties[] = { 435299976b0SCésar Belley DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw), 436299976b0SCésar Belley DEFINE_PROP_END_OF_LIST(), 437299976b0SCésar Belley }; 438299976b0SCésar Belley 439299976b0SCésar Belley static void u2f_passthru_class_init(ObjectClass *klass, void *data) 440299976b0SCésar Belley { 441299976b0SCésar Belley DeviceClass *dc = DEVICE_CLASS(klass); 442299976b0SCésar Belley U2FKeyClass *kc = U2F_KEY_CLASS(klass); 443299976b0SCésar Belley 444299976b0SCésar Belley kc->realize = u2f_passthru_realize; 445299976b0SCésar Belley kc->unrealize = u2f_passthru_unrealize; 446299976b0SCésar Belley kc->recv_from_guest = u2f_passthru_recv_from_guest; 447299976b0SCésar Belley dc->desc = "QEMU U2F passthrough key"; 448299976b0SCésar Belley dc->vmsd = &u2f_passthru_vmstate; 449299976b0SCésar Belley device_class_set_props(dc, u2f_passthru_properties); 450299976b0SCésar Belley } 451299976b0SCésar Belley 452299976b0SCésar Belley static const TypeInfo u2f_key_passthru_info = { 453299976b0SCésar Belley .name = TYPE_U2F_PASSTHRU, 454299976b0SCésar Belley .parent = TYPE_U2F_KEY, 455299976b0SCésar Belley .instance_size = sizeof(U2FPassthruState), 456299976b0SCésar Belley .class_init = u2f_passthru_class_init 457299976b0SCésar Belley }; 458299976b0SCésar Belley 459299976b0SCésar Belley static void u2f_key_passthru_register_types(void) 460299976b0SCésar Belley { 461299976b0SCésar Belley type_register_static(&u2f_key_passthru_info); 462299976b0SCésar Belley } 463299976b0SCésar Belley 464299976b0SCésar Belley type_init(u2f_key_passthru_register_types) 465