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
);
51 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
54 // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64.
55 *ValuePtr
= AsciiStrDecimalToUint64 (Ptr
);
61 Free an instance of XEN_BLOCK_FRONT_DEVICE.
63 @param Dev The instance to free.
68 IN XEN_BLOCK_FRONT_DEVICE
*Dev
71 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
73 if (Dev
->RingRef
!= 0) {
74 XenBusIo
->GrantEndAccess (XenBusIo
, Dev
->RingRef
);
76 if (Dev
->Ring
.sring
!= NULL
) {
77 FreePages (Dev
->Ring
.sring
, 1);
79 if (Dev
->EventChannel
!= 0) {
80 XenBusIo
->EventChannelClose (XenBusIo
, Dev
->EventChannel
);
86 Wait until until the backend has reached the ExpectedState.
88 @param Dev A XEN_BLOCK_FRONT_DEVICE instance.
89 @param ExpectedState The backend state expected.
90 @param LastStatePtr An optional pointer where to right the final state.
92 @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState
93 or return an error otherwise.
97 XenPvBlkWaitForBackendState (
98 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
99 IN XenbusState ExpectedState
,
100 OUT XenbusState
*LastStatePtr OPTIONAL
103 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
106 XENSTORE_STATUS Status
= XENSTORE_STATUS_SUCCESS
;
109 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
110 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
113 if (Value
> XenbusStateReconfigured
) {
115 // Value is not a State value.
117 return XENSTORE_STATUS_EIO
;
120 if (State
== ExpectedState
) {
122 } else if (State
> ExpectedState
) {
123 Status
= XENSTORE_STATUS_FAIL
;
127 "XenPvBlk: waiting backend state %d, current: %d\n",
128 ExpectedState
, State
));
129 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
132 if (LastStatePtr
!= NULL
) {
133 *LastStatePtr
= State
;
140 XenPvBlockFrontInitialization (
141 IN XENBUS_PROTOCOL
*XenBusIo
,
142 IN CONST CHAR8
*NodeName
,
143 OUT XEN_BLOCK_FRONT_DEVICE
**DevPtr
146 XENSTORE_TRANSACTION Transaction
;
148 blkif_sring_t
*SharedRing
;
149 XENSTORE_STATUS Status
;
150 XEN_BLOCK_FRONT_DEVICE
*Dev
;
155 ASSERT (NodeName
!= NULL
);
157 Dev
= AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE
));
159 return EFI_OUT_OF_RESOURCES
;
162 Dev
->Signature
= XEN_BLOCK_FRONT_SIGNATURE
;
163 Dev
->NodeName
= NodeName
;
164 Dev
->XenBusIo
= XenBusIo
;
165 Dev
->DeviceId
= XenBusIo
->DeviceId
;
167 XenBusIo
->XsRead (XenBusIo
, XST_NIL
, "device-type", (VOID
**)&DeviceType
);
168 if (AsciiStrCmp (DeviceType
, "cdrom") == 0) {
169 Dev
->MediaInfo
.CdRom
= TRUE
;
171 Dev
->MediaInfo
.CdRom
= FALSE
;
173 FreePool (DeviceType
);
175 if (Dev
->MediaInfo
.CdRom
) {
176 Status
= XenBusIo
->XsBackendRead (XenBusIo
, XST_NIL
, "params", (VOID
**)&Params
);
177 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
178 DEBUG ((DEBUG_ERROR
, "%a: Failed to read params (%d)\n", __FUNCTION__
, Status
));
181 if (AsciiStrLen (Params
) == 0 || AsciiStrCmp (Params
, "aio:") == 0) {
183 DEBUG ((DEBUG_INFO
, "%a: Empty cdrom\n", __FUNCTION__
));
189 Status
= XenBusReadUint64 (XenBusIo
, "backend-id", FALSE
, &Value
);
190 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT16
) {
191 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to get backend-id (%d)\n",
195 Dev
->DomainId
= (domid_t
)Value
;
196 XenBusIo
->EventChannelAllocate (XenBusIo
, Dev
->DomainId
, &Dev
->EventChannel
);
198 SharedRing
= (blkif_sring_t
*) AllocatePages (1);
199 SHARED_RING_INIT (SharedRing
);
200 FRONT_RING_INIT (&Dev
->Ring
, SharedRing
, EFI_PAGE_SIZE
);
201 XenBusIo
->GrantAccess (XenBusIo
,
203 (INTN
) SharedRing
>> EFI_PAGE_SHIFT
,
208 Status
= XenBusIo
->XsTransactionStart (XenBusIo
, &Transaction
);
209 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
210 DEBUG ((DEBUG_WARN
, "XenPvBlk: Failed to start transaction, %d\n", Status
));
214 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
, "ring-ref", "%d",
216 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
217 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to write ring-ref.\n"));
218 goto AbortTransaction
;
220 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
,
221 "event-channel", "%d", Dev
->EventChannel
);
222 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
223 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to write event-channel.\n"));
224 goto AbortTransaction
;
226 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
,
227 "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE
);
228 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
229 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to write protocol.\n"));
230 goto AbortTransaction
;
233 Status
= XenBusIo
->SetState (XenBusIo
, &Transaction
, XenbusStateConnected
);
234 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
235 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Failed to switch state.\n"));
236 goto AbortTransaction
;
239 Status
= XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, FALSE
);
240 if (Status
== XENSTORE_STATUS_EAGAIN
) {
244 XenBusIo
->RegisterWatchBackend (XenBusIo
, "state", &Dev
->StateWatchToken
);
247 // Waiting for backend
249 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateConnected
, &State
);
250 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
252 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",
253 XenBusIo
->Type
, XenBusIo
->DeviceId
, Status
, State
));
257 Status
= XenBusReadUint64 (XenBusIo
, "info", TRUE
, &Value
);
258 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT32
) {
261 Dev
->MediaInfo
.VDiskInfo
= (UINT32
)Value
;
262 if (Dev
->MediaInfo
.VDiskInfo
& VDISK_READONLY
) {
263 Dev
->MediaInfo
.ReadWrite
= FALSE
;
265 Dev
->MediaInfo
.ReadWrite
= TRUE
;
268 Status
= XenBusReadUint64 (XenBusIo
, "sectors", TRUE
, &Dev
->MediaInfo
.Sectors
);
269 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
273 Status
= XenBusReadUint64 (XenBusIo
, "sector-size", TRUE
, &Value
);
274 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT32
) {
277 if ((UINT32
)Value
% 512 != 0) {
279 // This is not supported by the driver.
281 DEBUG ((DEBUG_ERROR
, "XenPvBlk: Unsupported sector-size value %Lu, "
282 "it must be a multiple of 512\n", Value
));
285 Dev
->MediaInfo
.SectorSize
= (UINT32
)Value
;
289 XenBusReadUint64 (XenBusIo
, "feature-barrier", TRUE
, &Value
);
291 Dev
->MediaInfo
.FeatureBarrier
= TRUE
;
293 Dev
->MediaInfo
.FeatureBarrier
= FALSE
;
298 XenBusReadUint64 (XenBusIo
, "feature-flush-cache", TRUE
, &Value
);
300 Dev
->MediaInfo
.FeatureFlushCache
= TRUE
;
302 Dev
->MediaInfo
.FeatureFlushCache
= FALSE
;
305 DEBUG ((DEBUG_INFO
, "XenPvBlk: New disk with %ld sectors of %d bytes\n",
306 Dev
->MediaInfo
.Sectors
, Dev
->MediaInfo
.SectorSize
));
312 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
313 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
314 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
315 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
318 XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, TRUE
);
320 XenPvBlockFree (Dev
);
321 return EFI_DEVICE_ERROR
;
325 XenPvBlockFrontShutdown (
326 IN XEN_BLOCK_FRONT_DEVICE
*Dev
329 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
330 XENSTORE_STATUS Status
;
333 XenPvBlockSync (Dev
);
335 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosing
);
336 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
338 "XenPvBlk: error while changing state to Closing: %d\n",
343 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosing
, NULL
);
344 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
346 "XenPvBlk: error while waiting for closing backend state: %d\n",
351 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosed
);
352 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
354 "XenPvBlk: error while changing state to Closed: %d\n",
359 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosed
, NULL
);
360 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
362 "XenPvBlk: error while waiting for closed backend state: %d\n",
367 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateInitialising
);
368 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
370 "XenPvBlk: error while changing state to initialising: %d\n",
376 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
377 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
379 "XenPvBlk: error while waiting for new backend state: %d\n",
383 if (Value
<= XenbusStateInitWait
|| Value
>= XenbusStateClosed
) {
387 "XenPvBlk: waiting backend state %d, current: %Lu\n",
388 XenbusStateInitWait
, Value
));
389 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
393 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
394 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
395 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
396 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
398 XenPvBlockFree (Dev
);
404 IN XEN_BLOCK_FRONT_DEVICE
*Dev
407 /* Wait for a slot */
408 if (RING_FULL (&Dev
->Ring
)) {
410 XenPvBlockAsyncIoPoll (Dev
);
411 if (!RING_FULL (&Dev
->Ring
)) {
414 /* Really no slot, could wait for an event on Dev->EventChannel. */
421 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
425 XEN_BLOCK_FRONT_DEVICE
*Dev
= IoData
->Dev
;
426 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
427 blkif_request_t
*Request
;
430 INT32 NumSegments
, Index
;
433 // Can't io at non-sector-aligned location
434 ASSERT(!(IoData
->Sector
& ((Dev
->MediaInfo
.SectorSize
/ 512) - 1)));
435 // Can't io non-sector-sized amounts
436 ASSERT(!(IoData
->Size
& (Dev
->MediaInfo
.SectorSize
- 1)));
437 // Can't io non-sector-aligned buffer
438 ASSERT(!((UINTN
) IoData
->Buffer
& (Dev
->MediaInfo
.SectorSize
- 1)));
440 Start
= (UINTN
) IoData
->Buffer
& ~EFI_PAGE_MASK
;
441 End
= ((UINTN
) IoData
->Buffer
+ IoData
->Size
+ EFI_PAGE_SIZE
- 1) & ~EFI_PAGE_MASK
;
442 IoData
->NumRef
= NumSegments
= (INT32
)((End
- Start
) / EFI_PAGE_SIZE
);
444 ASSERT (NumSegments
<= BLKIF_MAX_SEGMENTS_PER_REQUEST
);
446 XenPvBlockWaitSlot (Dev
);
447 RingIndex
= Dev
->Ring
.req_prod_pvt
;
448 Request
= RING_GET_REQUEST (&Dev
->Ring
, RingIndex
);
450 Request
->operation
= IsWrite
? BLKIF_OP_WRITE
: BLKIF_OP_READ
;
451 Request
->nr_segments
= (UINT8
)NumSegments
;
452 Request
->handle
= Dev
->DeviceId
;
453 Request
->id
= (UINTN
) IoData
;
454 Request
->sector_number
= IoData
->Sector
;
456 for (Index
= 0; Index
< NumSegments
; Index
++) {
457 Request
->seg
[Index
].first_sect
= 0;
458 Request
->seg
[Index
].last_sect
= EFI_PAGE_SIZE
/ 512 - 1;
460 Request
->seg
[0].first_sect
= (UINT8
)(((UINTN
) IoData
->Buffer
& EFI_PAGE_MASK
) / 512);
461 Request
->seg
[NumSegments
- 1].last_sect
=
462 (UINT8
)((((UINTN
) IoData
->Buffer
+ IoData
->Size
- 1) & EFI_PAGE_MASK
) / 512);
463 for (Index
= 0; Index
< NumSegments
; Index
++) {
464 UINTN Data
= Start
+ Index
* EFI_PAGE_SIZE
;
465 XenBusIo
->GrantAccess (XenBusIo
, Dev
->DomainId
,
466 Data
>> EFI_PAGE_SHIFT
, IsWrite
,
467 &Request
->seg
[Index
].gref
);
468 IoData
->GrantRef
[Index
] = Request
->seg
[Index
].gref
;
471 Dev
->Ring
.req_prod_pvt
= RingIndex
+ 1;
474 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
478 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
479 if (ReturnCode
!= 0) {
481 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
489 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
494 // Status value that correspond to an IO in progress.
496 IoData
->Status
= EFI_ALREADY_STARTED
;
497 XenPvBlockAsyncIo (IoData
, IsWrite
);
499 while (IoData
->Status
== EFI_ALREADY_STARTED
) {
500 XenPvBlockAsyncIoPoll (IoData
->Dev
);
503 return IoData
->Status
;
508 XenPvBlockPushOperation (
509 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
515 blkif_request_t
*Request
;
518 XenPvBlockWaitSlot (Dev
);
519 Index
= Dev
->Ring
.req_prod_pvt
;
520 Request
= RING_GET_REQUEST(&Dev
->Ring
, Index
);
521 Request
->operation
= Operation
;
522 Request
->nr_segments
= 0;
523 Request
->handle
= Dev
->DeviceId
;
525 /* Not needed anyway, but the backend will check it */
526 Request
->sector_number
= 0;
527 Dev
->Ring
.req_prod_pvt
= Index
+ 1;
529 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
531 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
533 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
534 if (ReturnCode
!= 0) {
536 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
544 IN XEN_BLOCK_FRONT_DEVICE
*Dev
547 if (Dev
->MediaInfo
.ReadWrite
) {
548 if (Dev
->MediaInfo
.FeatureBarrier
) {
549 XenPvBlockPushOperation (Dev
, BLKIF_OP_WRITE_BARRIER
, 0);
552 if (Dev
->MediaInfo
.FeatureFlushCache
) {
553 XenPvBlockPushOperation (Dev
, BLKIF_OP_FLUSH_DISKCACHE
, 0);
557 /* Note: This won't finish if another thread enqueues requests. */
559 XenPvBlockAsyncIoPoll (Dev
);
560 if (RING_FREE_REQUESTS (&Dev
->Ring
) == RING_SIZE (&Dev
->Ring
)) {
567 XenPvBlockAsyncIoPoll (
568 IN XEN_BLOCK_FRONT_DEVICE
*Dev
571 RING_IDX ProducerIndex
, ConsumerIndex
;
572 blkif_response_t
*Response
;
576 ProducerIndex
= Dev
->Ring
.sring
->rsp_prod
;
577 /* Ensure we see queued responses up to 'ProducerIndex'. */
579 ConsumerIndex
= Dev
->Ring
.rsp_cons
;
581 while (ConsumerIndex
!= ProducerIndex
) {
582 XEN_BLOCK_FRONT_IO
*IoData
= NULL
;
585 Response
= RING_GET_RESPONSE (&Dev
->Ring
, ConsumerIndex
);
587 IoData
= (VOID
*) (UINTN
) Response
->id
;
588 Status
= Response
->status
;
590 switch (Response
->operation
) {
596 if (Status
!= BLKIF_RSP_OKAY
) {
599 "%a error %d on %a at sector %Lx, num bytes %Lx\n",
600 Response
->operation
== BLKIF_OP_READ
? "read" : "write",
601 Status
, IoData
->Dev
->NodeName
,
602 (UINT64
)IoData
->Sector
,
603 (UINT64
)IoData
->Size
));
606 for (Index
= 0; Index
< IoData
->NumRef
; Index
++) {
607 Dev
->XenBusIo
->GrantEndAccess (Dev
->XenBusIo
, IoData
->GrantRef
[Index
]);
613 case BLKIF_OP_WRITE_BARRIER
:
614 if (Status
!= BLKIF_RSP_OKAY
) {
615 DEBUG ((DEBUG_ERROR
, "XenPvBlk: write barrier error %d\n", Status
));
618 case BLKIF_OP_FLUSH_DISKCACHE
:
619 if (Status
!= BLKIF_RSP_OKAY
) {
620 DEBUG ((DEBUG_ERROR
, "XenPvBlk: flush error %d\n", Status
));
626 "XenPvBlk: unrecognized block operation %d response (status %d)\n",
627 Response
->operation
, Status
));
631 Dev
->Ring
.rsp_cons
= ++ConsumerIndex
;
632 if (IoData
!= NULL
) {
633 IoData
->Status
= Status
? EFI_DEVICE_ERROR
: EFI_SUCCESS
;
635 if (Dev
->Ring
.rsp_cons
!= ConsumerIndex
) {
636 /* We reentered, we must not continue here */
641 RING_FINAL_CHECK_FOR_RESPONSES (&Dev
->Ring
, More
);