11c7850f9SSasha Levin #include "kvm/virtio-pci-dev.h" 21c7850f9SSasha Levin #include "kvm/ioport.h" 31c7850f9SSasha Levin #include "kvm/util.h" 41c7850f9SSasha Levin #include "kvm/threadpool.h" 560eb42d5SSasha Levin #include "kvm/ioeventfd.h" 6bfc15268SAneesh Kumar K.V #include "kvm/irq.h" 7bfc15268SAneesh Kumar K.V #include "kvm/virtio-9p.h" 81c7850f9SSasha Levin 9bfc15268SAneesh Kumar K.V #include <stdio.h> 10bfc15268SAneesh Kumar K.V #include <stdlib.h> 111c7850f9SSasha Levin #include <fcntl.h> 121c7850f9SSasha Levin #include <sys/stat.h> 13bfc15268SAneesh Kumar K.V #include <unistd.h> 14bfc15268SAneesh Kumar K.V #include <string.h> 15bfc15268SAneesh Kumar K.V #include <errno.h> 161c7850f9SSasha Levin 172daa28d4SAneesh Kumar K.V #include <linux/virtio_ring.h> 182daa28d4SAneesh Kumar K.V #include <linux/virtio_9p.h> 192daa28d4SAneesh Kumar K.V #include <net/9p/9p.h> 202daa28d4SAneesh Kumar K.V 211c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */ 22b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev, 23b4422bf3SAneesh Kumar K.V const char *path, char *abs_path) 241c7850f9SSasha Levin { 25b4422bf3SAneesh Kumar K.V sprintf(abs_path, "%s/%s", p9dev->root_dir, path); 261c7850f9SSasha Levin 271c7850f9SSasha Levin return abs_path; 281c7850f9SSasha Levin } 291c7850f9SSasha Levin 30b4422bf3SAneesh Kumar K.V static bool virtio_p9_dev_in(struct p9_dev *p9dev, void *data, 31b4422bf3SAneesh Kumar K.V unsigned long offset, 32b4422bf3SAneesh Kumar K.V int size, u32 count) 331c7850f9SSasha Levin { 34b4422bf3SAneesh Kumar K.V u8 *config_space = (u8 *) p9dev->config; 351c7850f9SSasha Levin 361c7850f9SSasha Levin if (size != 1 || count != 1) 371c7850f9SSasha Levin return false; 381c7850f9SSasha Levin 391c7850f9SSasha Levin ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]); 401c7850f9SSasha Levin 411c7850f9SSasha Levin return true; 421c7850f9SSasha Levin } 431c7850f9SSasha Levin 44b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm, 45b4422bf3SAneesh Kumar K.V u16 port, void *data, int size, u32 count) 461c7850f9SSasha Levin { 471c7850f9SSasha Levin bool ret = true; 48b4422bf3SAneesh Kumar K.V unsigned long offset; 49b4422bf3SAneesh Kumar K.V struct p9_dev *p9dev = ioport->priv; 501c7850f9SSasha Levin 51b4422bf3SAneesh Kumar K.V 52b4422bf3SAneesh Kumar K.V offset = port - p9dev->base_addr; 531c7850f9SSasha Levin 541c7850f9SSasha Levin switch (offset) { 551c7850f9SSasha Levin case VIRTIO_PCI_HOST_FEATURES: 56b4422bf3SAneesh Kumar K.V ioport__write32(data, p9dev->features); 571c7850f9SSasha Levin ret = true; 581c7850f9SSasha Levin break; 591c7850f9SSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 601c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_SEL: 611c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: 621c7850f9SSasha Levin ret = false; 631c7850f9SSasha Levin break; 641c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_PFN: 65b4422bf3SAneesh Kumar K.V ioport__write32(data, p9dev->vqs[p9dev->queue_selector].pfn); 661c7850f9SSasha Levin break; 671c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_NUM: 68612038c5SAneesh Kumar K.V ioport__write16(data, VIRTQUEUE_NUM); 691c7850f9SSasha Levin break; 701c7850f9SSasha Levin case VIRTIO_PCI_STATUS: 71b4422bf3SAneesh Kumar K.V ioport__write8(data, p9dev->status); 721c7850f9SSasha Levin break; 731c7850f9SSasha Levin case VIRTIO_PCI_ISR: 74b4422bf3SAneesh Kumar K.V ioport__write8(data, p9dev->isr); 75b4422bf3SAneesh Kumar K.V kvm__irq_line(kvm, p9dev->pci_hdr.irq_line, VIRTIO_IRQ_LOW); 76b4422bf3SAneesh Kumar K.V p9dev->isr = VIRTIO_IRQ_LOW; 771c7850f9SSasha Levin break; 781c7850f9SSasha Levin default: 79b4422bf3SAneesh Kumar K.V ret = virtio_p9_dev_in(p9dev, data, offset, size, count); 801c7850f9SSasha Levin break; 811c7850f9SSasha Levin }; 821c7850f9SSasha Levin 831c7850f9SSasha Levin return ret; 841c7850f9SSasha Levin } 851c7850f9SSasha Levin 861c7850f9SSasha Levin static int omode2uflags(u8 mode) 871c7850f9SSasha Levin { 881c7850f9SSasha Levin int ret = 0; 891c7850f9SSasha Levin 901c7850f9SSasha Levin /* Basic open modes are same as uflags */ 911c7850f9SSasha Levin ret = mode & 3; 921c7850f9SSasha Levin 931c7850f9SSasha Levin /* Everything else is different */ 941c7850f9SSasha Levin if (mode & P9_OTRUNC) 951c7850f9SSasha Levin ret |= O_TRUNC; 961c7850f9SSasha Levin 971c7850f9SSasha Levin if (mode & P9_OAPPEND) 981c7850f9SSasha Levin ret |= O_APPEND; 991c7850f9SSasha Levin 1001c7850f9SSasha Levin if (mode & P9_OEXCL) 1011c7850f9SSasha Levin ret |= O_EXCL; 1021c7850f9SSasha Levin 1031c7850f9SSasha Levin return ret; 1041c7850f9SSasha Levin } 1051c7850f9SSasha Levin 1061c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid) 1071c7850f9SSasha Levin { 1081c7850f9SSasha Levin *qid = (struct p9_qid) { 1091c7850f9SSasha Levin .path = st->st_ino, 1101c7850f9SSasha Levin .version = st->st_mtime, 1111c7850f9SSasha Levin }; 1121c7850f9SSasha Levin 1131c7850f9SSasha Levin if (S_ISDIR(st->st_mode)) 1141c7850f9SSasha Levin qid->type |= P9_QTDIR; 1151c7850f9SSasha Levin } 1161c7850f9SSasha Levin 117b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid) 1181c7850f9SSasha Levin { 119b4422bf3SAneesh Kumar K.V if (p9dev->fids[fid].fd > 0) { 120b4422bf3SAneesh Kumar K.V close(p9dev->fids[fid].fd); 121b4422bf3SAneesh Kumar K.V p9dev->fids[fid].fd = -1; 1221c7850f9SSasha Levin } 123b4422bf3SAneesh Kumar K.V if (p9dev->fids[fid].dir) { 124b4422bf3SAneesh Kumar K.V closedir(p9dev->fids[fid].dir); 125b4422bf3SAneesh Kumar K.V p9dev->fids[fid].dir = NULL; 1261c7850f9SSasha Levin } 1271c7850f9SSasha Levin } 1281c7850f9SSasha Levin 129bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size) 1301c7850f9SSasha Levin { 131bfc15268SAneesh Kumar K.V u8 cmd; 132bfc15268SAneesh Kumar K.V u16 tag; 133bfc15268SAneesh Kumar K.V 134bfc15268SAneesh Kumar K.V pdu->read_offset = sizeof(u32); 135bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag); 136bfc15268SAneesh Kumar K.V pdu->write_offset = 0; 137bfc15268SAneesh Kumar K.V /* cmd + 1 is the reply message */ 138bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag); 1391c7850f9SSasha Levin } 1401c7850f9SSasha Levin 1416b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt) 1426b163a87SAneesh Kumar K.V { 1436b163a87SAneesh Kumar K.V int i; 1446b163a87SAneesh Kumar K.V u32 total = 0; 1456b163a87SAneesh Kumar K.V for (i = 0; (i < iov_cnt) && (total < count); i++) { 1466b163a87SAneesh Kumar K.V if (total + iov[i].iov_len > count) { 1476b163a87SAneesh Kumar K.V /* we don't need this iov fully */ 1486b163a87SAneesh Kumar K.V iov[i].iov_len -= ((total + iov[i].iov_len) - count); 1496b163a87SAneesh Kumar K.V i++; 1506b163a87SAneesh Kumar K.V break; 1516b163a87SAneesh Kumar K.V } 1526b163a87SAneesh Kumar K.V total += iov[i].iov_len; 1536b163a87SAneesh Kumar K.V } 1546b163a87SAneesh Kumar K.V return i; 1556b163a87SAneesh Kumar K.V } 1566b163a87SAneesh Kumar K.V 157eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev, 158eee1ba8eSAneesh Kumar K.V struct p9_pdu *pdu, int err, u32 *outlen) 159eee1ba8eSAneesh Kumar K.V { 160bfc15268SAneesh Kumar K.V u16 tag; 161eee1ba8eSAneesh Kumar K.V char *err_str; 162eee1ba8eSAneesh Kumar K.V 163eee1ba8eSAneesh Kumar K.V err_str = strerror(err); 164bfc15268SAneesh Kumar K.V pdu->write_offset = VIRTIO_P9_HDR_LEN; 1655f900f6dSSasha Levin virtio_p9_pdu_writef(pdu, "sd", err_str, err); 166bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 167eee1ba8eSAneesh Kumar K.V 168bfc15268SAneesh Kumar K.V pdu->read_offset = sizeof(u32) + sizeof(u8); 169bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "w", &tag); 170bfc15268SAneesh Kumar K.V 171bfc15268SAneesh Kumar K.V pdu->write_offset = 0; 172bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RERROR, tag); 173eee1ba8eSAneesh Kumar K.V } 174eee1ba8eSAneesh Kumar K.V 175ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev, 176af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 1771c7850f9SSasha Levin { 178bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "ds", 4096, VIRTIO_P9_VERSION); 1791c7850f9SSasha Levin 180bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 181bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 182ead43b01SAneesh Kumar K.V return; 1831c7850f9SSasha Levin } 1841c7850f9SSasha Levin 185ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev, 186af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 1871c7850f9SSasha Levin { 188bfc15268SAneesh Kumar K.V u32 fid; 1891c7850f9SSasha Levin 190bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "d", &fid); 191bfc15268SAneesh Kumar K.V close_fid(p9dev, fid); 1921c7850f9SSasha Levin 193bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 194bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 195ead43b01SAneesh Kumar K.V return; 1961c7850f9SSasha Levin } 1971c7850f9SSasha Levin 198ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev, 199af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 2001c7850f9SSasha Levin { 201bfc15268SAneesh Kumar K.V u8 mode; 202bfc15268SAneesh Kumar K.V u32 fid; 2031c7850f9SSasha Levin struct stat st; 204bfc15268SAneesh Kumar K.V struct p9_qid qid; 205bfc15268SAneesh Kumar K.V struct p9_fid *new_fid; 206bfc15268SAneesh Kumar K.V 207bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "db", &fid, &mode); 208bfc15268SAneesh Kumar K.V new_fid = &p9dev->fids[fid]; 2091c7850f9SSasha Levin 21030204a77SAneesh Kumar K.V if (lstat(new_fid->abs_path, &st) < 0) 211eee1ba8eSAneesh Kumar K.V goto err_out; 2121c7850f9SSasha Levin 213bfc15268SAneesh Kumar K.V st2qid(&st, &qid); 2141c7850f9SSasha Levin 215eee1ba8eSAneesh Kumar K.V if (new_fid->is_dir) { 2161c7850f9SSasha Levin new_fid->dir = opendir(new_fid->abs_path); 217eee1ba8eSAneesh Kumar K.V if (!new_fid->dir) 218eee1ba8eSAneesh Kumar K.V goto err_out; 219eee1ba8eSAneesh Kumar K.V } else { 220eee1ba8eSAneesh Kumar K.V new_fid->fd = open(new_fid->abs_path, 221bfc15268SAneesh Kumar K.V omode2uflags(mode) | O_NOFOLLOW); 222eee1ba8eSAneesh Kumar K.V if (new_fid->fd < 0) 223eee1ba8eSAneesh Kumar K.V goto err_out; 224eee1ba8eSAneesh Kumar K.V } 225bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "Qd", &qid, 0); 226bfc15268SAneesh Kumar K.V 227bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 228bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 229ead43b01SAneesh Kumar K.V return; 230eee1ba8eSAneesh Kumar K.V err_out: 231eee1ba8eSAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 232ead43b01SAneesh Kumar K.V return; 2331c7850f9SSasha Levin } 2341c7850f9SSasha Levin 235ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev, 236af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 2371c7850f9SSasha Levin { 2384bc9734aSAneesh Kumar K.V DIR *dir; 2394bc9734aSAneesh Kumar K.V int fd; 2404bc9734aSAneesh Kumar K.V int res; 2411c7850f9SSasha Levin u8 mode; 2421c7850f9SSasha Levin u32 perm; 243bfc15268SAneesh Kumar K.V char *name; 2445f900f6dSSasha Levin char *ext = NULL; 245bfc15268SAneesh Kumar K.V u32 fid_val; 246af045e53SAneesh Kumar K.V struct stat st; 247bfc15268SAneesh Kumar K.V struct p9_qid qid; 248bfc15268SAneesh Kumar K.V struct p9_fid *fid; 2494bc9734aSAneesh Kumar K.V char full_path[PATH_MAX]; 250af045e53SAneesh Kumar K.V 2515f900f6dSSasha Levin virtio_p9_pdu_readf(pdu, "dsdbs", &fid_val, &name, &perm, &mode, &ext); 252bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 2531c7850f9SSasha Levin 2544bc9734aSAneesh Kumar K.V sprintf(full_path, "%s/%s", fid->abs_path, name); 2551c7850f9SSasha Levin if (perm & P9_DMDIR) { 2564bc9734aSAneesh Kumar K.V res = mkdir(full_path, perm & 0xFFFF); 2574bc9734aSAneesh Kumar K.V if (res < 0) 2584bc9734aSAneesh Kumar K.V goto err_out; 2594bc9734aSAneesh Kumar K.V dir = opendir(full_path); 2604bc9734aSAneesh Kumar K.V if (!dir) 2614bc9734aSAneesh Kumar K.V goto err_out; 2624bc9734aSAneesh Kumar K.V close_fid(p9dev, fid_val); 2634bc9734aSAneesh Kumar K.V fid->dir = dir; 2641c7850f9SSasha Levin fid->is_dir = 1; 2655f900f6dSSasha Levin } else if (perm & P9_DMSYMLINK) { 2665f900f6dSSasha Levin if (symlink(ext, full_path) < 0) 2675f900f6dSSasha Levin goto err_out; 2685f900f6dSSasha Levin } else if (perm & P9_DMLINK) { 2695f900f6dSSasha Levin int ext_fid = atoi(ext); 2705f900f6dSSasha Levin 2715f900f6dSSasha Levin if (link(p9dev->fids[ext_fid].abs_path, full_path) < 0) 2725f900f6dSSasha Levin goto err_out; 2731c7850f9SSasha Levin } else { 2744bc9734aSAneesh Kumar K.V fd = open(full_path, omode2uflags(mode) | O_CREAT, 0777); 2754bc9734aSAneesh Kumar K.V if (fd < 0) 2764bc9734aSAneesh Kumar K.V goto err_out; 2774bc9734aSAneesh Kumar K.V close_fid(p9dev, fid_val); 2784bc9734aSAneesh Kumar K.V fid->fd = fd; 2791c7850f9SSasha Levin } 2804bc9734aSAneesh Kumar K.V if (lstat(full_path, &st) < 0) 2816c8ca053SAneesh Kumar K.V goto err_out; 2821c7850f9SSasha Levin 2834bc9734aSAneesh Kumar K.V sprintf(fid->path, "%s/%s", fid->path, name); 284bfc15268SAneesh Kumar K.V st2qid(&st, &qid); 285bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "Qd", &qid, 0); 286bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 287bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 2885f900f6dSSasha Levin 2895f900f6dSSasha Levin free(name); 2905f900f6dSSasha Levin free(ext); 2916c8ca053SAneesh Kumar K.V return; 2926c8ca053SAneesh Kumar K.V err_out: 2936c8ca053SAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 2945f900f6dSSasha Levin free(name); 2955f900f6dSSasha Levin free(ext); 296ead43b01SAneesh Kumar K.V return; 2971c7850f9SSasha Levin } 2981c7850f9SSasha Levin 299ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev, 300af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 3011c7850f9SSasha Levin { 302af045e53SAneesh Kumar K.V u8 i; 303bfc15268SAneesh Kumar K.V u16 nwqid; 304bfc15268SAneesh Kumar K.V char *str; 305bfc15268SAneesh Kumar K.V u16 nwname; 306bfc15268SAneesh Kumar K.V u32 fid_val; 307bfc15268SAneesh Kumar K.V u32 newfid_val; 308bfc15268SAneesh Kumar K.V struct p9_qid wqid; 309bfc15268SAneesh Kumar K.V struct p9_fid *new_fid; 3105f900f6dSSasha Levin int ret; 3111c7850f9SSasha Levin 312bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname); 313bfc15268SAneesh Kumar K.V new_fid = &p9dev->fids[newfid_val]; 3141c7850f9SSasha Levin 315bfc15268SAneesh Kumar K.V nwqid = 0; 316bfc15268SAneesh Kumar K.V if (nwname) { 317bfc15268SAneesh Kumar K.V struct p9_fid *fid = &p9dev->fids[fid_val]; 318bfc15268SAneesh Kumar K.V 319bfc15268SAneesh Kumar K.V /* skip the space for count */ 320bfc15268SAneesh Kumar K.V pdu->write_offset += sizeof(u16); 321bfc15268SAneesh Kumar K.V for (i = 0; i < nwname; i++) { 322bfc15268SAneesh Kumar K.V struct stat st; 3231c7850f9SSasha Levin char tmp[PATH_MAX] = {0}; 3241c7850f9SSasha Levin char full_path[PATH_MAX]; 325bfc15268SAneesh Kumar K.V 326bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "s", &str); 3271c7850f9SSasha Levin 3281c7850f9SSasha Levin /* Format the new path we're 'walk'ing into */ 329bfc15268SAneesh Kumar K.V sprintf(tmp, "%s/%.*s", 330bfc15268SAneesh Kumar K.V fid->path, (int)strlen(str), str); 3315f900f6dSSasha Levin 3325f900f6dSSasha Levin ret = lstat(rel_to_abs(p9dev, tmp, full_path), &st); 3335f900f6dSSasha Levin if (ret < 0) 3346c8ca053SAneesh Kumar K.V goto err_out; 3351c7850f9SSasha Levin 336bfc15268SAneesh Kumar K.V st2qid(&st, &wqid); 3371c7850f9SSasha Levin new_fid->is_dir = S_ISDIR(st.st_mode); 3381c7850f9SSasha Levin strcpy(new_fid->path, tmp); 339bfc15268SAneesh Kumar K.V new_fid->fid = newfid_val; 340bfc15268SAneesh Kumar K.V nwqid++; 341bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "Q", &wqid); 3421c7850f9SSasha Levin } 3431c7850f9SSasha Levin } else { 344bfc15268SAneesh Kumar K.V /* 345bfc15268SAneesh Kumar K.V * update write_offset so our outlen get correct value 346bfc15268SAneesh Kumar K.V */ 347bfc15268SAneesh Kumar K.V pdu->write_offset += sizeof(u16); 348bfc15268SAneesh Kumar K.V new_fid->is_dir = p9dev->fids[fid_val].is_dir; 349bfc15268SAneesh Kumar K.V strcpy(new_fid->path, p9dev->fids[fid_val].path); 350bfc15268SAneesh Kumar K.V new_fid->fid = newfid_val; 3511c7850f9SSasha Levin } 352bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 353bfc15268SAneesh Kumar K.V pdu->write_offset = VIRTIO_P9_HDR_LEN; 354bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "d", nwqid); 355bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 3566c8ca053SAneesh Kumar K.V return; 3576c8ca053SAneesh Kumar K.V err_out: 3586c8ca053SAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 359ead43b01SAneesh Kumar K.V return; 3601c7850f9SSasha Levin } 3611c7850f9SSasha Levin 362ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev, 363af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 3641c7850f9SSasha Levin { 365af045e53SAneesh Kumar K.V u32 i; 366bfc15268SAneesh Kumar K.V u32 fid_val; 367bfc15268SAneesh Kumar K.V u32 afid; 368bfc15268SAneesh Kumar K.V char *uname; 369bfc15268SAneesh Kumar K.V char *aname; 3701c7850f9SSasha Levin struct stat st; 371bfc15268SAneesh Kumar K.V struct p9_qid qid; 3721c7850f9SSasha Levin struct p9_fid *fid; 373bfc15268SAneesh Kumar K.V 374bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "ddss", &fid_val, &afid, &uname, &aname); 3751c7850f9SSasha Levin 3761c7850f9SSasha Levin /* Reset everything */ 3771c7850f9SSasha Levin for (i = 0; i < VIRTIO_P9_MAX_FID; i++) 378b4422bf3SAneesh Kumar K.V p9dev->fids[i].fid = P9_NOFID; 3791c7850f9SSasha Levin 38030204a77SAneesh Kumar K.V if (lstat(p9dev->root_dir, &st) < 0) 3816c8ca053SAneesh Kumar K.V goto err_out; 3821c7850f9SSasha Levin 383bfc15268SAneesh Kumar K.V st2qid(&st, &qid); 3841c7850f9SSasha Levin 385bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 386bfc15268SAneesh Kumar K.V fid->fid = fid_val; 3871c7850f9SSasha Levin fid->is_dir = 1; 3881c7850f9SSasha Levin strcpy(fid->path, "/"); 3891c7850f9SSasha Levin 390bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "Q", &qid); 391bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 392bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 3935f900f6dSSasha Levin free(uname); 3945f900f6dSSasha Levin free(aname); 3956c8ca053SAneesh Kumar K.V return; 3966c8ca053SAneesh Kumar K.V err_out: 3975f900f6dSSasha Levin free(uname); 3985f900f6dSSasha Levin free(aname); 3996c8ca053SAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 400ead43b01SAneesh Kumar K.V return; 4011c7850f9SSasha Levin } 4021c7850f9SSasha Levin 4035f900f6dSSasha Levin static void virtio_p9_free_stat(struct p9_wstat *wstat) 4045f900f6dSSasha Levin { 4055f900f6dSSasha Levin free(wstat->extension); 4065f900f6dSSasha Levin free(wstat->name); 4075f900f6dSSasha Levin } 4085f900f6dSSasha Levin 409bfc15268SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev, const char *name, 410bfc15268SAneesh Kumar K.V struct stat *st, struct p9_wstat *wstat) 4111c7850f9SSasha Levin { 412bfc15268SAneesh Kumar K.V wstat->type = 0; 413bfc15268SAneesh Kumar K.V wstat->dev = 0; 414bfc15268SAneesh Kumar K.V st2qid(st, &wstat->qid); 415bfc15268SAneesh Kumar K.V wstat->mode = st->st_mode; 416bfc15268SAneesh Kumar K.V wstat->length = st->st_size; 4171c7850f9SSasha Levin if (S_ISDIR(st->st_mode)) { 418bfc15268SAneesh Kumar K.V wstat->length = 0; 419bfc15268SAneesh Kumar K.V wstat->mode |= P9_DMDIR; 4201c7850f9SSasha Levin } 4215f900f6dSSasha Levin if (S_ISLNK(st->st_mode)) { 4225f900f6dSSasha Levin char tmp[PATH_MAX] = {0}, full_path[PATH_MAX] = {0}; 4235f900f6dSSasha Levin 4245f900f6dSSasha Levin rel_to_abs(p9dev, name, full_path); 4255f900f6dSSasha Levin 4265f900f6dSSasha Levin if (readlink(full_path, tmp, PATH_MAX) > 0) 4275f900f6dSSasha Levin wstat->extension = strdup(tmp); 4285f900f6dSSasha Levin wstat->mode |= P9_DMSYMLINK; 4295f900f6dSSasha Levin } else { 4305f900f6dSSasha Levin wstat->extension = NULL; 4315f900f6dSSasha Levin } 4321c7850f9SSasha Levin 433bfc15268SAneesh Kumar K.V wstat->atime = st->st_atime; 434bfc15268SAneesh Kumar K.V wstat->mtime = st->st_mtime; 4351c7850f9SSasha Levin 436bfc15268SAneesh Kumar K.V wstat->name = strdup(name); 437bfc15268SAneesh Kumar K.V wstat->uid = NULL; 438bfc15268SAneesh Kumar K.V wstat->gid = NULL; 439bfc15268SAneesh Kumar K.V wstat->muid = NULL; 4405f900f6dSSasha Levin wstat->n_uid = wstat->n_gid = wstat->n_muid = 0; 4411c7850f9SSasha Levin 4425f900f6dSSasha Levin /* 4435f900f6dSSasha Levin * NOTE: size shouldn't include its own length 4445f900f6dSSasha Levin * size[2] type[2] dev[4] qid[13] 4455f900f6dSSasha Levin * mode[4] atime[4] mtime[4] length[8] 4465f900f6dSSasha Levin * name[s] uid[s] gid[s] muid[s] 4475f900f6dSSasha Levin * ext[s] uid[4] gid[4] muid[4] 4485f900f6dSSasha Levin */ 4495f900f6dSSasha Levin wstat->size = 2+4+13+4+4+4+8+2+2+2+2+2+4+4+4; 450bfc15268SAneesh Kumar K.V if (wstat->name) 451bfc15268SAneesh Kumar K.V wstat->size += strlen(wstat->name); 4525f900f6dSSasha Levin if (wstat->extension) 4535f900f6dSSasha Levin wstat->size += strlen(wstat->extension); 4541c7850f9SSasha Levin } 4551c7850f9SSasha Levin 456ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev, 457af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 4581c7850f9SSasha Levin { 459bfc15268SAneesh Kumar K.V u64 offset; 460bfc15268SAneesh Kumar K.V u32 fid_val; 461bfc15268SAneesh Kumar K.V u32 count, rcount; 4621c7850f9SSasha Levin struct stat st; 463bfc15268SAneesh Kumar K.V struct p9_fid *fid; 464bfc15268SAneesh Kumar K.V struct p9_wstat wstat; 4651c7850f9SSasha Levin 466bfc15268SAneesh Kumar K.V rcount = 0; 467bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count); 468bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 4691c7850f9SSasha Levin if (fid->is_dir) { 4701c7850f9SSasha Levin /* If reading a dir, fill the buffer with p9_stat entries */ 4711c7850f9SSasha Levin char full_path[PATH_MAX]; 472bfc15268SAneesh Kumar K.V struct dirent *cur = readdir(fid->dir); 4731c7850f9SSasha Levin 474bfc15268SAneesh Kumar K.V /* Skip the space for writing count */ 475bfc15268SAneesh Kumar K.V pdu->write_offset += sizeof(u32); 4761c7850f9SSasha Levin while (cur) { 4771c7850f9SSasha Levin u32 read; 4781c7850f9SSasha Levin 47930204a77SAneesh Kumar K.V lstat(rel_to_abs(p9dev, cur->d_name, full_path), &st); 480bfc15268SAneesh Kumar K.V virtio_p9_fill_stat(p9dev, cur->d_name, &st, &wstat); 481bfc15268SAneesh Kumar K.V 482bfc15268SAneesh Kumar K.V read = pdu->write_offset; 483bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "S", &wstat); 484bfc15268SAneesh Kumar K.V rcount += pdu->write_offset - read; 4855f900f6dSSasha Levin virtio_p9_free_stat(&wstat); 486bfc15268SAneesh Kumar K.V 4871c7850f9SSasha Levin cur = readdir(fid->dir); 4881c7850f9SSasha Levin } 4891c7850f9SSasha Levin } else { 490*50c479e0SAneesh Kumar K.V u16 iov_cnt; 491*50c479e0SAneesh Kumar K.V void *iov_base; 492*50c479e0SAneesh Kumar K.V size_t iov_len; 493*50c479e0SAneesh Kumar K.V 494*50c479e0SAneesh Kumar K.V iov_base = pdu->in_iov[0].iov_base; 495*50c479e0SAneesh Kumar K.V iov_len = pdu->in_iov[0].iov_len; 496*50c479e0SAneesh Kumar K.V iov_cnt = pdu->in_iov_cnt; 497*50c479e0SAneesh Kumar K.V 498af045e53SAneesh Kumar K.V pdu->in_iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32); 499af045e53SAneesh Kumar K.V pdu->in_iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32); 5006b163a87SAneesh Kumar K.V pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov, 501bfc15268SAneesh Kumar K.V count, 5026b163a87SAneesh Kumar K.V pdu->in_iov_cnt); 503bfc15268SAneesh Kumar K.V rcount = preadv(fid->fd, pdu->in_iov, 504bfc15268SAneesh Kumar K.V pdu->in_iov_cnt, offset); 505bfc15268SAneesh Kumar K.V if (rcount > count) 506bfc15268SAneesh Kumar K.V rcount = count; 507bfc15268SAneesh Kumar K.V /* 508bfc15268SAneesh Kumar K.V * Update the iov_base back, so that rest of 509bfc15268SAneesh Kumar K.V * pdu_writef works correctly. 510bfc15268SAneesh Kumar K.V */ 511*50c479e0SAneesh Kumar K.V pdu->in_iov[0].iov_base = iov_base; 512*50c479e0SAneesh Kumar K.V pdu->in_iov[0].iov_len = iov_len; 513*50c479e0SAneesh Kumar K.V pdu->in_iov_cnt = iov_cnt; 514bfc15268SAneesh Kumar K.V } 515bfc15268SAneesh Kumar K.V pdu->write_offset = VIRTIO_P9_HDR_LEN; 516bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "d", rcount); 517bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset + rcount; 518bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 5191c7850f9SSasha Levin 520ead43b01SAneesh Kumar K.V return; 5211c7850f9SSasha Levin } 5221c7850f9SSasha Levin 523ead43b01SAneesh Kumar K.V static void virtio_p9_stat(struct p9_dev *p9dev, 524af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 5251c7850f9SSasha Levin { 526bfc15268SAneesh Kumar K.V u32 fid_val; 527af045e53SAneesh Kumar K.V struct stat st; 528bfc15268SAneesh Kumar K.V struct p9_fid *fid; 529bfc15268SAneesh Kumar K.V struct p9_wstat wstat; 5301c7850f9SSasha Levin 531bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "d", &fid_val); 532bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 53330204a77SAneesh Kumar K.V if (lstat(fid->abs_path, &st) < 0) 5346c8ca053SAneesh Kumar K.V goto err_out; 5351c7850f9SSasha Levin 536bfc15268SAneesh Kumar K.V virtio_p9_fill_stat(p9dev, fid->path, &st, &wstat); 5371c7850f9SSasha Levin 538bfc15268SAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "wS", 0, &wstat); 539bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 5405f900f6dSSasha Levin virtio_p9_free_stat(&wstat); 541bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 542ead43b01SAneesh Kumar K.V return; 5436c8ca053SAneesh Kumar K.V err_out: 5446c8ca053SAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 5456c8ca053SAneesh Kumar K.V return; 5461c7850f9SSasha Levin } 5471c7850f9SSasha Levin 548ead43b01SAneesh Kumar K.V static void virtio_p9_wstat(struct p9_dev *p9dev, 549af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 5501c7850f9SSasha Levin { 551aec426f0SCyrill Gorcunov int res = 0; 552bfc15268SAneesh Kumar K.V u32 fid_val; 553bfc15268SAneesh Kumar K.V u16 unused; 554bfc15268SAneesh Kumar K.V struct p9_fid *fid; 555bfc15268SAneesh Kumar K.V struct p9_wstat wstat; 556af045e53SAneesh Kumar K.V 557bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "dwS", &fid_val, &unused, &wstat); 558bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 5591c7850f9SSasha Levin 5604bc9734aSAneesh Kumar K.V if (wstat.length != -1UL) { 561c6fb59c4SAneesh Kumar K.V res = truncate(fid->abs_path, wstat.length); 5624bc9734aSAneesh Kumar K.V if (res < 0) 5634bc9734aSAneesh Kumar K.V goto err_out; 5644bc9734aSAneesh Kumar K.V } 5654bc9734aSAneesh Kumar K.V if (wstat.mode != -1U) { 5664bc9734aSAneesh Kumar K.V res = chmod(fid->abs_path, wstat.mode & 0xFFFF); 5674bc9734aSAneesh Kumar K.V if (res < 0) 5684bc9734aSAneesh Kumar K.V goto err_out; 5694bc9734aSAneesh Kumar K.V } 570bfc15268SAneesh Kumar K.V if (strlen(wstat.name) > 0) { 5711c7850f9SSasha Levin char new_name[PATH_MAX] = {0}; 5721c7850f9SSasha Levin char full_path[PATH_MAX]; 5731c7850f9SSasha Levin char *last_dir = strrchr(fid->path, '/'); 5741c7850f9SSasha Levin 5751c7850f9SSasha Levin /* We need to get the full file name out of twstat->name */ 5761c7850f9SSasha Levin if (last_dir) 5771c7850f9SSasha Levin strncpy(new_name, fid->path, last_dir - fid->path + 1); 5781c7850f9SSasha Levin 579bfc15268SAneesh Kumar K.V memcpy(new_name + strlen(new_name), 580bfc15268SAneesh Kumar K.V wstat.name, strlen(wstat.name)); 5811c7850f9SSasha Levin 5821c7850f9SSasha Levin /* fid is reused for the new file */ 5834bc9734aSAneesh Kumar K.V res = rename(fid->abs_path, 5844bc9734aSAneesh Kumar K.V rel_to_abs(p9dev, new_name, full_path)); 5854bc9734aSAneesh Kumar K.V if (res < 0) 5864bc9734aSAneesh Kumar K.V goto err_out; 5871c7850f9SSasha Levin sprintf(fid->path, "%s", new_name); 5881c7850f9SSasha Levin } 5891c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN; 590bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 591ead43b01SAneesh Kumar K.V return; 5924bc9734aSAneesh Kumar K.V err_out: 5934bc9734aSAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 5944bc9734aSAneesh Kumar K.V return; 5951c7850f9SSasha Levin } 5961c7850f9SSasha Levin 597ead43b01SAneesh Kumar K.V static void virtio_p9_remove(struct p9_dev *p9dev, 598af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 5991c7850f9SSasha Levin { 6004bc9734aSAneesh Kumar K.V int res; 601bfc15268SAneesh Kumar K.V u32 fid_val; 602bfc15268SAneesh Kumar K.V struct p9_fid *fid; 6031c7850f9SSasha Levin 604bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "d", &fid_val); 605bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 606bfc15268SAneesh Kumar K.V close_fid(p9dev, fid_val); 6071c7850f9SSasha Levin if (fid->is_dir) 6084bc9734aSAneesh Kumar K.V res = rmdir(fid->abs_path); 6091c7850f9SSasha Levin else 6104bc9734aSAneesh Kumar K.V res = unlink(fid->abs_path); 6114bc9734aSAneesh Kumar K.V if (res < 0) 6124bc9734aSAneesh Kumar K.V goto err_out; 6131c7850f9SSasha Levin 6141c7850f9SSasha Levin *outlen = VIRTIO_P9_HDR_LEN; 615bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 616ead43b01SAneesh Kumar K.V return; 6174bc9734aSAneesh Kumar K.V err_out: 6184bc9734aSAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 6194bc9734aSAneesh Kumar K.V return; 6201c7850f9SSasha Levin } 6211c7850f9SSasha Levin 622ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev, 623af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 6241c7850f9SSasha Levin { 6254bc9734aSAneesh Kumar K.V 626bfc15268SAneesh Kumar K.V u64 offset; 627bfc15268SAneesh Kumar K.V u32 fid_val; 6284bc9734aSAneesh Kumar K.V u32 count; 6294bc9734aSAneesh Kumar K.V ssize_t res; 630*50c479e0SAneesh Kumar K.V u16 iov_cnt; 631*50c479e0SAneesh Kumar K.V void *iov_base; 632*50c479e0SAneesh Kumar K.V size_t iov_len; 633bfc15268SAneesh Kumar K.V struct p9_fid *fid; 634b064b05aSAneesh Kumar K.V /* u32 fid + u64 offset + u32 count */ 635b064b05aSAneesh Kumar K.V int twrite_size = sizeof(u32) + sizeof(u64) + sizeof(u32); 6361c7850f9SSasha Levin 637bfc15268SAneesh Kumar K.V virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count); 638bfc15268SAneesh Kumar K.V fid = &p9dev->fids[fid_val]; 639af045e53SAneesh Kumar K.V 640*50c479e0SAneesh Kumar K.V iov_base = pdu->out_iov[0].iov_base; 641*50c479e0SAneesh Kumar K.V iov_len = pdu->out_iov[0].iov_len; 642*50c479e0SAneesh Kumar K.V iov_cnt = pdu->out_iov_cnt; 643*50c479e0SAneesh Kumar K.V 644bfc15268SAneesh Kumar K.V /* Adjust the iovec to skip the header and meta data */ 645b064b05aSAneesh Kumar K.V pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) + twrite_size); 646b064b05aSAneesh Kumar K.V pdu->out_iov[0].iov_len -= (sizeof(struct p9_msg) + twrite_size); 647bfc15268SAneesh Kumar K.V pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count, 6486b163a87SAneesh Kumar K.V pdu->out_iov_cnt); 6494bc9734aSAneesh Kumar K.V res = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset); 650*50c479e0SAneesh Kumar K.V /* 651*50c479e0SAneesh Kumar K.V * Update the iov_base back, so that rest of 652*50c479e0SAneesh Kumar K.V * pdu_readf works correctly. 653*50c479e0SAneesh Kumar K.V */ 654*50c479e0SAneesh Kumar K.V pdu->out_iov[0].iov_base = iov_base; 655*50c479e0SAneesh Kumar K.V pdu->out_iov[0].iov_len = iov_len; 656*50c479e0SAneesh Kumar K.V pdu->out_iov_cnt = iov_cnt; 6574bc9734aSAneesh Kumar K.V if (res < 0) 6584bc9734aSAneesh Kumar K.V goto err_out; 6594bc9734aSAneesh Kumar K.V virtio_p9_pdu_writef(pdu, "d", res); 660bfc15268SAneesh Kumar K.V *outlen = pdu->write_offset; 661bfc15268SAneesh Kumar K.V virtio_p9_set_reply_header(pdu, *outlen); 662ead43b01SAneesh Kumar K.V return; 6634bc9734aSAneesh Kumar K.V err_out: 6644bc9734aSAneesh Kumar K.V virtio_p9_error_reply(p9dev, pdu, errno, outlen); 6654bc9734aSAneesh Kumar K.V return; 6661c7850f9SSasha Levin } 6671c7850f9SSasha Levin 668ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev, 669af045e53SAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen); 670b4422bf3SAneesh Kumar K.V 671b4422bf3SAneesh Kumar K.V static p9_handler *virtio_9p_handler [] = { 672b4422bf3SAneesh Kumar K.V [P9_TVERSION] = virtio_p9_version, 673b4422bf3SAneesh Kumar K.V [P9_TATTACH] = virtio_p9_attach, 674b4422bf3SAneesh Kumar K.V [P9_TSTAT] = virtio_p9_stat, 675b4422bf3SAneesh Kumar K.V [P9_TCLUNK] = virtio_p9_clunk, 676b4422bf3SAneesh Kumar K.V [P9_TWALK] = virtio_p9_walk, 677b4422bf3SAneesh Kumar K.V [P9_TOPEN] = virtio_p9_open, 678b4422bf3SAneesh Kumar K.V [P9_TREAD] = virtio_p9_read, 679b4422bf3SAneesh Kumar K.V [P9_TCREATE] = virtio_p9_create, 680b4422bf3SAneesh Kumar K.V [P9_TWSTAT] = virtio_p9_wstat, 681b4422bf3SAneesh Kumar K.V [P9_TREMOVE] = virtio_p9_remove, 682b4422bf3SAneesh Kumar K.V [P9_TWRITE] = virtio_p9_write, 683b4422bf3SAneesh Kumar K.V }; 684b4422bf3SAneesh Kumar K.V 685af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq) 686af045e53SAneesh Kumar K.V { 687af045e53SAneesh Kumar K.V struct p9_pdu *pdu = calloc(1, sizeof(*pdu)); 688af045e53SAneesh Kumar K.V if (!pdu) 689af045e53SAneesh Kumar K.V return NULL; 690af045e53SAneesh Kumar K.V 691bfc15268SAneesh Kumar K.V /* skip the pdu header p9_msg */ 692bfc15268SAneesh Kumar K.V pdu->read_offset = VIRTIO_P9_HDR_LEN; 693bfc15268SAneesh Kumar K.V pdu->write_offset = VIRTIO_P9_HDR_LEN; 694af045e53SAneesh Kumar K.V pdu->queue_head = virt_queue__get_inout_iov(kvm, vq, pdu->in_iov, 695af045e53SAneesh Kumar K.V pdu->out_iov, 696af045e53SAneesh Kumar K.V &pdu->in_iov_cnt, 697af045e53SAneesh Kumar K.V &pdu->out_iov_cnt); 698af045e53SAneesh Kumar K.V return pdu; 699af045e53SAneesh Kumar K.V } 700af045e53SAneesh Kumar K.V 701af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu) 702af045e53SAneesh Kumar K.V { 703af045e53SAneesh Kumar K.V struct p9_msg *msg; 704af045e53SAneesh Kumar K.V /* 705af045e53SAneesh Kumar K.V * we can peek directly into pdu for a u8 706af045e53SAneesh Kumar K.V * value. The host endianess won't be an issue 707af045e53SAneesh Kumar K.V */ 708af045e53SAneesh Kumar K.V msg = pdu->out_iov[0].iov_base; 709af045e53SAneesh Kumar K.V return msg->cmd; 710af045e53SAneesh Kumar K.V } 711af045e53SAneesh Kumar K.V 71297b408afSAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev, 71397b408afSAneesh Kumar K.V struct p9_pdu *pdu, u32 *outlen) 71497b408afSAneesh Kumar K.V { 71597b408afSAneesh Kumar K.V return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen); 71697b408afSAneesh Kumar K.V } 71797b408afSAneesh Kumar K.V 718b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job) 7191c7850f9SSasha Levin { 720af045e53SAneesh Kumar K.V u8 cmd; 721b4422bf3SAneesh Kumar K.V u32 len = 0; 722b4422bf3SAneesh Kumar K.V p9_handler *handler; 723b4422bf3SAneesh Kumar K.V struct p9_dev *p9dev; 724af045e53SAneesh Kumar K.V struct virt_queue *vq; 725af045e53SAneesh Kumar K.V struct p9_pdu *p9pdu; 7261c7850f9SSasha Levin 727b4422bf3SAneesh Kumar K.V vq = job->vq; 728b4422bf3SAneesh Kumar K.V p9dev = job->p9dev; 7291c7850f9SSasha Levin 730af045e53SAneesh Kumar K.V p9pdu = virtio_p9_pdu_init(kvm, vq); 731af045e53SAneesh Kumar K.V cmd = virtio_p9_get_cmd(p9pdu); 732af045e53SAneesh Kumar K.V 733af045e53SAneesh Kumar K.V if (cmd >= ARRAY_SIZE(virtio_9p_handler) || 734dd78d9eaSAneesh Kumar K.V !virtio_9p_handler[cmd]) 73597b408afSAneesh Kumar K.V handler = virtio_p9_eopnotsupp; 736dd78d9eaSAneesh Kumar K.V else 737af045e53SAneesh Kumar K.V handler = virtio_9p_handler[cmd]; 738af045e53SAneesh Kumar K.V handler(p9dev, p9pdu, &len); 739af045e53SAneesh Kumar K.V virt_queue__set_used_elem(vq, p9pdu->queue_head, len); 740af045e53SAneesh Kumar K.V free(p9pdu); 7411c7850f9SSasha Levin return true; 7421c7850f9SSasha Levin } 7431c7850f9SSasha Levin 7441c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param) 7451c7850f9SSasha Levin { 746b4422bf3SAneesh Kumar K.V struct p9_dev_job *job = (struct p9_dev_job *)param; 747b4422bf3SAneesh Kumar K.V struct p9_dev *p9dev = job->p9dev; 748b4422bf3SAneesh Kumar K.V struct virt_queue *vq = job->vq; 7491c7850f9SSasha Levin 7501c7850f9SSasha Levin while (virt_queue__available(vq)) { 751b4422bf3SAneesh Kumar K.V virtio_p9_do_io_request(kvm, job); 752b4422bf3SAneesh Kumar K.V virt_queue__trigger_irq(vq, p9dev->pci_hdr.irq_line, 753b4422bf3SAneesh Kumar K.V &p9dev->isr, kvm); 7541c7850f9SSasha Levin } 7551c7850f9SSasha Levin } 7561c7850f9SSasha Levin 75760eb42d5SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param) 75860eb42d5SSasha Levin { 75960eb42d5SSasha Levin struct p9_dev_job *job = param; 76060eb42d5SSasha Levin 761df0c7f57SSasha Levin thread_pool__do_job(&job->job_id); 76260eb42d5SSasha Levin } 76360eb42d5SSasha Levin 764b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm, 765b4422bf3SAneesh Kumar K.V u16 port, void *data, int size, u32 count) 7661c7850f9SSasha Levin { 7671c7850f9SSasha Levin unsigned long offset; 7681c7850f9SSasha Levin bool ret = true; 769b4422bf3SAneesh Kumar K.V struct p9_dev *p9dev; 77060eb42d5SSasha Levin struct ioevent ioevent; 7711c7850f9SSasha Levin 772b4422bf3SAneesh Kumar K.V p9dev = ioport->priv; 773b4422bf3SAneesh Kumar K.V offset = port - p9dev->base_addr; 7741c7850f9SSasha Levin 7751c7850f9SSasha Levin switch (offset) { 7761c7850f9SSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 7771c7850f9SSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 7781c7850f9SSasha Levin break; 7791c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 7801c7850f9SSasha Levin void *p; 781b4422bf3SAneesh Kumar K.V struct p9_dev_job *job; 782b4422bf3SAneesh Kumar K.V struct virt_queue *queue; 7831c7850f9SSasha Levin 784b4422bf3SAneesh Kumar K.V job = &p9dev->jobs[p9dev->queue_selector]; 785b4422bf3SAneesh Kumar K.V queue = &p9dev->vqs[p9dev->queue_selector]; 7861c7850f9SSasha Levin queue->pfn = ioport__read32(data); 7871c7850f9SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 7881c7850f9SSasha Levin 789612038c5SAneesh Kumar K.V vring_init(&queue->vring, VIRTQUEUE_NUM, p, 790b4422bf3SAneesh Kumar K.V VIRTIO_PCI_VRING_ALIGN); 7911c7850f9SSasha Levin 792b4422bf3SAneesh Kumar K.V *job = (struct p9_dev_job) { 793b4422bf3SAneesh Kumar K.V .vq = queue, 794b4422bf3SAneesh Kumar K.V .p9dev = p9dev, 795b4422bf3SAneesh Kumar K.V }; 796df0c7f57SSasha Levin thread_pool__init_job(&job->job_id, kvm, virtio_p9_do_io, job); 79760eb42d5SSasha Levin 79860eb42d5SSasha Levin ioevent = (struct ioevent) { 79960eb42d5SSasha Levin .io_addr = p9dev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 80060eb42d5SSasha Levin .io_len = sizeof(u16), 80160eb42d5SSasha Levin .fn = ioevent_callback, 80260eb42d5SSasha Levin .datamatch = p9dev->queue_selector, 80360eb42d5SSasha Levin .fn_ptr = &p9dev->jobs[p9dev->queue_selector], 80460eb42d5SSasha Levin .fn_kvm = kvm, 80560eb42d5SSasha Levin .fd = eventfd(0, 0), 80660eb42d5SSasha Levin }; 80760eb42d5SSasha Levin 80860eb42d5SSasha Levin ioeventfd__add_event(&ioevent); 80960eb42d5SSasha Levin 8101c7850f9SSasha Levin break; 8111c7850f9SSasha Levin } 8121c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_SEL: 813b4422bf3SAneesh Kumar K.V p9dev->queue_selector = ioport__read16(data); 8141c7850f9SSasha Levin break; 8151c7850f9SSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 8161c7850f9SSasha Levin u16 queue_index; 81760eb42d5SSasha Levin 8181c7850f9SSasha Levin queue_index = ioport__read16(data); 819df0c7f57SSasha Levin thread_pool__do_job(&p9dev->jobs[queue_index].job_id); 8201c7850f9SSasha Levin break; 8211c7850f9SSasha Levin } 8221c7850f9SSasha Levin case VIRTIO_PCI_STATUS: 823b4422bf3SAneesh Kumar K.V p9dev->status = ioport__read8(data); 8241c7850f9SSasha Levin break; 8251c7850f9SSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 826b4422bf3SAneesh Kumar K.V p9dev->config_vector = VIRTIO_MSI_NO_VECTOR; 8271c7850f9SSasha Levin break; 8281c7850f9SSasha Levin default: 8291c7850f9SSasha Levin ret = false; 8301c7850f9SSasha Levin break; 8311c7850f9SSasha Levin }; 8321c7850f9SSasha Levin 8331c7850f9SSasha Levin return ret; 8341c7850f9SSasha Levin } 8351c7850f9SSasha Levin 8361c7850f9SSasha Levin static struct ioport_operations virtio_p9_io_ops = { 8371c7850f9SSasha Levin .io_in = virtio_p9_pci_io_in, 8381c7850f9SSasha Levin .io_out = virtio_p9_pci_io_out, 8391c7850f9SSasha Levin }; 8401c7850f9SSasha Levin 84154f6802dSPekka Enberg int virtio_9p__init(struct kvm *kvm, const char *root, const char *tag_name) 8421c7850f9SSasha Levin { 843b4422bf3SAneesh Kumar K.V struct p9_dev *p9dev; 84454f6802dSPekka Enberg u8 pin, line, dev; 84554f6802dSPekka Enberg u16 p9_base_addr; 84654f6802dSPekka Enberg u32 i, root_len; 84754f6802dSPekka Enberg int err = 0; 8481c7850f9SSasha Levin 849b4422bf3SAneesh Kumar K.V p9dev = calloc(1, sizeof(*p9dev)); 850b4422bf3SAneesh Kumar K.V if (!p9dev) 85154f6802dSPekka Enberg return -ENOMEM; 85254f6802dSPekka Enberg 853b4422bf3SAneesh Kumar K.V if (!tag_name) 854b4422bf3SAneesh Kumar K.V tag_name = VIRTIO_P9_DEFAULT_TAG; 85554f6802dSPekka Enberg 856b4422bf3SAneesh Kumar K.V p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1); 85754f6802dSPekka Enberg if (p9dev->config == NULL) { 85854f6802dSPekka Enberg err = -ENOMEM; 859b4422bf3SAneesh Kumar K.V goto free_p9dev; 86054f6802dSPekka Enberg } 8611c7850f9SSasha Levin 862b4422bf3SAneesh Kumar K.V strcpy(p9dev->root_dir, root); 8631c7850f9SSasha Levin root_len = strlen(root); 8641c7850f9SSasha Levin /* 8651c7850f9SSasha Levin * We prefix the full path in all fids, This allows us to get the 8661c7850f9SSasha Levin * absolute path of an fid without playing with strings. 8671c7850f9SSasha Levin */ 8681c7850f9SSasha Levin for (i = 0; i < VIRTIO_P9_MAX_FID; i++) { 869b4422bf3SAneesh Kumar K.V strcpy(p9dev->fids[i].abs_path, root); 870b4422bf3SAneesh Kumar K.V p9dev->fids[i].path = p9dev->fids[i].abs_path + root_len; 8711c7850f9SSasha Levin } 872b4422bf3SAneesh Kumar K.V p9dev->config->tag_len = strlen(tag_name); 87354f6802dSPekka Enberg if (p9dev->config->tag_len > MAX_TAG_LEN) { 87454f6802dSPekka Enberg err = -EINVAL; 875b4422bf3SAneesh Kumar K.V goto free_p9dev_config; 87654f6802dSPekka Enberg } 8771c7850f9SSasha Levin 878b4422bf3SAneesh Kumar K.V memcpy(p9dev->config->tag, tag_name, strlen(tag_name)); 879b4422bf3SAneesh Kumar K.V p9dev->features |= 1 << VIRTIO_9P_MOUNT_TAG; 8801c7850f9SSasha Levin 88154f6802dSPekka Enberg err = irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line); 88254f6802dSPekka Enberg if (err < 0) 883b4422bf3SAneesh Kumar K.V goto free_p9dev_config; 8841c7850f9SSasha Levin 88554f6802dSPekka Enberg p9_base_addr = ioport__register(IOPORT_EMPTY, &virtio_p9_io_ops, IOPORT_SIZE, p9dev); 88654f6802dSPekka Enberg 887b4422bf3SAneesh Kumar K.V p9dev->base_addr = p9_base_addr; 88854f6802dSPekka Enberg 889b4422bf3SAneesh Kumar K.V p9dev->pci_hdr = (struct pci_device_header) { 890b4422bf3SAneesh Kumar K.V .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 891b4422bf3SAneesh Kumar K.V .device_id = PCI_DEVICE_ID_VIRTIO_P9, 892b4422bf3SAneesh Kumar K.V .header_type = PCI_HEADER_TYPE_NORMAL, 893b4422bf3SAneesh Kumar K.V .revision_id = 0, 894b4422bf3SAneesh Kumar K.V .class = 0x010000, 895b4422bf3SAneesh Kumar K.V .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 896b4422bf3SAneesh Kumar K.V .subsys_id = VIRTIO_ID_9P, 897b4422bf3SAneesh Kumar K.V .irq_pin = pin, 898b4422bf3SAneesh Kumar K.V .irq_line = line, 899b4422bf3SAneesh Kumar K.V .bar[0] = p9_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 900b4422bf3SAneesh Kumar K.V }; 901b4422bf3SAneesh Kumar K.V pci__register(&p9dev->pci_hdr, dev); 902b4422bf3SAneesh Kumar K.V 90354f6802dSPekka Enberg return err; 90454f6802dSPekka Enberg 905b4422bf3SAneesh Kumar K.V free_p9dev_config: 906b4422bf3SAneesh Kumar K.V free(p9dev->config); 907b4422bf3SAneesh Kumar K.V free_p9dev: 908b4422bf3SAneesh Kumar K.V free(p9dev); 90954f6802dSPekka Enberg return err; 9101c7850f9SSasha Levin } 911