2 Minimal block driver for Mini-OS.
4 Copyright (c) 2007-2008 Samuel Thibault.
5 Copyright (C) 2014, Citrix Ltd.
6 Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/PrintLib.h>
12 #include <Library/DebugLib.h>
14 #include "BlockFront.h"
16 #include <IndustryStandard/Xen/io/protocols.h>
17 #include <IndustryStandard/Xen/io/xenbus.h>
20 Helper to read an integer from XenStore.
22 If the number overflows according to the range defined by UINT64,
25 @param This A pointer to a XENBUS_PROTOCOL instance.
26 @param Node The XenStore node to read from.
27 @param FromBackend Read frontend or backend value.
28 @param ValuePtr Where to put the value.
30 @retval XENSTORE_STATUS_SUCCESS If successful, will update ValuePtr.
31 @return Any other return value indicate the error,
32 ValuePtr is not updated in this case.
37 IN XENBUS_PROTOCOL
*This
,
39 IN BOOLEAN FromBackend
,
43 XENSTORE_STATUS Status
;
47 Status
= This
->XsRead (This
, XST_NIL
, Node
, (VOID
**)&Ptr
);
49 Status
= This
->XsBackendRead (This
, XST_NIL
, Node
, (VOID
**)&Ptr
);
52 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
56 // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64.
57 *ValuePtr
= AsciiStrDecimalToUint64 (Ptr
);
63 Free an instance of XEN_BLOCK_FRONT_DEVICE.
65 @param Dev The instance to free.
70 IN XEN_BLOCK_FRONT_DEVICE
*Dev
73 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
75 if (Dev
->RingRef
!= 0) {
76 XenBusIo
->GrantEndAccess (XenBusIo
, Dev
->RingRef
);
79 if (Dev
->Ring
.sring
!= NULL
) {
80 FreePages (Dev
->Ring
.sring
, 1);
83 if (Dev
->EventChannel
!= 0) {
84 XenBusIo
->EventChannelClose (XenBusIo
, Dev
->EventChannel
);
91 Wait until the backend has reached the ExpectedState.
93 @param Dev A XEN_BLOCK_FRONT_DEVICE instance.
94 @param ExpectedState The backend state expected.
95 @param LastStatePtr An optional pointer where to right the final state.
97 @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState
98 or return an error otherwise.
102 XenPvBlkWaitForBackendState (
103 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
104 IN XenbusState ExpectedState
,
105 OUT XenbusState
*LastStatePtr OPTIONAL
108 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
111 XENSTORE_STATUS Status
= XENSTORE_STATUS_SUCCESS
;
114 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
115 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
119 if (Value
> XenbusStateReconfigured
) {
121 // Value is not a State value.
123 return XENSTORE_STATUS_EIO
;
127 if (State
== ExpectedState
) {
129 } else if (State
> ExpectedState
) {
130 Status
= XENSTORE_STATUS_FAIL
;
136 "XenPvBlk: waiting backend state %d, current: %d\n",
140 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
143 if (LastStatePtr
!= NULL
) {
144 *LastStatePtr
= State
;
151 XenPvBlockFrontInitialization (
152 IN XENBUS_PROTOCOL
*XenBusIo
,
153 IN CONST CHAR8
*NodeName
,
154 OUT XEN_BLOCK_FRONT_DEVICE
**DevPtr
157 XENSTORE_TRANSACTION Transaction
;
159 blkif_sring_t
*SharedRing
;
160 XENSTORE_STATUS Status
;
161 XEN_BLOCK_FRONT_DEVICE
*Dev
;
166 ASSERT (NodeName
!= NULL
);
168 Dev
= AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE
));
170 return EFI_OUT_OF_RESOURCES
;
173 Dev
->Signature
= XEN_BLOCK_FRONT_SIGNATURE
;
174 Dev
->NodeName
= NodeName
;
175 Dev
->XenBusIo
= XenBusIo
;
176 Dev
->DeviceId
= XenBusIo
->DeviceId
;
178 XenBusIo
->XsRead (XenBusIo
, XST_NIL
, "device-type", (VOID
**)&DeviceType
);
179 if (AsciiStrCmp (DeviceType
, "cdrom") == 0) {
180 Dev
->MediaInfo
.CdRom
= TRUE
;
182 Dev
->MediaInfo
.CdRom
= FALSE
;
185 FreePool (DeviceType
);
187 if (Dev
->MediaInfo
.CdRom
) {
188 Status
= XenBusIo
->XsBackendRead (XenBusIo
, XST_NIL
, "params", (VOID
**)&Params
);
189 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
190 DEBUG ((DEBUG_ERROR
, "%a: Failed to read params (%d)\n", __FUNCTION__
, Status
));
194 if ((AsciiStrLen (Params
) == 0) || (AsciiStrCmp (Params
, "aio:") == 0)) {
196 DEBUG ((DEBUG_INFO
, "%a: Empty cdrom\n", __FUNCTION__
));
203 Status
= XenBusReadUint64 (XenBusIo
, "backend-id", FALSE
, &Value
);
204 if ((Status
!= XENSTORE_STATUS_SUCCESS
) || (Value
> MAX_UINT16
)) {
207 "XenPvBlk: Failed to get backend-id (%d)\n",
213 Dev
->DomainId
= (domid_t
)Value
;
214 XenBusIo
->EventChannelAllocate (XenBusIo
, Dev
->DomainId
, &Dev
->EventChannel
);
216 SharedRing
= (blkif_sring_t
*)AllocatePages (1);
217 SHARED_RING_INIT (SharedRing
);
218 FRONT_RING_INIT (&Dev
->Ring
, SharedRing
, EFI_PAGE_SIZE
);
219 XenBusIo
->GrantAccess (
222 (INTN
)SharedRing
>> EFI_PAGE_SHIFT
,
228 Status
= XenBusIo
->XsTransactionStart (XenBusIo
, &Transaction
);
229 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
230 DEBUG ((DEBUG_WARN
, "XenPvBlk: Failed to start transaction, %d\n", Status
));
234 Status
= XenBusIo
->XsPrintf (
242 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
243 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to write ring-ref.\n"));
244 goto AbortTransaction
;
247 Status
= XenBusIo
->XsPrintf (
255 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
256 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to write event-channel.\n"));
257 goto AbortTransaction
;
260 Status
= XenBusIo
->XsPrintf (
266 XEN_IO_PROTO_ABI_NATIVE
268 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
269 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to write protocol.\n"));
270 goto AbortTransaction
;
273 Status
= XenBusIo
->SetState (XenBusIo
, &Transaction
, XenbusStateConnected
);
274 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
275 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to switch state.\n"));
276 goto AbortTransaction
;
279 Status
= XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, FALSE
);
280 if (Status
== XENSTORE_STATUS_EAGAIN
) {
284 XenBusIo
->RegisterWatchBackend (XenBusIo
, "state", &Dev
->StateWatchToken
);
287 // Waiting for backend
289 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateConnected
, &State
);
290 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
293 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",
302 Status
= XenBusReadUint64 (XenBusIo
, "info", TRUE
, &Value
);
303 if ((Status
!= XENSTORE_STATUS_SUCCESS
) || (Value
> MAX_UINT32
)) {
307 Dev
->MediaInfo
.VDiskInfo
= (UINT32
)Value
;
308 if (Dev
->MediaInfo
.VDiskInfo
& VDISK_READONLY
) {
309 Dev
->MediaInfo
.ReadWrite
= FALSE
;
311 Dev
->MediaInfo
.ReadWrite
= TRUE
;
314 Status
= XenBusReadUint64 (XenBusIo
, "sectors", TRUE
, &Dev
->MediaInfo
.Sectors
);
315 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
319 Status
= XenBusReadUint64 (XenBusIo
, "sector-size", TRUE
, &Value
);
320 if ((Status
!= XENSTORE_STATUS_SUCCESS
) || (Value
> MAX_UINT32
)) {
324 if ((UINT32
)Value
% 512 != 0) {
326 // This is not supported by the driver.
330 "XenPvBlk: Unsupported sector-size value %Lu, "
331 "it must be a multiple of 512\n",
337 Dev
->MediaInfo
.SectorSize
= (UINT32
)Value
;
341 XenBusReadUint64 (XenBusIo
, "feature-barrier", TRUE
, &Value
);
343 Dev
->MediaInfo
.FeatureBarrier
= TRUE
;
345 Dev
->MediaInfo
.FeatureBarrier
= FALSE
;
350 XenBusReadUint64 (XenBusIo
, "feature-flush-cache", TRUE
, &Value
);
352 Dev
->MediaInfo
.FeatureFlushCache
= TRUE
;
354 Dev
->MediaInfo
.FeatureFlushCache
= FALSE
;
359 "XenPvBlk: New disk with %ld sectors of %d bytes\n",
360 Dev
->MediaInfo
.Sectors
,
361 Dev
->MediaInfo
.SectorSize
368 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
369 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
370 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
371 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
374 XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, TRUE
);
376 XenPvBlockFree (Dev
);
377 return EFI_DEVICE_ERROR
;
381 XenPvBlockFrontShutdown (
382 IN XEN_BLOCK_FRONT_DEVICE
*Dev
385 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
386 XENSTORE_STATUS Status
;
389 XenPvBlockSync (Dev
);
391 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosing
);
392 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
395 "XenPvBlk: error while changing state to Closing: %d\n",
401 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosing
, NULL
);
402 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
405 "XenPvBlk: error while waiting for closing backend state: %d\n",
411 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosed
);
412 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
415 "XenPvBlk: error while changing state to Closed: %d\n",
421 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosed
, NULL
);
422 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
425 "XenPvBlk: error while waiting for closed backend state: %d\n",
431 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateInitialising
);
432 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
435 "XenPvBlk: error while changing state to initialising: %d\n",
442 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
443 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
446 "XenPvBlk: error while waiting for new backend state: %d\n",
452 if ((Value
<= XenbusStateInitWait
) || (Value
>= XenbusStateClosed
)) {
458 "XenPvBlk: waiting backend state %d, current: %Lu\n",
462 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
466 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
467 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
468 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
469 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
471 XenPvBlockFree (Dev
);
477 IN XEN_BLOCK_FRONT_DEVICE
*Dev
480 /* Wait for a slot */
481 if (RING_FULL (&Dev
->Ring
)) {
483 XenPvBlockAsyncIoPoll (Dev
);
484 if (!RING_FULL (&Dev
->Ring
)) {
488 /* Really no slot, could wait for an event on Dev->EventChannel. */
495 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
499 XEN_BLOCK_FRONT_DEVICE
*Dev
= IoData
->Dev
;
500 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
501 blkif_request_t
*Request
;
504 INT32 NumSegments
, Index
;
507 // Can't io at non-sector-aligned location
508 ASSERT (!(IoData
->Sector
& ((Dev
->MediaInfo
.SectorSize
/ 512) - 1)));
509 // Can't io non-sector-sized amounts
510 ASSERT (!(IoData
->Size
& (Dev
->MediaInfo
.SectorSize
- 1)));
511 // Can't io non-sector-aligned buffer
512 ASSERT (!((UINTN
)IoData
->Buffer
& (Dev
->MediaInfo
.SectorSize
- 1)));
514 Start
= (UINTN
)IoData
->Buffer
& ~EFI_PAGE_MASK
;
515 End
= ((UINTN
)IoData
->Buffer
+ IoData
->Size
+ EFI_PAGE_SIZE
- 1) & ~EFI_PAGE_MASK
;
516 IoData
->NumRef
= NumSegments
= (INT32
)((End
- Start
) / EFI_PAGE_SIZE
);
518 ASSERT (NumSegments
<= BLKIF_MAX_SEGMENTS_PER_REQUEST
);
520 XenPvBlockWaitSlot (Dev
);
521 RingIndex
= Dev
->Ring
.req_prod_pvt
;
522 Request
= RING_GET_REQUEST (&Dev
->Ring
, RingIndex
);
524 Request
->operation
= IsWrite
? BLKIF_OP_WRITE
: BLKIF_OP_READ
;
525 Request
->nr_segments
= (UINT8
)NumSegments
;
526 Request
->handle
= Dev
->DeviceId
;
527 Request
->id
= (UINTN
)IoData
;
528 Request
->sector_number
= IoData
->Sector
;
530 for (Index
= 0; Index
< NumSegments
; Index
++) {
531 Request
->seg
[Index
].first_sect
= 0;
532 Request
->seg
[Index
].last_sect
= EFI_PAGE_SIZE
/ 512 - 1;
535 Request
->seg
[0].first_sect
= (UINT8
)(((UINTN
)IoData
->Buffer
& EFI_PAGE_MASK
) / 512);
536 Request
->seg
[NumSegments
- 1].last_sect
=
537 (UINT8
)((((UINTN
)IoData
->Buffer
+ IoData
->Size
- 1) & EFI_PAGE_MASK
) / 512);
538 for (Index
= 0; Index
< NumSegments
; Index
++) {
539 UINTN Data
= Start
+ Index
* EFI_PAGE_SIZE
;
540 XenBusIo
->GrantAccess (
543 Data
>> EFI_PAGE_SHIFT
,
545 &Request
->seg
[Index
].gref
547 IoData
->GrantRef
[Index
] = Request
->seg
[Index
].gref
;
550 Dev
->Ring
.req_prod_pvt
= RingIndex
+ 1;
553 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
557 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
558 if (ReturnCode
!= 0) {
561 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
570 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
575 // Status value that correspond to an IO in progress.
577 IoData
->Status
= EFI_ALREADY_STARTED
;
578 XenPvBlockAsyncIo (IoData
, IsWrite
);
580 while (IoData
->Status
== EFI_ALREADY_STARTED
) {
581 XenPvBlockAsyncIoPoll (IoData
->Dev
);
584 return IoData
->Status
;
589 XenPvBlockPushOperation (
590 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
596 blkif_request_t
*Request
;
599 XenPvBlockWaitSlot (Dev
);
600 Index
= Dev
->Ring
.req_prod_pvt
;
601 Request
= RING_GET_REQUEST (&Dev
->Ring
, Index
);
602 Request
->operation
= Operation
;
603 Request
->nr_segments
= 0;
604 Request
->handle
= Dev
->DeviceId
;
606 /* Not needed anyway, but the backend will check it */
607 Request
->sector_number
= 0;
608 Dev
->Ring
.req_prod_pvt
= Index
+ 1;
610 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
612 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
614 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
615 if (ReturnCode
!= 0) {
618 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
627 IN XEN_BLOCK_FRONT_DEVICE
*Dev
630 if (Dev
->MediaInfo
.ReadWrite
) {
631 if (Dev
->MediaInfo
.FeatureBarrier
) {
632 XenPvBlockPushOperation (Dev
, BLKIF_OP_WRITE_BARRIER
, 0);
635 if (Dev
->MediaInfo
.FeatureFlushCache
) {
636 XenPvBlockPushOperation (Dev
, BLKIF_OP_FLUSH_DISKCACHE
, 0);
640 /* Note: This won't finish if another thread enqueues requests. */
642 XenPvBlockAsyncIoPoll (Dev
);
643 if (RING_FREE_REQUESTS (&Dev
->Ring
) == RING_SIZE (&Dev
->Ring
)) {
650 XenPvBlockAsyncIoPoll (
651 IN XEN_BLOCK_FRONT_DEVICE
*Dev
654 RING_IDX ProducerIndex
, ConsumerIndex
;
655 blkif_response_t
*Response
;
659 ProducerIndex
= Dev
->Ring
.sring
->rsp_prod
;
660 /* Ensure we see queued responses up to 'ProducerIndex'. */
662 ConsumerIndex
= Dev
->Ring
.rsp_cons
;
664 while (ConsumerIndex
!= ProducerIndex
) {
665 XEN_BLOCK_FRONT_IO
*IoData
= NULL
;
668 Response
= RING_GET_RESPONSE (&Dev
->Ring
, ConsumerIndex
);
670 IoData
= (VOID
*)(UINTN
)Response
->id
;
671 Status
= Response
->status
;
673 switch (Response
->operation
) {
679 if (Status
!= BLKIF_RSP_OKAY
) {
683 "%a error %d on %a at sector %Lx, num bytes %Lx\n",
684 Response
->operation
== BLKIF_OP_READ
? "read" : "write",
686 IoData
->Dev
->NodeName
,
687 (UINT64
)IoData
->Sector
,
692 for (Index
= 0; Index
< IoData
->NumRef
; Index
++) {
693 Dev
->XenBusIo
->GrantEndAccess (Dev
->XenBusIo
, IoData
->GrantRef
[Index
]);
699 case BLKIF_OP_WRITE_BARRIER
:
700 if (Status
!= BLKIF_RSP_OKAY
) {
701 DEBUG ((DEBUG_ERROR
, "XenPvBlk: write barrier error %d\n", Status
));
705 case BLKIF_OP_FLUSH_DISKCACHE
:
706 if (Status
!= BLKIF_RSP_OKAY
) {
707 DEBUG ((DEBUG_ERROR
, "XenPvBlk: flush error %d\n", Status
));
715 "XenPvBlk: unrecognized block operation %d response (status %d)\n",
722 Dev
->Ring
.rsp_cons
= ++ConsumerIndex
;
723 if (IoData
!= NULL
) {
724 IoData
->Status
= Status
? EFI_DEVICE_ERROR
: EFI_SUCCESS
;
727 if (Dev
->Ring
.rsp_cons
!= ConsumerIndex
) {
728 /* We reentered, we must not continue here */
733 RING_FINAL_CHECK_FOR_RESPONSES (&Dev
->Ring
, More
);