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 succefull, 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
));
158 Dev
->Signature
= XEN_BLOCK_FRONT_SIGNATURE
;
159 Dev
->NodeName
= NodeName
;
160 Dev
->XenBusIo
= XenBusIo
;
161 Dev
->DeviceId
= XenBusIo
->DeviceId
;
163 XenBusIo
->XsRead (XenBusIo
, XST_NIL
, "device-type", (VOID
**)&DeviceType
);
164 if (AsciiStrCmp (DeviceType
, "cdrom") == 0) {
165 Dev
->MediaInfo
.CdRom
= TRUE
;
167 Dev
->MediaInfo
.CdRom
= FALSE
;
169 FreePool (DeviceType
);
171 if (Dev
->MediaInfo
.CdRom
) {
172 Status
= XenBusIo
->XsBackendRead (XenBusIo
, XST_NIL
, "params", (VOID
**)&Params
);
173 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
174 DEBUG ((EFI_D_ERROR
, "%a: Failed to read params (%d)\n", __FUNCTION__
, Status
));
177 if (AsciiStrLen (Params
) == 0 || AsciiStrCmp (Params
, "aio:") == 0) {
179 DEBUG ((EFI_D_INFO
, "%a: Empty cdrom\n", __FUNCTION__
));
185 Status
= XenBusReadUint64 (XenBusIo
, "backend-id", FALSE
, &Value
);
186 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT16
) {
187 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to get backend-id (%d)\n",
191 Dev
->DomainId
= (domid_t
)Value
;
192 XenBusIo
->EventChannelAllocate (XenBusIo
, Dev
->DomainId
, &Dev
->EventChannel
);
194 SharedRing
= (blkif_sring_t
*) AllocatePages (1);
195 SHARED_RING_INIT (SharedRing
);
196 FRONT_RING_INIT (&Dev
->Ring
, SharedRing
, EFI_PAGE_SIZE
);
197 XenBusIo
->GrantAccess (XenBusIo
,
199 (INTN
) SharedRing
>> EFI_PAGE_SHIFT
,
204 Status
= XenBusIo
->XsTransactionStart (XenBusIo
, &Transaction
);
205 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
206 DEBUG ((EFI_D_WARN
, "XenPvBlk: Failed to start transaction, %d\n", Status
));
210 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
, "ring-ref", "%d",
212 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
213 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write ring-ref.\n"));
214 goto AbortTransaction
;
216 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
,
217 "event-channel", "%d", Dev
->EventChannel
);
218 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
219 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write event-channel.\n"));
220 goto AbortTransaction
;
222 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
,
223 "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE
);
224 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
225 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write protocol.\n"));
226 goto AbortTransaction
;
229 Status
= XenBusIo
->SetState (XenBusIo
, &Transaction
, XenbusStateConnected
);
230 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
231 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to switch state.\n"));
232 goto AbortTransaction
;
235 Status
= XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, FALSE
);
236 if (Status
== XENSTORE_STATUS_EAGAIN
) {
240 XenBusIo
->RegisterWatchBackend (XenBusIo
, "state", &Dev
->StateWatchToken
);
243 // Waiting for backend
245 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateConnected
, &State
);
246 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
248 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",
249 XenBusIo
->Type
, XenBusIo
->DeviceId
, Status
, State
));
253 Status
= XenBusReadUint64 (XenBusIo
, "info", TRUE
, &Value
);
254 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT32
) {
257 Dev
->MediaInfo
.VDiskInfo
= (UINT32
)Value
;
258 if (Dev
->MediaInfo
.VDiskInfo
& VDISK_READONLY
) {
259 Dev
->MediaInfo
.ReadWrite
= FALSE
;
261 Dev
->MediaInfo
.ReadWrite
= TRUE
;
264 Status
= XenBusReadUint64 (XenBusIo
, "sectors", TRUE
, &Dev
->MediaInfo
.Sectors
);
265 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
269 Status
= XenBusReadUint64 (XenBusIo
, "sector-size", TRUE
, &Value
);
270 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT32
) {
273 if ((UINT32
)Value
% 512 != 0) {
275 // This is not supported by the driver.
277 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Unsupported sector-size value %Lu, "
278 "it must be a multiple of 512\n", Value
));
281 Dev
->MediaInfo
.SectorSize
= (UINT32
)Value
;
285 XenBusReadUint64 (XenBusIo
, "feature-barrier", TRUE
, &Value
);
287 Dev
->MediaInfo
.FeatureBarrier
= TRUE
;
289 Dev
->MediaInfo
.FeatureBarrier
= FALSE
;
294 XenBusReadUint64 (XenBusIo
, "feature-flush-cache", TRUE
, &Value
);
296 Dev
->MediaInfo
.FeatureFlushCache
= TRUE
;
298 Dev
->MediaInfo
.FeatureFlushCache
= FALSE
;
301 DEBUG ((EFI_D_INFO
, "XenPvBlk: New disk with %ld sectors of %d bytes\n",
302 Dev
->MediaInfo
.Sectors
, Dev
->MediaInfo
.SectorSize
));
308 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
309 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
310 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
311 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
314 XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, TRUE
);
316 XenPvBlockFree (Dev
);
317 return EFI_DEVICE_ERROR
;
321 XenPvBlockFrontShutdown (
322 IN XEN_BLOCK_FRONT_DEVICE
*Dev
325 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
326 XENSTORE_STATUS Status
;
329 XenPvBlockSync (Dev
);
331 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosing
);
332 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
334 "XenPvBlk: error while changing state to Closing: %d\n",
339 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosing
, NULL
);
340 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
342 "XenPvBlk: error while waiting for closing backend state: %d\n",
347 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosed
);
348 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
350 "XenPvBlk: error while changing state to Closed: %d\n",
355 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosed
, NULL
);
356 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
358 "XenPvBlk: error while waiting for closed backend state: %d\n",
363 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateInitialising
);
364 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
366 "XenPvBlk: error while changing state to initialising: %d\n",
372 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
373 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
375 "XenPvBlk: error while waiting for new backend state: %d\n",
379 if (Value
<= XenbusStateInitWait
|| Value
>= XenbusStateClosed
) {
383 "XenPvBlk: waiting backend state %d, current: %Lu\n",
384 XenbusStateInitWait
, Value
));
385 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
389 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
390 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
391 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
392 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
394 XenPvBlockFree (Dev
);
400 IN XEN_BLOCK_FRONT_DEVICE
*Dev
403 /* Wait for a slot */
404 if (RING_FULL (&Dev
->Ring
)) {
406 XenPvBlockAsyncIoPoll (Dev
);
407 if (!RING_FULL (&Dev
->Ring
)) {
410 /* Really no slot, could wait for an event on Dev->EventChannel. */
417 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
421 XEN_BLOCK_FRONT_DEVICE
*Dev
= IoData
->Dev
;
422 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
423 blkif_request_t
*Request
;
426 INT32 NumSegments
, Index
;
429 // Can't io at non-sector-aligned location
430 ASSERT(!(IoData
->Sector
& ((Dev
->MediaInfo
.SectorSize
/ 512) - 1)));
431 // Can't io non-sector-sized amounts
432 ASSERT(!(IoData
->Size
& (Dev
->MediaInfo
.SectorSize
- 1)));
433 // Can't io non-sector-aligned buffer
434 ASSERT(!((UINTN
) IoData
->Buffer
& (Dev
->MediaInfo
.SectorSize
- 1)));
436 Start
= (UINTN
) IoData
->Buffer
& ~EFI_PAGE_MASK
;
437 End
= ((UINTN
) IoData
->Buffer
+ IoData
->Size
+ EFI_PAGE_SIZE
- 1) & ~EFI_PAGE_MASK
;
438 IoData
->NumRef
= NumSegments
= (INT32
)((End
- Start
) / EFI_PAGE_SIZE
);
440 ASSERT (NumSegments
<= BLKIF_MAX_SEGMENTS_PER_REQUEST
);
442 XenPvBlockWaitSlot (Dev
);
443 RingIndex
= Dev
->Ring
.req_prod_pvt
;
444 Request
= RING_GET_REQUEST (&Dev
->Ring
, RingIndex
);
446 Request
->operation
= IsWrite
? BLKIF_OP_WRITE
: BLKIF_OP_READ
;
447 Request
->nr_segments
= (UINT8
)NumSegments
;
448 Request
->handle
= Dev
->DeviceId
;
449 Request
->id
= (UINTN
) IoData
;
450 Request
->sector_number
= IoData
->Sector
;
452 for (Index
= 0; Index
< NumSegments
; Index
++) {
453 Request
->seg
[Index
].first_sect
= 0;
454 Request
->seg
[Index
].last_sect
= EFI_PAGE_SIZE
/ 512 - 1;
456 Request
->seg
[0].first_sect
= (UINT8
)(((UINTN
) IoData
->Buffer
& EFI_PAGE_MASK
) / 512);
457 Request
->seg
[NumSegments
- 1].last_sect
=
458 (UINT8
)((((UINTN
) IoData
->Buffer
+ IoData
->Size
- 1) & EFI_PAGE_MASK
) / 512);
459 for (Index
= 0; Index
< NumSegments
; Index
++) {
460 UINTN Data
= Start
+ Index
* EFI_PAGE_SIZE
;
461 XenBusIo
->GrantAccess (XenBusIo
, Dev
->DomainId
,
462 Data
>> EFI_PAGE_SHIFT
, IsWrite
,
463 &Request
->seg
[Index
].gref
);
464 IoData
->GrantRef
[Index
] = Request
->seg
[Index
].gref
;
467 Dev
->Ring
.req_prod_pvt
= RingIndex
+ 1;
470 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
474 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
475 if (ReturnCode
!= 0) {
477 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
485 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
490 // Status value that correspond to an IO in progress.
492 IoData
->Status
= EFI_ALREADY_STARTED
;
493 XenPvBlockAsyncIo (IoData
, IsWrite
);
495 while (IoData
->Status
== EFI_ALREADY_STARTED
) {
496 XenPvBlockAsyncIoPoll (IoData
->Dev
);
499 return IoData
->Status
;
504 XenPvBlockPushOperation (
505 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
511 blkif_request_t
*Request
;
514 XenPvBlockWaitSlot (Dev
);
515 Index
= Dev
->Ring
.req_prod_pvt
;
516 Request
= RING_GET_REQUEST(&Dev
->Ring
, Index
);
517 Request
->operation
= Operation
;
518 Request
->nr_segments
= 0;
519 Request
->handle
= Dev
->DeviceId
;
521 /* Not needed anyway, but the backend will check it */
522 Request
->sector_number
= 0;
523 Dev
->Ring
.req_prod_pvt
= Index
+ 1;
525 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
527 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
529 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
530 if (ReturnCode
!= 0) {
532 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
540 IN XEN_BLOCK_FRONT_DEVICE
*Dev
543 if (Dev
->MediaInfo
.ReadWrite
) {
544 if (Dev
->MediaInfo
.FeatureBarrier
) {
545 XenPvBlockPushOperation (Dev
, BLKIF_OP_WRITE_BARRIER
, 0);
548 if (Dev
->MediaInfo
.FeatureFlushCache
) {
549 XenPvBlockPushOperation (Dev
, BLKIF_OP_FLUSH_DISKCACHE
, 0);
553 /* Note: This won't finish if another thread enqueues requests. */
555 XenPvBlockAsyncIoPoll (Dev
);
556 if (RING_FREE_REQUESTS (&Dev
->Ring
) == RING_SIZE (&Dev
->Ring
)) {
563 XenPvBlockAsyncIoPoll (
564 IN XEN_BLOCK_FRONT_DEVICE
*Dev
567 RING_IDX ProducerIndex
, ConsumerIndex
;
568 blkif_response_t
*Response
;
572 ProducerIndex
= Dev
->Ring
.sring
->rsp_prod
;
573 /* Ensure we see queued responses up to 'ProducerIndex'. */
575 ConsumerIndex
= Dev
->Ring
.rsp_cons
;
577 while (ConsumerIndex
!= ProducerIndex
) {
578 XEN_BLOCK_FRONT_IO
*IoData
= NULL
;
581 Response
= RING_GET_RESPONSE (&Dev
->Ring
, ConsumerIndex
);
583 IoData
= (VOID
*) (UINTN
) Response
->id
;
584 Status
= Response
->status
;
586 switch (Response
->operation
) {
592 if (Status
!= BLKIF_RSP_OKAY
) {
595 "%a error %d on %a at sector %Lx, num bytes %Lx\n",
596 Response
->operation
== BLKIF_OP_READ
? "read" : "write",
597 Status
, IoData
->Dev
->NodeName
,
598 (UINT64
)IoData
->Sector
,
599 (UINT64
)IoData
->Size
));
602 for (Index
= 0; Index
< IoData
->NumRef
; Index
++) {
603 Dev
->XenBusIo
->GrantEndAccess (Dev
->XenBusIo
, IoData
->GrantRef
[Index
]);
609 case BLKIF_OP_WRITE_BARRIER
:
610 if (Status
!= BLKIF_RSP_OKAY
) {
611 DEBUG ((EFI_D_ERROR
, "XenPvBlk: write barrier error %d\n", Status
));
614 case BLKIF_OP_FLUSH_DISKCACHE
:
615 if (Status
!= BLKIF_RSP_OKAY
) {
616 DEBUG ((EFI_D_ERROR
, "XenPvBlk: flush error %d\n", Status
));
622 "XenPvBlk: unrecognized block operation %d response (status %d)\n",
623 Response
->operation
, Status
));
627 Dev
->Ring
.rsp_cons
= ++ConsumerIndex
;
628 if (IoData
!= NULL
) {
629 IoData
->Status
= Status
? EFI_DEVICE_ERROR
: EFI_SUCCESS
;
631 if (Dev
->Ring
.rsp_cons
!= ConsumerIndex
) {
632 /* We reentered, we must not continue here */
637 RING_FINAL_CHECK_FOR_RESPONSES (&Dev
->Ring
, More
);