]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/XenPvBlkDxe/BlockFront.c
OptionRomPkg: Replace BSD License with BSD+Patent License
[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
0f34a051 172 CHAR8 *Params;\r
5cce8524
ST
173\r
174 ASSERT (NodeName != NULL);\r
175\r
176 Dev = AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE));\r
177 Dev->Signature = XEN_BLOCK_FRONT_SIGNATURE;\r
178 Dev->NodeName = NodeName;\r
179 Dev->XenBusIo = XenBusIo;\r
180 Dev->DeviceId = XenBusIo->DeviceId;\r
181\r
182 XenBusIo->XsRead (XenBusIo, XST_NIL, "device-type", (VOID**)&DeviceType);\r
183 if (AsciiStrCmp (DeviceType, "cdrom") == 0) {\r
184 Dev->MediaInfo.CdRom = TRUE;\r
185 } else {\r
186 Dev->MediaInfo.CdRom = FALSE;\r
187 }\r
188 FreePool (DeviceType);\r
189\r
0f34a051
SS
190 if (Dev->MediaInfo.CdRom) {\r
191 Status = XenBusIo->XsBackendRead (XenBusIo, XST_NIL, "params", (VOID**)&Params);\r
192 if (Status != XENSTORE_STATUS_SUCCESS) {\r
193 DEBUG ((EFI_D_ERROR, "%a: Failed to read params (%d)\n", __FUNCTION__, Status));\r
194 goto Error;\r
195 }\r
196 if (AsciiStrLen (Params) == 0 || AsciiStrCmp (Params, "aio:") == 0) {\r
197 FreePool (Params);\r
198 DEBUG ((EFI_D_INFO, "%a: Empty cdrom\n", __FUNCTION__));\r
199 goto Error;\r
200 }\r
201 FreePool (Params);\r
202 }\r
203\r
5cce8524 204 Status = XenBusReadUint64 (XenBusIo, "backend-id", FALSE, &Value);\r
4d3b9d33 205 if (Status != XENSTORE_STATUS_SUCCESS || Value > MAX_UINT16) {\r
5cce8524
ST
206 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to get backend-id (%d)\n",\r
207 Status));\r
208 goto Error;\r
209 }\r
860088f2 210 Dev->DomainId = (domid_t)Value;\r
5cce8524
ST
211 XenBusIo->EventChannelAllocate (XenBusIo, Dev->DomainId, &Dev->EventChannel);\r
212\r
213 SharedRing = (blkif_sring_t*) AllocatePages (1);\r
214 SHARED_RING_INIT (SharedRing);\r
215 FRONT_RING_INIT (&Dev->Ring, SharedRing, EFI_PAGE_SIZE);\r
216 XenBusIo->GrantAccess (XenBusIo,\r
217 Dev->DomainId,\r
218 (INTN) SharedRing >> EFI_PAGE_SHIFT,\r
219 FALSE,\r
220 &Dev->RingRef);\r
221\r
222Again:\r
e26a83cd 223 Status = XenBusIo->XsTransactionStart (XenBusIo, &Transaction);\r
5cce8524
ST
224 if (Status != XENSTORE_STATUS_SUCCESS) {\r
225 DEBUG ((EFI_D_WARN, "XenPvBlk: Failed to start transaction, %d\n", Status));\r
226 goto Error;\r
227 }\r
228\r
e26a83cd 229 Status = XenBusIo->XsPrintf (XenBusIo, &Transaction, NodeName, "ring-ref", "%d",\r
5cce8524
ST
230 Dev->RingRef);\r
231 if (Status != XENSTORE_STATUS_SUCCESS) {\r
232 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write ring-ref.\n"));\r
233 goto AbortTransaction;\r
234 }\r
e26a83cd 235 Status = XenBusIo->XsPrintf (XenBusIo, &Transaction, NodeName,\r
5cce8524
ST
236 "event-channel", "%d", Dev->EventChannel);\r
237 if (Status != XENSTORE_STATUS_SUCCESS) {\r
238 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write event-channel.\n"));\r
239 goto AbortTransaction;\r
240 }\r
e26a83cd 241 Status = XenBusIo->XsPrintf (XenBusIo, &Transaction, NodeName,\r
5cce8524
ST
242 "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE);\r
243 if (Status != XENSTORE_STATUS_SUCCESS) {\r
244 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write protocol.\n"));\r
245 goto AbortTransaction;\r
246 }\r
247\r
e26a83cd 248 Status = XenBusIo->SetState (XenBusIo, &Transaction, XenbusStateConnected);\r
5cce8524
ST
249 if (Status != XENSTORE_STATUS_SUCCESS) {\r
250 DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to switch state.\n"));\r
251 goto AbortTransaction;\r
252 }\r
253\r
e26a83cd 254 Status = XenBusIo->XsTransactionEnd (XenBusIo, &Transaction, FALSE);\r
5cce8524
ST
255 if (Status == XENSTORE_STATUS_EAGAIN) {\r
256 goto Again;\r
257 }\r
258\r
259 XenBusIo->RegisterWatchBackend (XenBusIo, "state", &Dev->StateWatchToken);\r
260\r
261 //\r
262 // Waiting for backend\r
263 //\r
264 Status = XenPvBlkWaitForBackendState (Dev, XenbusStateConnected, &State);\r
265 if (Status != XENSTORE_STATUS_SUCCESS) {\r
266 DEBUG ((EFI_D_ERROR,\r
267 "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",\r
268 XenBusIo->Type, XenBusIo->DeviceId, Status, State));\r
269 goto Error2;\r
270 }\r
271\r
272 Status = XenBusReadUint64 (XenBusIo, "info", TRUE, &Value);\r
4d3b9d33 273 if (Status != XENSTORE_STATUS_SUCCESS || Value > MAX_UINT32) {\r
5cce8524
ST
274 goto Error2;\r
275 }\r
860088f2 276 Dev->MediaInfo.VDiskInfo = (UINT32)Value;\r
5cce8524
ST
277 if (Dev->MediaInfo.VDiskInfo & VDISK_READONLY) {\r
278 Dev->MediaInfo.ReadWrite = FALSE;\r
279 } else {\r
280 Dev->MediaInfo.ReadWrite = TRUE;\r
281 }\r
282\r
283 Status = XenBusReadUint64 (XenBusIo, "sectors", TRUE, &Dev->MediaInfo.Sectors);\r
284 if (Status != XENSTORE_STATUS_SUCCESS) {\r
285 goto Error2;\r
286 }\r
287\r
288 Status = XenBusReadUint64 (XenBusIo, "sector-size", TRUE, &Value);\r
4d3b9d33 289 if (Status != XENSTORE_STATUS_SUCCESS || Value > MAX_UINT32) {\r
5cce8524
ST
290 goto Error2;\r
291 }\r
860088f2 292 if ((UINT32)Value % 512 != 0) {\r
5cce8524
ST
293 //\r
294 // This is not supported by the driver.\r
295 //\r
6394c35a 296 DEBUG ((EFI_D_ERROR, "XenPvBlk: Unsupported sector-size value %Lu, "\r
5cce8524
ST
297 "it must be a multiple of 512\n", Value));\r
298 goto Error2;\r
299 }\r
860088f2 300 Dev->MediaInfo.SectorSize = (UINT32)Value;\r
5cce8524
ST
301\r
302 // Default value\r
303 Value = 0;\r
304 XenBusReadUint64 (XenBusIo, "feature-barrier", TRUE, &Value);\r
305 if (Value == 1) {\r
306 Dev->MediaInfo.FeatureBarrier = TRUE;\r
307 } else {\r
308 Dev->MediaInfo.FeatureBarrier = FALSE;\r
309 }\r
310\r
311 // Default value\r
312 Value = 0;\r
313 XenBusReadUint64 (XenBusIo, "feature-flush-cache", TRUE, &Value);\r
314 if (Value == 1) {\r
315 Dev->MediaInfo.FeatureFlushCache = TRUE;\r
316 } else {\r
317 Dev->MediaInfo.FeatureFlushCache = FALSE;\r
318 }\r
319\r
320 DEBUG ((EFI_D_INFO, "XenPvBlk: New disk with %ld sectors of %d bytes\n",\r
321 Dev->MediaInfo.Sectors, Dev->MediaInfo.SectorSize));\r
322\r
323 *DevPtr = Dev;\r
324 return EFI_SUCCESS;\r
325\r
326Error2:\r
327 XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken);\r
328 XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref");\r
329 XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel");\r
330 XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol");\r
331 goto Error;\r
332AbortTransaction:\r
e26a83cd 333 XenBusIo->XsTransactionEnd (XenBusIo, &Transaction, TRUE);\r
5cce8524
ST
334Error:\r
335 XenPvBlockFree (Dev);\r
336 return EFI_DEVICE_ERROR;\r
337}\r
338\r
339VOID\r
340XenPvBlockFrontShutdown (\r
341 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
342 )\r
343{\r
344 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
345 XENSTORE_STATUS Status;\r
346 UINT64 Value;\r
347\r
348 XenPvBlockSync (Dev);\r
349\r
350 Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosing);\r
351 if (Status != XENSTORE_STATUS_SUCCESS) {\r
352 DEBUG ((EFI_D_ERROR,\r
353 "XenPvBlk: error while changing state to Closing: %d\n",\r
354 Status));\r
355 goto Close;\r
356 }\r
357\r
358 Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosing, NULL);\r
359 if (Status != XENSTORE_STATUS_SUCCESS) {\r
360 DEBUG ((EFI_D_ERROR,\r
361 "XenPvBlk: error while waiting for closing backend state: %d\n",\r
362 Status));\r
363 goto Close;\r
364 }\r
365\r
366 Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosed);\r
367 if (Status != XENSTORE_STATUS_SUCCESS) {\r
368 DEBUG ((EFI_D_ERROR,\r
369 "XenPvBlk: error while changing state to Closed: %d\n",\r
370 Status));\r
371 goto Close;\r
372 }\r
373\r
374 Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosed, NULL);\r
375 if (Status != XENSTORE_STATUS_SUCCESS) {\r
376 DEBUG ((EFI_D_ERROR,\r
377 "XenPvBlk: error while waiting for closed backend state: %d\n",\r
378 Status));\r
379 goto Close;\r
380 }\r
381\r
382 Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateInitialising);\r
383 if (Status != XENSTORE_STATUS_SUCCESS) {\r
384 DEBUG ((EFI_D_ERROR,\r
385 "XenPvBlk: error while changing state to initialising: %d\n",\r
386 Status));\r
387 goto Close;\r
388 }\r
389\r
390 while (TRUE) {\r
391 Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value);\r
392 if (Status != XENSTORE_STATUS_SUCCESS) {\r
393 DEBUG ((EFI_D_ERROR,\r
394 "XenPvBlk: error while waiting for new backend state: %d\n",\r
395 Status));\r
396 goto Close;\r
397 }\r
398 if (Value <= XenbusStateInitWait || Value >= XenbusStateClosed) {\r
399 break;\r
400 }\r
401 DEBUG ((EFI_D_INFO,\r
6394c35a 402 "XenPvBlk: waiting backend state %d, current: %Lu\n",\r
5cce8524
ST
403 XenbusStateInitWait, Value));\r
404 XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken);\r
405 }\r
406\r
407Close:\r
408 XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken);\r
409 XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref");\r
410 XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel");\r
411 XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol");\r
412\r
413 XenPvBlockFree (Dev);\r
414}\r
415\r
416STATIC\r
417VOID\r
418XenPvBlockWaitSlot (\r
419 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
420 )\r
421{\r
422 /* Wait for a slot */\r
423 if (RING_FULL (&Dev->Ring)) {\r
424 while (TRUE) {\r
425 XenPvBlockAsyncIoPoll (Dev);\r
426 if (!RING_FULL (&Dev->Ring)) {\r
427 break;\r
428 }\r
429 /* Really no slot, could wait for an event on Dev->EventChannel. */\r
430 }\r
431 }\r
432}\r
433\r
434VOID\r
435XenPvBlockAsyncIo (\r
436 IN OUT XEN_BLOCK_FRONT_IO *IoData,\r
437 IN BOOLEAN IsWrite\r
438 )\r
439{\r
440 XEN_BLOCK_FRONT_DEVICE *Dev = IoData->Dev;\r
441 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
442 blkif_request_t *Request;\r
443 RING_IDX RingIndex;\r
444 BOOLEAN Notify;\r
445 INT32 NumSegments, Index;\r
446 UINTN Start, End;\r
447\r
448 // Can't io at non-sector-aligned location\r
449 ASSERT(!(IoData->Sector & ((Dev->MediaInfo.SectorSize / 512) - 1)));\r
450 // Can't io non-sector-sized amounts\r
451 ASSERT(!(IoData->Size & (Dev->MediaInfo.SectorSize - 1)));\r
452 // Can't io non-sector-aligned buffer\r
453 ASSERT(!((UINTN) IoData->Buffer & (Dev->MediaInfo.SectorSize - 1)));\r
454\r
455 Start = (UINTN) IoData->Buffer & ~EFI_PAGE_MASK;\r
456 End = ((UINTN) IoData->Buffer + IoData->Size + EFI_PAGE_SIZE - 1) & ~EFI_PAGE_MASK;\r
860088f2 457 IoData->NumRef = NumSegments = (INT32)((End - Start) / EFI_PAGE_SIZE);\r
5cce8524
ST
458\r
459 ASSERT (NumSegments <= BLKIF_MAX_SEGMENTS_PER_REQUEST);\r
460\r
461 XenPvBlockWaitSlot (Dev);\r
462 RingIndex = Dev->Ring.req_prod_pvt;\r
463 Request = RING_GET_REQUEST (&Dev->Ring, RingIndex);\r
464\r
465 Request->operation = IsWrite ? BLKIF_OP_WRITE : BLKIF_OP_READ;\r
860088f2 466 Request->nr_segments = (UINT8)NumSegments;\r
5cce8524
ST
467 Request->handle = Dev->DeviceId;\r
468 Request->id = (UINTN) IoData;\r
469 Request->sector_number = IoData->Sector;\r
470\r
471 for (Index = 0; Index < NumSegments; Index++) {\r
472 Request->seg[Index].first_sect = 0;\r
473 Request->seg[Index].last_sect = EFI_PAGE_SIZE / 512 - 1;\r
474 }\r
860088f2 475 Request->seg[0].first_sect = (UINT8)(((UINTN) IoData->Buffer & EFI_PAGE_MASK) / 512);\r
5cce8524 476 Request->seg[NumSegments - 1].last_sect =\r
860088f2 477 (UINT8)((((UINTN) IoData->Buffer + IoData->Size - 1) & EFI_PAGE_MASK) / 512);\r
5cce8524
ST
478 for (Index = 0; Index < NumSegments; Index++) {\r
479 UINTN Data = Start + Index * EFI_PAGE_SIZE;\r
480 XenBusIo->GrantAccess (XenBusIo, Dev->DomainId,\r
481 Data >> EFI_PAGE_SHIFT, IsWrite,\r
482 &Request->seg[Index].gref);\r
483 IoData->GrantRef[Index] = Request->seg[Index].gref;\r
484 }\r
485\r
486 Dev->Ring.req_prod_pvt = RingIndex + 1;\r
487\r
488 MemoryFence ();\r
489 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify);\r
490\r
491 if (Notify) {\r
492 UINT32 ReturnCode;\r
493 ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel);\r
494 if (ReturnCode != 0) {\r
495 DEBUG ((EFI_D_ERROR,\r
496 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",\r
497 ReturnCode));\r
498 }\r
499 }\r
500}\r
501\r
502EFI_STATUS\r
503XenPvBlockIo (\r
504 IN OUT XEN_BLOCK_FRONT_IO *IoData,\r
505 IN BOOLEAN IsWrite\r
506 )\r
507{\r
508 //\r
509 // Status value that correspond to an IO in progress.\r
510 //\r
511 IoData->Status = EFI_ALREADY_STARTED;\r
512 XenPvBlockAsyncIo (IoData, IsWrite);\r
513\r
514 while (IoData->Status == EFI_ALREADY_STARTED) {\r
515 XenPvBlockAsyncIoPoll (IoData->Dev);\r
516 }\r
517\r
518 return IoData->Status;\r
519}\r
520\r
521STATIC\r
522VOID\r
523XenPvBlockPushOperation (\r
524 IN XEN_BLOCK_FRONT_DEVICE *Dev,\r
525 IN UINT8 Operation,\r
526 IN UINT64 Id\r
527 )\r
528{\r
529 INT32 Index;\r
530 blkif_request_t *Request;\r
531 BOOLEAN Notify;\r
532\r
533 XenPvBlockWaitSlot (Dev);\r
534 Index = Dev->Ring.req_prod_pvt;\r
535 Request = RING_GET_REQUEST(&Dev->Ring, Index);\r
536 Request->operation = Operation;\r
537 Request->nr_segments = 0;\r
538 Request->handle = Dev->DeviceId;\r
539 Request->id = Id;\r
540 /* Not needed anyway, but the backend will check it */\r
541 Request->sector_number = 0;\r
542 Dev->Ring.req_prod_pvt = Index + 1;\r
543 MemoryFence ();\r
544 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify);\r
545 if (Notify) {\r
546 XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
547 UINT32 ReturnCode;\r
548 ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel);\r
549 if (ReturnCode != 0) {\r
550 DEBUG ((EFI_D_ERROR,\r
551 "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",\r
552 ReturnCode));\r
553 }\r
554 }\r
555}\r
556\r
557VOID\r
558XenPvBlockSync (\r
559 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
560 )\r
561{\r
562 if (Dev->MediaInfo.ReadWrite) {\r
563 if (Dev->MediaInfo.FeatureBarrier) {\r
564 XenPvBlockPushOperation (Dev, BLKIF_OP_WRITE_BARRIER, 0);\r
565 }\r
566\r
567 if (Dev->MediaInfo.FeatureFlushCache) {\r
568 XenPvBlockPushOperation (Dev, BLKIF_OP_FLUSH_DISKCACHE, 0);\r
569 }\r
570 }\r
571\r
572 /* Note: This won't finish if another thread enqueues requests. */\r
573 while (TRUE) {\r
574 XenPvBlockAsyncIoPoll (Dev);\r
575 if (RING_FREE_REQUESTS (&Dev->Ring) == RING_SIZE (&Dev->Ring)) {\r
576 break;\r
577 }\r
578 }\r
579}\r
580\r
581VOID\r
582XenPvBlockAsyncIoPoll (\r
583 IN XEN_BLOCK_FRONT_DEVICE *Dev\r
584 )\r
585{\r
586 RING_IDX ProducerIndex, ConsumerIndex;\r
587 blkif_response_t *Response;\r
588 INT32 More;\r
589\r
590 do {\r
591 ProducerIndex = Dev->Ring.sring->rsp_prod;\r
592 /* Ensure we see queued responses up to 'ProducerIndex'. */\r
593 MemoryFence ();\r
594 ConsumerIndex = Dev->Ring.rsp_cons;\r
595\r
596 while (ConsumerIndex != ProducerIndex) {\r
597 XEN_BLOCK_FRONT_IO *IoData = NULL;\r
598 INT16 Status;\r
599\r
600 Response = RING_GET_RESPONSE (&Dev->Ring, ConsumerIndex);\r
601\r
602 IoData = (VOID *) (UINTN) Response->id;\r
603 Status = Response->status;\r
604\r
605 switch (Response->operation) {\r
606 case BLKIF_OP_READ:\r
607 case BLKIF_OP_WRITE:\r
608 {\r
609 INT32 Index;\r
610\r
611 if (Status != BLKIF_RSP_OKAY) {\r
612 DEBUG ((EFI_D_ERROR,\r
613 "XenPvBlk: "\r
6394c35a 614 "%a error %d on %a at sector %Lx, num bytes %Lx\n",\r
5cce8524
ST
615 Response->operation == BLKIF_OP_READ ? "read" : "write",\r
616 Status, IoData->Dev->NodeName,\r
6394c35a
LE
617 (UINT64)IoData->Sector,\r
618 (UINT64)IoData->Size));\r
5cce8524
ST
619 }\r
620\r
621 for (Index = 0; Index < IoData->NumRef; Index++) {\r
622 Dev->XenBusIo->GrantEndAccess (Dev->XenBusIo, IoData->GrantRef[Index]);\r
623 }\r
624\r
625 break;\r
626 }\r
627\r
628 case BLKIF_OP_WRITE_BARRIER:\r
629 if (Status != BLKIF_RSP_OKAY) {\r
630 DEBUG ((EFI_D_ERROR, "XenPvBlk: write barrier error %d\n", Status));\r
631 }\r
632 break;\r
633 case BLKIF_OP_FLUSH_DISKCACHE:\r
634 if (Status != BLKIF_RSP_OKAY) {\r
635 DEBUG ((EFI_D_ERROR, "XenPvBlk: flush error %d\n", Status));\r
636 }\r
637 break;\r
638\r
639 default:\r
640 DEBUG ((EFI_D_ERROR,\r
641 "XenPvBlk: unrecognized block operation %d response (status %d)\n",\r
642 Response->operation, Status));\r
643 break;\r
644 }\r
645\r
646 Dev->Ring.rsp_cons = ++ConsumerIndex;\r
647 if (IoData != NULL) {\r
648 IoData->Status = Status ? EFI_DEVICE_ERROR : EFI_SUCCESS;\r
649 }\r
650 if (Dev->Ring.rsp_cons != ConsumerIndex) {\r
651 /* We reentered, we must not continue here */\r
652 break;\r
653 }\r
654 }\r
655\r
656 RING_FINAL_CHECK_FOR_RESPONSES (&Dev->Ring, More);\r
657 } while (More != 0);\r
658}\r