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 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <Library/PrintLib.h>
31 #include <Library/DebugLib.h>
33 #include "BlockFront.h"
35 #include <IndustryStandard/Xen/io/protocols.h>
36 #include <IndustryStandard/Xen/io/xenbus.h>
39 Helper to read an integer from XenStore.
41 If the number overflows according to the range defined by UINT64,
44 @param This A pointer to a XENBUS_PROTOCOL instance.
45 @param Node The XenStore node to read from.
46 @param FromBackend Read frontend or backend value.
47 @param ValuePtr Where to put the value.
49 @retval XENSTORE_STATUS_SUCCESS If succefull, will update ValuePtr.
50 @return Any other return value indicate the error,
51 ValuePtr is not updated in this case.
56 IN XENBUS_PROTOCOL
*This
,
58 IN BOOLEAN FromBackend
,
62 XENSTORE_STATUS Status
;
66 Status
= This
->XsRead (This
, XST_NIL
, Node
, (VOID
**)&Ptr
);
68 Status
= This
->XsBackendRead (This
, XST_NIL
, Node
, (VOID
**)&Ptr
);
70 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
73 // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64.
74 *ValuePtr
= AsciiStrDecimalToUint64 (Ptr
);
80 Free an instance of XEN_BLOCK_FRONT_DEVICE.
82 @param Dev The instance to free.
87 IN XEN_BLOCK_FRONT_DEVICE
*Dev
90 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
92 if (Dev
->RingRef
!= 0) {
93 XenBusIo
->GrantEndAccess (XenBusIo
, Dev
->RingRef
);
95 if (Dev
->Ring
.sring
!= NULL
) {
96 FreePages (Dev
->Ring
.sring
, 1);
98 if (Dev
->EventChannel
!= 0) {
99 XenBusIo
->EventChannelClose (XenBusIo
, Dev
->EventChannel
);
105 Wait until until the backend has reached the ExpectedState.
107 @param Dev A XEN_BLOCK_FRONT_DEVICE instance.
108 @param ExpectedState The backend state expected.
109 @param LastStatePtr An optional pointer where to right the final state.
111 @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState
112 or return an error otherwise.
116 XenPvBlkWaitForBackendState (
117 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
118 IN XenbusState ExpectedState
,
119 OUT XenbusState
*LastStatePtr OPTIONAL
122 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
125 XENSTORE_STATUS Status
= XENSTORE_STATUS_SUCCESS
;
128 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
129 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
132 if (Value
> XenbusStateReconfigured
) {
134 // Value is not a State value.
136 return XENSTORE_STATUS_EIO
;
139 if (State
== ExpectedState
) {
141 } else if (State
> ExpectedState
) {
142 Status
= XENSTORE_STATUS_FAIL
;
146 "XenPvBlk: waiting backend state %d, current: %d\n",
147 ExpectedState
, State
));
148 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
151 if (LastStatePtr
!= NULL
) {
152 *LastStatePtr
= State
;
159 XenPvBlockFrontInitialization (
160 IN XENBUS_PROTOCOL
*XenBusIo
,
161 IN CONST CHAR8
*NodeName
,
162 OUT XEN_BLOCK_FRONT_DEVICE
**DevPtr
165 XENSTORE_TRANSACTION Transaction
;
167 blkif_sring_t
*SharedRing
;
168 XENSTORE_STATUS Status
;
169 XEN_BLOCK_FRONT_DEVICE
*Dev
;
173 ASSERT (NodeName
!= NULL
);
175 Dev
= AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE
));
176 Dev
->Signature
= XEN_BLOCK_FRONT_SIGNATURE
;
177 Dev
->NodeName
= NodeName
;
178 Dev
->XenBusIo
= XenBusIo
;
179 Dev
->DeviceId
= XenBusIo
->DeviceId
;
181 XenBusIo
->XsRead (XenBusIo
, XST_NIL
, "device-type", (VOID
**)&DeviceType
);
182 if (AsciiStrCmp (DeviceType
, "cdrom") == 0) {
183 Dev
->MediaInfo
.CdRom
= TRUE
;
185 Dev
->MediaInfo
.CdRom
= FALSE
;
187 FreePool (DeviceType
);
189 Status
= XenBusReadUint64 (XenBusIo
, "backend-id", FALSE
, &Value
);
190 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT16
) {
191 DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_ERROR
, "XenPvBlk: Unsupported sector-size value %d, "
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 ((EFI_D_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: %d\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 %p, num bytes %p\n",
600 Response
->operation
== BLKIF_OP_READ
? "read" : "write",
601 Status
, IoData
->Dev
->NodeName
,
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 ((EFI_D_ERROR
, "XenPvBlk: write barrier error %d\n", Status
));
618 case BLKIF_OP_FLUSH_DISKCACHE
:
619 if (Status
!= BLKIF_RSP_OKAY
) {
620 DEBUG ((EFI_D_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
);