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