Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


SEABIOS updates to support reset and to simplify
[palacios.git] / bios / seabios / src / virtio-blk.c
1 // Virtio block boot support.
2 //
3 // Copyright (C) 2010 Red Hat Inc.
4 //
5 // Authors:
6 //  Gleb Natapov <gnatapov@redhat.com>
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" // boot_add_hd
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_op(struct disk_op_s *op, int write)
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 = write ? VIRTIO_BLK_T_OUT : 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     if (write)
57         vring_add_buf(vq, sg, 2, 1, 0, 0);
58     else
59         vring_add_buf(vq, sg, 1, 2, 0, 0);
60     vring_kick(GET_GLOBAL(vdrive_g->ioaddr), vq, 1);
61
62     /* Wait for reply */
63     while (!vring_more_used(vq))
64         usleep(5);
65
66     /* Reclaim virtqueue element */
67     vring_get_buf(vq, NULL);
68
69     /* Clear interrupt status register.  Avoid leaving interrupts stuck if
70      * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
71      */
72     vp_get_isr(GET_GLOBAL(vdrive_g->ioaddr));
73
74     return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
75 }
76
77 int
78 process_virtio_op(struct disk_op_s *op)
79 {
80     if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT)
81         return 0;
82     switch (op->command) {
83     case CMD_READ:
84         return virtio_blk_op(op, 0);
85     case CMD_WRITE:
86         return virtio_blk_op(op, 1);
87     case CMD_FORMAT:
88     case CMD_RESET:
89     case CMD_ISREADY:
90     case CMD_VERIFY:
91     case CMD_SEEK:
92         return DISK_RET_SUCCESS;
93     default:
94         op->count = 0;
95         return DISK_RET_EPARAM;
96     }
97 }
98
99 static void
100 init_virtio_blk(struct pci_device *pci)
101 {
102     u16 bdf = pci->bdf;
103     dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
104             pci_bdf_to_dev(bdf));
105     struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g));
106     struct vring_virtqueue *vq = memalign_low(PAGE_SIZE, sizeof(*vq));
107     if (!vdrive_g || !vq) {
108         warn_noalloc();
109         goto fail;
110     }
111     memset(vdrive_g, 0, sizeof(*vdrive_g));
112     memset(vq, 0, sizeof(*vq));
113     vdrive_g->drive.type = DTYPE_VIRTIO;
114     vdrive_g->drive.cntl_id = bdf;
115     vdrive_g->vq = vq;
116
117     u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) &
118         PCI_BASE_ADDRESS_IO_MASK;
119
120     vdrive_g->ioaddr = ioaddr;
121
122     vp_reset(ioaddr);
123     vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
124                   VIRTIO_CONFIG_S_DRIVER );
125
126     if (vp_find_vq(ioaddr, 0, vdrive_g->vq) < 0 ) {
127         dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
128                 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
129         goto fail;
130     }
131
132     struct virtio_blk_config cfg;
133     vp_get(ioaddr, 0, &cfg, sizeof(cfg));
134
135     u32 f = vp_get_features(ioaddr);
136     vdrive_g->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ?
137         cfg.blk_size : DISK_SECTOR_SIZE;
138
139     vdrive_g->drive.sectors = cfg.capacity;
140     dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
141             pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
142             vdrive_g->drive.blksize, (u32)vdrive_g->drive.sectors);
143
144     if (vdrive_g->drive.blksize != DISK_SECTOR_SIZE) {
145         dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n",
146                 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
147                 vdrive_g->drive.blksize);
148         goto fail;
149     }
150
151     vdrive_g->drive.pchs.cylinders = cfg.cylinders;
152     vdrive_g->drive.pchs.heads = cfg.heads;
153     vdrive_g->drive.pchs.spt = cfg.sectors;
154     char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x",
155                           pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
156
157     boot_add_hd(&vdrive_g->drive, desc, bootprio_find_pci_device(pci));
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(vq);
166 }
167
168 void
169 virtio_blk_setup(void)
170 {
171     ASSERT32FLAT();
172     if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT)
173         return;
174
175     dprintf(3, "init virtio-blk\n");
176
177     struct pci_device *pci;
178     foreachpci(pci) {
179         if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET
180             || pci->device != PCI_DEVICE_ID_VIRTIO_BLK)
181             continue;
182         init_virtio_blk(pci);
183     }
184 }