]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/XenPvBlkDxe/BlockFront.c
OvmfPkg/XenPvBlkDxe: Don't include system inttypes.h
[mirror_edk2.git] / OvmfPkg / XenPvBlkDxe / BlockFront.c
CommitLineData
5cce8524
ST
1/** @file\r
2 Minimal block driver for Mini-OS.\r
3\r
4 Copyright (c) 2007-2008 Samuel Thibault.\r
5 Copyright (C) 2014, Citrix Ltd.\r
4d3b9d33 6 Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>\r
5cce8524
ST
7\r
8 Redistribution and use in source and binary forms, with or without\r
9 modification, are permitted provided that the following conditions\r
10 are met:\r
11 1. Redistributions of source code must retain the above copyright\r
12 notice, this list of conditions and the following disclaimer.\r
13 2. Redistributions in binary form must reproduce the above copyright\r
14 notice, this list of conditions and the following disclaimer in the\r
15 documentation and/or other materials provided with the distribution.\r
16\r
17 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND\r
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
20 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE\r
21 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
23 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
24 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
25 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
26 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
27 SUCH DAMAGE.\r
28**/\r
29\r
30#include <Library/PrintLib.h>\r
31#include <Library/DebugLib.h>\r
32\r
33#include "BlockFront.h"\r
34\r
35#include <IndustryStandard/Xen/io/protocols.h>\r
36#include <IndustryStandard/Xen/io/xenbus.h>\r
37\r
5cce8524
ST
38/**\r
39 Helper to read an integer from XenStore.\r
40\r
41 If the number overflows according to the range defined by UINT64,\r
42 then ASSERT().\r
43\r
44 @param This A pointer to a XENBUS_PROTOCOL instance.\r
45 @param Node The XenStore node to read from.\r
46 @param FromBackend Read frontend or backend value.\r
47 @param ValuePtr Where to put the value.\r
48\r
49 @retval XENSTORE_STATUS_SUCCESS If succefull, will update ValuePtr.\r
50 @return Any other return value indicate the error,\r
51 ValuePtr is not updated in this case.\r
52**/\r
53STATIC\r
54XENSTORE_STATUS\r
55XenBusReadUint64 (\r
56 IN XENBUS_PROTOCOL *This,\r
57 IN CONST CHAR8 *Node,\r
58 IN BOOLEAN FromBackend,\r
59 OUT UINT64 *ValuePtr\r
60 )\r
61{\r
62 XENSTORE_STATUS Status;\r
63 CHAR8 *Ptr;\r
64\r
65 if (!FromBackend) {\r
66 Status = This->XsRead (This, XST_NIL, Node, (VOID**)&Ptr);\r
67 } else {\r
68 Status = This->XsBackendRead (This, XST_NIL, Node, (VOID**)&Ptr);\r
69 }\r
70 if (Status != XENSTORE_STATUS_SUCCESS) {\r
71 return Status;\r
72 }\r
73 // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64.\r
74 *ValuePtr = AsciiStrDecimalToUint64 (Ptr);\r
75 FreePool (Ptr);\r
76 return Status;\r
77}\r
78\r
79/**\r
80 Free an instance of XEN_BLOCK_FRONT_DEVICE.\r
81\r
82 @param Dev The instance to free.\r
83**/\r
84STATIC\r
85VOID\r
86XenPvBlockFree (\r
87 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
88 )\r
89{\r
90 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
91\r
92 if (Dev->RingRef != 0) {\r
93 XenBusIo->GrantEndAccess (XenBusIo, Dev->RingRef);\r
94 }\r
95 if (Dev->Ring.sring != NULL) {\r
96 FreePages (Dev->Ring.sring, 1);\r
97 }\r
98 if (Dev->EventChannel != 0) {\r
99 XenBusIo->EventChannelClose (XenBusIo, Dev->EventChannel);\r
100 }\r
101 FreePool (Dev);\r
102}\r
103\r
104/**\r
105 Wait until until the backend has reached the ExpectedState.\r
106\r
107 @param Dev A XEN_BLOCK_FRONT_DEVICE instance.\r
108 @param ExpectedState The backend state expected.\r
109 @param LastStatePtr An optional pointer where to right the final state.\r
110\r
111 @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState\r
112 or return an error otherwise.\r
113**/\r
114STATIC\r
115XENSTORE_STATUS\r
116XenPvBlkWaitForBackendState (\r
117 IN XEN_BLOCK_FRONT_DEVICE *Dev,\r
118 IN XenbusState ExpectedState,\r
119 OUT XenbusState *LastStatePtr OPTIONAL\r
120 )\r
121{\r
122 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
123 XenbusState State;\r
124 UINT64 Value;\r
125 XENSTORE_STATUS Status = XENSTORE_STATUS_SUCCESS;\r
126\r
127 while (TRUE) {\r
128 Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value);\r
129 if (Status != XENSTORE_STATUS_SUCCESS) {\r
130 return Status;\r
131 }\r
132 if (Value > XenbusStateReconfigured) {\r
133 //\r
134 // Value is not a State value.\r
135 //\r
136 return XENSTORE_STATUS_EIO;\r
137 }\r
138 State = Value;\r
139 if (State == ExpectedState) {\r
140 break;\r
141 } else if (State > ExpectedState) {\r
142 Status = XENSTORE_STATUS_FAIL;\r
143 break;\r
144 }\r
145 DEBUG ((EFI_D_INFO,\r
146 "XenPvBlk: waiting backend state %d, current: %d\n",\r
147 ExpectedState, State));\r
148 XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken);\r
149 }\r
150\r
151 if (LastStatePtr != NULL) {\r
152 *LastStatePtr = State;\r
153 }\r
154\r
155 return Status;\r
156}\r
157\r
158EFI_STATUS\r
159XenPvBlockFrontInitialization (\r
160 IN XENBUS_PROTOCOL *XenBusIo,\r
161 IN CONST CHAR8 *NodeName,\r
162 OUT XEN_BLOCK_FRONT_DEVICE **DevPtr\r
163 )\r
164{\r
e26a83cd 165 XENSTORE_TRANSACTION Transaction;\r
5cce8524
ST
166 CHAR8 *DeviceType;\r
167 blkif_sring_t *SharedRing;\r
168 XENSTORE_STATUS Status;\r
169 XEN_BLOCK_FRONT_DEVICE *Dev;\r
170 XenbusState State;\r
171 UINT64 Value;\r
172\r
173 ASSERT (NodeName != NULL);\r
174\r
175 Dev = AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE));\r
176 Dev->Signature = XEN_BLOCK_FRONT_SIGNATURE;\r
177 Dev->NodeName = NodeName;\r
178 Dev->XenBusIo = XenBusIo;\r
179 Dev->DeviceId = XenBusIo->DeviceId;\r
180\r
181 XenBusIo->XsRead (XenBusIo, XST_NIL, "device-type", (VOID**)&DeviceType);\r
182 if (AsciiStrCmp (DeviceType, "cdrom") == 0) {\r
183 Dev->MediaInfo.CdRom = TRUE;\r
184 } else {\r
185 Dev->MediaInfo.CdRom = FALSE;\r
186 }\r
187 FreePool (DeviceType);\r
188\r
189 Status = XenBusReadUint64 (XenBusIo, "backend-id", FALSE, &Value);\r
4d3b9d33 190 if (Status != XENSTORE_STATUS_SUCCESS || Value > MAX_UINT16) {\r
5cce8524
ST
191 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to get backend-id (%d)\n",\r
192 Status));\r
193 goto Error;\r
194 }\r
195 Dev->DomainId = Value;\r
196 XenBusIo->EventChannelAllocate (XenBusIo, Dev->DomainId, &Dev->EventChannel);\r
197\r
198 SharedRing = (blkif_sring_t*) AllocatePages (1);\r
199 SHARED_RING_INIT (SharedRing);\r
200 FRONT_RING_INIT (&Dev->Ring, SharedRing, EFI_PAGE_SIZE);\r
201 XenBusIo->GrantAccess (XenBusIo,\r
202 Dev->DomainId,\r
203 (INTN) SharedRing >> EFI_PAGE_SHIFT,\r
204 FALSE,\r
205 &Dev->RingRef);\r
206\r
207Again:\r
e26a83cd 208 Status = XenBusIo->XsTransactionStart (XenBusIo, &Transaction);\r
5cce8524
ST
209 if (Status != XENSTORE_STATUS_SUCCESS) {\r
210 DEBUG ((EFI_D_WARN, "XenPvBlk: Failed to start transaction, %d\n", Status));\r
211 goto Error;\r
212 }\r
213\r
e26a83cd 214 Status = XenBusIo->XsPrintf (XenBusIo, &Transaction, NodeName, "ring-ref", "%d",\r
5cce8524
ST
215 Dev->RingRef);\r
216 if (Status != XENSTORE_STATUS_SUCCESS) {\r
217 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write ring-ref.\n"));\r
218 goto AbortTransaction;\r
219 }\r
e26a83cd 220 Status = XenBusIo->XsPrintf (XenBusIo, &Transaction, NodeName,\r
5cce8524
ST
221 "event-channel", "%d", Dev->EventChannel);\r
222 if (Status != XENSTORE_STATUS_SUCCESS) {\r
223 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write event-channel.\n"));\r
224 goto AbortTransaction;\r
225 }\r
e26a83cd 226 Status = XenBusIo->XsPrintf (XenBusIo, &Transaction, NodeName,\r
5cce8524
ST
227 "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE);\r
228 if (Status != XENSTORE_STATUS_SUCCESS) {\r
229 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write protocol.\n"));\r
230 goto AbortTransaction;\r
231 }\r
232\r
e26a83cd 233 Status = XenBusIo->SetState (XenBusIo, &Transaction, XenbusStateConnected);\r
5cce8524
ST
234 if (Status != XENSTORE_STATUS_SUCCESS) {\r
235 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to switch state.\n"));\r
236 goto AbortTransaction;\r
237 }\r
238\r
e26a83cd 239 Status = XenBusIo->XsTransactionEnd (XenBusIo, &Transaction, FALSE);\r
5cce8524
ST
240 if (Status == XENSTORE_STATUS_EAGAIN) {\r
241 goto Again;\r
242 }\r
243\r
244 XenBusIo->RegisterWatchBackend (XenBusIo, "state", &Dev->StateWatchToken);\r
245\r
246 //\r
247 // Waiting for backend\r
248 //\r
249 Status = XenPvBlkWaitForBackendState (Dev, XenbusStateConnected, &State);\r
250 if (Status != XENSTORE_STATUS_SUCCESS) {\r
251 DEBUG ((EFI_D_ERROR,\r
252 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",\r
253 XenBusIo->Type, XenBusIo->DeviceId, Status, State));\r
254 goto Error2;\r
255 }\r
256\r
257 Status = XenBusReadUint64 (XenBusIo, "info", TRUE, &Value);\r
4d3b9d33 258 if (Status != XENSTORE_STATUS_SUCCESS || Value > MAX_UINT32) {\r
5cce8524
ST
259 goto Error2;\r
260 }\r
261 Dev->MediaInfo.VDiskInfo = Value;\r
262 if (Dev->MediaInfo.VDiskInfo & VDISK_READONLY) {\r
263 Dev->MediaInfo.ReadWrite = FALSE;\r
264 } else {\r
265 Dev->MediaInfo.ReadWrite = TRUE;\r
266 }\r
267\r
268 Status = XenBusReadUint64 (XenBusIo, "sectors", TRUE, &Dev->MediaInfo.Sectors);\r
269 if (Status != XENSTORE_STATUS_SUCCESS) {\r
270 goto Error2;\r
271 }\r
272\r
273 Status = XenBusReadUint64 (XenBusIo, "sector-size", TRUE, &Value);\r
4d3b9d33 274 if (Status != XENSTORE_STATUS_SUCCESS || Value > MAX_UINT32) {\r
5cce8524
ST
275 goto Error2;\r
276 }\r
277 if (Value % 512 != 0) {\r
278 //\r
279 // This is not supported by the driver.\r
280 //\r
281 DEBUG ((EFI_D_ERROR, "XenPvBlk: Unsupported sector-size value %d, "\r
282 "it must be a multiple of 512\n", Value));\r
283 goto Error2;\r
284 }\r
285 Dev->MediaInfo.SectorSize = Value;\r
286\r
287 // Default value\r
288 Value = 0;\r
289 XenBusReadUint64 (XenBusIo, "feature-barrier", TRUE, &Value);\r
290 if (Value == 1) {\r
291 Dev->MediaInfo.FeatureBarrier = TRUE;\r
292 } else {\r
293 Dev->MediaInfo.FeatureBarrier = FALSE;\r
294 }\r
295\r
296 // Default value\r
297 Value = 0;\r
298 XenBusReadUint64 (XenBusIo, "feature-flush-cache", TRUE, &Value);\r
299 if (Value == 1) {\r
300 Dev->MediaInfo.FeatureFlushCache = TRUE;\r
301 } else {\r
302 Dev->MediaInfo.FeatureFlushCache = FALSE;\r
303 }\r
304\r
305 DEBUG ((EFI_D_INFO, "XenPvBlk: New disk with %ld sectors of %d bytes\n",\r
306 Dev->MediaInfo.Sectors, Dev->MediaInfo.SectorSize));\r
307\r
308 *DevPtr = Dev;\r
309 return EFI_SUCCESS;\r
310\r
311Error2:\r
312 XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken);\r
313 XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref");\r
314 XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel");\r
315 XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol");\r
316 goto Error;\r
317AbortTransaction:\r
e26a83cd 318 XenBusIo->XsTransactionEnd (XenBusIo, &Transaction, TRUE);\r
5cce8524
ST
319Error:\r
320 XenPvBlockFree (Dev);\r
321 return EFI_DEVICE_ERROR;\r
322}\r
323\r
324VOID\r
325XenPvBlockFrontShutdown (\r
326 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
327 )\r
328{\r
329 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
330 XENSTORE_STATUS Status;\r
331 UINT64 Value;\r
332\r
333 XenPvBlockSync (Dev);\r
334\r
335 Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosing);\r
336 if (Status != XENSTORE_STATUS_SUCCESS) {\r
337 DEBUG ((EFI_D_ERROR,\r
338 "XenPvBlk: error while changing state to Closing: %d\n",\r
339 Status));\r
340 goto Close;\r
341 }\r
342\r
343 Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosing, NULL);\r
344 if (Status != XENSTORE_STATUS_SUCCESS) {\r
345 DEBUG ((EFI_D_ERROR,\r
346 "XenPvBlk: error while waiting for closing backend state: %d\n",\r
347 Status));\r
348 goto Close;\r
349 }\r
350\r
351 Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosed);\r
352 if (Status != XENSTORE_STATUS_SUCCESS) {\r
353 DEBUG ((EFI_D_ERROR,\r
354 "XenPvBlk: error while changing state to Closed: %d\n",\r
355 Status));\r
356 goto Close;\r
357 }\r
358\r
359 Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosed, NULL);\r
360 if (Status != XENSTORE_STATUS_SUCCESS) {\r
361 DEBUG ((EFI_D_ERROR,\r
362 "XenPvBlk: error while waiting for closed backend state: %d\n",\r
363 Status));\r
364 goto Close;\r
365 }\r
366\r
367 Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateInitialising);\r
368 if (Status != XENSTORE_STATUS_SUCCESS) {\r
369 DEBUG ((EFI_D_ERROR,\r
370 "XenPvBlk: error while changing state to initialising: %d\n",\r
371 Status));\r
372 goto Close;\r
373 }\r
374\r
375 while (TRUE) {\r
376 Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value);\r
377 if (Status != XENSTORE_STATUS_SUCCESS) {\r
378 DEBUG ((EFI_D_ERROR,\r
379 "XenPvBlk: error while waiting for new backend state: %d\n",\r
380 Status));\r
381 goto Close;\r
382 }\r
383 if (Value <= XenbusStateInitWait || Value >= XenbusStateClosed) {\r
384 break;\r
385 }\r
386 DEBUG ((EFI_D_INFO,\r
387 "XenPvBlk: waiting backend state %d, current: %d\n",\r
388 XenbusStateInitWait, Value));\r
389 XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken);\r
390 }\r
391\r
392Close:\r
393 XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken);\r
394 XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref");\r
395 XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel");\r
396 XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol");\r
397\r
398 XenPvBlockFree (Dev);\r
399}\r
400\r
401STATIC\r
402VOID\r
403XenPvBlockWaitSlot (\r
404 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
405 )\r
406{\r
407 /* Wait for a slot */\r
408 if (RING_FULL (&Dev->Ring)) {\r
409 while (TRUE) {\r
410 XenPvBlockAsyncIoPoll (Dev);\r
411 if (!RING_FULL (&Dev->Ring)) {\r
412 break;\r
413 }\r
414 /* Really no slot, could wait for an event on Dev->EventChannel. */\r
415 }\r
416 }\r
417}\r
418\r
419VOID\r
420XenPvBlockAsyncIo (\r
421 IN OUT XEN_BLOCK_FRONT_IO *IoData,\r
422 IN BOOLEAN IsWrite\r
423 )\r
424{\r
425 XEN_BLOCK_FRONT_DEVICE *Dev = IoData->Dev;\r
426 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
427 blkif_request_t *Request;\r
428 RING_IDX RingIndex;\r
429 BOOLEAN Notify;\r
430 INT32 NumSegments, Index;\r
431 UINTN Start, End;\r
432\r
433 // Can't io at non-sector-aligned location\r
434 ASSERT(!(IoData->Sector & ((Dev->MediaInfo.SectorSize / 512) - 1)));\r
435 // Can't io non-sector-sized amounts\r
436 ASSERT(!(IoData->Size & (Dev->MediaInfo.SectorSize - 1)));\r
437 // Can't io non-sector-aligned buffer\r
438 ASSERT(!((UINTN) IoData->Buffer & (Dev->MediaInfo.SectorSize - 1)));\r
439\r
440 Start = (UINTN) IoData->Buffer & ~EFI_PAGE_MASK;\r
441 End = ((UINTN) IoData->Buffer + IoData->Size + EFI_PAGE_SIZE - 1) & ~EFI_PAGE_MASK;\r
442 IoData->NumRef = NumSegments = (End - Start) / EFI_PAGE_SIZE;\r
443\r
444 ASSERT (NumSegments <= BLKIF_MAX_SEGMENTS_PER_REQUEST);\r
445\r
446 XenPvBlockWaitSlot (Dev);\r
447 RingIndex = Dev->Ring.req_prod_pvt;\r
448 Request = RING_GET_REQUEST (&Dev->Ring, RingIndex);\r
449\r
450 Request->operation = IsWrite ? BLKIF_OP_WRITE : BLKIF_OP_READ;\r
451 Request->nr_segments = NumSegments;\r
452 Request->handle = Dev->DeviceId;\r
453 Request->id = (UINTN) IoData;\r
454 Request->sector_number = IoData->Sector;\r
455\r
456 for (Index = 0; Index < NumSegments; Index++) {\r
457 Request->seg[Index].first_sect = 0;\r
458 Request->seg[Index].last_sect = EFI_PAGE_SIZE / 512 - 1;\r
459 }\r
460 Request->seg[0].first_sect = ((UINTN) IoData->Buffer & EFI_PAGE_MASK) / 512;\r
461 Request->seg[NumSegments - 1].last_sect =\r
462 (((UINTN) IoData->Buffer + IoData->Size - 1) & EFI_PAGE_MASK) / 512;\r
463 for (Index = 0; Index < NumSegments; Index++) {\r
464 UINTN Data = Start + Index * EFI_PAGE_SIZE;\r
465 XenBusIo->GrantAccess (XenBusIo, Dev->DomainId,\r
466 Data >> EFI_PAGE_SHIFT, IsWrite,\r
467 &Request->seg[Index].gref);\r
468 IoData->GrantRef[Index] = Request->seg[Index].gref;\r
469 }\r
470\r
471 Dev->Ring.req_prod_pvt = RingIndex + 1;\r
472\r
473 MemoryFence ();\r
474 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify);\r
475\r
476 if (Notify) {\r
477 UINT32 ReturnCode;\r
478 ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel);\r
479 if (ReturnCode != 0) {\r
480 DEBUG ((EFI_D_ERROR,\r
481 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",\r
482 ReturnCode));\r
483 }\r
484 }\r
485}\r
486\r
487EFI_STATUS\r
488XenPvBlockIo (\r
489 IN OUT XEN_BLOCK_FRONT_IO *IoData,\r
490 IN BOOLEAN IsWrite\r
491 )\r
492{\r
493 //\r
494 // Status value that correspond to an IO in progress.\r
495 //\r
496 IoData->Status = EFI_ALREADY_STARTED;\r
497 XenPvBlockAsyncIo (IoData, IsWrite);\r
498\r
499 while (IoData->Status == EFI_ALREADY_STARTED) {\r
500 XenPvBlockAsyncIoPoll (IoData->Dev);\r
501 }\r
502\r
503 return IoData->Status;\r
504}\r
505\r
506STATIC\r
507VOID\r
508XenPvBlockPushOperation (\r
509 IN XEN_BLOCK_FRONT_DEVICE *Dev,\r
510 IN UINT8 Operation,\r
511 IN UINT64 Id\r
512 )\r
513{\r
514 INT32 Index;\r
515 blkif_request_t *Request;\r
516 BOOLEAN Notify;\r
517\r
518 XenPvBlockWaitSlot (Dev);\r
519 Index = Dev->Ring.req_prod_pvt;\r
520 Request = RING_GET_REQUEST(&Dev->Ring, Index);\r
521 Request->operation = Operation;\r
522 Request->nr_segments = 0;\r
523 Request->handle = Dev->DeviceId;\r
524 Request->id = Id;\r
525 /* Not needed anyway, but the backend will check it */\r
526 Request->sector_number = 0;\r
527 Dev->Ring.req_prod_pvt = Index + 1;\r
528 MemoryFence ();\r
529 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify);\r
530 if (Notify) {\r
531 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
532 UINT32 ReturnCode;\r
533 ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel);\r
534 if (ReturnCode != 0) {\r
535 DEBUG ((EFI_D_ERROR,\r
536 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",\r
537 ReturnCode));\r
538 }\r
539 }\r
540}\r
541\r
542VOID\r
543XenPvBlockSync (\r
544 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
545 )\r
546{\r
547 if (Dev->MediaInfo.ReadWrite) {\r
548 if (Dev->MediaInfo.FeatureBarrier) {\r
549 XenPvBlockPushOperation (Dev, BLKIF_OP_WRITE_BARRIER, 0);\r
550 }\r
551\r
552 if (Dev->MediaInfo.FeatureFlushCache) {\r
553 XenPvBlockPushOperation (Dev, BLKIF_OP_FLUSH_DISKCACHE, 0);\r
554 }\r
555 }\r
556\r
557 /* Note: This won't finish if another thread enqueues requests. */\r
558 while (TRUE) {\r
559 XenPvBlockAsyncIoPoll (Dev);\r
560 if (RING_FREE_REQUESTS (&Dev->Ring) == RING_SIZE (&Dev->Ring)) {\r
561 break;\r
562 }\r
563 }\r
564}\r
565\r
566VOID\r
567XenPvBlockAsyncIoPoll (\r
568 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
569 )\r
570{\r
571 RING_IDX ProducerIndex, ConsumerIndex;\r
572 blkif_response_t *Response;\r
573 INT32 More;\r
574\r
575 do {\r
576 ProducerIndex = Dev->Ring.sring->rsp_prod;\r
577 /* Ensure we see queued responses up to 'ProducerIndex'. */\r
578 MemoryFence ();\r
579 ConsumerIndex = Dev->Ring.rsp_cons;\r
580\r
581 while (ConsumerIndex != ProducerIndex) {\r
582 XEN_BLOCK_FRONT_IO *IoData = NULL;\r
583 INT16 Status;\r
584\r
585 Response = RING_GET_RESPONSE (&Dev->Ring, ConsumerIndex);\r
586\r
587 IoData = (VOID *) (UINTN) Response->id;\r
588 Status = Response->status;\r
589\r
590 switch (Response->operation) {\r
591 case BLKIF_OP_READ:\r
592 case BLKIF_OP_WRITE:\r
593 {\r
594 INT32 Index;\r
595\r
596 if (Status != BLKIF_RSP_OKAY) {\r
597 DEBUG ((EFI_D_ERROR,\r
598 "XenPvBlk: "\r
599 "%a error %d on %a at sector %p, num bytes %p\n",\r
600 Response->operation == BLKIF_OP_READ ? "read" : "write",\r
601 Status, IoData->Dev->NodeName,\r
602 IoData->Sector,\r
603 IoData->Size));\r
604 }\r
605\r
606 for (Index = 0; Index < IoData->NumRef; Index++) {\r
607 Dev->XenBusIo->GrantEndAccess (Dev->XenBusIo, IoData->GrantRef[Index]);\r
608 }\r
609\r
610 break;\r
611 }\r
612\r
613 case BLKIF_OP_WRITE_BARRIER:\r
614 if (Status != BLKIF_RSP_OKAY) {\r
615 DEBUG ((EFI_D_ERROR, "XenPvBlk: write barrier error %d\n", Status));\r
616 }\r
617 break;\r
618 case BLKIF_OP_FLUSH_DISKCACHE:\r
619 if (Status != BLKIF_RSP_OKAY) {\r
620 DEBUG ((EFI_D_ERROR, "XenPvBlk: flush error %d\n", Status));\r
621 }\r
622 break;\r
623\r
624 default:\r
625 DEBUG ((EFI_D_ERROR,\r
626 "XenPvBlk: unrecognized block operation %d response (status %d)\n",\r
627 Response->operation, Status));\r
628 break;\r
629 }\r
630\r
631 Dev->Ring.rsp_cons = ++ConsumerIndex;\r
632 if (IoData != NULL) {\r
633 IoData->Status = Status ? EFI_DEVICE_ERROR : EFI_SUCCESS;\r
634 }\r
635 if (Dev->Ring.rsp_cons != ConsumerIndex) {\r
636 /* We reentered, we must not continue here */\r
637 break;\r
638 }\r
639 }\r
640\r
641 RING_FINAL_CHECK_FOR_RESPONSES (&Dev->Ring, More);\r
642 } while (More != 0);\r
643}\r