--- /dev/null
+/** @file\r
+ Low-level kernel interface to the XenStore.\r
+\r
+ The XenStore interface is a simple storage system that is a means of\r
+ communicating state and configuration data between the Xen Domain 0\r
+ and the various guest domains. All configuration data other than\r
+ a small amount of essential information required during the early\r
+ boot process of launching a Xen aware guest, is managed using the\r
+ XenStore.\r
+\r
+ The XenStore is ASCII string based, and has a structure and semantics\r
+ similar to a filesystem. There are files and directories, the directories\r
+ able to contain files or other directories. The depth of the hierachy\r
+ is only limited by the XenStore's maximum path length.\r
+\r
+ The communication channel between the XenStore service and other\r
+ domains is via two, guest specific, ring buffers in a shared memory\r
+ area. One ring buffer is used for communicating in each direction.\r
+ The grant table references for this shared memory are given to the\r
+ guest either via the xen_start_info structure for a fully para-\r
+ virtualized guest, or via HVM hypercalls for a hardware virtualized\r
+ guest.\r
+\r
+ The XenStore communication relies on an event channel and thus\r
+ interrupts. But under OVMF this XenStore client will pull the\r
+ state of the event channel.\r
+\r
+ Several Xen services depend on the XenStore, most notably the\r
+ XenBus used to discover and manage Xen devices.\r
+\r
+ Copyright (C) 2005 Rusty Russell, IBM Corporation\r
+ Copyright (C) 2009,2010 Spectra Logic Corporation\r
+ Copyright (C) 2014, Citrix Ltd.\r
+\r
+ This file may be distributed separately from the Linux kernel, or\r
+ incorporated into other software packages, subject to the following license:\r
+\r
+ Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ of this source file (the "Software"), to deal in the Software without\r
+ restriction, including without limitation the rights to use, copy, modify,\r
+ merge, publish, distribute, sublicense, and/or sell copies of the Software,\r
+ and to permit persons to whom the Software is furnished to do so, subject to\r
+ the following conditions:\r
+\r
+ The above copyright notice and this permission notice shall be included in\r
+ all copies or substantial portions of the Software.\r
+\r
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\r
+ IN THE SOFTWARE.\r
+**/\r
+\r
+#include "XenStore.h"\r
+\r
+#include <Library/PrintLib.h>\r
+\r
+#include <IndustryStandard/Xen/hvm/params.h>\r
+\r
+#include "XenHypercall.h"\r
+#include "EventChannel.h"\r
+\r
+//\r
+// Private Data Structures\r
+//\r
+\r
+typedef struct {\r
+ CONST VOID *Data;\r
+ UINTN Len;\r
+} WRITE_REQUEST;\r
+\r
+/* Register callback to watch subtree (node) in the XenStore. */\r
+#define XENSTORE_WATCH_SIGNATURE SIGNATURE_32 ('X','S','w','a')\r
+struct _XENSTORE_WATCH\r
+{\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+\r
+ /* Path being watched. */\r
+ CHAR8 *Node;\r
+};\r
+\r
+#define XENSTORE_WATCH_FROM_LINK(l) \\r
+ CR (l, XENSTORE_WATCH, Link, XENSTORE_WATCH_SIGNATURE)\r
+\r
+\r
+/**\r
+ * Structure capturing messages received from the XenStore service.\r
+ */\r
+#define XENSTORE_MESSAGE_SIGNATURE SIGNATURE_32 ('X', 'S', 's', 'm')\r
+typedef struct {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+\r
+ struct xsd_sockmsg Header;\r
+\r
+ union {\r
+ /* Queued replies. */\r
+ struct {\r
+ CHAR8 *Body;\r
+ } Reply;\r
+\r
+ /* Queued watch events. */\r
+ struct {\r
+ XENSTORE_WATCH *Handle;\r
+ CONST CHAR8 **Vector;\r
+ UINT32 VectorSize;\r
+ } Watch;\r
+ } u;\r
+} XENSTORE_MESSAGE;\r
+#define XENSTORE_MESSAGE_FROM_LINK(r) \\r
+ CR (r, XENSTORE_MESSAGE, Link, XENSTORE_MESSAGE_SIGNATURE)\r
+\r
+/**\r
+ * Container for all XenStore related state.\r
+ */\r
+typedef struct {\r
+ /**\r
+ * Pointer to shared memory communication structures allowing us\r
+ * to communicate with the XenStore service.\r
+ */\r
+ struct xenstore_domain_interface *XenStore;\r
+\r
+ XENBUS_DEVICE *Dev;\r
+\r
+ /**\r
+ * A list of replies to our requests.\r
+ *\r
+ * The reply list is filled by xs_rcv_thread(). It\r
+ * is consumed by the context that issued the request\r
+ * to which a reply is made. The requester blocks in\r
+ * XenStoreReadReply ().\r
+ *\r
+ * /note Only one requesting context can be active at a time.\r
+ */\r
+ LIST_ENTRY ReplyList;\r
+\r
+ /** Lock protecting the reply list. */\r
+ EFI_LOCK ReplyLock;\r
+\r
+ /**\r
+ * List of registered watches.\r
+ */\r
+ LIST_ENTRY RegisteredWatches;\r
+\r
+ /** Lock protecting the registered watches list. */\r
+ EFI_LOCK RegisteredWatchesLock;\r
+\r
+ /**\r
+ * List of pending watch callback events.\r
+ */\r
+ LIST_ENTRY WatchEvents;\r
+\r
+ /** Lock protecting the watch calback list. */\r
+ EFI_LOCK WatchEventsLock;\r
+\r
+ /**\r
+ * The event channel for communicating with the\r
+ * XenStore service.\r
+ */\r
+ evtchn_port_t EventChannel;\r
+\r
+ /** Handle for XenStore events. */\r
+ EFI_EVENT EventChannelEvent;\r
+} XENSTORE_PRIVATE;\r
+\r
+//\r
+// Global Data\r
+//\r
+static XENSTORE_PRIVATE xs;\r
+\r
+\r
+//\r
+// Private Utility Functions\r
+//\r
+\r
+/**\r
+ Count and optionally record pointers to a number of NUL terminated\r
+ strings in a buffer.\r
+\r
+ @param Strings A pointer to a contiguous buffer of NUL terminated strings.\r
+ @param Len The length of the buffer pointed to by strings.\r
+ @param Dst An array to store pointers to each string found in strings.\r
+\r
+ @return A count of the number of strings found.\r
+**/\r
+STATIC\r
+UINT32\r
+ExtractStrings (\r
+ IN CONST CHAR8 *Strings,\r
+ IN UINTN Len,\r
+ OUT CONST CHAR8 **Dst OPTIONAL\r
+ )\r
+{\r
+ UINT32 Num = 0;\r
+ CONST CHAR8 *Ptr;\r
+\r
+ for (Ptr = Strings; Ptr < Strings + Len; Ptr += AsciiStrSize (Ptr)) {\r
+ if (Dst != NULL) {\r
+ *Dst++ = Ptr;\r
+ }\r
+ Num++;\r
+ }\r
+\r
+ return Num;\r
+}\r
+\r
+/**\r
+ Convert a contiguous buffer containing a series of NUL terminated\r
+ strings into an array of pointers to strings.\r
+\r
+ The returned pointer references the array of string pointers which\r
+ is followed by the storage for the string data. It is the client's\r
+ responsibility to free this storage.\r
+\r
+ The storage addressed by Strings is free'd prior to Split returning.\r
+\r
+ @param Strings A pointer to a contiguous buffer of NUL terminated strings.\r
+ @param Len The length of the buffer pointed to by strings.\r
+ @param NumPtr The number of strings found and returned in the strings\r
+ array.\r
+\r
+ @return An array of pointers to the strings found in the input buffer.\r
+**/\r
+STATIC\r
+CONST CHAR8 **\r
+Split (\r
+ IN CHAR8 *Strings,\r
+ IN UINTN Len,\r
+ OUT UINT32 *NumPtr\r
+ )\r
+{\r
+ CONST CHAR8 **Dst;\r
+\r
+ ASSERT(NumPtr != NULL);\r
+ ASSERT(Strings != NULL);\r
+\r
+ /* Protect against unterminated buffers. */\r
+ if (Len > 0) {\r
+ Strings[Len - 1] = '\0';\r
+ }\r
+\r
+ /* Count the Strings. */\r
+ *NumPtr = ExtractStrings (Strings, Len, NULL);\r
+\r
+ /* Transfer to one big alloc for easy freeing by the caller. */\r
+ Dst = AllocatePool (*NumPtr * sizeof (CHAR8 *) + Len);\r
+ CopyMem (&Dst[*NumPtr], Strings, Len);\r
+ FreePool (Strings);\r
+\r
+ /* Extract pointers to newly allocated array. */\r
+ Strings = (CHAR8 *) &Dst[*NumPtr];\r
+ ExtractStrings (Strings, Len, Dst);\r
+\r
+ return (Dst);\r
+}\r
+\r
+/**\r
+ Convert from watch token (unique identifier) to the associated\r
+ internal tracking structure for this watch.\r
+\r
+ @param Tocken The unique identifier for the watch to find.\r
+\r
+ @return A pointer to the found watch structure or NULL.\r
+**/\r
+STATIC\r
+XENSTORE_WATCH *\r
+XenStoreFindWatch (\r
+ IN CONST CHAR8 *Token\r
+ )\r
+{\r
+ XENSTORE_WATCH *Watch, *WantedWatch;\r
+ LIST_ENTRY *Entry;\r
+\r
+ WantedWatch = (VOID *) AsciiStrHexToUintn (Token);\r
+\r
+ if (IsListEmpty (&xs.RegisteredWatches)) {\r
+ return NULL;\r
+ }\r
+ for (Entry = GetFirstNode (&xs.RegisteredWatches);\r
+ !IsNull (&xs.RegisteredWatches, Entry);\r
+ Entry = GetNextNode (&xs.RegisteredWatches, Entry)) {\r
+ Watch = XENSTORE_WATCH_FROM_LINK (Entry);\r
+ if (Watch == WantedWatch)\r
+ return Watch;\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+//\r
+// Public Utility Functions\r
+// API comments for these methods can be found in XenStore.h\r
+//\r
+\r
+CHAR8 *\r
+XenStoreJoin (\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node\r
+ )\r
+{\r
+ CHAR8 *Buf;\r
+\r
+ /* +1 for '/' and +1 for '\0' */\r
+ Buf = AllocateZeroPool (\r
+ AsciiStrLen (DirectoryPath) + AsciiStrLen (Node) + 2);\r
+ AsciiStrCat (Buf, DirectoryPath);\r
+ if (Node[0] != '\0') {\r
+ AsciiStrCat (Buf, "/");\r
+ AsciiStrCat (Buf, Node);\r
+ }\r
+\r
+ return Buf;\r
+}\r
+\r
+//\r
+// Low Level Communication Management\r
+//\r
+\r
+/**\r
+ Verify that the indexes for a ring are valid.\r
+\r
+ The difference between the producer and consumer cannot\r
+ exceed the size of the ring.\r
+\r
+ @param Cons The consumer index for the ring to test.\r
+ @param Prod The producer index for the ring to test.\r
+\r
+ @retval TRUE If indexes are in range.\r
+ @retval FALSE If the indexes are out of range.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+XenStoreCheckIndexes (\r
+ XENSTORE_RING_IDX Cons,\r
+ XENSTORE_RING_IDX Prod\r
+ )\r
+{\r
+ return ((Prod - Cons) <= XENSTORE_RING_SIZE);\r
+}\r
+\r
+/**\r
+ Return a pointer to, and the length of, the contiguous\r
+ free region available for output in a ring buffer.\r
+\r
+ @param Cons The consumer index for the ring.\r
+ @param Prod The producer index for the ring.\r
+ @param Buffer The base address of the ring's storage.\r
+ @param LenPtr The amount of contiguous storage available.\r
+\r
+ @return A pointer to the start location of the free region.\r
+**/\r
+STATIC\r
+VOID *\r
+XenStoreGetOutputChunk (\r
+ IN XENSTORE_RING_IDX Cons,\r
+ IN XENSTORE_RING_IDX Prod,\r
+ IN CHAR8 *Buffer,\r
+ OUT UINT32 *LenPtr\r
+ )\r
+{\r
+ UINT32 Len;\r
+ Len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX (Prod);\r
+ if ((XENSTORE_RING_SIZE - (Prod - Cons)) < Len) {\r
+ Len = XENSTORE_RING_SIZE - (Prod - Cons);\r
+ }\r
+ *LenPtr = Len;\r
+ return (Buffer + MASK_XENSTORE_IDX (Prod));\r
+}\r
+\r
+/**\r
+ Return a pointer to, and the length of, the contiguous\r
+ data available to read from a ring buffer.\r
+\r
+ @param Cons The consumer index for the ring.\r
+ @param Prod The producer index for the ring.\r
+ @param Buffer The base address of the ring's storage.\r
+ @param LenPtr The amount of contiguous data available to read.\r
+\r
+ @return A pointer to the start location of the available data.\r
+**/\r
+STATIC\r
+CONST VOID *\r
+XenStoreGetInputChunk (\r
+ IN XENSTORE_RING_IDX Cons,\r
+ IN XENSTORE_RING_IDX Prod,\r
+ IN CONST CHAR8 *Buffer,\r
+ OUT UINT32 *LenPtr\r
+ )\r
+{\r
+ UINT32 Len;\r
+\r
+ Len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX (Cons);\r
+ if ((Prod - Cons) < Len) {\r
+ Len = Prod - Cons;\r
+ }\r
+ *LenPtr = Len;\r
+ return (Buffer + MASK_XENSTORE_IDX (Cons));\r
+}\r
+\r
+/**\r
+ Wait for an event or timeout.\r
+\r
+ @param Event Event to wait for.\r
+ @param Timeout A timeout value in 100ns units.\r
+\r
+ @retval EFI_SUCCESS Event have been triggered or the current TPL is not\r
+ TPL_APPLICATION.\r
+ @retval EFI_TIMEOUT Timeout have expired.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+XenStoreWaitForEvent (\r
+ IN EFI_EVENT Event,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_STATUS Status;\r
+ EFI_EVENT TimerEvent;\r
+ EFI_EVENT WaitList[2];\r
+\r
+ gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);\r
+ gBS->SetTimer (TimerEvent, TimerRelative, Timeout);\r
+\r
+ WaitList[0] = xs.EventChannelEvent;\r
+ WaitList[1] = TimerEvent;\r
+ Status = gBS->WaitForEvent (2, WaitList, &Index);\r
+ ASSERT (Status != EFI_INVALID_PARAMETER);\r
+ gBS->CloseEvent (TimerEvent);\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ if (Index == 1) {\r
+ return EFI_TIMEOUT;\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Transmit data to the XenStore service.\r
+\r
+ The buffer pointed to by DataPtr is at least Len bytes in length.\r
+\r
+ @param DataPtr A pointer to the contiguous data to send.\r
+ @param Len The amount of data to send.\r
+\r
+ @return On success 0, otherwise an errno value indicating the\r
+ cause of failure.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreWriteStore (\r
+ IN CONST VOID *DataPtr,\r
+ IN UINTN Len\r
+ )\r
+{\r
+ XENSTORE_RING_IDX Cons, Prod;\r
+ CONST CHAR8 *Data = (CONST CHAR8 *)DataPtr;\r
+\r
+ while (Len != 0) {\r
+ void *Dest;\r
+ UINT32 Available;\r
+\r
+ Cons = xs.XenStore->req_cons;\r
+ Prod = xs.XenStore->req_prod;\r
+ if ((Prod - Cons) == XENSTORE_RING_SIZE) {\r
+ /*\r
+ * Output ring is full. Wait for a ring event.\r
+ *\r
+ * Note that the events from both queues are combined, so being woken\r
+ * does not guarantee that data exist in the read ring.\r
+ */\r
+ EFI_STATUS Status;\r
+\r
+ Status = XenStoreWaitForEvent (xs.EventChannelEvent,\r
+ EFI_TIMER_PERIOD_SECONDS (1));\r
+ if (Status == EFI_TIMEOUT) {\r
+ DEBUG ((EFI_D_WARN, "XenStore Write, waiting for a ring event.\n"));\r
+ }\r
+ continue;\r
+ }\r
+\r
+ /* Verify queue sanity. */\r
+ if (!XenStoreCheckIndexes (Cons, Prod)) {\r
+ xs.XenStore->req_cons = xs.XenStore->req_prod = 0;\r
+ return XENSTORE_STATUS_EIO;\r
+ }\r
+\r
+ Dest = XenStoreGetOutputChunk (Cons, Prod, xs.XenStore->req, &Available);\r
+ if (Available > Len) {\r
+ Available = Len;\r
+ }\r
+\r
+ CopyMem (Dest, Data, Available);\r
+ Data += Available;\r
+ Len -= Available;\r
+\r
+ /*\r
+ * The store to the producer index, which indicates\r
+ * to the other side that new data has arrived, must\r
+ * be visible only after our copy of the data into the\r
+ * ring has completed.\r
+ */\r
+ MemoryFence ();\r
+ xs.XenStore->req_prod += Available;\r
+\r
+ /*\r
+ * The other side will see the change to req_prod at the time of the\r
+ * interrupt.\r
+ */\r
+ MemoryFence ();\r
+ XenEventChannelNotify (xs.Dev, xs.EventChannel);\r
+ }\r
+\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+/**\r
+ Receive data from the XenStore service.\r
+\r
+ The buffer pointed to by DataPtr is at least Len bytes in length.\r
+\r
+ @param DataPtr A pointer to the contiguous buffer to receive the data.\r
+ @param Len The amount of data to receive.\r
+\r
+ @return On success 0, otherwise an errno value indicating the\r
+ cause of failure.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreReadStore (\r
+ OUT VOID *DataPtr,\r
+ IN UINTN Len\r
+ )\r
+{\r
+ XENSTORE_RING_IDX Cons, Prod;\r
+ CHAR8 *Data = (CHAR8 *) DataPtr;\r
+\r
+ while (Len != 0) {\r
+ UINT32 Available;\r
+ CONST CHAR8 *Src;\r
+\r
+ Cons = xs.XenStore->rsp_cons;\r
+ Prod = xs.XenStore->rsp_prod;\r
+ if (Cons == Prod) {\r
+ /*\r
+ * Nothing to read. Wait for a ring event.\r
+ *\r
+ * Note that the events from both queues are combined, so being woken\r
+ * does not guarantee that data exist in the read ring.\r
+ */\r
+ EFI_STATUS Status;\r
+\r
+ Status = XenStoreWaitForEvent (xs.EventChannelEvent,\r
+ EFI_TIMER_PERIOD_SECONDS (1));\r
+ if (Status == EFI_TIMEOUT) {\r
+ DEBUG ((EFI_D_WARN, "XenStore Read, waiting for a ring event.\n"));\r
+ }\r
+ continue;\r
+ }\r
+\r
+ /* Verify queue sanity. */\r
+ if (!XenStoreCheckIndexes (Cons, Prod)) {\r
+ xs.XenStore->rsp_cons = xs.XenStore->rsp_prod = 0;\r
+ return XENSTORE_STATUS_EIO;\r
+ }\r
+\r
+ Src = XenStoreGetInputChunk (Cons, Prod, xs.XenStore->rsp, &Available);\r
+ if (Available > Len) {\r
+ Available = Len;\r
+ }\r
+\r
+ /*\r
+ * Insure the data we read is related to the indexes\r
+ * we read above.\r
+ */\r
+ MemoryFence ();\r
+\r
+ CopyMem (Data, Src, Available);\r
+ Data += Available;\r
+ Len -= Available;\r
+\r
+ /*\r
+ * Insure that the producer of this ring does not see\r
+ * the ring space as free until after we have copied it\r
+ * out.\r
+ */\r
+ MemoryFence ();\r
+ xs.XenStore->rsp_cons += Available;\r
+\r
+ /*\r
+ * The producer will see the updated consumer index when the event is\r
+ * delivered.\r
+ */\r
+ MemoryFence ();\r
+ XenEventChannelNotify (xs.Dev, xs.EventChannel);\r
+ }\r
+\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+//\r
+// Received Message Processing\r
+//\r
+\r
+/**\r
+ Block reading the next message from the XenStore service and\r
+ process the result.\r
+\r
+ @return XENSTORE_STATUS_SUCCESS on success. Otherwise an errno value\r
+ indicating the type of failure encountered.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreProcessMessage (\r
+ VOID\r
+ )\r
+{\r
+ XENSTORE_MESSAGE *Message;\r
+ CHAR8 *Body;\r
+ XENSTORE_STATUS Status;\r
+\r
+ Message = AllocateZeroPool (sizeof (XENSTORE_MESSAGE));\r
+ Message->Signature = XENSTORE_MESSAGE_SIGNATURE;\r
+ Status = XenStoreReadStore (&Message->Header, sizeof (Message->Header));\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ FreePool (Message);\r
+ DEBUG ((EFI_D_ERROR, "XenStore: Error read store (%d)\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Body = AllocatePool (Message->Header.len + 1);\r
+ Status = XenStoreReadStore (Body, Message->Header.len);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ FreePool (Body);\r
+ FreePool (Message);\r
+ DEBUG ((EFI_D_ERROR, "XenStore: Error read store (%d)\n", Status));\r
+ return Status;\r
+ }\r
+ Body[Message->Header.len] = '\0';\r
+\r
+ if (Message->Header.type == XS_WATCH_EVENT) {\r
+ Message->u.Watch.Vector = Split(Body, Message->Header.len,\r
+ &Message->u.Watch.VectorSize);\r
+\r
+ EfiAcquireLock (&xs.RegisteredWatchesLock);\r
+ Message->u.Watch.Handle =\r
+ XenStoreFindWatch (Message->u.Watch.Vector[XS_WATCH_TOKEN]);\r
+ DEBUG ((EFI_D_INFO, "XenStore: Watch event %a\n",\r
+ Message->u.Watch.Vector[XS_WATCH_TOKEN]));\r
+ if (Message->u.Watch.Handle != NULL) {\r
+ EfiAcquireLock (&xs.WatchEventsLock);\r
+ InsertHeadList (&xs.WatchEvents, &Message->Link);\r
+ EfiReleaseLock (&xs.WatchEventsLock);\r
+ } else {\r
+ DEBUG ((EFI_D_WARN, "XenStore: Watch handle %a not found\n",\r
+ Message->u.Watch.Vector[XS_WATCH_TOKEN]));\r
+ FreePool(Message->u.Watch.Vector);\r
+ FreePool(Message);\r
+ }\r
+ EfiReleaseLock (&xs.RegisteredWatchesLock);\r
+ } else {\r
+ Message->u.Reply.Body = Body;\r
+ EfiAcquireLock (&xs.ReplyLock);\r
+ InsertTailList (&xs.ReplyList, &Message->Link);\r
+ EfiReleaseLock (&xs.ReplyLock);\r
+ }\r
+\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+//\r
+// XenStore Message Request/Reply Processing\r
+//\r
+\r
+/**\r
+ Convert a XenStore error string into an errno number.\r
+\r
+ Unknown error strings are converted to EINVAL.\r
+\r
+ @param errorstring The error string to convert.\r
+\r
+ @return The errno best matching the input string.\r
+\r
+**/\r
+typedef struct {\r
+ XENSTORE_STATUS Status;\r
+ CONST CHAR8 *ErrorStr;\r
+} XenStoreErrors;\r
+\r
+static XenStoreErrors gXenStoreErrors[] = {\r
+ { XENSTORE_STATUS_EINVAL, "EINVAL" },\r
+ { XENSTORE_STATUS_EACCES, "EACCES" },\r
+ { XENSTORE_STATUS_EEXIST, "EEXIST" },\r
+ { XENSTORE_STATUS_EISDIR, "EISDIR" },\r
+ { XENSTORE_STATUS_ENOENT, "ENOENT" },\r
+ { XENSTORE_STATUS_ENOMEM, "ENOMEM" },\r
+ { XENSTORE_STATUS_ENOSPC, "ENOSPC" },\r
+ { XENSTORE_STATUS_EIO, "EIO" },\r
+ { XENSTORE_STATUS_ENOTEMPTY, "ENOTEMPTY" },\r
+ { XENSTORE_STATUS_ENOSYS, "ENOSYS" },\r
+ { XENSTORE_STATUS_EROFS, "EROFS" },\r
+ { XENSTORE_STATUS_EBUSY, "EBUSY" },\r
+ { XENSTORE_STATUS_EAGAIN, "EAGAIN" },\r
+ { XENSTORE_STATUS_EISCONN, "EISCONN" },\r
+ { XENSTORE_STATUS_E2BIG, "E2BIG" }\r
+};\r
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))\r
+\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreGetError (\r
+ CONST CHAR8 *ErrorStr\r
+ )\r
+{\r
+ UINT32 Index;\r
+\r
+ for (Index = 0; Index < ARRAY_SIZE(gXenStoreErrors); Index++) {\r
+ if (!AsciiStrCmp (ErrorStr, gXenStoreErrors[Index].ErrorStr)) {\r
+ return gXenStoreErrors[Index].Status;\r
+ }\r
+ }\r
+ DEBUG ((EFI_D_WARN, "XenStore gave unknown error %a\n", ErrorStr));\r
+ return XENSTORE_STATUS_EINVAL;\r
+}\r
+\r
+/**\r
+ Block waiting for a reply to a message request.\r
+\r
+ @param TypePtr The returned type of the reply.\r
+ @param LenPtr The returned body length of the reply.\r
+ @param Result The returned body of the reply.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreReadReply (\r
+ OUT enum xsd_sockmsg_type *TypePtr,\r
+ OUT UINT32 *LenPtr OPTIONAL,\r
+ OUT VOID **Result\r
+ )\r
+{\r
+ XENSTORE_MESSAGE *Message;\r
+ LIST_ENTRY *Entry;\r
+ CHAR8 *Body;\r
+\r
+ while (IsListEmpty (&xs.ReplyList)) {\r
+ XENSTORE_STATUS Status;\r
+ Status = XenStoreProcessMessage ();\r
+ if (Status != XENSTORE_STATUS_SUCCESS && Status != XENSTORE_STATUS_EAGAIN) {\r
+ DEBUG ((EFI_D_ERROR, "XenStore, error while reading the ring (%d).",\r
+ Status));\r
+ return Status;\r
+ }\r
+ }\r
+ EfiAcquireLock (&xs.ReplyLock);\r
+ Entry = GetFirstNode (&xs.ReplyList);\r
+ Message = XENSTORE_MESSAGE_FROM_LINK (Entry);\r
+ RemoveEntryList (Entry);\r
+ EfiReleaseLock (&xs.ReplyLock);\r
+\r
+ *TypePtr = Message->Header.type;\r
+ if (LenPtr != NULL) {\r
+ *LenPtr = Message->Header.len;\r
+ }\r
+ Body = Message->u.Reply.Body;\r
+\r
+ FreePool (Message);\r
+ *Result = Body;\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+/**\r
+ Send a message with an optionally muti-part body to the XenStore service.\r
+\r
+ @param Transaction The transaction to use for this request.\r
+ @param RequestType The type of message to send.\r
+ @param WriteRequest Pointers to the body sections of the request.\r
+ @param NumRequests The number of body sections in the request.\r
+ @param LenPtr The returned length of the reply.\r
+ @param ResultPtr The returned body of the reply.\r
+\r
+ @return XENSTORE_STATUS_SUCCESS on success. Otherwise an errno indicating\r
+ the cause of failure.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreTalkv (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN enum xsd_sockmsg_type RequestType,\r
+ IN CONST WRITE_REQUEST *WriteRequest,\r
+ IN UINT32 NumRequests,\r
+ OUT UINT32 *LenPtr OPTIONAL,\r
+ OUT VOID **ResultPtr OPTIONAL\r
+ )\r
+{\r
+ struct xsd_sockmsg Message;\r
+ void *Return = NULL;\r
+ UINT32 Index;\r
+ XENSTORE_STATUS Status;\r
+\r
+ Message.tx_id = Transaction.Id;\r
+ Message.req_id = 0;\r
+ Message.type = RequestType;\r
+ Message.len = 0;\r
+ for (Index = 0; Index < NumRequests; Index++) {\r
+ Message.len += WriteRequest[Index].Len;\r
+ }\r
+\r
+ Status = XenStoreWriteStore (&Message, sizeof (Message));\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR, "XenStoreTalkv failed %d\n", Status));\r
+ goto Error;\r
+ }\r
+\r
+ for (Index = 0; Index < NumRequests; Index++) {\r
+ Status = XenStoreWriteStore (WriteRequest[Index].Data, WriteRequest[Index].Len);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR, "XenStoreTalkv failed %d\n", Status));\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ Status = XenStoreReadReply (&Message.type, LenPtr, &Return);\r
+\r
+Error:\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ return Status;\r
+ }\r
+\r
+ if (Message.type == XS_ERROR) {\r
+ Status = XenStoreGetError (Return);\r
+ FreePool (Return);\r
+ return Status;\r
+ }\r
+\r
+ /* Reply is either error or an echo of our request message type. */\r
+ ASSERT (Message.type == RequestType);\r
+\r
+ if (ResultPtr) {\r
+ *ResultPtr = Return;\r
+ } else {\r
+ FreePool (Return);\r
+ }\r
+\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+/**\r
+ Wrapper for XenStoreTalkv allowing easy transmission of a message with\r
+ a single, contiguous, message body.\r
+\r
+ The returned result is provided in malloced storage and thus must be free'd\r
+ by the caller.\r
+\r
+ @param Transaction The transaction to use for this request.\r
+ @param RequestType The type of message to send.\r
+ @param Body The body of the request.\r
+ @param LenPtr The returned length of the reply.\r
+ @param Result The returned body of the reply.\r
+\r
+ @return 0 on success. Otherwise an errno indicating\r
+ the cause of failure.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreSingle (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN enum xsd_sockmsg_type RequestType,\r
+ IN CONST CHAR8 *Body,\r
+ OUT UINT32 *LenPtr OPTIONAL,\r
+ OUT VOID **Result OPTIONAL\r
+ )\r
+{\r
+ WRITE_REQUEST WriteRequest;\r
+\r
+ WriteRequest.Data = (VOID *) Body;\r
+ WriteRequest.Len = AsciiStrSize (Body);\r
+\r
+ return XenStoreTalkv (Transaction, RequestType, &WriteRequest, 1,\r
+ LenPtr, Result);\r
+}\r
+\r
+//\r
+// XenStore Watch Support\r
+//\r
+\r
+/**\r
+ Transmit a watch request to the XenStore service.\r
+\r
+ @param Path The path in the XenStore to watch.\r
+ @param Tocken A unique identifier for this watch.\r
+\r
+ @return XENSTORE_STATUS_SUCCESS on success. Otherwise an errno indicating the\r
+ cause of failure.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreWatch (\r
+ CONST CHAR8 *Path,\r
+ CONST CHAR8 *Token\r
+ )\r
+{\r
+ WRITE_REQUEST WriteRequest[2];\r
+\r
+ WriteRequest[0].Data = (VOID *) Path;\r
+ WriteRequest[0].Len = AsciiStrSize (Path);\r
+ WriteRequest[1].Data = (VOID *) Token;\r
+ WriteRequest[1].Len = AsciiStrSize (Token);\r
+\r
+ return XenStoreTalkv (XST_NIL, XS_WATCH, WriteRequest, 2, NULL, NULL);\r
+}\r
+\r
+/**\r
+ Transmit an uwatch request to the XenStore service.\r
+\r
+ @param Path The path in the XenStore to watch.\r
+ @param Tocken A unique identifier for this watch.\r
+\r
+ @return XENSTORE_STATUS_SUCCESS on success. Otherwise an errno indicating\r
+ the cause of failure.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenStoreUnwatch (\r
+ CONST CHAR8 *Path,\r
+ CONST CHAR8 *Token\r
+ )\r
+{\r
+ WRITE_REQUEST WriteRequest[2];\r
+\r
+ WriteRequest[0].Data = (VOID *) Path;\r
+ WriteRequest[0].Len = AsciiStrSize (Path);\r
+ WriteRequest[1].Data = (VOID *) Token;\r
+ WriteRequest[1].Len = AsciiStrSize (Token);\r
+\r
+ return XenStoreTalkv (XST_NIL, XS_UNWATCH, WriteRequest, 2, NULL, NULL);\r
+}\r
+\r
+VOID\r
+EFIAPI\r
+NotifyEventChannelCheckForEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ XENSTORE_PRIVATE *xs;\r
+ xs = (XENSTORE_PRIVATE *)Context;\r
+ if (TestAndClearBit (xs->EventChannel, xs->Dev->SharedInfo->evtchn_pending)) {\r
+ gBS->SignalEvent (Event);\r
+ }\r
+}\r
+\r
+/**\r
+ Setup communication channels with the XenStore service.\r
+\r
+ @retval EFI_SUCCESS if everything went well.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+XenStoreInitComms (\r
+ XENSTORE_PRIVATE *xs\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT TimerEvent;\r
+ struct xenstore_domain_interface *XenStore = xs->XenStore;\r
+\r
+ Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);\r
+ Status = gBS->SetTimer (TimerEvent, TimerRelative,\r
+ EFI_TIMER_PERIOD_SECONDS (5));\r
+ while (XenStore->rsp_prod != XenStore->rsp_cons) {\r
+ Status = gBS->CheckEvent (TimerEvent);\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_WARN, "XENSTORE response ring is not quiescent "\r
+ "(%08x:%08x): fixing up\n",\r
+ XenStore->rsp_cons, XenStore->rsp_prod));\r
+ XenStore->rsp_cons = XenStore->rsp_prod;\r
+ }\r
+ }\r
+ gBS->CloseEvent (TimerEvent);\r
+\r
+ Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_NOTIFY,\r
+ NotifyEventChannelCheckForEvent, xs,\r
+ &xs->EventChannelEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Initialize XenStore.\r
+\r
+ @param Dev A XENBUS_DEVICE instance.\r
+\r
+ @retval EFI_SUCCESS if everything went well.\r
+**/\r
+EFI_STATUS\r
+XenStoreInit (\r
+ XENBUS_DEVICE *Dev\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ /**\r
+ * The HVM guest pseudo-physical frame number. This is Xen's mapping\r
+ * of the true machine frame number into our "physical address space".\r
+ */\r
+ UINTN XenStoreGpfn;\r
+\r
+ xs.Dev = Dev;\r
+\r
+ xs.EventChannel = XenHypercallHvmGetParam (Dev, HVM_PARAM_STORE_EVTCHN);\r
+ XenStoreGpfn = XenHypercallHvmGetParam (Dev, HVM_PARAM_STORE_PFN);\r
+ xs.XenStore = (VOID *) (XenStoreGpfn << EFI_PAGE_SHIFT);\r
+ DEBUG ((EFI_D_INFO, "XenBusInit: XenBus rings @%p, event channel %x\n",\r
+ xs.XenStore, xs.EventChannel));\r
+\r
+ InitializeListHead (&xs.ReplyList);\r
+ InitializeListHead (&xs.WatchEvents);\r
+ InitializeListHead (&xs.RegisteredWatches);\r
+\r
+ EfiInitializeLock (&xs.ReplyLock, TPL_NOTIFY);\r
+ EfiInitializeLock (&xs.RegisteredWatchesLock, TPL_NOTIFY);\r
+ EfiInitializeLock (&xs.WatchEventsLock, TPL_NOTIFY);\r
+\r
+ /* Initialize the shared memory rings to talk to xenstored */\r
+ Status = XenStoreInitComms (&xs);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+VOID\r
+XenStoreDeinit (\r
+ IN XENBUS_DEVICE *Dev\r
+ )\r
+{\r
+ //\r
+ // Emptying the list RegisteredWatches, but this list should already be\r
+ // empty. Every driver that is using Watches should unregister them when\r
+ // it is stopped.\r
+ //\r
+ if (!IsListEmpty (&xs.RegisteredWatches)) {\r
+ XENSTORE_WATCH *Watch;\r
+ LIST_ENTRY *Entry;\r
+ DEBUG ((EFI_D_WARN, "XenStore: RegisteredWatches is not empty, cleaning up..."));\r
+ Entry = GetFirstNode (&xs.RegisteredWatches);\r
+ while (!IsNull (&xs.RegisteredWatches, Entry)) {\r
+ Watch = XENSTORE_WATCH_FROM_LINK (Entry);\r
+ Entry = GetNextNode (&xs.RegisteredWatches, Entry);\r
+\r
+ XenStoreUnregisterWatch (Watch);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Emptying the list WatchEvents, but this list should already be empty after\r
+ // having cleanup the list RegisteredWatches.\r
+ //\r
+ if (!IsListEmpty (&xs.WatchEvents)) {\r
+ LIST_ENTRY *Entry;\r
+ DEBUG ((EFI_D_WARN, "XenStore: WatchEvents is not empty, cleaning up..."));\r
+ Entry = GetFirstNode (&xs.WatchEvents);\r
+ while (!IsNull (&xs.WatchEvents, Entry)) {\r
+ XENSTORE_MESSAGE *Message = XENSTORE_MESSAGE_FROM_LINK (Entry);\r
+ Entry = GetNextNode (&xs.WatchEvents, Entry);\r
+ RemoveEntryList (&Message->Link);\r
+ FreePool (Message->u.Watch.Vector);\r
+ FreePool (Message);\r
+ }\r
+ }\r
+\r
+ if (!IsListEmpty (&xs.ReplyList)) {\r
+ XENSTORE_MESSAGE *Message;\r
+ LIST_ENTRY *Entry;\r
+ Entry = GetFirstNode (&xs.ReplyList);\r
+ while (!IsNull (&xs.ReplyList, Entry)) {\r
+ Message = XENSTORE_MESSAGE_FROM_LINK (Entry);\r
+ Entry = GetNextNode (&xs.ReplyList, Entry);\r
+ RemoveEntryList (&Message->Link);\r
+ FreePool (Message->u.Reply.Body);\r
+ FreePool (Message);\r
+ }\r
+ }\r
+\r
+ gBS->CloseEvent (xs.EventChannelEvent);\r
+\r
+ if (xs.XenStore->server_features & XENSTORE_SERVER_FEATURE_RECONNECTION) {\r
+ xs.XenStore->connection = XENSTORE_RECONNECT;\r
+ XenEventChannelNotify (xs.Dev, xs.EventChannel);\r
+ while (*(volatile UINT32*)&xs.XenStore->connection == XENSTORE_RECONNECT) {\r
+ XenStoreWaitForEvent (xs.EventChannelEvent, EFI_TIMER_PERIOD_MILLISECONDS (100));\r
+ }\r
+ } else {\r
+ /* If the backend reads the state while we're erasing it then the\r
+ * ring state will become corrupted, preventing guest frontends from\r
+ * connecting. This is rare. To help diagnose the failure, we fill\r
+ * the ring with XS_INVALID packets. */\r
+ SetMem (xs.XenStore->req, XENSTORE_RING_SIZE, 0xff);\r
+ SetMem (xs.XenStore->rsp, XENSTORE_RING_SIZE, 0xff);\r
+ xs.XenStore->req_cons = xs.XenStore->req_prod = 0;\r
+ xs.XenStore->rsp_cons = xs.XenStore->rsp_prod = 0;\r
+ }\r
+ xs.XenStore = NULL;\r
+}\r
+\r
+//\r
+// Public API\r
+// API comments for these methods can be found in XenStore.h\r
+//\r
+\r
+XENSTORE_STATUS\r
+XenStoreListDirectory (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node,\r
+ OUT UINT32 *DirectoryCountPtr,\r
+ OUT CONST CHAR8 ***DirectoryListPtr\r
+ )\r
+{\r
+ CHAR8 *Path;\r
+ CHAR8 *TempStr;\r
+ UINT32 Len = 0;\r
+ XENSTORE_STATUS Status;\r
+\r
+ Path = XenStoreJoin (DirectoryPath, Node);\r
+ Status = XenStoreSingle (Transaction, XS_DIRECTORY, Path, &Len,\r
+ (VOID **) &TempStr);\r
+ FreePool (Path);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ return Status;\r
+ }\r
+\r
+ *DirectoryListPtr = Split (TempStr, Len, DirectoryCountPtr);\r
+\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+BOOLEAN\r
+XenStorePathExists (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *Directory,\r
+ IN CONST CHAR8 *Node\r
+ )\r
+{\r
+ CONST CHAR8 **TempStr;\r
+ XENSTORE_STATUS Status;\r
+ UINT32 TempNum;\r
+\r
+ Status = XenStoreListDirectory (Transaction, Directory, Node,\r
+ &TempNum, &TempStr);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ return FALSE;\r
+ }\r
+ FreePool (TempStr);\r
+ return TRUE;\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreRead (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node,\r
+ OUT UINT32 *LenPtr OPTIONAL,\r
+ OUT VOID **Result\r
+ )\r
+{\r
+ CHAR8 *Path;\r
+ VOID *Value;\r
+ XENSTORE_STATUS Status;\r
+\r
+ Path = XenStoreJoin (DirectoryPath, Node);\r
+ Status = XenStoreSingle (Transaction, XS_READ, Path, LenPtr, &Value);\r
+ FreePool (Path);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ return Status;\r
+ }\r
+\r
+ *Result = Value;\r
+ return XENSTORE_STATUS_SUCCESS;\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreWrite (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node,\r
+ IN CONST CHAR8 *Str\r
+ )\r
+{\r
+ CHAR8 *Path;\r
+ WRITE_REQUEST WriteRequest[2];\r
+ XENSTORE_STATUS Status;\r
+\r
+ Path = XenStoreJoin (DirectoryPath, Node);\r
+\r
+ WriteRequest[0].Data = (VOID *) Path;\r
+ WriteRequest[0].Len = AsciiStrSize (Path);\r
+ WriteRequest[1].Data = (VOID *) Str;\r
+ WriteRequest[1].Len = AsciiStrLen (Str);\r
+\r
+ Status = XenStoreTalkv (Transaction, XS_WRITE, WriteRequest, 2, NULL, NULL);\r
+ FreePool (Path);\r
+\r
+ return Status;\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreRemove (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node\r
+ )\r
+{\r
+ CHAR8 *Path;\r
+ XENSTORE_STATUS Status;\r
+\r
+ Path = XenStoreJoin (DirectoryPath, Node);\r
+ Status = XenStoreSingle (Transaction, XS_RM, Path, NULL, NULL);\r
+ FreePool (Path);\r
+\r
+ return Status;\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreTransactionStart (\r
+ OUT XENSTORE_TRANSACTION *Transaction\r
+ )\r
+{\r
+ CHAR8 *IdStr;\r
+ XENSTORE_STATUS Status;\r
+\r
+ Status = XenStoreSingle (XST_NIL, XS_TRANSACTION_START, "", NULL,\r
+ (VOID **) &IdStr);\r
+ if (Status == XENSTORE_STATUS_SUCCESS) {\r
+ Transaction->Id = AsciiStrDecimalToUintn (IdStr);\r
+ FreePool (IdStr);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreTransactionEnd (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN BOOLEAN Abort\r
+ )\r
+{\r
+ CHAR8 AbortStr[2];\r
+\r
+ if (Abort) {\r
+ AsciiStrCpy (AbortStr, "F");\r
+ } else {\r
+ AsciiStrCpy (AbortStr, "T");\r
+ }\r
+\r
+ return XenStoreSingle (Transaction, XS_TRANSACTION_END, AbortStr, NULL, NULL);\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreVSPrint (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node,\r
+ IN CONST CHAR8 *FormatString,\r
+ IN VA_LIST Marker\r
+ )\r
+{\r
+ CHAR8 *Buf;\r
+ XENSTORE_STATUS Status;\r
+ UINTN BufSize;\r
+\r
+ BufSize = SPrintLengthAsciiFormat (FormatString, Marker) + 1;\r
+ Buf = AllocateZeroPool (BufSize);\r
+ AsciiVSPrint (Buf, BufSize, FormatString, Marker);\r
+ Status = XenStoreWrite (Transaction, DirectoryPath, Node, Buf);\r
+ FreePool (Buf);\r
+\r
+ return Status;\r
+}\r
+\r
+XENSTORE_STATUS\r
+EFIAPI\r
+XenStoreSPrint (\r
+ IN XENSTORE_TRANSACTION Transaction,\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node,\r
+ IN CONST CHAR8 *FormatString,\r
+ ...\r
+ )\r
+{\r
+ VA_LIST Marker;\r
+ XENSTORE_STATUS Status;\r
+\r
+ VA_START (Marker, FormatString);\r
+ Status = XenStoreVSPrint (Transaction, DirectoryPath, Node, FormatString, Marker);\r
+ VA_END (Marker);\r
+\r
+ return Status;\r
+}\r
+\r
+XENSTORE_STATUS\r
+XenStoreRegisterWatch (\r
+ IN CONST CHAR8 *DirectoryPath,\r
+ IN CONST CHAR8 *Node,\r
+ OUT XENSTORE_WATCH **WatchPtr\r
+ )\r
+{\r
+ /* Pointer in ascii is the token. */\r
+ CHAR8 Token[sizeof (XENSTORE_WATCH) * 2 + 1];\r
+ XENSTORE_STATUS Status;\r
+ XENSTORE_WATCH *Watch;\r
+\r
+ Watch = AllocateZeroPool (sizeof (XENSTORE_WATCH));\r
+ Watch->Signature = XENSTORE_WATCH_SIGNATURE;\r
+ Watch->Node = XenStoreJoin (DirectoryPath, Node);\r
+\r
+ EfiAcquireLock (&xs.RegisteredWatchesLock);\r
+ InsertTailList (&xs.RegisteredWatches, &Watch->Link);\r
+ EfiReleaseLock (&xs.RegisteredWatchesLock);\r
+\r
+ AsciiSPrint (Token, sizeof (Token), "%p", (VOID*) Watch);\r
+ Status = XenStoreWatch (Watch->Node, Token);\r
+\r
+ /* Ignore errors due to multiple registration. */\r
+ if (Status == XENSTORE_STATUS_EEXIST) {\r
+ Status = XENSTORE_STATUS_SUCCESS;\r
+ }\r
+\r
+ if (Status == XENSTORE_STATUS_SUCCESS) {\r
+ *WatchPtr = Watch;\r
+ } else {\r
+ EfiAcquireLock (&xs.RegisteredWatchesLock);\r
+ RemoveEntryList (&Watch->Link);\r
+ EfiReleaseLock (&xs.RegisteredWatchesLock);\r
+ FreePool (Watch->Node);\r
+ FreePool (Watch);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+VOID\r
+XenStoreUnregisterWatch (\r
+ IN XENSTORE_WATCH *Watch\r
+ )\r
+{\r
+ CHAR8 Token[sizeof (Watch) * 2 + 1];\r
+ LIST_ENTRY *Entry;\r
+\r
+ ASSERT (Watch->Signature == XENSTORE_WATCH_SIGNATURE);\r
+\r
+ AsciiSPrint (Token, sizeof (Token), "%p", (VOID *) Watch);\r
+ if (XenStoreFindWatch (Token) == NULL) {\r
+ return;\r
+ }\r
+\r
+ EfiAcquireLock (&xs.RegisteredWatchesLock);\r
+ RemoveEntryList (&Watch->Link);\r
+ EfiReleaseLock (&xs.RegisteredWatchesLock);\r
+\r
+ XenStoreUnwatch (Watch->Node, Token);\r
+\r
+ /* Cancel pending watch events. */\r
+ EfiAcquireLock (&xs.WatchEventsLock);\r
+ Entry = GetFirstNode (&xs.WatchEvents);\r
+ while (!IsNull (&xs.WatchEvents, Entry)) {\r
+ XENSTORE_MESSAGE *Message = XENSTORE_MESSAGE_FROM_LINK (Entry);\r
+ Entry = GetNextNode (&xs.WatchEvents, Entry);\r
+ if (Message->u.Watch.Handle == Watch) {\r
+ RemoveEntryList (&Message->Link);\r
+ FreePool (Message->u.Watch.Vector);\r
+ FreePool (Message);\r
+ }\r
+ }\r
+ EfiReleaseLock (&xs.WatchEventsLock);\r
+\r
+ FreePool (Watch->Node);\r
+ FreePool (Watch);\r
+}\r