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