vio_mmio.cc revision 12740:beed0805c651
1/*
2 * Copyright (c) 2016-2018 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Andreas Sandberg
38 */
39
40#include "dev/arm/vio_mmio.hh"
41
42#include "debug/VIOIface.hh"
43#include "dev/arm/base_gic.hh"
44#include "mem/packet_access.hh"
45#include "params/MmioVirtIO.hh"
46
47MmioVirtIO::MmioVirtIO(const MmioVirtIOParams *params)
48    : BasicPioDevice(params, params->pio_size),
49      hostFeaturesSelect(0), guestFeaturesSelect(0), pageSize(0),
50      interruptStatus(0),
51      callbackKick(this), vio(*params->vio), interrupt(params->interrupt)
52{
53    fatal_if(!interrupt, "No MMIO VirtIO interrupt specified\n");
54
55    vio.registerKickCallback(&callbackKick);
56}
57
58MmioVirtIO::~MmioVirtIO()
59{
60}
61
62Tick
63MmioVirtIO::read(PacketPtr pkt)
64{
65    const Addr offset = pkt->getAddr() - pioAddr;
66    const unsigned size(pkt->getSize());
67
68    DPRINTF(VIOIface, "Reading %u bytes @ 0x%x:\n", size, offset);
69
70    // Forward device configuration writes to the device VirtIO model
71    if (offset >= OFF_CONFIG) {
72        vio.readConfig(pkt, offset - OFF_CONFIG);
73        return 0;
74    }
75
76    panic_if(size != 4, "Unexpected read size: %u\n", size);
77
78    const uint32_t value = read(offset);
79    DPRINTF(VIOIface, "    value: 0x%x\n", value);
80    pkt->makeResponse();
81    pkt->set<uint32_t>(value);
82
83    return 0;
84}
85
86uint32_t
87MmioVirtIO::read(Addr offset)
88{
89    switch(offset) {
90      case OFF_MAGIC:
91        return MAGIC;
92
93      case OFF_VERSION:
94        return VERSION;
95
96      case OFF_DEVICE_ID:
97        return vio.deviceId;
98
99      case OFF_VENDOR_ID:
100        return VENDOR_ID;
101
102      case OFF_HOST_FEATURES:
103        // We only implement 32 bits of this register
104        if (hostFeaturesSelect == 0)
105            return vio.deviceFeatures;
106        else
107            return 0;
108
109      case OFF_HOST_FEATURES_SELECT:
110        return hostFeaturesSelect;
111
112      case OFF_GUEST_FEATURES:
113        // We only implement 32 bits of this register
114        if (guestFeaturesSelect == 0)
115            return vio.getGuestFeatures();
116        else
117            return 0;
118
119      case OFF_GUEST_FEATURES_SELECT:
120        return hostFeaturesSelect;
121
122      case OFF_GUEST_PAGE_SIZE:
123        return pageSize;
124
125      case OFF_QUEUE_SELECT:
126        return vio.getQueueSelect();
127
128      case OFF_QUEUE_NUM_MAX:
129        return vio.getQueueSize();
130
131      case OFF_QUEUE_NUM:
132        // TODO: We don't support queue resizing, so ignore this for now.
133        return vio.getQueueSize();
134
135      case OFF_QUEUE_ALIGN:
136        // TODO: Implement this once we support other alignment sizes
137        return VirtQueue::ALIGN_SIZE;
138
139      case OFF_QUEUE_PFN:
140        return vio.getQueueAddress();
141
142      case OFF_INTERRUPT_STATUS:
143        return interruptStatus;
144
145      case OFF_STATUS:
146        return vio.getDeviceStatus();
147
148        // Write-only registers
149      case OFF_QUEUE_NOTIFY:
150      case OFF_INTERRUPT_ACK:
151        warn("Guest is trying to read to write-only register 0x%\n",
152             offset);
153        return 0;
154
155      default:
156        panic("Unhandled read offset (0x%x)\n", offset);
157    }
158}
159
160Tick
161MmioVirtIO::write(PacketPtr pkt)
162{
163    const Addr offset = pkt->getAddr() - pioAddr;
164    const unsigned size(pkt->getSize());
165
166    DPRINTF(VIOIface, "Writing %u bytes @ 0x%x:\n", size, offset);
167
168    // Forward device configuration writes to the device VirtIO model
169    if (offset >= OFF_CONFIG) {
170        vio.writeConfig(pkt, offset - OFF_CONFIG);
171        return 0;
172    }
173
174    panic_if(size != 4, "Unexpected write size @ 0x%x: %u\n", offset, size);
175    DPRINTF(VIOIface, "    value: 0x%x\n", pkt->get<uint32_t>());
176    pkt->makeResponse();
177    write(offset, pkt->get<uint32_t>());
178    return 0;
179}
180
181void
182MmioVirtIO::write(Addr offset, uint32_t value)
183{
184    switch(offset) {
185      case OFF_HOST_FEATURES_SELECT:
186        hostFeaturesSelect = value;
187        return;
188
189      case OFF_GUEST_FEATURES:
190        if (guestFeaturesSelect == 0) {
191            vio.setGuestFeatures(value);
192        } else if (value != 0) {
193            warn("Setting unimplemented guest features register %u: %u\n",
194                 guestFeaturesSelect, value);
195        }
196        return;
197
198      case OFF_GUEST_FEATURES_SELECT:
199        guestFeaturesSelect = value;
200        return;
201
202      case OFF_GUEST_PAGE_SIZE:
203        // TODO: We only support 4096 byte pages at the moment
204        panic_if(value != VirtQueue::ALIGN_SIZE,
205                 "Unhandled VirtIO page size: %u", value);
206        pageSize = value;
207        return;
208
209      case OFF_QUEUE_SELECT:
210        vio.setQueueSelect(value);
211        return;
212
213      case OFF_QUEUE_NUM:
214        // TODO: We don't support queue resizing, so ignore this for now.
215        warn_once("Ignoring queue resize hint. Requested size: %u\n", value);
216        return;
217
218      case OFF_QUEUE_ALIGN:
219        // TODO: We currently only support the hard-coded 4k alignment used
220        // in legacy VirtIO.
221        panic_if(value != VirtQueue::ALIGN_SIZE,
222                 "Unhandled VirtIO alignment size: %u", value);
223        return;
224
225      case OFF_QUEUE_PFN:
226        vio.setQueueAddress(value);
227        return;
228
229      case OFF_QUEUE_NOTIFY:
230        vio.onNotify(value);
231        return;
232
233      case OFF_INTERRUPT_ACK:
234        setInterrupts(interruptStatus & (~value));
235        return;
236
237      case OFF_STATUS:
238        panic_if(value > 0xff, "Unexpected status: 0x%x\n", value);
239        vio.setDeviceStatus(value);
240        return;
241
242        /* Read-only registers */
243      case OFF_MAGIC:
244      case OFF_VERSION:
245      case OFF_DEVICE_ID:
246      case OFF_VENDOR_ID:
247      case OFF_HOST_FEATURES:
248      case OFF_QUEUE_NUM_MAX:
249      case OFF_INTERRUPT_STATUS:
250        warn("Guest is trying to write to read-only register 0x%\n",
251             offset);
252        return;
253
254      default:
255        panic("Unhandled read offset (0x%x)\n", offset);
256    }
257}
258
259void
260MmioVirtIO::kick()
261{
262    DPRINTF(VIOIface, "kick(): Sending interrupt...\n");
263    setInterrupts(interruptStatus | INT_USED_RING);
264}
265
266void
267MmioVirtIO::setInterrupts(uint32_t value)
268{
269    const uint32_t old_ints = interruptStatus;
270    interruptStatus = value;
271
272    if (!old_ints && interruptStatus) {
273        interrupt->raise();
274    } else if (old_ints && !interruptStatus) {
275        interrupt->clear();
276    }
277}
278
279
280MmioVirtIO *
281MmioVirtIOParams::create()
282{
283    return new MmioVirtIO(this);
284}
285