2 Minimal block driver for Mini-OS.
4 Copyright (c) 2007-2008 Samuel Thibault.
5 Copyright (C) 2014, Citrix Ltd.
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <Library/PrintLib.h>
30 #include <Library/DebugLib.h>
32 #include "BlockFront.h"
34 #include <IndustryStandard/Xen/io/protocols.h>
35 #include <IndustryStandard/Xen/io/xenbus.h>
38 // Header used for UINT32_MAX and UINT16_MAX
43 Helper to read an integer from XenStore.
45 If the number overflows according to the range defined by UINT64,
48 @param This A pointer to a XENBUS_PROTOCOL instance.
49 @param Node The XenStore node to read from.
50 @param FromBackend Read frontend or backend value.
51 @param ValuePtr Where to put the value.
53 @retval XENSTORE_STATUS_SUCCESS If succefull, will update ValuePtr.
54 @return Any other return value indicate the error,
55 ValuePtr is not updated in this case.
60 IN XENBUS_PROTOCOL
*This
,
62 IN BOOLEAN FromBackend
,
66 XENSTORE_STATUS Status
;
70 Status
= This
->XsRead (This
, XST_NIL
, Node
, (VOID
**)&Ptr
);
72 Status
= This
->XsBackendRead (This
, XST_NIL
, Node
, (VOID
**)&Ptr
);
74 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
77 // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64.
78 *ValuePtr
= AsciiStrDecimalToUint64 (Ptr
);
84 Free an instance of XEN_BLOCK_FRONT_DEVICE.
86 @param Dev The instance to free.
91 IN XEN_BLOCK_FRONT_DEVICE
*Dev
94 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
96 if (Dev
->RingRef
!= 0) {
97 XenBusIo
->GrantEndAccess (XenBusIo
, Dev
->RingRef
);
99 if (Dev
->Ring
.sring
!= NULL
) {
100 FreePages (Dev
->Ring
.sring
, 1);
102 if (Dev
->EventChannel
!= 0) {
103 XenBusIo
->EventChannelClose (XenBusIo
, Dev
->EventChannel
);
109 Wait until until the backend has reached the ExpectedState.
111 @param Dev A XEN_BLOCK_FRONT_DEVICE instance.
112 @param ExpectedState The backend state expected.
113 @param LastStatePtr An optional pointer where to right the final state.
115 @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState
116 or return an error otherwise.
120 XenPvBlkWaitForBackendState (
121 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
122 IN XenbusState ExpectedState
,
123 OUT XenbusState
*LastStatePtr OPTIONAL
126 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
129 XENSTORE_STATUS Status
= XENSTORE_STATUS_SUCCESS
;
132 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
133 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
136 if (Value
> XenbusStateReconfigured
) {
138 // Value is not a State value.
140 return XENSTORE_STATUS_EIO
;
143 if (State
== ExpectedState
) {
145 } else if (State
> ExpectedState
) {
146 Status
= XENSTORE_STATUS_FAIL
;
150 "XenPvBlk: waiting backend state %d, current: %d\n",
151 ExpectedState
, State
));
152 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
155 if (LastStatePtr
!= NULL
) {
156 *LastStatePtr
= State
;
163 XenPvBlockFrontInitialization (
164 IN XENBUS_PROTOCOL
*XenBusIo
,
165 IN CONST CHAR8
*NodeName
,
166 OUT XEN_BLOCK_FRONT_DEVICE
**DevPtr
169 XENSTORE_TRANSACTION xbt
;
171 blkif_sring_t
*SharedRing
;
172 XENSTORE_STATUS Status
;
173 XEN_BLOCK_FRONT_DEVICE
*Dev
;
177 ASSERT (NodeName
!= NULL
);
179 Dev
= AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE
));
180 Dev
->Signature
= XEN_BLOCK_FRONT_SIGNATURE
;
181 Dev
->NodeName
= NodeName
;
182 Dev
->XenBusIo
= XenBusIo
;
183 Dev
->DeviceId
= XenBusIo
->DeviceId
;
185 XenBusIo
->XsRead (XenBusIo
, XST_NIL
, "device-type", (VOID
**)&DeviceType
);
186 if (AsciiStrCmp (DeviceType
, "cdrom") == 0) {
187 Dev
->MediaInfo
.CdRom
= TRUE
;
189 Dev
->MediaInfo
.CdRom
= FALSE
;
191 FreePool (DeviceType
);
193 Status
= XenBusReadUint64 (XenBusIo
, "backend-id", FALSE
, &Value
);
194 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> UINT16_MAX
) {
195 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to get backend-id (%d)\n",
199 Dev
->DomainId
= Value
;
200 XenBusIo
->EventChannelAllocate (XenBusIo
, Dev
->DomainId
, &Dev
->EventChannel
);
202 SharedRing
= (blkif_sring_t
*) AllocatePages (1);
203 SHARED_RING_INIT (SharedRing
);
204 FRONT_RING_INIT (&Dev
->Ring
, SharedRing
, EFI_PAGE_SIZE
);
205 XenBusIo
->GrantAccess (XenBusIo
,
207 (INTN
) SharedRing
>> EFI_PAGE_SHIFT
,
212 Status
= XenBusIo
->XsTransactionStart (XenBusIo
, &xbt
);
213 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
214 DEBUG ((EFI_D_WARN
, "XenPvBlk: Failed to start transaction, %d\n", Status
));
218 Status
= XenBusIo
->XsPrintf (XenBusIo
, xbt
, NodeName
, "ring-ref", "%d",
220 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
221 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write ring-ref.\n"));
222 goto AbortTransaction
;
224 Status
= XenBusIo
->XsPrintf (XenBusIo
, xbt
, NodeName
,
225 "event-channel", "%d", Dev
->EventChannel
);
226 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
227 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write event-channel.\n"));
228 goto AbortTransaction
;
230 Status
= XenBusIo
->XsPrintf (XenBusIo
, xbt
, NodeName
,
231 "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE
);
232 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
233 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to write protocol.\n"));
234 goto AbortTransaction
;
237 Status
= XenBusIo
->SetState (XenBusIo
, xbt
, XenbusStateConnected
);
238 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
239 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Failed to switch state.\n"));
240 goto AbortTransaction
;
243 Status
= XenBusIo
->XsTransactionEnd (XenBusIo
, xbt
, FALSE
);
244 if (Status
== XENSTORE_STATUS_EAGAIN
) {
248 XenBusIo
->RegisterWatchBackend (XenBusIo
, "state", &Dev
->StateWatchToken
);
251 // Waiting for backend
253 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateConnected
, &State
);
254 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
256 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",
257 XenBusIo
->Type
, XenBusIo
->DeviceId
, Status
, State
));
261 Status
= XenBusReadUint64 (XenBusIo
, "info", TRUE
, &Value
);
262 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> UINT32_MAX
) {
265 Dev
->MediaInfo
.VDiskInfo
= Value
;
266 if (Dev
->MediaInfo
.VDiskInfo
& VDISK_READONLY
) {
267 Dev
->MediaInfo
.ReadWrite
= FALSE
;
269 Dev
->MediaInfo
.ReadWrite
= TRUE
;
272 Status
= XenBusReadUint64 (XenBusIo
, "sectors", TRUE
, &Dev
->MediaInfo
.Sectors
);
273 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
277 Status
= XenBusReadUint64 (XenBusIo
, "sector-size", TRUE
, &Value
);
278 if (Status
!= XENSTORE_STATUS_SUCCESS
|| Value
> UINT32_MAX
) {
281 if (Value
% 512 != 0) {
283 // This is not supported by the driver.
285 DEBUG ((EFI_D_ERROR
, "XenPvBlk: Unsupported sector-size value %d, "
286 "it must be a multiple of 512\n", Value
));
289 Dev
->MediaInfo
.SectorSize
= Value
;
293 XenBusReadUint64 (XenBusIo
, "feature-barrier", TRUE
, &Value
);
295 Dev
->MediaInfo
.FeatureBarrier
= TRUE
;
297 Dev
->MediaInfo
.FeatureBarrier
= FALSE
;
302 XenBusReadUint64 (XenBusIo
, "feature-flush-cache", TRUE
, &Value
);
304 Dev
->MediaInfo
.FeatureFlushCache
= TRUE
;
306 Dev
->MediaInfo
.FeatureFlushCache
= FALSE
;
309 DEBUG ((EFI_D_INFO
, "XenPvBlk: New disk with %ld sectors of %d bytes\n",
310 Dev
->MediaInfo
.Sectors
, Dev
->MediaInfo
.SectorSize
));
316 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
317 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
318 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
319 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
322 XenBusIo
->XsTransactionEnd (XenBusIo
, xbt
, TRUE
);
324 XenPvBlockFree (Dev
);
325 return EFI_DEVICE_ERROR
;
329 XenPvBlockFrontShutdown (
330 IN XEN_BLOCK_FRONT_DEVICE
*Dev
333 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
334 XENSTORE_STATUS Status
;
337 XenPvBlockSync (Dev
);
339 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosing
);
340 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
342 "XenPvBlk: error while changing state to Closing: %d\n",
347 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosing
, NULL
);
348 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
350 "XenPvBlk: error while waiting for closing backend state: %d\n",
355 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateClosed
);
356 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
358 "XenPvBlk: error while changing state to Closed: %d\n",
363 Status
= XenPvBlkWaitForBackendState (Dev
, XenbusStateClosed
, NULL
);
364 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
366 "XenPvBlk: error while waiting for closed backend state: %d\n",
371 Status
= XenBusIo
->SetState (XenBusIo
, XST_NIL
, XenbusStateInitialising
);
372 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
374 "XenPvBlk: error while changing state to initialising: %d\n",
380 Status
= XenBusReadUint64 (XenBusIo
, "state", TRUE
, &Value
);
381 if (Status
!= XENSTORE_STATUS_SUCCESS
) {
383 "XenPvBlk: error while waiting for new backend state: %d\n",
387 if (Value
<= XenbusStateInitWait
|| Value
>= XenbusStateClosed
) {
391 "XenPvBlk: waiting backend state %d, current: %d\n",
392 XenbusStateInitWait
, Value
));
393 XenBusIo
->WaitForWatch (XenBusIo
, Dev
->StateWatchToken
);
397 XenBusIo
->UnregisterWatch (XenBusIo
, Dev
->StateWatchToken
);
398 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "ring-ref");
399 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "event-channel");
400 XenBusIo
->XsRemove (XenBusIo
, XST_NIL
, "protocol");
402 XenPvBlockFree (Dev
);
408 IN XEN_BLOCK_FRONT_DEVICE
*Dev
411 /* Wait for a slot */
412 if (RING_FULL (&Dev
->Ring
)) {
414 XenPvBlockAsyncIoPoll (Dev
);
415 if (!RING_FULL (&Dev
->Ring
)) {
418 /* Really no slot, could wait for an event on Dev->EventChannel. */
425 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
429 XEN_BLOCK_FRONT_DEVICE
*Dev
= IoData
->Dev
;
430 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
431 blkif_request_t
*Request
;
434 INT32 NumSegments
, Index
;
437 // Can't io at non-sector-aligned location
438 ASSERT(!(IoData
->Sector
& ((Dev
->MediaInfo
.SectorSize
/ 512) - 1)));
439 // Can't io non-sector-sized amounts
440 ASSERT(!(IoData
->Size
& (Dev
->MediaInfo
.SectorSize
- 1)));
441 // Can't io non-sector-aligned buffer
442 ASSERT(!((UINTN
) IoData
->Buffer
& (Dev
->MediaInfo
.SectorSize
- 1)));
444 Start
= (UINTN
) IoData
->Buffer
& ~EFI_PAGE_MASK
;
445 End
= ((UINTN
) IoData
->Buffer
+ IoData
->Size
+ EFI_PAGE_SIZE
- 1) & ~EFI_PAGE_MASK
;
446 IoData
->NumRef
= NumSegments
= (End
- Start
) / EFI_PAGE_SIZE
;
448 ASSERT (NumSegments
<= BLKIF_MAX_SEGMENTS_PER_REQUEST
);
450 XenPvBlockWaitSlot (Dev
);
451 RingIndex
= Dev
->Ring
.req_prod_pvt
;
452 Request
= RING_GET_REQUEST (&Dev
->Ring
, RingIndex
);
454 Request
->operation
= IsWrite
? BLKIF_OP_WRITE
: BLKIF_OP_READ
;
455 Request
->nr_segments
= NumSegments
;
456 Request
->handle
= Dev
->DeviceId
;
457 Request
->id
= (UINTN
) IoData
;
458 Request
->sector_number
= IoData
->Sector
;
460 for (Index
= 0; Index
< NumSegments
; Index
++) {
461 Request
->seg
[Index
].first_sect
= 0;
462 Request
->seg
[Index
].last_sect
= EFI_PAGE_SIZE
/ 512 - 1;
464 Request
->seg
[0].first_sect
= ((UINTN
) IoData
->Buffer
& EFI_PAGE_MASK
) / 512;
465 Request
->seg
[NumSegments
- 1].last_sect
=
466 (((UINTN
) IoData
->Buffer
+ IoData
->Size
- 1) & EFI_PAGE_MASK
) / 512;
467 for (Index
= 0; Index
< NumSegments
; Index
++) {
468 UINTN Data
= Start
+ Index
* EFI_PAGE_SIZE
;
469 XenBusIo
->GrantAccess (XenBusIo
, Dev
->DomainId
,
470 Data
>> EFI_PAGE_SHIFT
, IsWrite
,
471 &Request
->seg
[Index
].gref
);
472 IoData
->GrantRef
[Index
] = Request
->seg
[Index
].gref
;
475 Dev
->Ring
.req_prod_pvt
= RingIndex
+ 1;
478 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
482 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
483 if (ReturnCode
!= 0) {
485 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
493 IN OUT XEN_BLOCK_FRONT_IO
*IoData
,
498 // Status value that correspond to an IO in progress.
500 IoData
->Status
= EFI_ALREADY_STARTED
;
501 XenPvBlockAsyncIo (IoData
, IsWrite
);
503 while (IoData
->Status
== EFI_ALREADY_STARTED
) {
504 XenPvBlockAsyncIoPoll (IoData
->Dev
);
507 return IoData
->Status
;
512 XenPvBlockPushOperation (
513 IN XEN_BLOCK_FRONT_DEVICE
*Dev
,
519 blkif_request_t
*Request
;
522 XenPvBlockWaitSlot (Dev
);
523 Index
= Dev
->Ring
.req_prod_pvt
;
524 Request
= RING_GET_REQUEST(&Dev
->Ring
, Index
);
525 Request
->operation
= Operation
;
526 Request
->nr_segments
= 0;
527 Request
->handle
= Dev
->DeviceId
;
529 /* Not needed anyway, but the backend will check it */
530 Request
->sector_number
= 0;
531 Dev
->Ring
.req_prod_pvt
= Index
+ 1;
533 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev
->Ring
, Notify
);
535 XENBUS_PROTOCOL
*XenBusIo
= Dev
->XenBusIo
;
537 ReturnCode
= XenBusIo
->EventChannelNotify (XenBusIo
, Dev
->EventChannel
);
538 if (ReturnCode
!= 0) {
540 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",
548 IN XEN_BLOCK_FRONT_DEVICE
*Dev
551 if (Dev
->MediaInfo
.ReadWrite
) {
552 if (Dev
->MediaInfo
.FeatureBarrier
) {
553 XenPvBlockPushOperation (Dev
, BLKIF_OP_WRITE_BARRIER
, 0);
556 if (Dev
->MediaInfo
.FeatureFlushCache
) {
557 XenPvBlockPushOperation (Dev
, BLKIF_OP_FLUSH_DISKCACHE
, 0);
561 /* Note: This won't finish if another thread enqueues requests. */
563 XenPvBlockAsyncIoPoll (Dev
);
564 if (RING_FREE_REQUESTS (&Dev
->Ring
) == RING_SIZE (&Dev
->Ring
)) {
571 XenPvBlockAsyncIoPoll (
572 IN XEN_BLOCK_FRONT_DEVICE
*Dev
575 RING_IDX ProducerIndex
, ConsumerIndex
;
576 blkif_response_t
*Response
;
580 ProducerIndex
= Dev
->Ring
.sring
->rsp_prod
;
581 /* Ensure we see queued responses up to 'ProducerIndex'. */
583 ConsumerIndex
= Dev
->Ring
.rsp_cons
;
585 while (ConsumerIndex
!= ProducerIndex
) {
586 XEN_BLOCK_FRONT_IO
*IoData
= NULL
;
589 Response
= RING_GET_RESPONSE (&Dev
->Ring
, ConsumerIndex
);
591 IoData
= (VOID
*) (UINTN
) Response
->id
;
592 Status
= Response
->status
;
594 switch (Response
->operation
) {
600 if (Status
!= BLKIF_RSP_OKAY
) {
603 "%a error %d on %a at sector %p, num bytes %p\n",
604 Response
->operation
== BLKIF_OP_READ
? "read" : "write",
605 Status
, IoData
->Dev
->NodeName
,
610 for (Index
= 0; Index
< IoData
->NumRef
; Index
++) {
611 Dev
->XenBusIo
->GrantEndAccess (Dev
->XenBusIo
, IoData
->GrantRef
[Index
]);
617 case BLKIF_OP_WRITE_BARRIER
:
618 if (Status
!= BLKIF_RSP_OKAY
) {
619 DEBUG ((EFI_D_ERROR
, "XenPvBlk: write barrier error %d\n", Status
));
622 case BLKIF_OP_FLUSH_DISKCACHE
:
623 if (Status
!= BLKIF_RSP_OKAY
) {
624 DEBUG ((EFI_D_ERROR
, "XenPvBlk: flush error %d\n", Status
));
630 "XenPvBlk: unrecognized block operation %d response (status %d)\n",
631 Response
->operation
, Status
));
635 Dev
->Ring
.rsp_cons
= ++ConsumerIndex
;
636 if (IoData
!= NULL
) {
637 IoData
->Status
= Status
? EFI_DEVICE_ERROR
: EFI_SUCCESS
;
639 if (Dev
->Ring
.rsp_cons
!= ConsumerIndex
) {
640 /* We reentered, we must not continue here */
645 RING_FINAL_CHECK_FOR_RESPONSES (&Dev
->Ring
, More
);