11c7850f9SSasha Levin #include "kvm/virtio-9p.h" 21c7850f9SSasha Levin #include "kvm/virtio-pci-dev.h" 31c7850f9SSasha Levin #include "kvm/virtio.h" 41c7850f9SSasha Levin #include "kvm/ioport.h" 51c7850f9SSasha Levin #include "kvm/mutex.h" 61c7850f9SSasha Levin #include "kvm/util.h" 71c7850f9SSasha Levin #include "kvm/kvm.h" 81c7850f9SSasha Levin #include "kvm/pci.h" 91c7850f9SSasha Levin #include "kvm/threadpool.h" 101c7850f9SSasha Levin #include "kvm/irq.h" 111c7850f9SSasha Levin 121c7850f9SSasha Levin #include <linux/virtio_ring.h> 131c7850f9SSasha Levin #include <linux/virtio_9p.h> 141c7850f9SSasha Levin #include <net/9p/9p.h> 151c7850f9SSasha Levin 161c7850f9SSasha Levin #include <fcntl.h> 171c7850f9SSasha Levin #include <sys/types.h> 181c7850f9SSasha Levin #include <sys/stat.h> 191c7850f9SSasha Levin #include <pthread.h> 201c7850f9SSasha Levin #include <dirent.h> 211c7850f9SSasha Levin 221c7850f9SSasha Levin #define NUM_VIRT_QUEUES 1 231c7850f9SSasha Levin #define VIRTIO_P9_QUEUE_SIZE 128 241c7850f9SSasha Levin #define VIRTIO_P9_TAG "kvm_9p" 251c7850f9SSasha Levin #define VIRTIO_P9_HDR_LEN (sizeof(u32)+sizeof(u8)+sizeof(u16)) 261c7850f9SSasha Levin #define VIRTIO_P9_MAX_FID 128 271c7850f9SSasha Levin #define VIRTIO_P9_VERSION "9P2000" 281c7850f9SSasha Levin 291c7850f9SSasha Levin struct p9_msg { 301c7850f9SSasha Levin u32 size; 311c7850f9SSasha Levin u8 cmd; 321c7850f9SSasha Levin u16 tag; 331c7850f9SSasha Levin u8 msg[0]; 341c7850f9SSasha Levin } __attribute__((packed)); 351c7850f9SSasha Levin 361c7850f9SSasha Levin struct p9_fid { 371c7850f9SSasha Levin u32 fid; 381c7850f9SSasha Levin u8 is_dir; 391c7850f9SSasha Levin char abs_path[PATH_MAX]; 401c7850f9SSasha Levin char *path; 411c7850f9SSasha Levin DIR *dir; 421c7850f9SSasha Levin int fd; 431c7850f9SSasha Levin }; 441c7850f9SSasha Levin 451c7850f9SSasha Levin static struct pci_device_header virtio_p9_pci_device = { 461c7850f9SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 471c7850f9SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_P9, 481c7850f9SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 491c7850f9SSasha Levin .revision_id = 0, 501c7850f9SSasha Levin .class = 0x010000, 511c7850f9SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 521c7850f9SSasha Levin .subsys_id = VIRTIO_ID_9P, 531c7850f9SSasha Levin .bar[0] = IOPORT_VIRTIO_P9 | PCI_BASE_ADDRESS_SPACE_IO, 541c7850f9SSasha Levin }; 551c7850f9SSasha Levin 561c7850f9SSasha Levin struct p9_dev { 571c7850f9SSasha Levin u8 status; 581c7850f9SSasha Levin u8 isr; 591c7850f9SSasha Levin u16 config_vector; 601c7850f9SSasha Levin u32 features; 611c7850f9SSasha Levin struct virtio_9p_config *config; 621c7850f9SSasha Levin 631c7850f9SSasha Levin /* virtio queue */ 641c7850f9SSasha Levin u16 queue_selector; 651c7850f9SSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 661c7850f9SSasha Levin void *jobs[NUM_VIRT_QUEUES]; 671c7850f9SSasha Levin 681c7850f9SSasha Levin struct p9_fid fids[VIRTIO_P9_MAX_FID]; 691c7850f9SSasha Levin char root_dir[PATH_MAX]; 701c7850f9SSasha Levin }; 711c7850f9SSasha Levin 721c7850f9SSasha Levin static struct p9_dev p9dev; 731c7850f9SSasha Levin 741c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */ 751c7850f9SSasha Levin static const char *rel_to_abs(const char *path, char *abs_path) 761c7850f9SSasha Levin { 771c7850f9SSasha Levin sprintf(abs_path, "%s/%s", p9dev.root_dir, path); 781c7850f9SSasha Levin 791c7850f9SSasha Levin return abs_path; 801c7850f9SSasha Levin } 811c7850f9SSasha Levin 821c7850f9SSasha Levin static bool virtio_p9_dev_in(void *data, unsigned long offset, int size, u32 count) 831c7850f9SSasha Levin { 841c7850f9SSasha Levin u8 *config_space = (u8 *) p9dev.config; 851c7850f9SSasha Levin 861c7850f9SSasha Levin if (size != 1 || count != 1) 871c7850f9SSasha Levin return false; 881c7850f9SSasha Levin 891c7850f9SSasha Levin ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]); 901c7850f9SSasha Levin 911c7850f9SSasha Levin return true; 921c7850f9SSasha Levin } 931c7850f9SSasha Levin 94*3d62dea6SSasha Levin static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 951c7850f9SSasha Levin { 961c7850f9SSasha Levin unsigned long offset; 971c7850f9SSasha Levin bool ret = true; 981c7850f9SSasha Levin 991c7850f9SSasha Levin offset = port - IOPORT_VIRTIO_P9; 1001c7850f9SSasha Levin 1011c7850f9SSasha Levin switch (offset) { 1021c7850f9SSasha Levin case VIRTIO_PCI_HOST_FEATURES: 1031c7850f9SSasha Levin ioport__write32(data, p9dev.features); 1041c7850f9SSasha Levin ret = true; 1051c7850f9SSasha Levin break; 1061c7850f9SSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 1071c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_SEL: 1081c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: 1091c7850f9SSasha Levin ret = false; 1101c7850f9SSasha Levin break; 1111c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_PFN: 1121c7850f9SSasha Levin ioport__write32(data, p9dev.vqs[p9dev.queue_selector].pfn); 1131c7850f9SSasha Levin break; 1141c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_NUM: 1151c7850f9SSasha Levin ioport__write16(data, VIRTIO_P9_QUEUE_SIZE); 1161c7850f9SSasha Levin break; 1171c7850f9SSasha Levin case VIRTIO_PCI_STATUS: 1181c7850f9SSasha Levin ioport__write8(data, p9dev.status); 1191c7850f9SSasha Levin break; 1201c7850f9SSasha Levin case VIRTIO_PCI_ISR: 1211c7850f9SSasha Levin ioport__write8(data, p9dev.isr); 1221c7850f9SSasha Levin kvm__irq_line(kvm, virtio_p9_pci_device.irq_line, VIRTIO_IRQ_LOW); 1231c7850f9SSasha Levin p9dev.isr = VIRTIO_IRQ_LOW; 1241c7850f9SSasha Levin break; 1251c7850f9SSasha Levin default: 1261c7850f9SSasha Levin ret = virtio_p9_dev_in(data, offset, size, count); 1271c7850f9SSasha Levin break; 1281c7850f9SSasha Levin }; 1291c7850f9SSasha Levin 1301c7850f9SSasha Levin return ret; 1311c7850f9SSasha Levin } 1321c7850f9SSasha Levin 1331c7850f9SSasha Levin static int omode2uflags(u8 mode) 1341c7850f9SSasha Levin { 1351c7850f9SSasha Levin int ret = 0; 1361c7850f9SSasha Levin 1371c7850f9SSasha Levin /* Basic open modes are same as uflags */ 1381c7850f9SSasha Levin ret = mode & 3; 1391c7850f9SSasha Levin 1401c7850f9SSasha Levin /* Everything else is different */ 1411c7850f9SSasha Levin if (mode & P9_OTRUNC) 1421c7850f9SSasha Levin ret |= O_TRUNC; 1431c7850f9SSasha Levin 1441c7850f9SSasha Levin if (mode & P9_OAPPEND) 1451c7850f9SSasha Levin ret |= O_APPEND; 1461c7850f9SSasha Levin 1471c7850f9SSasha Levin if (mode & P9_OEXCL) 1481c7850f9SSasha Levin ret |= O_EXCL; 1491c7850f9SSasha Levin 1501c7850f9SSasha Levin return ret; 1511c7850f9SSasha Levin } 1521c7850f9SSasha Levin 1531c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid) 1541c7850f9SSasha Levin { 1551c7850f9SSasha Levin *qid = (struct p9_qid) { 1561c7850f9SSasha Levin .path = st->st_ino, 1571c7850f9SSasha Levin .version = st->st_mtime, 1581c7850f9SSasha Levin }; 1591c7850f9SSasha Levin 1601c7850f9SSasha Levin if (S_ISDIR(st->st_mode)) 1611c7850f9SSasha Levin qid->type |= P9_QTDIR; 1621c7850f9SSasha Levin } 1631c7850f9SSasha Levin 1641c7850f9SSasha Levin static void close_fid(u32 fid) 1651c7850f9SSasha Levin { 1661c7850f9SSasha Levin if (p9dev.fids[fid].fd > 0) { 1671c7850f9SSasha Levin close(p9dev.fids[fid].fd); 1681c7850f9SSasha Levin p9dev.fids[fid].fd = -1; 1691c7850f9SSasha Levin } 1701c7850f9SSasha Levin if (p9dev.fids[fid].dir) { 1711c7850f9SSasha Levin closedir(p9dev.fids[fid].dir); 1721c7850f9SSasha Levin p9dev.fids[fid].dir = NULL; 1731c7850f9SSasha Levin } 1741c7850f9SSasha Levin } 1751c7850f9SSasha Levin 1761c7850f9SSasha Levin static void set_p9msg_hdr(struct p9_msg *msg, u32 size, u8 cmd, u16 tag) 1771c7850f9SSasha Levin { 1781c7850f9SSasha Levin *msg = (struct p9_msg) { 1791c7850f9SSasha Levin .size = size, 1801c7850f9SSasha Levin .tag = tag, 1811c7850f9SSasha Levin .cmd = cmd, 1821c7850f9SSasha Levin }; 1831c7850f9SSasha Levin } 1841c7850f9SSasha Levin 1851c7850f9SSasha Levin static bool virtio_p9_version(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 1861c7850f9SSasha Levin { 1871c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 1881c7850f9SSasha Levin struct p9_rversion *rversion = (struct p9_rversion *)outmsg->msg; 1891c7850f9SSasha Levin 1901c7850f9SSasha Levin rversion->msize = 4096; 1911c7850f9SSasha Levin rversion->version.len = strlen(VIRTIO_P9_VERSION); 1921c7850f9SSasha Levin memcpy(&rversion->version.str, VIRTIO_P9_VERSION, rversion->version.len); 1931c7850f9SSasha Levin 1941c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + rversion->version.len + sizeof(u16) + sizeof(u32); 1951c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RVERSION, msg->tag); 1961c7850f9SSasha Levin 1971c7850f9SSasha Levin return true; 1981c7850f9SSasha Levin } 1991c7850f9SSasha Levin 2001c7850f9SSasha Levin static bool virtio_p9_clunk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 2011c7850f9SSasha Levin { 2021c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 2031c7850f9SSasha Levin struct p9_tclunk *tclunk = (struct p9_tclunk *)msg->msg; 2041c7850f9SSasha Levin 2051c7850f9SSasha Levin close_fid(tclunk->fid); 2061c7850f9SSasha Levin 2071c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN; 2081c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RCLUNK, msg->tag); 2091c7850f9SSasha Levin 2101c7850f9SSasha Levin return true; 2111c7850f9SSasha Levin } 2121c7850f9SSasha Levin 2131c7850f9SSasha Levin static bool virtio_p9_open(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 2141c7850f9SSasha Levin { 2151c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 2161c7850f9SSasha Levin struct p9_topen *topen = (struct p9_topen *)msg->msg; 2171c7850f9SSasha Levin struct p9_ropen *ropen = (struct p9_ropen *)outmsg->msg; 2181c7850f9SSasha Levin struct p9_fid *new_fid = &p9dev.fids[topen->fid]; 2191c7850f9SSasha Levin struct stat st; 2201c7850f9SSasha Levin 2211c7850f9SSasha Levin if (stat(new_fid->abs_path, &st) < 0) 2221c7850f9SSasha Levin return false; 2231c7850f9SSasha Levin 2241c7850f9SSasha Levin st2qid(&st, &ropen->qid); 2251c7850f9SSasha Levin ropen->iounit = 0; 2261c7850f9SSasha Levin 2271c7850f9SSasha Levin if (new_fid->is_dir) 2281c7850f9SSasha Levin new_fid->dir = opendir(new_fid->abs_path); 2291c7850f9SSasha Levin else 2301c7850f9SSasha Levin new_fid->fd = open(new_fid->abs_path, omode2uflags(topen->mode)); 2311c7850f9SSasha Levin 2321c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + sizeof(*ropen); 2331c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_ROPEN, msg->tag); 2341c7850f9SSasha Levin 2351c7850f9SSasha Levin return true; 2361c7850f9SSasha Levin } 2371c7850f9SSasha Levin 2381c7850f9SSasha Levin static bool virtio_p9_create(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 2391c7850f9SSasha Levin { 2401c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 2411c7850f9SSasha Levin struct p9_tcreate *tcreate = (struct p9_tcreate *)msg->msg; 2421c7850f9SSasha Levin struct p9_rcreate *rcreate = (struct p9_rcreate *)outmsg->msg; 2431c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[tcreate->fid]; 2441c7850f9SSasha Levin struct stat st; 2451c7850f9SSasha Levin u8 mode; 2461c7850f9SSasha Levin u32 perm; 2471c7850f9SSasha Levin 2481c7850f9SSasha Levin rcreate->iounit = 0; 2491c7850f9SSasha Levin 2501c7850f9SSasha Levin /* Get last byte of the variable length struct */ 2511c7850f9SSasha Levin mode = *((u8 *)msg + msg->size - 1); 2521c7850f9SSasha Levin perm = *(u32 *)((u8 *)msg + msg->size - 5); 2531c7850f9SSasha Levin 2541c7850f9SSasha Levin sprintf(fid->path, "%s/%.*s", fid->path, tcreate->name.len, (char *)&tcreate->name.str); 2551c7850f9SSasha Levin 2561c7850f9SSasha Levin close_fid(tcreate->fid); 2571c7850f9SSasha Levin 2581c7850f9SSasha Levin if (perm & P9_DMDIR) { 2591c7850f9SSasha Levin mkdir(fid->abs_path, perm & 0xFFFF); 2601c7850f9SSasha Levin fid->dir = opendir(fid->abs_path); 2611c7850f9SSasha Levin fid->is_dir = 1; 2621c7850f9SSasha Levin } else { 2631c7850f9SSasha Levin fid->fd = open(fid->abs_path, omode2uflags(mode) | O_CREAT, 0777); 2641c7850f9SSasha Levin } 2651c7850f9SSasha Levin 2661c7850f9SSasha Levin if (stat(fid->abs_path, &st) < 0) 2671c7850f9SSasha Levin return false; 2681c7850f9SSasha Levin 2691c7850f9SSasha Levin st2qid(&st, &rcreate->qid); 2701c7850f9SSasha Levin 2711c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + sizeof(*rcreate); 2721c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RCREATE, msg->tag); 2731c7850f9SSasha Levin 2741c7850f9SSasha Levin return true; 2751c7850f9SSasha Levin } 2761c7850f9SSasha Levin 2771c7850f9SSasha Levin static bool virtio_p9_walk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 2781c7850f9SSasha Levin { 2791c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 2801c7850f9SSasha Levin struct p9_twalk *twalk = (struct p9_twalk *)msg->msg; 2811c7850f9SSasha Levin struct p9_rwalk *rwalk = (struct p9_rwalk *)outmsg->msg; 2821c7850f9SSasha Levin struct p9_str *str = twalk->wnames; 2831c7850f9SSasha Levin struct p9_fid *new_fid = &p9dev.fids[twalk->newfid]; 2841c7850f9SSasha Levin u8 i; 2851c7850f9SSasha Levin 2861c7850f9SSasha Levin rwalk->nwqid = 0; 2871c7850f9SSasha Levin if (twalk->nwname) { 2881c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[twalk->fid]; 2891c7850f9SSasha Levin 2901c7850f9SSasha Levin for (i = 0; i < twalk->nwname; i++) { 2911c7850f9SSasha Levin char tmp[PATH_MAX] = {0}; 2921c7850f9SSasha Levin char full_path[PATH_MAX]; 2931c7850f9SSasha Levin struct stat st; 2941c7850f9SSasha Levin 2951c7850f9SSasha Levin /* Format the new path we're 'walk'ing into */ 2961c7850f9SSasha Levin sprintf(tmp, "%s/%.*s", fid->path, str->len, (char *)&str->str); 2971c7850f9SSasha Levin 2981c7850f9SSasha Levin if (stat(rel_to_abs(tmp, full_path), &st) < 0) 2991c7850f9SSasha Levin break; 3001c7850f9SSasha Levin 3011c7850f9SSasha Levin st2qid(&st, &rwalk->wqids[i]); 3021c7850f9SSasha Levin new_fid->is_dir = S_ISDIR(st.st_mode); 3031c7850f9SSasha Levin strcpy(new_fid->path, tmp); 3041c7850f9SSasha Levin new_fid->fid = twalk->newfid; 3051c7850f9SSasha Levin rwalk->nwqid++; 3061c7850f9SSasha Levin } 3071c7850f9SSasha Levin } else { 3081c7850f9SSasha Levin new_fid->is_dir = p9dev.fids[twalk->fid].is_dir; 3091c7850f9SSasha Levin strcpy(new_fid->path, p9dev.fids[twalk->fid].path); 3101c7850f9SSasha Levin new_fid->fid = twalk->newfid; 3111c7850f9SSasha Levin } 3121c7850f9SSasha Levin 3131c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + sizeof(u16) + sizeof(struct p9_qid) * rwalk->nwqid; 3141c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RWALK, msg->tag); 3151c7850f9SSasha Levin 3161c7850f9SSasha Levin return true; 3171c7850f9SSasha Levin } 3181c7850f9SSasha Levin 3191c7850f9SSasha Levin static bool virtio_p9_attach(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 3201c7850f9SSasha Levin { 3211c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 3221c7850f9SSasha Levin struct p9_rattach *rattach = (struct p9_rattach *)outmsg->msg; 3231c7850f9SSasha Levin struct p9_tattach *tattach = (struct p9_tattach *)msg->msg; 3241c7850f9SSasha Levin struct stat st; 3251c7850f9SSasha Levin struct p9_fid *fid; 3261c7850f9SSasha Levin u32 i; 3271c7850f9SSasha Levin 3281c7850f9SSasha Levin /* Reset everything */ 3291c7850f9SSasha Levin for (i = 0; i < VIRTIO_P9_MAX_FID; i++) 3301c7850f9SSasha Levin p9dev.fids[i].fid = P9_NOFID; 3311c7850f9SSasha Levin 3321c7850f9SSasha Levin if (stat(p9dev.root_dir, &st) < 0) 3331c7850f9SSasha Levin return false; 3341c7850f9SSasha Levin 3351c7850f9SSasha Levin st2qid(&st, &rattach->qid); 3361c7850f9SSasha Levin 3371c7850f9SSasha Levin fid = &p9dev.fids[tattach->fid]; 3381c7850f9SSasha Levin fid->fid = tattach->fid; 3391c7850f9SSasha Levin fid->is_dir = 1; 3401c7850f9SSasha Levin strcpy(fid->path, "/"); 3411c7850f9SSasha Levin 3421c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + sizeof(*rattach); 3431c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RATTACH, msg->tag); 3441c7850f9SSasha Levin 3451c7850f9SSasha Levin return true; 3461c7850f9SSasha Levin } 3471c7850f9SSasha Levin 3481c7850f9SSasha Levin static u32 virtio_p9_fill_stat(const char *name, struct stat *st, struct p9_rstat *rstat) 3491c7850f9SSasha Levin { 3501c7850f9SSasha Levin struct p9_str *str; 3511c7850f9SSasha Levin 3521c7850f9SSasha Levin rstat->stat.type = 0; 3531c7850f9SSasha Levin rstat->stat.dev = 0; 3541c7850f9SSasha Levin st2qid(st, &rstat->stat.qid); 3551c7850f9SSasha Levin rstat->stat.mode = st->st_mode; 3561c7850f9SSasha Levin rstat->stat.length = st->st_size; 3571c7850f9SSasha Levin if (S_ISDIR(st->st_mode)) { 3581c7850f9SSasha Levin rstat->stat.length = 0; 3591c7850f9SSasha Levin rstat->stat.mode |= P9_DMDIR; 3601c7850f9SSasha Levin } 3611c7850f9SSasha Levin 3621c7850f9SSasha Levin rstat->stat.atime = st->st_atime; 3631c7850f9SSasha Levin rstat->stat.mtime = st->st_mtime; 3641c7850f9SSasha Levin 3651c7850f9SSasha Levin str = (struct p9_str *)&rstat->stat.name; 3661c7850f9SSasha Levin str->len = strlen(name); 3671c7850f9SSasha Levin memcpy(&str->str, name, str->len); 3681c7850f9SSasha Levin str = (void *)str + str->len + sizeof(u16); 3691c7850f9SSasha Levin 3701c7850f9SSasha Levin /* TODO: Pass usernames to the client */ 3711c7850f9SSasha Levin str->len = 0; 3721c7850f9SSasha Levin str = (void *)str + sizeof(u16); 3731c7850f9SSasha Levin str->len = 0; 3741c7850f9SSasha Levin str = (void *)str + sizeof(u16); 3751c7850f9SSasha Levin str->len = 0; 3761c7850f9SSasha Levin str = (void *)str + sizeof(u16); 3771c7850f9SSasha Levin 3781c7850f9SSasha Levin /* We subtract a u16 here because rstat->size doesn't include rstat->size itself */ 3791c7850f9SSasha Levin rstat->stat.size = (void *)str - (void *)&rstat->stat - sizeof(u16); 3801c7850f9SSasha Levin 3811c7850f9SSasha Levin return rstat->stat.size + sizeof(u16); 3821c7850f9SSasha Levin } 3831c7850f9SSasha Levin 3841c7850f9SSasha Levin static bool virtio_p9_read(struct p9_msg *msg, u32 len, struct iovec *iov, int iovcnt, u32 *outlen) 3851c7850f9SSasha Levin { 3861c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 3871c7850f9SSasha Levin struct p9_tread *tread = (struct p9_tread *)msg->msg; 3881c7850f9SSasha Levin struct p9_rread *rread = (struct p9_rread *)outmsg->msg; 3891c7850f9SSasha Levin struct p9_rstat *rstat = (struct p9_rstat *)iov[1].iov_base; 3901c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[tread->fid]; 3911c7850f9SSasha Levin struct stat st; 3921c7850f9SSasha Levin 3931c7850f9SSasha Levin rread->count = 0; 3941c7850f9SSasha Levin 3951c7850f9SSasha Levin if (fid->is_dir) { 3961c7850f9SSasha Levin /* If reading a dir, fill the buffer with p9_stat entries */ 3971c7850f9SSasha Levin struct dirent *cur = readdir(fid->dir); 3981c7850f9SSasha Levin char full_path[PATH_MAX]; 3991c7850f9SSasha Levin 4001c7850f9SSasha Levin while (cur) { 4011c7850f9SSasha Levin u32 read; 4021c7850f9SSasha Levin 4031c7850f9SSasha Levin stat(rel_to_abs(cur->d_name, full_path), &st); 4041c7850f9SSasha Levin read = virtio_p9_fill_stat(cur->d_name, &st, rstat); 4051c7850f9SSasha Levin rread->count += read; 4061c7850f9SSasha Levin rstat = (void *)rstat + read; 4071c7850f9SSasha Levin cur = readdir(fid->dir); 4081c7850f9SSasha Levin } 4091c7850f9SSasha Levin } else { 4101c7850f9SSasha Levin iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32); 4111c7850f9SSasha Levin iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32); 4121c7850f9SSasha Levin rread->count = preadv(fid->fd, iov, iovcnt, tread->offset); 4131c7850f9SSasha Levin if (rread->count > tread->count) 4141c7850f9SSasha Levin rread->count = tread->count; 4151c7850f9SSasha Levin } 4161c7850f9SSasha Levin 4171c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + sizeof(u32) + rread->count; 4181c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RREAD, msg->tag); 4191c7850f9SSasha Levin 4201c7850f9SSasha Levin return true; 4211c7850f9SSasha Levin } 4221c7850f9SSasha Levin 4231c7850f9SSasha Levin static bool virtio_p9_stat(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 4241c7850f9SSasha Levin { 4251c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 4261c7850f9SSasha Levin struct p9_tstat *tstat = (struct p9_tstat *)msg->msg; 4271c7850f9SSasha Levin struct p9_rstat *rstat = (struct p9_rstat *)(outmsg->msg + sizeof(u16)); 4281c7850f9SSasha Levin struct stat st; 4291c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[tstat->fid]; 4301c7850f9SSasha Levin u32 ret; 4311c7850f9SSasha Levin 4321c7850f9SSasha Levin if (stat(fid->abs_path, &st) < 0) 4331c7850f9SSasha Levin return false; 4341c7850f9SSasha Levin 4351c7850f9SSasha Levin ret = virtio_p9_fill_stat(fid->path, &st, rstat); 4361c7850f9SSasha Levin 4371c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + ret + sizeof(u16) * 2; 4381c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RSTAT, msg->tag); 4391c7850f9SSasha Levin return true; 4401c7850f9SSasha Levin } 4411c7850f9SSasha Levin 4421c7850f9SSasha Levin static bool virtio_p9_wstat(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 4431c7850f9SSasha Levin { 4441c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 4451c7850f9SSasha Levin struct p9_twstat *twstat = (struct p9_twstat *)msg->msg; 4461c7850f9SSasha Levin struct p9_str *str; 4471c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[twstat->fid]; 448aec426f0SCyrill Gorcunov int res = 0; 4491c7850f9SSasha Levin 4501c7850f9SSasha Levin if (twstat->stat.length != -1UL) 4511c7850f9SSasha Levin res = ftruncate(fid->fd, twstat->stat.length); 4521c7850f9SSasha Levin 4531c7850f9SSasha Levin if (twstat->stat.mode != -1U) 4541c7850f9SSasha Levin chmod(fid->abs_path, twstat->stat.mode & 0xFFFF); 4551c7850f9SSasha Levin 4561c7850f9SSasha Levin str = (void *)&twstat->stat.name + sizeof(u16); 4571c7850f9SSasha Levin if (str->len > 0) { 4581c7850f9SSasha Levin char new_name[PATH_MAX] = {0}; 4591c7850f9SSasha Levin char full_path[PATH_MAX]; 4601c7850f9SSasha Levin char *last_dir = strrchr(fid->path, '/'); 4611c7850f9SSasha Levin 4621c7850f9SSasha Levin /* We need to get the full file name out of twstat->name */ 4631c7850f9SSasha Levin if (last_dir) 4641c7850f9SSasha Levin strncpy(new_name, fid->path, last_dir - fid->path + 1); 4651c7850f9SSasha Levin 4661c7850f9SSasha Levin memcpy(new_name + strlen(new_name), &str->str, str->len); 4671c7850f9SSasha Levin 4681c7850f9SSasha Levin /* fid is reused for the new file */ 4691c7850f9SSasha Levin rename(fid->abs_path, rel_to_abs(new_name, full_path)); 4701c7850f9SSasha Levin sprintf(fid->path, "%s", new_name); 4711c7850f9SSasha Levin } 4721c7850f9SSasha Levin 4731c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN; 4741c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RWSTAT, msg->tag); 475aec426f0SCyrill Gorcunov 476aec426f0SCyrill Gorcunov return res == 0; 4771c7850f9SSasha Levin } 4781c7850f9SSasha Levin 4791c7850f9SSasha Levin static bool virtio_p9_remove(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen) 4801c7850f9SSasha Levin { 4811c7850f9SSasha Levin struct p9_msg *outmsg = iov[0].iov_base; 4821c7850f9SSasha Levin struct p9_tremove *tremove = (struct p9_tremove *)msg->msg; 4831c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[tremove->fid]; 4841c7850f9SSasha Levin 4851c7850f9SSasha Levin close_fid(tremove->fid); 4861c7850f9SSasha Levin if (fid->is_dir) 4871c7850f9SSasha Levin rmdir(fid->abs_path); 4881c7850f9SSasha Levin else 4891c7850f9SSasha Levin unlink(fid->abs_path); 4901c7850f9SSasha Levin 4911c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN; 4921c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RREMOVE, msg->tag); 4931c7850f9SSasha Levin return true; 4941c7850f9SSasha Levin } 4951c7850f9SSasha Levin 4961c7850f9SSasha Levin static bool virtio_p9_write(struct p9_msg *msg, u32 len, struct iovec *iov, int iovcnt, u32 *outlen) 4971c7850f9SSasha Levin { 4981c7850f9SSasha Levin struct p9_msg *outmsg; 4991c7850f9SSasha Levin struct p9_rwrite *rwrite; 5001c7850f9SSasha Levin struct p9_twrite *twrite = (struct p9_twrite *)msg->msg; 5011c7850f9SSasha Levin struct p9_fid *fid = &p9dev.fids[twrite->fid]; 5021c7850f9SSasha Levin 5031c7850f9SSasha Levin if (iovcnt == 1) { 5041c7850f9SSasha Levin outmsg = iov[0].iov_base; 5051c7850f9SSasha Levin rwrite = (struct p9_rwrite *)outmsg->msg; 5061c7850f9SSasha Levin rwrite->count = pwrite(fid->fd, &twrite->data, twrite->count, twrite->offset); 5071c7850f9SSasha Levin } else { 5081c7850f9SSasha Levin outmsg = iov[2].iov_base; 5091c7850f9SSasha Levin rwrite = (struct p9_rwrite *)outmsg->msg; 5101c7850f9SSasha Levin rwrite->count = pwrite(fid->fd, iov[1].iov_base, twrite->count, twrite->offset); 5111c7850f9SSasha Levin } 5121c7850f9SSasha Levin 5131c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN + sizeof(u32); 5141c7850f9SSasha Levin set_p9msg_hdr(outmsg, *outlen, P9_RWRITE, msg->tag); 5151c7850f9SSasha Levin 5161c7850f9SSasha Levin return true; 5171c7850f9SSasha Levin } 5181c7850f9SSasha Levin 5191c7850f9SSasha Levin static bool virtio_p9_do_io_request(struct kvm *kvm, struct virt_queue *queue) 5201c7850f9SSasha Levin { 5211c7850f9SSasha Levin struct iovec iov[VIRTIO_P9_QUEUE_SIZE]; 5221c7850f9SSasha Levin u16 out, in, head; 5231c7850f9SSasha Levin struct p9_msg *msg; 5241c7850f9SSasha Levin u32 len = 0; 5251c7850f9SSasha Levin 5261c7850f9SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 5271c7850f9SSasha Levin msg = iov[0].iov_base; 5281c7850f9SSasha Levin 5291c7850f9SSasha Levin switch (msg->cmd) { 5301c7850f9SSasha Levin case P9_TVERSION: 5311c7850f9SSasha Levin virtio_p9_version(msg, iov[0].iov_len, iov+1, &len); 5321c7850f9SSasha Levin break; 5331c7850f9SSasha Levin case P9_TATTACH: 5341c7850f9SSasha Levin virtio_p9_attach(msg, iov[0].iov_len, iov+1, &len); 5351c7850f9SSasha Levin break; 5361c7850f9SSasha Levin case P9_TSTAT: 5371c7850f9SSasha Levin virtio_p9_stat(msg, iov[0].iov_len, iov+1, &len); 5381c7850f9SSasha Levin break; 5391c7850f9SSasha Levin case P9_TCLUNK: 5401c7850f9SSasha Levin virtio_p9_clunk(msg, iov[0].iov_len, iov+1, &len); 5411c7850f9SSasha Levin break; 5421c7850f9SSasha Levin case P9_TWALK: 5431c7850f9SSasha Levin virtio_p9_walk(msg, iov[0].iov_len, iov+1, &len); 5441c7850f9SSasha Levin break; 5451c7850f9SSasha Levin case P9_TOPEN: 5461c7850f9SSasha Levin virtio_p9_open(msg, iov[0].iov_len, iov+1, &len); 5471c7850f9SSasha Levin break; 5481c7850f9SSasha Levin case P9_TREAD: 5491c7850f9SSasha Levin virtio_p9_read(msg, iov[0].iov_len, iov+1, in, &len); 5501c7850f9SSasha Levin break; 5511c7850f9SSasha Levin case P9_TCREATE: 5521c7850f9SSasha Levin virtio_p9_create(msg, iov[0].iov_len, iov+1, &len); 5531c7850f9SSasha Levin break; 5541c7850f9SSasha Levin case P9_TWSTAT: 5551c7850f9SSasha Levin virtio_p9_wstat(msg, iov[0].iov_len, iov+1, &len); 5561c7850f9SSasha Levin break; 5571c7850f9SSasha Levin case P9_TREMOVE: 5581c7850f9SSasha Levin virtio_p9_remove(msg, iov[0].iov_len, iov+1, &len); 5591c7850f9SSasha Levin break; 5601c7850f9SSasha Levin case P9_TWRITE: 5611c7850f9SSasha Levin virtio_p9_write(msg, iov[0].iov_len, iov+1, out, &len); 5621c7850f9SSasha Levin break; 5631c7850f9SSasha Levin default: 5641c7850f9SSasha Levin printf("Unsupported P9 message type: %u\n", msg->cmd); 5651c7850f9SSasha Levin break; 5661c7850f9SSasha Levin } 5671c7850f9SSasha Levin virt_queue__set_used_elem(queue, head, len); 5681c7850f9SSasha Levin 5691c7850f9SSasha Levin return true; 5701c7850f9SSasha Levin } 5711c7850f9SSasha Levin 5721c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param) 5731c7850f9SSasha Levin { 5741c7850f9SSasha Levin struct virt_queue *vq = param; 5751c7850f9SSasha Levin 5761c7850f9SSasha Levin while (virt_queue__available(vq)) { 5771c7850f9SSasha Levin virtio_p9_do_io_request(kvm, vq); 5781c7850f9SSasha Levin virt_queue__trigger_irq(vq, virtio_p9_pci_device.irq_line, &p9dev.isr, kvm); 5791c7850f9SSasha Levin } 5801c7850f9SSasha Levin } 5811c7850f9SSasha Levin 582*3d62dea6SSasha Levin static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 5831c7850f9SSasha Levin { 5841c7850f9SSasha Levin unsigned long offset; 5851c7850f9SSasha Levin bool ret = true; 5861c7850f9SSasha Levin 5871c7850f9SSasha Levin offset = port - IOPORT_VIRTIO_P9; 5881c7850f9SSasha Levin 5891c7850f9SSasha Levin switch (offset) { 5901c7850f9SSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 5911c7850f9SSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 5921c7850f9SSasha Levin break; 5931c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 5941c7850f9SSasha Levin struct virt_queue *queue; 5951c7850f9SSasha Levin void *p; 5961c7850f9SSasha Levin 5971c7850f9SSasha Levin queue = &p9dev.vqs[p9dev.queue_selector]; 5981c7850f9SSasha Levin queue->pfn = ioport__read32(data); 5991c7850f9SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 6001c7850f9SSasha Levin 6011c7850f9SSasha Levin vring_init(&queue->vring, VIRTIO_P9_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 6021c7850f9SSasha Levin 6031c7850f9SSasha Levin p9dev.jobs[p9dev.queue_selector] = thread_pool__add_job(kvm, virtio_p9_do_io, queue); 6041c7850f9SSasha Levin 6051c7850f9SSasha Levin break; 6061c7850f9SSasha Levin } 6071c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_SEL: 6081c7850f9SSasha Levin p9dev.queue_selector = ioport__read16(data); 6091c7850f9SSasha Levin break; 6101c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 6111c7850f9SSasha Levin u16 queue_index; 6121c7850f9SSasha Levin queue_index = ioport__read16(data); 6131c7850f9SSasha Levin thread_pool__do_job(p9dev.jobs[queue_index]); 6141c7850f9SSasha Levin break; 6151c7850f9SSasha Levin } 6161c7850f9SSasha Levin case VIRTIO_PCI_STATUS: 6171c7850f9SSasha Levin p9dev.status = ioport__read8(data); 6181c7850f9SSasha Levin break; 6191c7850f9SSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 6201c7850f9SSasha Levin p9dev.config_vector = VIRTIO_MSI_NO_VECTOR; 6211c7850f9SSasha Levin break; 6221c7850f9SSasha Levin default: 6231c7850f9SSasha Levin ret = false; 6241c7850f9SSasha Levin break; 6251c7850f9SSasha Levin }; 6261c7850f9SSasha Levin 6271c7850f9SSasha Levin return ret; 6281c7850f9SSasha Levin } 6291c7850f9SSasha Levin 6301c7850f9SSasha Levin static struct ioport_operations virtio_p9_io_ops = { 6311c7850f9SSasha Levin .io_in = virtio_p9_pci_io_in, 6321c7850f9SSasha Levin .io_out = virtio_p9_pci_io_out, 6331c7850f9SSasha Levin }; 6341c7850f9SSasha Levin 6351c7850f9SSasha Levin void virtio_9p__init(struct kvm *kvm, const char *root) 6361c7850f9SSasha Levin { 6371c7850f9SSasha Levin u8 pin, line, dev; 6381c7850f9SSasha Levin u32 i, root_len; 6391c7850f9SSasha Levin 6401c7850f9SSasha Levin p9dev.config = calloc(1, sizeof(*p9dev.config) + sizeof(VIRTIO_P9_TAG)); 6411c7850f9SSasha Levin if (p9dev.config == NULL) 6421c7850f9SSasha Levin return; 6431c7850f9SSasha Levin 6441c7850f9SSasha Levin strcpy(p9dev.root_dir, root); 6451c7850f9SSasha Levin root_len = strlen(root); 6461c7850f9SSasha Levin 6471c7850f9SSasha Levin /* 6481c7850f9SSasha Levin * We prefix the full path in all fids, This allows us to get the 6491c7850f9SSasha Levin * absolute path of an fid without playing with strings. 6501c7850f9SSasha Levin */ 6511c7850f9SSasha Levin for (i = 0; i < VIRTIO_P9_MAX_FID; i++) { 6521c7850f9SSasha Levin strcpy(p9dev.fids[i].abs_path, root); 6531c7850f9SSasha Levin p9dev.fids[i].path = p9dev.fids[i].abs_path + root_len; 6541c7850f9SSasha Levin } 6551c7850f9SSasha Levin 6561c7850f9SSasha Levin p9dev.config->tag_len = strlen(VIRTIO_P9_TAG); 6571c7850f9SSasha Levin memcpy(p9dev.config->tag, VIRTIO_P9_TAG, strlen(VIRTIO_P9_TAG)); 6581c7850f9SSasha Levin p9dev.features |= 1 << VIRTIO_9P_MOUNT_TAG; 6591c7850f9SSasha Levin 6601c7850f9SSasha Levin if (irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line) < 0) 6611c7850f9SSasha Levin return; 6621c7850f9SSasha Levin 6631c7850f9SSasha Levin virtio_p9_pci_device.irq_pin = pin; 6641c7850f9SSasha Levin virtio_p9_pci_device.irq_line = line; 6651c7850f9SSasha Levin pci__register(&virtio_p9_pci_device, dev); 6661c7850f9SSasha Levin 667*3d62dea6SSasha Levin ioport__register(IOPORT_VIRTIO_P9, &virtio_p9_io_ops, IOPORT_VIRTIO_P9_SIZE, NULL); 6681c7850f9SSasha Levin } 669