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),
52      interrupt(params->interrupt->get())
53{
54    fatal_if(!interrupt, "No MMIO VirtIO interrupt specified\n");
55
56    vio.registerKickCallback(&callbackKick);
57}
58
59MmioVirtIO::~MmioVirtIO()
60{
61}
62
63Tick
64MmioVirtIO::read(PacketPtr pkt)
65{
66    const Addr offset = pkt->getAddr() - pioAddr;
67    const unsigned size(pkt->getSize());
68
69    DPRINTF(VIOIface, "Reading %u bytes @ 0x%x:\n", size, offset);
70
71    // Forward device configuration writes to the device VirtIO model
72    if (offset >= OFF_CONFIG) {
73        vio.readConfig(pkt, offset - OFF_CONFIG);
74        return 0;
75    }
76
77    panic_if(size != 4, "Unexpected read size: %u\n", size);
78
79    const uint32_t value = read(offset);
80    DPRINTF(VIOIface, "    value: 0x%x\n", value);
81    pkt->makeResponse();
82    pkt->setLE<uint32_t>(value);
83
84    return 0;
85}
86
87uint32_t
88MmioVirtIO::read(Addr offset)
89{
90    switch(offset) {
91      case OFF_MAGIC:
92        return MAGIC;
93
94      case OFF_VERSION:
95        return VERSION;
96
97      case OFF_DEVICE_ID:
98        return vio.deviceId;
99
100      case OFF_VENDOR_ID:
101        return VENDOR_ID;
102
103      case OFF_HOST_FEATURES:
104        // We only implement 32 bits of this register
105        if (hostFeaturesSelect == 0)
106            return vio.deviceFeatures;
107        else
108            return 0;
109
110      case OFF_HOST_FEATURES_SELECT:
111        return hostFeaturesSelect;
112
113      case OFF_GUEST_FEATURES:
114        // We only implement 32 bits of this register
115        if (guestFeaturesSelect == 0)
116            return vio.getGuestFeatures();
117        else
118            return 0;
119
120      case OFF_GUEST_FEATURES_SELECT:
121        return hostFeaturesSelect;
122
123      case OFF_GUEST_PAGE_SIZE:
124        return pageSize;
125
126      case OFF_QUEUE_SELECT:
127        return vio.getQueueSelect();
128
129      case OFF_QUEUE_NUM_MAX:
130        return vio.getQueueSize();
131
132      case OFF_QUEUE_NUM:
133        // TODO: We don't support queue resizing, so ignore this for now.
134        return vio.getQueueSize();
135
136      case OFF_QUEUE_ALIGN:
137        // TODO: Implement this once we support other alignment sizes
138        return VirtQueue::ALIGN_SIZE;
139
140      case OFF_QUEUE_PFN:
141        return vio.getQueueAddress();
142
143      case OFF_INTERRUPT_STATUS:
144        return interruptStatus;
145
146      case OFF_STATUS:
147        return vio.getDeviceStatus();
148
149        // Write-only registers
150      case OFF_QUEUE_NOTIFY:
151      case OFF_INTERRUPT_ACK:
152        warn("Guest is trying to read to write-only register 0x%\n",
153             offset);
154        return 0;
155
156      default:
157        panic("Unhandled read offset (0x%x)\n", offset);
158    }
159}
160
161Tick
162MmioVirtIO::write(PacketPtr pkt)
163{
164    const Addr offset = pkt->getAddr() - pioAddr;
165    const unsigned size(pkt->getSize());
166
167    DPRINTF(VIOIface, "Writing %u bytes @ 0x%x:\n", size, offset);
168
169    // Forward device configuration writes to the device VirtIO model
170    if (offset >= OFF_CONFIG) {
171        vio.writeConfig(pkt, offset - OFF_CONFIG);
172        return 0;
173    }
174
175    panic_if(size != 4, "Unexpected write size @ 0x%x: %u\n", offset, size);
176    DPRINTF(VIOIface, "    value: 0x%x\n", pkt->getLE<uint32_t>());
177    pkt->makeResponse();
178    write(offset, pkt->getLE<uint32_t>());
179    return 0;
180}
181
182void
183MmioVirtIO::write(Addr offset, uint32_t value)
184{
185    switch(offset) {
186      case OFF_HOST_FEATURES_SELECT:
187        hostFeaturesSelect = value;
188        return;
189
190      case OFF_GUEST_FEATURES:
191        if (guestFeaturesSelect == 0) {
192            vio.setGuestFeatures(value);
193        } else if (value != 0) {
194            warn("Setting unimplemented guest features register %u: %u\n",
195                 guestFeaturesSelect, value);
196        }
197        return;
198
199      case OFF_GUEST_FEATURES_SELECT:
200        guestFeaturesSelect = value;
201        return;
202
203      case OFF_GUEST_PAGE_SIZE:
204        // TODO: We only support 4096 byte pages at the moment
205        panic_if(value != VirtQueue::ALIGN_SIZE,
206                 "Unhandled VirtIO page size: %u", value);
207        pageSize = value;
208        return;
209
210      case OFF_QUEUE_SELECT:
211        vio.setQueueSelect(value);
212        return;
213
214      case OFF_QUEUE_NUM:
215        // TODO: We don't support queue resizing, so ignore this for now.
216        warn_once("Ignoring queue resize hint. Requested size: %u\n", value);
217        return;
218
219      case OFF_QUEUE_ALIGN:
220        // TODO: We currently only support the hard-coded 4k alignment used
221        // in legacy VirtIO.
222        panic_if(value != VirtQueue::ALIGN_SIZE,
223                 "Unhandled VirtIO alignment size: %u", value);
224        return;
225
226      case OFF_QUEUE_PFN:
227        vio.setQueueAddress(value);
228        return;
229
230      case OFF_QUEUE_NOTIFY:
231        vio.onNotify(value);
232        return;
233
234      case OFF_INTERRUPT_ACK:
235        setInterrupts(interruptStatus & (~value));
236        return;
237
238      case OFF_STATUS:
239        panic_if(value > 0xff, "Unexpected status: 0x%x\n", value);
240        vio.setDeviceStatus(value);
241        return;
242
243        /* Read-only registers */
244      case OFF_MAGIC:
245      case OFF_VERSION:
246      case OFF_DEVICE_ID:
247      case OFF_VENDOR_ID:
248      case OFF_HOST_FEATURES:
249      case OFF_QUEUE_NUM_MAX:
250      case OFF_INTERRUPT_STATUS:
251        warn("Guest is trying to write to read-only register 0x%\n",
252             offset);
253        return;
254
255      default:
256        panic("Unhandled read offset (0x%x)\n", offset);
257    }
258}
259
260void
261MmioVirtIO::kick()
262{
263    DPRINTF(VIOIface, "kick(): Sending interrupt...\n");
264    setInterrupts(interruptStatus | INT_USED_RING);
265}
266
267void
268MmioVirtIO::setInterrupts(uint32_t value)
269{
270    const uint32_t old_ints = interruptStatus;
271    interruptStatus = value;
272
273    if (!old_ints && interruptStatus) {
274        interrupt->raise();
275    } else if (old_ints && !interruptStatus) {
276        interrupt->clear();
277    }
278}
279
280
281MmioVirtIO *
282MmioVirtIOParams::create()
283{
284    return new MmioVirtIO(this);
285}
286