/src/hbfa-fl/HBFA/UefiHostFuzzTestCasePkg/TestStub/VirtioBlkStubLib/VirtioBlkStubLib.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** @file |
2 | | |
3 | | Copyright (c) 2018, Intel Corporation. All rights reserved.<BR> |
4 | | SPDX-License-Identifier: BSD-2-Clause-Patent |
5 | | |
6 | | **/ |
7 | | #include <Uefi.h> |
8 | | |
9 | | #include <Library/BaseLib.h> |
10 | | #include <Library/DebugLib.h> |
11 | | #include <Library/BaseMemoryLib.h> |
12 | | #include <Library/MemoryAllocationLib.h> |
13 | | #include <Library/VirtioLib.h> |
14 | | |
15 | | #include <IndustryStandard/VirtioBlk.h> |
16 | | #include <Library/VirtioBlkStubLib.h> |
17 | | |
18 | | EFI_STATUS |
19 | | EFIAPI |
20 | | VirtioBlkReset ( |
21 | | IN EFI_BLOCK_IO_PROTOCOL *This, |
22 | | IN BOOLEAN ExtendedVerification |
23 | | ); |
24 | | |
25 | | EFI_STATUS |
26 | | EFIAPI |
27 | | VirtioBlkReadBlocks ( |
28 | | IN EFI_BLOCK_IO_PROTOCOL *This, |
29 | | IN UINT32 MediaId, |
30 | | IN EFI_LBA Lba, |
31 | | IN UINTN BufferSize, |
32 | | OUT VOID *Buffer |
33 | | ); |
34 | | |
35 | | EFI_STATUS |
36 | | EFIAPI |
37 | | VirtioBlkWriteBlocks ( |
38 | | IN EFI_BLOCK_IO_PROTOCOL *This, |
39 | | IN UINT32 MediaId, |
40 | | IN EFI_LBA Lba, |
41 | | IN UINTN BufferSize, |
42 | | IN VOID *Buffer |
43 | | ); |
44 | | |
45 | | |
46 | | EFI_STATUS |
47 | | EFIAPI |
48 | | VirtioBlkFlushBlocks ( |
49 | | IN EFI_BLOCK_IO_PROTOCOL *This |
50 | | ); |
51 | | |
52 | | #define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \ |
53 | | (Dev)->VirtIo, \ |
54 | | OFFSET_OF_VBLK (Field), \ |
55 | | SIZE_OF_VBLK (Field), \ |
56 | | (Value) \ |
57 | | )) |
58 | | |
59 | 652 | #define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \ |
60 | 652 | (Dev)->VirtIo, \ |
61 | 652 | OFFSET_OF_VBLK (Field), \ |
62 | 652 | SIZE_OF_VBLK (Field), \ |
63 | 652 | sizeof *(Pointer), \ |
64 | 652 | (Pointer) \ |
65 | 652 | )) |
66 | | |
67 | | EFI_STATUS |
68 | | EFIAPI |
69 | | VirtioBlkInit ( |
70 | | IN OUT VBLK_DEV *Dev |
71 | | ) |
72 | 304 | { |
73 | 304 | UINT8 NextDevStat; |
74 | 304 | EFI_STATUS Status; |
75 | | |
76 | 304 | UINT64 Features; |
77 | 304 | UINT64 NumSectors; |
78 | 304 | UINT32 BlockSize; |
79 | 304 | UINT8 PhysicalBlockExp; |
80 | 304 | UINT8 AlignmentOffset; |
81 | 304 | UINT32 OptIoSize; |
82 | 304 | UINT16 QueueSize; |
83 | 304 | UINT64 RingBaseShift; |
84 | | |
85 | 304 | PhysicalBlockExp = 0; |
86 | 304 | AlignmentOffset = 0; |
87 | 304 | OptIoSize = 0; |
88 | | // |
89 | | // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. |
90 | | // |
91 | 304 | NextDevStat = 0; // step 1 -- reset device |
92 | 304 | Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); |
93 | 304 | if (EFI_ERROR (Status)) { |
94 | 0 | goto Failed; |
95 | 0 | } |
96 | | |
97 | 304 | NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence |
98 | 304 | Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); |
99 | 304 | if (EFI_ERROR (Status)) { |
100 | 0 | goto Failed; |
101 | 0 | } |
102 | | |
103 | 304 | NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it |
104 | 304 | Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); |
105 | 304 | if (EFI_ERROR (Status)) { |
106 | 0 | goto Failed; |
107 | 0 | } |
108 | | // printf ("Set page size\n"); |
109 | | // |
110 | | // Set Page Size - MMIO VirtIo Specific |
111 | | // |
112 | 304 | Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); |
113 | 304 | if (EFI_ERROR (Status)) { |
114 | 0 | goto Failed; |
115 | 0 | } |
116 | | // printf ("get device features\n"); |
117 | | // |
118 | | // step 4a -- retrieve and validate features |
119 | | // |
120 | 304 | Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); |
121 | 304 | if (EFI_ERROR (Status)) { |
122 | 0 | goto Failed; |
123 | 0 | } |
124 | | // printf ("read device features\n"); |
125 | 304 | Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors); |
126 | 304 | if (EFI_ERROR (Status)) { |
127 | 0 | goto Failed; |
128 | 0 | } |
129 | 304 | if (NumSectors == 0) { |
130 | 2 | Status = EFI_UNSUPPORTED; |
131 | 2 | goto Failed; |
132 | 2 | } |
133 | 302 | if (Features & VIRTIO_BLK_F_BLK_SIZE) { |
134 | 196 | Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize); |
135 | 196 | if (EFI_ERROR (Status)) { |
136 | 0 | goto Failed; |
137 | 0 | } |
138 | 196 | if (BlockSize == 0 || BlockSize % 512 != 0 || |
139 | 196 | ModU64x32 (NumSectors, BlockSize / 512) != 0) { |
140 | | // |
141 | | // We can only handle a logical block consisting of whole sectors, |
142 | | // and only a disk composed of whole logical blocks. |
143 | | // |
144 | 144 | Status = EFI_UNSUPPORTED; |
145 | 144 | goto Failed; |
146 | 144 | } |
147 | 196 | } |
148 | 106 | else { |
149 | 106 | BlockSize = 512; |
150 | 106 | } |
151 | 158 | if (Features & VIRTIO_BLK_F_TOPOLOGY) { |
152 | 62 | Status = VIRTIO_CFG_READ (Dev, Topology.PhysicalBlockExp, |
153 | 62 | &PhysicalBlockExp); |
154 | 62 | if (EFI_ERROR (Status)) { |
155 | 0 | goto Failed; |
156 | 0 | } |
157 | 62 | if (PhysicalBlockExp >= 32) { |
158 | 17 | Status = EFI_UNSUPPORTED; |
159 | 17 | goto Failed; |
160 | 17 | } |
161 | | |
162 | 45 | Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset); |
163 | 45 | if (EFI_ERROR (Status)) { |
164 | 0 | goto Failed; |
165 | 0 | } |
166 | | |
167 | 45 | Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize); |
168 | 45 | if (EFI_ERROR (Status)) { |
169 | 0 | goto Failed; |
170 | 0 | } |
171 | 45 | } |
172 | | |
173 | 141 | Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO | |
174 | 141 | VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1 | |
175 | 141 | VIRTIO_F_IOMMU_PLATFORM; |
176 | | |
177 | | // printf ("write features\n"); |
178 | | // |
179 | | // In virtio-1.0, feature negotiation is expected to complete before queue |
180 | | // discovery, and the device can also reject the selected set of features. |
181 | | // |
182 | 141 | if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { |
183 | 67 | Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); |
184 | 67 | if (EFI_ERROR (Status)) { |
185 | 0 | goto Failed; |
186 | 0 | } |
187 | 67 | } |
188 | | // |
189 | | // step 4b -- allocate virtqueue |
190 | | // |
191 | 141 | Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0); |
192 | 141 | if (EFI_ERROR (Status)) { |
193 | 0 | goto Failed; |
194 | 0 | } |
195 | 141 | Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize); |
196 | 141 | if (EFI_ERROR (Status)) { |
197 | 0 | goto Failed; |
198 | 0 | } |
199 | 141 | if (QueueSize < 3) { // SynchronousRequest() uses at most three descriptors |
200 | 9 | Status = EFI_UNSUPPORTED; |
201 | 9 | goto Failed; |
202 | 9 | } |
203 | | |
204 | 132 | Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring); |
205 | 132 | if (EFI_ERROR (Status)) { |
206 | 0 | goto Failed; |
207 | 0 | } |
208 | | // |
209 | | // If anything fails from here on, we must release the ring resources |
210 | | // |
211 | 132 | Status = VirtioRingMap ( |
212 | 132 | Dev->VirtIo, |
213 | 132 | &Dev->Ring, |
214 | 132 | &RingBaseShift, |
215 | 132 | &Dev->RingMap |
216 | 132 | ); |
217 | 132 | if (EFI_ERROR (Status)) { |
218 | 0 | goto ReleaseQueue; |
219 | 0 | } |
220 | | |
221 | | // |
222 | | // Additional steps for MMIO: align the queue appropriately, and set the |
223 | | // size. If anything fails from here on, we must unmap the ring resources. |
224 | | // |
225 | 132 | Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); |
226 | 132 | if (EFI_ERROR (Status)) { |
227 | 0 | goto UnmapQueue; |
228 | 0 | } |
229 | | |
230 | 132 | Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); |
231 | 132 | if (EFI_ERROR (Status)) { |
232 | 0 | goto UnmapQueue; |
233 | 0 | } |
234 | | // |
235 | | // step 4c -- Report GPFN (guest-physical frame number) of queue. |
236 | | // |
237 | 132 | Status = Dev->VirtIo->SetQueueAddress ( |
238 | 132 | Dev->VirtIo, |
239 | 132 | &Dev->Ring, |
240 | 132 | RingBaseShift |
241 | 132 | ); |
242 | 132 | if (EFI_ERROR (Status)) { |
243 | 0 | goto UnmapQueue; |
244 | 0 | } |
245 | | |
246 | | |
247 | | // |
248 | | // step 5 -- Report understood features. |
249 | | // |
250 | 132 | if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { |
251 | 68 | Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); |
252 | 68 | Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); |
253 | 68 | if (EFI_ERROR (Status)) { |
254 | 0 | goto UnmapQueue; |
255 | 0 | } |
256 | 68 | } |
257 | | |
258 | | // |
259 | | // step 6 -- initialization complete |
260 | | // |
261 | 132 | NextDevStat |= VSTAT_DRIVER_OK; |
262 | 132 | Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); |
263 | 132 | if (EFI_ERROR (Status)) { |
264 | 0 | goto UnmapQueue; |
265 | 0 | } |
266 | | |
267 | | |
268 | | |
269 | | // |
270 | | // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI |
271 | | // Block I/O Protocol. |
272 | | // |
273 | 132 | Dev->BlockIo.Revision = 0; |
274 | 132 | Dev->BlockIo.Media = &Dev->BlockIoMedia; |
275 | 132 | Dev->BlockIo.Reset = &VirtioBlkReset; |
276 | 132 | Dev->BlockIo.ReadBlocks = &VirtioBlkReadBlocks; |
277 | 132 | Dev->BlockIo.WriteBlocks = &VirtioBlkWriteBlocks; |
278 | 132 | Dev->BlockIo.FlushBlocks = &VirtioBlkFlushBlocks; |
279 | 132 | Dev->BlockIoMedia.MediaId = 0; |
280 | 132 | Dev->BlockIoMedia.RemovableMedia = FALSE; |
281 | 132 | Dev->BlockIoMedia.MediaPresent = TRUE; |
282 | 132 | Dev->BlockIoMedia.LogicalPartition = FALSE; |
283 | 132 | Dev->BlockIoMedia.ReadOnly = (BOOLEAN) ((Features & VIRTIO_BLK_F_RO) != 0); |
284 | 132 | Dev->BlockIoMedia.WriteCaching = (BOOLEAN) ((Features & VIRTIO_BLK_F_FLUSH) != 0); |
285 | 132 | Dev->BlockIoMedia.BlockSize = BlockSize; |
286 | 132 | Dev->BlockIoMedia.IoAlign = 0; |
287 | 132 | Dev->BlockIoMedia.LastBlock = DivU64x32 (NumSectors, |
288 | 132 | BlockSize / 512) - 1; |
289 | | |
290 | 132 | DEBUG ((DEBUG_INFO, "%a: LbaSize=0x%x[B] NumBlocks=0x%Lx[Lba]\n", |
291 | 132 | __FUNCTION__, Dev->BlockIoMedia.BlockSize, |
292 | 132 | Dev->BlockIoMedia.LastBlock + 1)); |
293 | | |
294 | 132 | if (Features & VIRTIO_BLK_F_TOPOLOGY) { |
295 | 43 | Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; |
296 | | |
297 | 43 | Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset; |
298 | 43 | Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp; |
299 | 43 | Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize; |
300 | | |
301 | 43 | DEBUG ((DEBUG_INFO, "%a: FirstAligned=0x%Lx[Lba] PhysBlkSize=0x%x[Lba]\n", |
302 | 43 | __FUNCTION__, Dev->BlockIoMedia.LowestAlignedLba, |
303 | 43 | Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock)); |
304 | 43 | DEBUG ((DEBUG_INFO, "%a: OptimalTransferLengthGranularity=0x%x[Lba]\n", |
305 | 43 | __FUNCTION__, Dev->BlockIoMedia.OptimalTransferLengthGranularity)); |
306 | 43 | } |
307 | 132 | return EFI_SUCCESS; |
308 | | |
309 | 0 | UnmapQueue: |
310 | 0 | Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); |
311 | |
|
312 | 0 | ReleaseQueue: |
313 | 0 | VirtioRingUninit (Dev->VirtIo, &Dev->Ring); |
314 | |
|
315 | 172 | Failed: |
316 | | // |
317 | | // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device |
318 | | // Status. VirtIo access failure here should not mask the original error. |
319 | | // |
320 | 172 | NextDevStat |= VSTAT_FAILED; |
321 | 172 | Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); |
322 | | |
323 | 172 | return Status; // reached only via Failed above |
324 | 0 | } |