|
|
1.1 ! root 1: // Virtio block boot support. ! 2: // ! 3: // Copyright (C) 2010 Red Hat Inc. ! 4: // ! 5: // Authors: ! 6: // Gleb Natapov <[email protected]> ! 7: // ! 8: // This file may be distributed under the terms of the GNU LGPLv3 license. ! 9: ! 10: #include "util.h" // dprintf ! 11: #include "pci.h" // foreachpci ! 12: #include "config.h" // CONFIG_* ! 13: #include "biosvar.h" // GET_GLOBAL ! 14: #include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK ! 15: #include "pci_regs.h" // PCI_VENDOR_ID ! 16: #include "boot.h" // add_bcv_internal ! 17: #include "virtio-pci.h" ! 18: #include "virtio-ring.h" ! 19: #include "virtio-blk.h" ! 20: #include "disk.h" ! 21: ! 22: struct virtiodrive_s { ! 23: struct drive_s drive; ! 24: struct vring_virtqueue *vq; ! 25: u16 ioaddr; ! 26: }; ! 27: ! 28: static int ! 29: virtio_blk_read(struct disk_op_s *op) ! 30: { ! 31: struct virtiodrive_s *vdrive_g = ! 32: container_of(op->drive_g, struct virtiodrive_s, drive); ! 33: struct vring_virtqueue *vq = GET_GLOBAL(vdrive_g->vq); ! 34: struct virtio_blk_outhdr hdr = { ! 35: .type = VIRTIO_BLK_T_IN, ! 36: .ioprio = 0, ! 37: .sector = op->lba, ! 38: }; ! 39: u8 status = VIRTIO_BLK_S_UNSUPP; ! 40: struct vring_list sg[] = { ! 41: { ! 42: .addr = MAKE_FLATPTR(GET_SEG(SS), &hdr), ! 43: .length = sizeof(hdr), ! 44: }, ! 45: { ! 46: .addr = op->buf_fl, ! 47: .length = GET_GLOBAL(vdrive_g->drive.blksize) * op->count, ! 48: }, ! 49: { ! 50: .addr = MAKE_FLATPTR(GET_SEG(SS), &status), ! 51: .length = sizeof(status), ! 52: }, ! 53: }; ! 54: ! 55: /* Add to virtqueue and kick host */ ! 56: vring_add_buf(vq, sg, 1, 2, 0, 0); ! 57: vring_kick(GET_GLOBAL(vdrive_g->ioaddr), vq, 1); ! 58: ! 59: /* Wait for reply */ ! 60: while (!vring_more_used(vq)) ! 61: usleep(5); ! 62: ! 63: /* Reclaim virtqueue element */ ! 64: vring_get_buf(vq, NULL); ! 65: ! 66: /* Clear interrupt status register. Avoid leaving interrupts stuck if ! 67: * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. ! 68: */ ! 69: vp_get_isr(GET_GLOBAL(vdrive_g->ioaddr)); ! 70: ! 71: return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; ! 72: } ! 73: ! 74: int ! 75: process_virtio_op(struct disk_op_s *op) ! 76: { ! 77: if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT) ! 78: return 0; ! 79: switch (op->command) { ! 80: case CMD_READ: ! 81: return virtio_blk_read(op); ! 82: case CMD_FORMAT: ! 83: case CMD_WRITE: ! 84: return DISK_RET_EWRITEPROTECT; ! 85: case CMD_RESET: ! 86: case CMD_ISREADY: ! 87: case CMD_VERIFY: ! 88: case CMD_SEEK: ! 89: return DISK_RET_SUCCESS; ! 90: default: ! 91: op->count = 0; ! 92: return DISK_RET_EPARAM; ! 93: } ! 94: } ! 95: ! 96: static void ! 97: init_virtio_blk(u16 bdf) ! 98: { ! 99: dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf), ! 100: pci_bdf_to_dev(bdf)); ! 101: char *desc = malloc_tmphigh(MAXDESCSIZE); ! 102: struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g)); ! 103: struct vring_virtqueue *vq = memalign_low(PAGE_SIZE, sizeof(*vq)); ! 104: if (!vdrive_g || !desc || !vq) { ! 105: warn_noalloc(); ! 106: goto fail; ! 107: } ! 108: memset(vdrive_g, 0, sizeof(*vdrive_g)); ! 109: vdrive_g->drive.type = DTYPE_VIRTIO; ! 110: vdrive_g->drive.cntl_id = bdf; ! 111: vdrive_g->vq = vq; ! 112: ! 113: u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & ! 114: PCI_BASE_ADDRESS_IO_MASK; ! 115: ! 116: vdrive_g->ioaddr = ioaddr; ! 117: ! 118: vp_reset(ioaddr); ! 119: vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | ! 120: VIRTIO_CONFIG_S_DRIVER ); ! 121: ! 122: if (vp_find_vq(ioaddr, 0, vdrive_g->vq) < 0 ) { ! 123: dprintf(1, "fail to find vq for virtio-blk %x:%x\n", ! 124: pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); ! 125: goto fail; ! 126: } ! 127: ! 128: struct virtio_blk_config cfg; ! 129: vp_get(ioaddr, 0, &cfg, sizeof(cfg)); ! 130: ! 131: u32 f = vp_get_features(ioaddr); ! 132: vdrive_g->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ? ! 133: cfg.blk_size : DISK_SECTOR_SIZE; ! 134: ! 135: vdrive_g->drive.sectors = cfg.capacity; ! 136: dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", ! 137: pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), ! 138: vdrive_g->drive.blksize, (u32)vdrive_g->drive.sectors); ! 139: ! 140: if (vdrive_g->drive.blksize != DISK_SECTOR_SIZE) { ! 141: dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", ! 142: pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), ! 143: vdrive_g->drive.blksize); ! 144: goto fail; ! 145: } ! 146: ! 147: vdrive_g->drive.pchs.cylinders = cfg.cylinders; ! 148: vdrive_g->drive.pchs.heads = cfg.heads; ! 149: vdrive_g->drive.pchs.spt = cfg.sectors; ! 150: ! 151: setup_translation(&vdrive_g->drive); ! 152: add_bcv_internal(&vdrive_g->drive); ! 153: ! 154: snprintf(desc, MAXDESCSIZE, "Virtio disk PCI:%x:%x", ! 155: pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); ! 156: ! 157: vdrive_g->drive.desc = desc; ! 158: ! 159: vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | ! 160: VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); ! 161: return; ! 162: ! 163: fail: ! 164: free(vdrive_g); ! 165: free(desc); ! 166: free(vq); ! 167: } ! 168: ! 169: void ! 170: virtio_blk_setup(void) ! 171: { ! 172: ASSERT32FLAT(); ! 173: if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT) ! 174: return; ! 175: ! 176: dprintf(3, "init virtio-blk\n"); ! 177: ! 178: int bdf, max; ! 179: u32 id = PCI_VENDOR_ID_REDHAT_QUMRANET | (PCI_DEVICE_ID_VIRTIO_BLK << 16); ! 180: foreachpci(bdf, max) { ! 181: u32 v = pci_config_readl(bdf, PCI_VENDOR_ID); ! 182: if (v != id) ! 183: continue; ! 184: init_virtio_blk(bdf); ! 185: } ! 186: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.