Support for booting from virtio disks
[seabios.git] / 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 "virtio-pci.h"
14 #include "virtio-blk.h"
15 #include "disk.h"
16
17 struct virtiodrive_s {
18     struct drive_s drive;
19     struct vring_virtqueue *vq;
20     u16 ioaddr;
21 };
22
23 static int
24 virtio_blk_read(struct disk_op_s *op)
25 {
26     struct virtiodrive_s *vdrive_g =
27         container_of(op->drive_g, struct virtiodrive_s, drive);
28     struct vring_virtqueue *vq = GET_GLOBAL(vdrive_g->vq);
29     struct virtio_blk_outhdr hdr = {
30         .type = VIRTIO_BLK_T_IN,
31         .ioprio = 0,
32         .sector = op->lba,
33     };
34     u8 status = VIRTIO_BLK_S_UNSUPP;
35     struct vring_list sg[] = {
36         {
37             .addr       = MAKE_FLATPTR(GET_SEG(SS), &hdr),
38             .length     = sizeof(hdr),
39         },
40         {
41             .addr       = op->buf_fl,
42             .length     = GET_GLOBAL(vdrive_g->drive.blksize) * op->count,
43         },
44         {
45             .addr       = MAKE_FLATPTR(GET_SEG(SS), &status),
46             .length     = sizeof(status),
47         },
48     };
49
50     /* Add to virtqueue and kick host */
51     vring_add_buf(vq, sg, 1, 2, 0, 0);
52     vring_kick(GET_GLOBAL(vdrive_g->ioaddr), vq, 1);
53
54     /* Wait for reply */
55     while (!vring_more_used(vq))
56         udelay(5);
57
58     /* Reclaim virtqueue element */
59     vring_get_buf(vq, NULL);
60     return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
61 }
62
63 int
64 process_virtio_op(struct disk_op_s *op)
65 {
66     switch (op->command) {
67     case CMD_READ:
68         return virtio_blk_read(op);
69     case CMD_FORMAT:
70     case CMD_WRITE:
71         return DISK_RET_EWRITEPROTECT;
72     case CMD_RESET:
73     case CMD_ISREADY:
74     case CMD_VERIFY:
75     case CMD_SEEK:
76         return DISK_RET_SUCCESS;
77     default:
78         op->count = 0;
79         return DISK_RET_EPARAM;
80     }
81 }
82
83 void
84 virtio_blk_setup(void)
85 {
86     ASSERT32FLAT();
87     if (! CONFIG_VIRTIO_BLK)
88         return;
89
90     dprintf(3, "init virtio-blk\n");
91
92     int bdf, max;
93     u32 id = PCI_VENDOR_ID_REDHAT_QUMRANET | (PCI_DEVICE_ID_VIRTIO_BLK << 16);
94     foreachpci(bdf, max) {
95         u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
96         if (v != id)
97             continue;
98         dprintf(3, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
99                 pci_bdf_to_dev(bdf));
100         char *desc = malloc_tmphigh(MAXDESCSIZE);
101         struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g));
102         struct vring_virtqueue *vq = malloc_low(sizeof(*vq));
103         if (!vdrive_g || !desc || !vq) {
104             free(vdrive_g);
105             free(desc);
106             free(vq);
107             warn_noalloc();
108             return;
109         }
110         memset(vdrive_g, 0, sizeof(*vdrive_g));
111         vdrive_g->drive.type = DTYPE_VIRTIO;
112         vdrive_g->drive.cntl_id = bdf;
113         vdrive_g->vq = vq;
114
115         u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) &
116             PCI_BASE_ADDRESS_IO_MASK;
117
118         vdrive_g->ioaddr = ioaddr;
119
120         vp_reset(ioaddr);
121         vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
122                       VIRTIO_CONFIG_S_DRIVER );
123
124         if (vp_find_vq(ioaddr, 0, vdrive_g->vq) < 0 ) {
125             free(vdrive_g);
126             free(desc);
127             free(vq);
128             dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
129                     pci_bdf_to_bus (bdf), pci_bdf_to_dev(bdf));
130             continue;
131         }
132
133         struct virtio_blk_config cfg;
134         vp_get(ioaddr, 0, &cfg, sizeof(cfg));
135
136         vdrive_g->drive.blksize = cfg.blk_size;
137         vdrive_g->drive.sectors = cfg.capacity;
138         dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
139                 pci_bdf_to_bus (bdf), pci_bdf_to_dev(bdf),
140                 vdrive_g->drive.blksize, (u32)vdrive_g->drive.sectors);
141
142         vdrive_g->drive.pchs.cylinders = cfg.cylinders;
143         vdrive_g->drive.pchs.heads = cfg.heads;
144         vdrive_g->drive.pchs.spt = cfg.sectors;
145
146         setup_translation(&vdrive_g->drive);
147         add_bcv_internal(&vdrive_g->drive);
148
149         snprintf(desc, MAXDESCSIZE, "Virtio disk PCI:%x:%x",
150                  pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
151
152         vdrive_g->drive.desc = desc;
153
154         vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
155                       VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
156     }
157 }
158