/src/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | Utility functions used by virtio device drivers. |
4 | | |
5 | | Copyright (C) 2012-2016, Red Hat, Inc. |
6 | | Portion of Copyright (C) 2013, ARM Ltd. |
7 | | Copyright (C) 2017, AMD Inc, All rights reserved.<BR> |
8 | | |
9 | | SPDX-License-Identifier: BSD-2-Clause-Patent |
10 | | |
11 | | **/ |
12 | | |
13 | | #include <Library/BaseLib.h> |
14 | | #include <Library/BaseMemoryLib.h> |
15 | | #include <Library/DebugLib.h> |
16 | | #include <Library/UefiBootServicesTableLib.h> |
17 | | |
18 | | #include <Library/VirtioLib.h> |
19 | | |
20 | | /** |
21 | | |
22 | | Configure a virtio ring. |
23 | | |
24 | | This function sets up internal storage (the guest-host communication area) |
25 | | and lays out several "navigation" (ie. no-ownership) pointers to parts of |
26 | | that storage. |
27 | | |
28 | | Relevant sections from the virtio-0.9.5 spec: |
29 | | - 1.1 Virtqueues, |
30 | | - 2.3 Virtqueue Configuration. |
31 | | |
32 | | @param[in] VirtIo The virtio device which will use the ring. |
33 | | |
34 | | @param[in] The number of descriptors to allocate for the |
35 | | virtio ring, as requested by the host. |
36 | | |
37 | | @param[out] Ring The virtio ring to set up. |
38 | | |
39 | | @return Status codes propagated from |
40 | | VirtIo->AllocateSharedPages(). |
41 | | |
42 | | @retval EFI_SUCCESS Allocation and setup successful. Ring->Base |
43 | | (and nothing else) is responsible for |
44 | | deallocation. |
45 | | |
46 | | **/ |
47 | | EFI_STATUS |
48 | | EFIAPI |
49 | | VirtioRingInit ( |
50 | | IN VIRTIO_DEVICE_PROTOCOL *VirtIo, |
51 | | IN UINT16 QueueSize, |
52 | | OUT VRING *Ring |
53 | | ) |
54 | 144 | { |
55 | 144 | EFI_STATUS Status; |
56 | 144 | UINTN RingSize; |
57 | 144 | volatile UINT8 *RingPagesPtr; |
58 | | |
59 | 144 | RingSize = ALIGN_VALUE ( |
60 | 144 | sizeof *Ring->Desc * QueueSize + |
61 | 144 | sizeof *Ring->Avail.Flags + |
62 | 144 | sizeof *Ring->Avail.Idx + |
63 | 144 | sizeof *Ring->Avail.Ring * QueueSize + |
64 | 144 | sizeof *Ring->Avail.UsedEvent, |
65 | 144 | EFI_PAGE_SIZE |
66 | 144 | ); |
67 | | |
68 | 144 | RingSize += ALIGN_VALUE ( |
69 | 144 | sizeof *Ring->Used.Flags + |
70 | 144 | sizeof *Ring->Used.Idx + |
71 | 144 | sizeof *Ring->Used.UsedElem * QueueSize + |
72 | 144 | sizeof *Ring->Used.AvailEvent, |
73 | 144 | EFI_PAGE_SIZE |
74 | 144 | ); |
75 | | |
76 | | // |
77 | | // Allocate a shared ring buffer |
78 | | // |
79 | 144 | Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize); |
80 | 144 | Status = VirtIo->AllocateSharedPages ( |
81 | 144 | VirtIo, |
82 | 144 | Ring->NumPages, |
83 | 144 | &Ring->Base |
84 | 144 | ); |
85 | 144 | if (EFI_ERROR (Status)) { |
86 | 0 | return Status; |
87 | 0 | } |
88 | | |
89 | 144 | SetMem (Ring->Base, RingSize, 0x00); |
90 | 144 | RingPagesPtr = Ring->Base; |
91 | | |
92 | 144 | Ring->Desc = (volatile VOID *)RingPagesPtr; |
93 | 144 | RingPagesPtr += sizeof *Ring->Desc * QueueSize; |
94 | | |
95 | 144 | Ring->Avail.Flags = (volatile VOID *)RingPagesPtr; |
96 | 144 | RingPagesPtr += sizeof *Ring->Avail.Flags; |
97 | | |
98 | 144 | Ring->Avail.Idx = (volatile VOID *)RingPagesPtr; |
99 | 144 | RingPagesPtr += sizeof *Ring->Avail.Idx; |
100 | | |
101 | 144 | Ring->Avail.Ring = (volatile VOID *)RingPagesPtr; |
102 | 144 | RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize; |
103 | | |
104 | 144 | Ring->Avail.UsedEvent = (volatile VOID *)RingPagesPtr; |
105 | 144 | RingPagesPtr += sizeof *Ring->Avail.UsedEvent; |
106 | | |
107 | 144 | RingPagesPtr = (volatile UINT8 *)Ring->Base + |
108 | 144 | ALIGN_VALUE ( |
109 | 144 | RingPagesPtr - (volatile UINT8 *)Ring->Base, |
110 | 144 | EFI_PAGE_SIZE |
111 | 144 | ); |
112 | | |
113 | 144 | Ring->Used.Flags = (volatile VOID *)RingPagesPtr; |
114 | 144 | RingPagesPtr += sizeof *Ring->Used.Flags; |
115 | | |
116 | 144 | Ring->Used.Idx = (volatile VOID *)RingPagesPtr; |
117 | 144 | RingPagesPtr += sizeof *Ring->Used.Idx; |
118 | | |
119 | 144 | Ring->Used.UsedElem = (volatile VOID *)RingPagesPtr; |
120 | 144 | RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize; |
121 | | |
122 | 144 | Ring->Used.AvailEvent = (volatile VOID *)RingPagesPtr; |
123 | 144 | RingPagesPtr += sizeof *Ring->Used.AvailEvent; |
124 | | |
125 | 144 | Ring->QueueSize = QueueSize; |
126 | 144 | return EFI_SUCCESS; |
127 | 144 | } |
128 | | |
129 | | /** |
130 | | |
131 | | Tear down the internal resources of a configured virtio ring. |
132 | | |
133 | | The caller is responsible to stop the host from using this ring before |
134 | | invoking this function: the VSTAT_DRIVER_OK bit must be clear in |
135 | | VhdrDeviceStatus. |
136 | | |
137 | | @param[in] VirtIo The virtio device which was using the ring. |
138 | | |
139 | | @param[out] Ring The virtio ring to clean up. |
140 | | |
141 | | **/ |
142 | | VOID |
143 | | EFIAPI |
144 | | VirtioRingUninit ( |
145 | | IN VIRTIO_DEVICE_PROTOCOL *VirtIo, |
146 | | IN OUT VRING *Ring |
147 | | ) |
148 | 0 | { |
149 | 0 | VirtIo->FreeSharedPages (VirtIo, Ring->NumPages, Ring->Base); |
150 | 0 | SetMem (Ring, sizeof *Ring, 0x00); |
151 | 0 | } |
152 | | |
153 | | /** |
154 | | |
155 | | Turn off interrupt notifications from the host, and prepare for appending |
156 | | multiple descriptors to the virtio ring. |
157 | | |
158 | | The calling driver must be in VSTAT_DRIVER_OK state. |
159 | | |
160 | | @param[in,out] Ring The virtio ring we intend to append descriptors to. |
161 | | |
162 | | @param[out] Indices The DESC_INDICES structure to initialize. |
163 | | |
164 | | **/ |
165 | | VOID |
166 | | EFIAPI |
167 | | VirtioPrepare ( |
168 | | IN OUT VRING *Ring, |
169 | | OUT DESC_INDICES *Indices |
170 | | ) |
171 | 0 | { |
172 | | // |
173 | | // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device. |
174 | | // We're going to poll the answer, the host should not send an interrupt. |
175 | | // |
176 | 0 | *Ring->Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT; |
177 | | |
178 | | // |
179 | | // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device. |
180 | | // |
181 | | // Since we support only one in-flight descriptor chain, we can always build |
182 | | // that chain starting at entry #0 of the descriptor table. |
183 | | // |
184 | 0 | Indices->HeadDescIdx = 0; |
185 | 0 | Indices->NextDescIdx = Indices->HeadDescIdx; |
186 | 0 | } |
187 | | |
188 | | /** |
189 | | |
190 | | Append a contiguous buffer for transmission / reception via the virtio ring. |
191 | | |
192 | | This function implements the following section from virtio-0.9.5: |
193 | | - 2.4.1.1 Placing Buffers into the Descriptor Table |
194 | | |
195 | | Free space is taken as granted, since the individual drivers support only |
196 | | synchronous requests and host side status is processed in lock-step with |
197 | | request submission. It is the calling driver's responsibility to verify the |
198 | | ring size in advance. |
199 | | |
200 | | The caller is responsible for initializing *Indices with VirtioPrepare() |
201 | | first. |
202 | | |
203 | | @param[in,out] Ring The virtio ring to append the buffer to, |
204 | | as a descriptor. |
205 | | |
206 | | @param[in] BufferDeviceAddress (Bus master device) start address of the |
207 | | transmit / receive buffer. |
208 | | |
209 | | @param[in] BufferSize Number of bytes to transmit or receive. |
210 | | |
211 | | @param[in] Flags A bitmask of VRING_DESC_F_* flags. The |
212 | | caller computes this mask dependent on |
213 | | further buffers to append and transfer |
214 | | direction. VRING_DESC_F_INDIRECT is |
215 | | unsupported. The VRING_DESC.Next field is |
216 | | always set, but the host only interprets |
217 | | it dependent on VRING_DESC_F_NEXT. |
218 | | |
219 | | @param[in,out] Indices Indices->HeadDescIdx is not accessed. |
220 | | On input, Indices->NextDescIdx identifies |
221 | | the next descriptor to carry the buffer. |
222 | | On output, Indices->NextDescIdx is |
223 | | incremented by one, modulo 2^16. |
224 | | |
225 | | **/ |
226 | | VOID |
227 | | EFIAPI |
228 | | VirtioAppendDesc ( |
229 | | IN OUT VRING *Ring, |
230 | | IN UINT64 BufferDeviceAddress, |
231 | | IN UINT32 BufferSize, |
232 | | IN UINT16 Flags, |
233 | | IN OUT DESC_INDICES *Indices |
234 | | ) |
235 | 0 | { |
236 | 0 | volatile VRING_DESC *Desc; |
237 | |
|
238 | 0 | Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize]; |
239 | 0 | Desc->Addr = BufferDeviceAddress; |
240 | 0 | Desc->Len = BufferSize; |
241 | 0 | Desc->Flags = Flags; |
242 | 0 | Desc->Next = Indices->NextDescIdx % Ring->QueueSize; |
243 | 0 | } |
244 | | |
245 | | /** |
246 | | |
247 | | Notify the host about the descriptor chain just built, and wait until the |
248 | | host processes it. |
249 | | |
250 | | @param[in] VirtIo The target virtio device to notify. |
251 | | |
252 | | @param[in] VirtQueueId Identifies the queue for the target device. |
253 | | |
254 | | @param[in,out] Ring The virtio ring with descriptors to submit. |
255 | | |
256 | | @param[in] Indices Indices->NextDescIdx is not accessed. |
257 | | Indices->HeadDescIdx identifies the head descriptor |
258 | | of the descriptor chain. |
259 | | |
260 | | @param[out] UsedLen On success, the total number of bytes, consecutively |
261 | | across the buffers linked by the descriptor chain, |
262 | | that the host wrote. May be NULL if the caller |
263 | | doesn't care, or can compute the same information |
264 | | from device-specific request structures linked by the |
265 | | descriptor chain. |
266 | | |
267 | | @return Error code from VirtIo->SetQueueNotify() if it fails. |
268 | | |
269 | | @retval EFI_SUCCESS Otherwise, the host processed all descriptors. |
270 | | |
271 | | **/ |
272 | | EFI_STATUS |
273 | | EFIAPI |
274 | | VirtioFlush ( |
275 | | IN VIRTIO_DEVICE_PROTOCOL *VirtIo, |
276 | | IN UINT16 VirtQueueId, |
277 | | IN OUT VRING *Ring, |
278 | | IN DESC_INDICES *Indices, |
279 | | OUT UINT32 *UsedLen OPTIONAL |
280 | | ) |
281 | 0 | { |
282 | 0 | UINT16 NextAvailIdx; |
283 | 0 | UINT16 LastUsedIdx; |
284 | 0 | EFI_STATUS Status; |
285 | 0 | UINTN PollPeriodUsecs; |
286 | | |
287 | | // |
288 | | // virtio-0.9.5, 2.4.1.2 Updating the Available Ring |
289 | | // |
290 | | // It is not exactly clear from the wording of the virtio-0.9.5 |
291 | | // specification, but each entry in the Available Ring references only the |
292 | | // head descriptor of any given descriptor chain. |
293 | | // |
294 | 0 | NextAvailIdx = *Ring->Avail.Idx; |
295 | | // |
296 | | // (Due to our lock-step progress, this is where the host will produce the |
297 | | // used element with the head descriptor's index in it.) |
298 | | // |
299 | 0 | LastUsedIdx = NextAvailIdx; |
300 | 0 | Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] = |
301 | 0 | Indices->HeadDescIdx % Ring->QueueSize; |
302 | | |
303 | | // |
304 | | // virtio-0.9.5, 2.4.1.3 Updating the Index Field |
305 | | // |
306 | 0 | MemoryFence (); |
307 | 0 | *Ring->Avail.Idx = NextAvailIdx; |
308 | | |
309 | | // |
310 | | // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are |
311 | | // OK. |
312 | | // |
313 | 0 | MemoryFence (); |
314 | 0 | Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId); |
315 | 0 | if (EFI_ERROR (Status)) { |
316 | 0 | return Status; |
317 | 0 | } |
318 | | |
319 | | // |
320 | | // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device |
321 | | // Wait until the host processes and acknowledges our descriptor chain. The |
322 | | // condition we use for polling is greatly simplified and relies on the |
323 | | // synchronous, lock-step progress. |
324 | | // |
325 | | // Keep slowing down until we reach a poll period of slightly above 1 ms. |
326 | | // |
327 | 0 | PollPeriodUsecs = 1; |
328 | 0 | MemoryFence (); |
329 | 0 | while (*Ring->Used.Idx != NextAvailIdx) { |
330 | 0 | gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay |
331 | |
|
332 | 0 | if (PollPeriodUsecs < 1024) { |
333 | 0 | PollPeriodUsecs *= 2; |
334 | 0 | } |
335 | |
|
336 | 0 | MemoryFence (); |
337 | 0 | } |
338 | |
|
339 | 0 | MemoryFence (); |
340 | |
|
341 | 0 | if (UsedLen != NULL) { |
342 | 0 | volatile CONST VRING_USED_ELEM *UsedElem; |
343 | |
|
344 | 0 | UsedElem = &Ring->Used.UsedElem[LastUsedIdx % Ring->QueueSize]; |
345 | 0 | ASSERT (UsedElem->Id == Indices->HeadDescIdx); |
346 | 0 | *UsedLen = UsedElem->Len; |
347 | 0 | } |
348 | |
|
349 | 0 | return EFI_SUCCESS; |
350 | 0 | } |
351 | | |
352 | | /** |
353 | | |
354 | | Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver |
355 | | understands. |
356 | | |
357 | | In VirtIo 1.0, a device can reject a self-inconsistent feature bitmap through |
358 | | the new VSTAT_FEATURES_OK status bit. (For example if the driver requests a |
359 | | higher level feature but clears a prerequisite feature.) This function is a |
360 | | small wrapper around VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures() that also |
361 | | verifies if the VirtIo 1.0 device accepts the feature bitmap. |
362 | | |
363 | | @param[in] VirtIo Report feature bits to this device. |
364 | | |
365 | | @param[in] Features The set of feature bits that the driver wishes |
366 | | to report. The caller is responsible to perform |
367 | | any masking before calling this function; the |
368 | | value is directly written with |
369 | | VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures(). |
370 | | |
371 | | @param[in,out] DeviceStatus On input, the status byte most recently written |
372 | | to the device's status register. On output (even |
373 | | on error), DeviceStatus will be updated so that |
374 | | it is suitable for further status bit |
375 | | manipulation and writing to the device's status |
376 | | register. |
377 | | |
378 | | @retval EFI_SUCCESS The device accepted the configuration in Features. |
379 | | |
380 | | @return EFI_UNSUPPORTED The device rejected the configuration in Features. |
381 | | |
382 | | @retval EFI_UNSUPPORTED VirtIo->Revision is smaller than 1.0.0. |
383 | | |
384 | | @return Error codes from the SetGuestFeatures(), |
385 | | SetDeviceStatus(), GetDeviceStatus() member |
386 | | functions. |
387 | | |
388 | | **/ |
389 | | EFI_STATUS |
390 | | EFIAPI |
391 | | Virtio10WriteFeatures ( |
392 | | IN VIRTIO_DEVICE_PROTOCOL *VirtIo, |
393 | | IN UINT64 Features, |
394 | | IN OUT UINT8 *DeviceStatus |
395 | | ) |
396 | 75 | { |
397 | 75 | EFI_STATUS Status; |
398 | | |
399 | 75 | if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { |
400 | 0 | return EFI_UNSUPPORTED; |
401 | 0 | } |
402 | | |
403 | 75 | Status = VirtIo->SetGuestFeatures (VirtIo, Features); |
404 | 75 | if (EFI_ERROR (Status)) { |
405 | 0 | return Status; |
406 | 0 | } |
407 | | |
408 | 75 | *DeviceStatus |= VSTAT_FEATURES_OK; |
409 | 75 | Status = VirtIo->SetDeviceStatus (VirtIo, *DeviceStatus); |
410 | 75 | if (EFI_ERROR (Status)) { |
411 | 0 | return Status; |
412 | 0 | } |
413 | | |
414 | 75 | Status = VirtIo->GetDeviceStatus (VirtIo, DeviceStatus); |
415 | 75 | if (EFI_ERROR (Status)) { |
416 | 0 | return Status; |
417 | 0 | } |
418 | | |
419 | 75 | if ((*DeviceStatus & VSTAT_FEATURES_OK) == 0) { |
420 | 0 | Status = EFI_UNSUPPORTED; |
421 | 0 | } |
422 | | |
423 | 75 | return Status; |
424 | 75 | } |
425 | | |
426 | | /** |
427 | | Provides the virtio device address required to access system memory from a |
428 | | DMA bus master. |
429 | | |
430 | | The interface follows the same usage pattern as defined in UEFI spec 2.6 |
431 | | (Section 13.2 PCI Root Bridge I/O Protocol) |
432 | | |
433 | | The VirtioMapAllBytesInSharedBuffer() is similar to VIRTIO_MAP_SHARED |
434 | | with exception that NumberOfBytes is IN-only parameter. The function |
435 | | maps all the bytes specified in NumberOfBytes param in one consecutive |
436 | | range. |
437 | | |
438 | | @param[in] VirtIo The virtio device for which the mapping is |
439 | | requested. |
440 | | |
441 | | @param[in] Operation Indicates if the bus master is going to |
442 | | read or write to system memory. |
443 | | |
444 | | @param[in] HostAddress The system memory address to map to shared |
445 | | buffer address. |
446 | | |
447 | | @param[in] NumberOfBytes Number of bytes to map. |
448 | | |
449 | | @param[out] DeviceAddress The resulting shared map address for the |
450 | | bus master to access the hosts HostAddress. |
451 | | |
452 | | @param[out] Mapping A resulting token to pass to |
453 | | VIRTIO_UNMAP_SHARED. |
454 | | |
455 | | |
456 | | @retval EFI_SUCCESS The NumberOfBytes is successfully mapped. |
457 | | @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a |
458 | | common buffer. |
459 | | @retval EFI_INVALID_PARAMETER One or more parameters are invalid. |
460 | | @retval EFI_OUT_OF_RESOURCES The request could not be completed due to |
461 | | a lack of resources. This includes the case |
462 | | when NumberOfBytes bytes cannot be mapped |
463 | | in one consecutive range. |
464 | | @retval EFI_DEVICE_ERROR The system hardware could not map the |
465 | | requested address. |
466 | | **/ |
467 | | EFI_STATUS |
468 | | EFIAPI |
469 | | VirtioMapAllBytesInSharedBuffer ( |
470 | | IN VIRTIO_DEVICE_PROTOCOL *VirtIo, |
471 | | IN VIRTIO_MAP_OPERATION Operation, |
472 | | IN VOID *HostAddress, |
473 | | IN UINTN NumberOfBytes, |
474 | | OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, |
475 | | OUT VOID **Mapping |
476 | | ) |
477 | 144 | { |
478 | 144 | EFI_STATUS Status; |
479 | 144 | VOID *MapInfo; |
480 | 144 | UINTN Size; |
481 | 144 | EFI_PHYSICAL_ADDRESS PhysicalAddress; |
482 | | |
483 | 144 | Size = NumberOfBytes; |
484 | 144 | Status = VirtIo->MapSharedBuffer ( |
485 | 144 | VirtIo, |
486 | 144 | Operation, |
487 | 144 | HostAddress, |
488 | 144 | &Size, |
489 | 144 | &PhysicalAddress, |
490 | 144 | &MapInfo |
491 | 144 | ); |
492 | 144 | if (EFI_ERROR (Status)) { |
493 | 0 | return Status; |
494 | 0 | } |
495 | | |
496 | 144 | if (Size < NumberOfBytes) { |
497 | 0 | goto Failed; |
498 | 0 | } |
499 | | |
500 | 144 | *Mapping = MapInfo; |
501 | 144 | *DeviceAddress = PhysicalAddress; |
502 | | |
503 | 144 | return EFI_SUCCESS; |
504 | | |
505 | 0 | Failed: |
506 | 0 | VirtIo->UnmapSharedBuffer (VirtIo, MapInfo); |
507 | 0 | return EFI_OUT_OF_RESOURCES; |
508 | 144 | } |
509 | | |
510 | | /** |
511 | | |
512 | | Map the ring buffer so that it can be accessed equally by both guest |
513 | | and hypervisor. |
514 | | |
515 | | @param[in] VirtIo The virtio device instance. |
516 | | |
517 | | @param[in] Ring The virtio ring to map. |
518 | | |
519 | | @param[out] RingBaseShift A resulting translation offset, to be |
520 | | passed to VirtIo->SetQueueAddress(). |
521 | | |
522 | | @param[out] Mapping A resulting token to pass to |
523 | | VirtIo->UnmapSharedBuffer(). |
524 | | |
525 | | @return Status code from VirtIo->MapSharedBuffer() |
526 | | **/ |
527 | | EFI_STATUS |
528 | | EFIAPI |
529 | | VirtioRingMap ( |
530 | | IN VIRTIO_DEVICE_PROTOCOL *VirtIo, |
531 | | IN VRING *Ring, |
532 | | OUT UINT64 *RingBaseShift, |
533 | | OUT VOID **Mapping |
534 | | ) |
535 | 144 | { |
536 | 144 | EFI_STATUS Status; |
537 | 144 | EFI_PHYSICAL_ADDRESS DeviceAddress; |
538 | | |
539 | 144 | Status = VirtioMapAllBytesInSharedBuffer ( |
540 | 144 | VirtIo, |
541 | 144 | VirtioOperationBusMasterCommonBuffer, |
542 | 144 | Ring->Base, |
543 | 144 | EFI_PAGES_TO_SIZE (Ring->NumPages), |
544 | 144 | &DeviceAddress, |
545 | 144 | Mapping |
546 | 144 | ); |
547 | 144 | if (EFI_ERROR (Status)) { |
548 | 0 | return Status; |
549 | 0 | } |
550 | | |
551 | 144 | *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base; |
552 | 144 | return EFI_SUCCESS; |
553 | 144 | } |