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
;
174 ASSERT (NodeName
!= NULL
);
176 Dev
= AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE
));
177 Dev
->Signature
= XEN_BLOCK_FRONT_SIGNATURE
;
178 Dev
->NodeName
= NodeName
;
179 Dev
->XenBusIo
= XenBusIo
;
180 Dev
->DeviceId
= XenBusIo
->DeviceId
;
182 XenBusIo
->XsRead (XenBusIo
, XST_NIL
, "device-type", (VOID
**)&DeviceType
);
183 if (AsciiStrCmp (DeviceType
, "cdrom") == 0) {
184 Dev
->MediaInfo
.CdRom
= TRUE
;
186 Dev
->MediaInfo
.CdRom
= FALSE
;
188 FreePool (DeviceType
);
190 if (Dev
->MediaInfo
.CdRom
) {
191 Status
= XenBusIo
->XsBackendRead (XenBusIo
, XST_NIL
, "params", (VOID
**)&Params
);
192 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
193 DEBUG ((EFI_D_ERROR
, "%a: Failed to read params (%d)\n", __FUNCTION__
, Status
));
196 if (AsciiStrLen (Params
) == 0 || AsciiStrCmp (Params
, "aio:") == 0) {
198 DEBUG ((EFI_D_INFO
, "%a: Empty cdrom\n", __FUNCTION__
));
204 Status
= XenBusReadUint64 (XenBusIo
, "backend-id", FALSE
, &Value
);
205 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT16
) {
206 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to get backend-id (%d)\n",
210 Dev
->DomainId
= (domid_t
)Value
;
211 XenBusIo
->EventChannelAllocate (XenBusIo
, Dev
->DomainId
, &Dev
->EventChannel
);
213 SharedRing
= (blkif_sring_t
*) AllocatePages (1);
214 SHARED_RING_INIT (SharedRing
);
215 FRONT_RING_INIT (&Dev
->Ring
, SharedRing
, EFI_PAGE_SIZE
);
216 XenBusIo
->GrantAccess (XenBusIo
,
218 (INTN
) SharedRing
>> EFI_PAGE_SHIFT
,
223 Status
= XenBusIo
->XsTransactionStart (XenBusIo
, &Transaction
);
224 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
225 DEBUG ((EFI_D_WARN
, "XenPvBlk: Failed to start transaction, %d\n", Status
));
229 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
, "ring-ref", "%d",
231 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
232 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write ring-ref.\n"));
233 goto AbortTransaction
;
235 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
,
236 "event-channel", "%d", Dev
->EventChannel
);
237 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
238 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write event-channel.\n"));
239 goto AbortTransaction
;
241 Status
= XenBusIo
->XsPrintf (XenBusIo
, &Transaction
, NodeName
,
242 "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE
);
243 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
244 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write protocol.\n"));
245 goto AbortTransaction
;
248 Status
= XenBusIo
->SetState (XenBusIo
, &Transaction
, XenbusStateConnected
);
249 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
250 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to switch state.\n"));
251 goto AbortTransaction
;
254 Status
= XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, FALSE
);
255 if (Status
== XENSTORE_STATUS_EAGAIN
) {
259 XenBusIo
->RegisterWatchBackend (XenBusIo
, "state", &Dev
->StateWatchToken
);
262 // Waiting for backend
264 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateConnected
, &State
);
265 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
267 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",
268 XenBusIo
->Type
, XenBusIo
->DeviceId
, Status
, State
));
272 Status
= XenBusReadUint64 (XenBusIo
, "info", TRUE
, &Value
);
273 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT32
) {
276 Dev
->MediaInfo
.VDiskInfo
= (UINT32
)Value
;
277 if (Dev
->MediaInfo
.VDiskInfo
& VDISK_READONLY
) {
278 Dev
->MediaInfo
.ReadWrite
= FALSE
;
280 Dev
->MediaInfo
.ReadWrite
= TRUE
;
283 Status
= XenBusReadUint64 (XenBusIo
, "sectors", TRUE
, &Dev
->MediaInfo
.Sectors
);
284 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
288 Status
= XenBusReadUint64 (XenBusIo
, "sector-size", TRUE
, &Value
);
289 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> MAX_UINT32
) {
292 if ((UINT32
)Value
% 512 != 0) {
294 // This is not supported by the driver.
296 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Unsupported sector-size value %Lu, "
297 "it must be a multiple of 512\n", Value
));
300 Dev
->MediaInfo
.SectorSize
= (UINT32
)Value
;
304 XenBusReadUint64 (XenBusIo
, "feature-barrier", TRUE
, &Value
);
306 Dev
->MediaInfo
.FeatureBarrier
= TRUE
;
308 Dev
->MediaInfo
.FeatureBarrier
= FALSE
;
313 XenBusReadUint64 (XenBusIo
, "feature-flush-cache", TRUE
, &Value
);
315 Dev
->MediaInfo
.FeatureFlushCache
= TRUE
;
317 Dev
->MediaInfo
.FeatureFlushCache
= FALSE
;
320 DEBUG ((EFI_D_INFO
, "XenPvBlk: New disk with %ld sectors of %d bytes\n",
321 Dev
->MediaInfo
.Sectors
, Dev
->MediaInfo
.SectorSize
));
327 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
328 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
329 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
330 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
333 XenBusIo
->XsTransactionEnd (XenBusIo
, &Transaction
, TRUE
);
335 XenPvBlockFree (Dev
);
336 return EFI_DEVICE_ERROR
;
340 XenPvBlockFrontShutdown (
341 IN XEN_BLOCK_FRONT_DEVICE
*Dev
344 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
345 XENSTORE_STATUS Status
;
348 XenPvBlockSync (Dev
);
350 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosing
);
351 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
353 "XenPvBlk: error while changing state to Closing: %d\n",
358 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosing
, NULL
);
359 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
361 "XenPvBlk: error while waiting for closing backend state: %d\n",
366 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosed
);
367 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
369 "XenPvBlk: error while changing state to Closed: %d\n",
374 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosed
, NULL
);
375 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
377 "XenPvBlk: error while waiting for closed backend state: %d\n",
382 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateInitialising
);
383 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
385 "XenPvBlk: error while changing state to initialising: %d\n",
391 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
392 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
394 "XenPvBlk: error while waiting for new backend state: %d\n",
398 if (Value
<= XenbusStateInitWait
|| Value
>= XenbusStateClosed
) {
402 "XenPvBlk: waiting backend state %d, current: %Lu\n",
403 XenbusStateInitWait
, Value
));
404 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
408 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
409 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
410 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
411 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
413 XenPvBlockFree (Dev
);
419 IN XEN_BLOCK_FRONT_DEVICE
*Dev
422 /* Wait for a slot */
423 if (RING_FULL (&Dev
->Ring
)) {
425 XenPvBlockAsyncIoPoll (Dev
);
426 if (!RING_FULL (&Dev
->Ring
)) {
429 /* Really no slot, could wait for an event on Dev->EventChannel. */
436 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
440 XEN_BLOCK_FRONT_DEVICE
*Dev
= IoData
->Dev
;
441 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
442 blkif_request_t
*Request
;
445 INT32 NumSegments
, Index
;
448 // Can't io at non-sector-aligned location
449 ASSERT(!(IoData
->Sector
& ((Dev
->MediaInfo
.SectorSize
/ 512) - 1)));
450 // Can't io non-sector-sized amounts
451 ASSERT(!(IoData
->Size
& (Dev
->MediaInfo
.SectorSize
- 1)));
452 // Can't io non-sector-aligned buffer
453 ASSERT(!((UINTN
) IoData
->Buffer
& (Dev
->MediaInfo
.SectorSize
- 1)));
455 Start
= (UINTN
) IoData
->Buffer
& ~EFI_PAGE_MASK
;
456 End
= ((UINTN
) IoData
->Buffer
+ IoData
->Size
+ EFI_PAGE_SIZE
- 1) & ~EFI_PAGE_MASK
;
457 IoData
->NumRef
= NumSegments
= (INT32
)((End
- Start
) / EFI_PAGE_SIZE
);
459 ASSERT (NumSegments
<= BLKIF_MAX_SEGMENTS_PER_REQUEST
);
461 XenPvBlockWaitSlot (Dev
);
462 RingIndex
= Dev
->Ring
.req_prod_pvt
;
463 Request
= RING_GET_REQUEST (&Dev
->Ring
, RingIndex
);
465 Request
->operation
= IsWrite
? BLKIF_OP_WRITE
: BLKIF_OP_READ
;
466 Request
->nr_segments
= (UINT8
)NumSegments
;
467 Request
->handle
= Dev
->DeviceId
;
468 Request
->id
= (UINTN
) IoData
;
469 Request
->sector_number
= IoData
->Sector
;
471 for (Index
= 0; Index
< NumSegments
; Index
++) {
472 Request
->seg
[Index
].first_sect
= 0;
473 Request
->seg
[Index
].last_sect
= EFI_PAGE_SIZE
/ 512 - 1;
475 Request
->seg
[0].first_sect
= (UINT8
)(((UINTN
) IoData
->Buffer
& EFI_PAGE_MASK
) / 512);
476 Request
->seg
[NumSegments
- 1].last_sect
=
477 (UINT8
)((((UINTN
) IoData
->Buffer
+ IoData
->Size
- 1) & EFI_PAGE_MASK
) / 512);
478 for (Index
= 0; Index
< NumSegments
; Index
++) {
479 UINTN Data
= Start
+ Index
* EFI_PAGE_SIZE
;
480 XenBusIo
->GrantAccess (XenBusIo
, Dev
->DomainId
,
481 Data
>> EFI_PAGE_SHIFT
, IsWrite
,
482 &Request
->seg
[Index
].gref
);
483 IoData
->GrantRef
[Index
] = Request
->seg
[Index
].gref
;
486 Dev
->Ring
.req_prod_pvt
= RingIndex
+ 1;
489 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
493 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
494 if (ReturnCode
!= 0) {
496 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
504 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
509 // Status value that correspond to an IO in progress.
511 IoData
->Status
= EFI_ALREADY_STARTED
;
512 XenPvBlockAsyncIo (IoData
, IsWrite
);
514 while (IoData
->Status
== EFI_ALREADY_STARTED
) {
515 XenPvBlockAsyncIoPoll (IoData
->Dev
);
518 return IoData
->Status
;
523 XenPvBlockPushOperation (
524 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
530 blkif_request_t
*Request
;
533 XenPvBlockWaitSlot (Dev
);
534 Index
= Dev
->Ring
.req_prod_pvt
;
535 Request
= RING_GET_REQUEST(&Dev
->Ring
, Index
);
536 Request
->operation
= Operation
;
537 Request
->nr_segments
= 0;
538 Request
->handle
= Dev
->DeviceId
;
540 /* Not needed anyway, but the backend will check it */
541 Request
->sector_number
= 0;
542 Dev
->Ring
.req_prod_pvt
= Index
+ 1;
544 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
546 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
548 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
549 if (ReturnCode
!= 0) {
551 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
559 IN XEN_BLOCK_FRONT_DEVICE
*Dev
562 if (Dev
->MediaInfo
.ReadWrite
) {
563 if (Dev
->MediaInfo
.FeatureBarrier
) {
564 XenPvBlockPushOperation (Dev
, BLKIF_OP_WRITE_BARRIER
, 0);
567 if (Dev
->MediaInfo
.FeatureFlushCache
) {
568 XenPvBlockPushOperation (Dev
, BLKIF_OP_FLUSH_DISKCACHE
, 0);
572 /* Note: This won't finish if another thread enqueues requests. */
574 XenPvBlockAsyncIoPoll (Dev
);
575 if (RING_FREE_REQUESTS (&Dev
->Ring
) == RING_SIZE (&Dev
->Ring
)) {
582 XenPvBlockAsyncIoPoll (
583 IN XEN_BLOCK_FRONT_DEVICE
*Dev
586 RING_IDX ProducerIndex
, ConsumerIndex
;
587 blkif_response_t
*Response
;
591 ProducerIndex
= Dev
->Ring
.sring
->rsp_prod
;
592 /* Ensure we see queued responses up to 'ProducerIndex'. */
594 ConsumerIndex
= Dev
->Ring
.rsp_cons
;
596 while (ConsumerIndex
!= ProducerIndex
) {
597 XEN_BLOCK_FRONT_IO
*IoData
= NULL
;
600 Response
= RING_GET_RESPONSE (&Dev
->Ring
, ConsumerIndex
);
602 IoData
= (VOID
*) (UINTN
) Response
->id
;
603 Status
= Response
->status
;
605 switch (Response
->operation
) {
611 if (Status
!= BLKIF_RSP_OKAY
) {
614 "%a error %d on %a at sector %Lx, num bytes %Lx\n",
615 Response
->operation
== BLKIF_OP_READ
? "read" : "write",
616 Status
, IoData
->Dev
->NodeName
,
617 (UINT64
)IoData
->Sector
,
618 (UINT64
)IoData
->Size
));
621 for (Index
= 0; Index
< IoData
->NumRef
; Index
++) {
622 Dev
->XenBusIo
->GrantEndAccess (Dev
->XenBusIo
, IoData
->GrantRef
[Index
]);
628 case BLKIF_OP_WRITE_BARRIER
:
629 if (Status
!= BLKIF_RSP_OKAY
) {
630 DEBUG ((EFI_D_ERROR
, "XenPvBlk: write barrier error %d\n", Status
));
633 case BLKIF_OP_FLUSH_DISKCACHE
:
634 if (Status
!= BLKIF_RSP_OKAY
) {
635 DEBUG ((EFI_D_ERROR
, "XenPvBlk: flush error %d\n", Status
));
641 "XenPvBlk: unrecognized block operation %d response (status %d)\n",
642 Response
->operation
, Status
));
646 Dev
->Ring
.rsp_cons
= ++ConsumerIndex
;
647 if (IoData
!= NULL
) {
648 IoData
->Status
= Status
? EFI_DEVICE_ERROR
: EFI_SUCCESS
;
650 if (Dev
->Ring
.rsp_cons
!= ConsumerIndex
) {
651 /* We reentered, we must not continue here */
656 RING_FINAL_CHECK_FOR_RESPONSES (&Dev
->Ring
, More
);