From: Anthony PERARD Date: Wed, 29 Oct 2014 06:50:50 +0000 (+0000) Subject: OvmfPkg/XenBusDxe: Add XenStore client implementation X-Git-Tag: edk2-stable201903~10743 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=a9090a94bb4a8dae61eb79428a5769916d621940 OvmfPkg/XenBusDxe: Add XenStore client implementation XenStore is a key/value database, which is running on another virtual machine. It can be accessed through shared memory. This is a client implementation. Change in V3: - moving xs_wire.h from patch #1 to this patch - fix return value of XenStoreListDirectory - Use a timeout to print a debug message if the other side of the xenstore ring does not notify through the event channel. This is done with the new XenStoreWaitForEvent function. - Have XenStoreReadReply check status of XenStoreProcessMessage and return an error if needed. - Have XenStoreTalkv return the status of XenStoreReadReply. - Have a loop to check for the quiescent of the response ring in the XenStoreInitComms function. (with a timeout of 5 seconds) - use the recently introduced XenStore 'closing' feature. Change in V2: - Change comment style, from freebsd to ovmf - Fix type of EventChannel - Fix debug print, no more cast - Implement XenStoreDeinit. - Clean up comments - Fix few codding style issue - Add FAIL xenstore status value. Origin: FreeBSD 10.0 License: This patch adds several files under the MIT licence. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Anthony PERARD Reviewed-by: Konrad Rzeszutek Wilk Acked-by: Jordan Justen git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16267 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/OvmfPkg/Include/IndustryStandard/Xen/io/xs_wire.h b/OvmfPkg/Include/IndustryStandard/Xen/io/xs_wire.h new file mode 100644 index 0000000000..6f302d1447 --- /dev/null +++ b/OvmfPkg/Include/IndustryStandard/Xen/io/xs_wire.h @@ -0,0 +1,149 @@ +/* + * Details of the "wire" protocol between Xen Store Daemon and client + * library or guest kernel. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (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. + * + * Copyright (C) 2005 Rusty Russell IBM Corporation + */ + +#ifndef _XS_WIRE_H +#define _XS_WIRE_H + +enum xsd_sockmsg_type +{ + XS_DEBUG, + XS_DIRECTORY, + XS_READ, + XS_GET_PERMS, + XS_WATCH, + XS_UNWATCH, + XS_TRANSACTION_START, + XS_TRANSACTION_END, + XS_INTRODUCE, + XS_RELEASE, + XS_GET_DOMAIN_PATH, + XS_WRITE, + XS_MKDIR, + XS_RM, + XS_SET_PERMS, + XS_WATCH_EVENT, + XS_ERROR, + XS_IS_DOMAIN_INTRODUCED, + XS_RESUME, + XS_SET_TARGET, + XS_RESTRICT, + XS_RESET_WATCHES, + + XS_INVALID = 0xffff /* Guaranteed to remain an invalid type */ +}; + +#define XS_WRITE_NONE "NONE" +#define XS_WRITE_CREATE "CREATE" +#define XS_WRITE_CREATE_EXCL "CREATE|EXCL" + +/* We hand errors as strings, for portability. */ +struct xsd_errors +{ + INT32 errnum; + const CHAR8 *errstring; +}; +#ifdef EINVAL +#define XSD_ERROR(x) { x, #x } +/* LINTED: static unused */ +static struct xsd_errors xsd_errors[] +#if defined(__GNUC__) +__attribute__((unused)) +#endif + = { + XSD_ERROR(EINVAL), + XSD_ERROR(EACCES), + XSD_ERROR(EEXIST), + XSD_ERROR(EISDIR), + XSD_ERROR(ENOENT), + XSD_ERROR(ENOMEM), + XSD_ERROR(ENOSPC), + XSD_ERROR(EIO), + XSD_ERROR(ENOTEMPTY), + XSD_ERROR(ENOSYS), + XSD_ERROR(EROFS), + XSD_ERROR(EBUSY), + XSD_ERROR(EAGAIN), + XSD_ERROR(EISCONN), + XSD_ERROR(E2BIG) +}; +#endif + +struct xsd_sockmsg +{ + UINT32 type; /* XS_??? */ + UINT32 req_id;/* Request identifier, echoed in daemon's response. */ + UINT32 tx_id; /* Transaction id (0 if not related to a transaction). */ + UINT32 len; /* Length of data following this. */ + + /* Generally followed by nul-terminated string(s). */ +}; + +enum xs_watch_type +{ + XS_WATCH_PATH = 0, + XS_WATCH_TOKEN +}; + +/* + * `incontents 150 xenstore_struct XenStore wire protocol. + * + * Inter-domain shared memory communications. */ +#define XENSTORE_RING_SIZE 1024 +typedef UINT32 XENSTORE_RING_IDX; +#define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE-1)) +struct xenstore_domain_interface { + CHAR8 req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */ + CHAR8 rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */ + XENSTORE_RING_IDX req_cons, req_prod; + XENSTORE_RING_IDX rsp_cons, rsp_prod; + UINT32 server_features; /* Bitmap of features supported by the server */ + UINT32 connection; +}; + +/* Violating this is very bad. See docs/misc/xenstore.txt. */ +#define XENSTORE_PAYLOAD_MAX 4096 + +/* Violating these just gets you an error back */ +#define XENSTORE_ABS_PATH_MAX 3072 +#define XENSTORE_REL_PATH_MAX 2048 + +/* The ability to reconnect a ring */ +#define XENSTORE_SERVER_FEATURE_RECONNECTION 1 + +/* Valid values for the connection field */ +#define XENSTORE_CONNECTED 0 /* the steady-state */ +#define XENSTORE_RECONNECT 1 /* guest has initiated a reconnect */ + +#endif /* _XS_WIRE_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/OvmfPkg/Include/Protocol/XenBus.h b/OvmfPkg/Include/Protocol/XenBus.h index 89bf74fbc5..565d491db6 100644 --- a/OvmfPkg/Include/Protocol/XenBus.h +++ b/OvmfPkg/Include/Protocol/XenBus.h @@ -32,6 +32,35 @@ /// typedef struct _XENBUS_PROTOCOL XENBUS_PROTOCOL; +typedef enum xenbus_state XenBusState; + +typedef struct +{ + UINT32 Id; +} XENSTORE_TRANSACTION; + +#define XST_NIL ((XENSTORE_TRANSACTION) { 0 }) + +typedef enum { + XENSTORE_STATUS_SUCCESS = 0, + XENSTORE_STATUS_FAIL, + XENSTORE_STATUS_EINVAL, + XENSTORE_STATUS_EACCES, + XENSTORE_STATUS_EEXIST, + XENSTORE_STATUS_EISDIR, + XENSTORE_STATUS_ENOENT, + XENSTORE_STATUS_ENOMEM, + XENSTORE_STATUS_ENOSPC, + XENSTORE_STATUS_EIO, + XENSTORE_STATUS_ENOTEMPTY, + XENSTORE_STATUS_ENOSYS, + XENSTORE_STATUS_EROFS, + XENSTORE_STATUS_EBUSY, + XENSTORE_STATUS_EAGAIN, + XENSTORE_STATUS_EISCONN, + XENSTORE_STATUS_E2BIG +} XENSTORE_STATUS; + #include diff --git a/OvmfPkg/XenBusDxe/XenBusDxe.c b/OvmfPkg/XenBusDxe/XenBusDxe.c index edeb20ef38..679fe3b592 100644 --- a/OvmfPkg/XenBusDxe/XenBusDxe.c +++ b/OvmfPkg/XenBusDxe/XenBusDxe.c @@ -31,6 +31,7 @@ #include "XenHypercall.h" #include "GrantTable.h" +#include "XenStore.h" /// @@ -346,6 +347,9 @@ XenBusDxeDriverBindingStart ( XenGrantTableInit (Dev, MmioAddr); + Status = XenStoreInit (Dev); + ASSERT_EFI_ERROR (Status); + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, NotifyExitBoot, (VOID*) Dev, @@ -399,6 +403,7 @@ XenBusDxeDriverBindingStop ( XENBUS_DEVICE *Dev = mMyDevice; gBS->CloseEvent (Dev->ExitBootEvent); + XenStoreDeinit (Dev); XenGrantTableDeinit (Dev); gBS->CloseProtocol (ControllerHandle, &gEfiPciIoProtocolGuid, diff --git a/OvmfPkg/XenBusDxe/XenBusDxe.inf b/OvmfPkg/XenBusDxe/XenBusDxe.inf index 742b7c615b..1343808ae3 100644 --- a/OvmfPkg/XenBusDxe/XenBusDxe.inf +++ b/OvmfPkg/XenBusDxe/XenBusDxe.inf @@ -42,6 +42,8 @@ GrantTable.h EventChannel.c EventChannel.h + XenStore.c + XenStore.h [Sources.IA32] Ia32/hypercall.S 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 + +#include + +#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); +} diff --git a/OvmfPkg/XenBusDxe/XenStore.h b/OvmfPkg/XenBusDxe/XenStore.h new file mode 100644 index 0000000000..1503ed0473 --- /dev/null +++ b/OvmfPkg/XenBusDxe/XenStore.h @@ -0,0 +1,292 @@ +/** @file + Method declarations and structures for accessing the XenStore + + Copyright (C) 2005 Rusty Russell, IBM Corporation + Copyright (C) 2005 XenSource Ltd. + 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. +**/ + +#ifndef _XEN_XENSTORE_XENSTOREVAR_H +#define _XEN_XENSTORE_XENSTOREVAR_H + +#include "XenBusDxe.h" + +#include + +typedef struct _XENSTORE_WATCH XENSTORE_WATCH; + +/** + Fetch the contents of a directory in the XenStore. + + @param Transaction The XenStore transaction covering this request. + @param DirectoryPath The dirname of the path to read. + @param Node The basename of the path to read. + @param DirectoryCountPtr The returned number of directory entries. + @param DirectoryListPtr An array of directory entry strings. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of failure. + + @note The results buffer is alloced and should be free'd by the + caller. +**/ +XENSTORE_STATUS +XenStoreListDirectory ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node, + OUT UINT32 *DirectoryCountPtr, + OUT CONST CHAR8 ***DirectoryListPtr + ); + +/** + Determine if a path exists in the XenStore. + + @param Transaction The XenStore transaction covering this request. + @param Directory The dirname of the path to read. + @param Node The basename of the path to read. + + @retval TRUE The path exists. + @retval FALSE The path does not exist or an error occurred attempting + to make that determination. +**/ +BOOLEAN +XenStorePathExists ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *Directory, + IN CONST CHAR8 *Node + ); + +/** + Get the contents of a single "file". Returns the contents in *Result which + should be freed after use. The length of the value in bytes is returned in + *LenPtr. + + @param Transaction The XenStore transaction covering this request. + @param DirectoryPath The dirname of the file to read. + @param Node The basename of the file to read. + @param LenPtr The amount of data read. + @param Result The returned contents from this file. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of failure. + + @note The results buffer is malloced and should be free'd by the + caller. +**/ +XENSTORE_STATUS +XenStoreRead ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node, + OUT UINT32 *LenPtr OPTIONAL, + OUT VOID **Result + ); + +/** + Write to a single file. + + @param Transaction The XenStore transaction covering this request. + @param DirectoryPath The dirname of the file to write. + @param Node The basename of the file to write. + @param Str The NUL terminated string of data to write. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of failure. +**/ +XENSTORE_STATUS +XenStoreWrite ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node, + IN CONST CHAR8 *Str + ); + +/** + Remove a file or directory (directories must be empty). + + @param Transaction The XenStore transaction covering this request. + @param DirectoryPath The dirname of the directory to remove. + @param Node The basename of the directory to remove. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of failure. +**/ +XENSTORE_STATUS +XenStoreRemove ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node + ); + +/** + Start a transaction. + + Changes by others will not be seen during the lifetime of this + transaction, and changes will not be visible to others until it + is committed (XenStoreTransactionEnd). + + @param Transaction The returned transaction. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of failure. +**/ +XENSTORE_STATUS +XenStoreTransactionStart ( + OUT XENSTORE_TRANSACTION *Transaction + ); + +/** + End a transaction. + + @param Transaction The transaction to end/commit. + @param Abort If TRUE, the transaction is discarded + instead of committed. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of failure. +**/ +XENSTORE_STATUS +XenStoreTransactionEnd ( + IN XENSTORE_TRANSACTION Transaction, + IN BOOLEAN Abort + ); + +/** + Printf formatted write to a XenStore file. + + @param Transaction The XenStore transaction covering this request. + @param DirectoryPath The dirname of the path to read. + @param Node The basename of the path to read. + @param FormatString AsciiSPrint format string followed by a variable number + of arguments. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of write failure. +**/ +XENSTORE_STATUS +EFIAPI +XenStoreSPrint ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node, + IN CONST CHAR8 *FormatString, + ... + ); + +/** + VA_LIST version of XenStoreSPrint(). + + @param Transaction The XenStore transaction covering this request. + @param DirectoryPath The dirname of the path to read. + @param Node The basename of the path to read. + @param FormatString Printf format string. + @param Marker VA_LIST of printf arguments. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of write failure. +**/ +XENSTORE_STATUS +XenStoreVSPrint ( + IN XENSTORE_TRANSACTION Transaction, + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node, + IN CONST CHAR8 *FormatString, + IN VA_LIST Marker + ); + +/** + Register a XenStore watch. + + XenStore watches allow a client to be notified via a callback (embedded + within the watch object) of changes to an object in the XenStore. + + @param DirectoryPath The dirname of the path to watch. + @param Node The basename of the path to watch. + @param WatchPtr A returned XENSTORE_WATCH pointer. + + @return On success, XENSTORE_STATUS_SUCCESS. Otherwise an errno value + indicating the type of write failure. EEXIST errors from the + XenStore are supressed, allowing multiple, physically different, + xenbus_watch objects, to watch the same path in the XenStore. +**/ +XENSTORE_STATUS +XenStoreRegisterWatch ( + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node, + OUT XENSTORE_WATCH **WatchPtr + ); + +/** + Unregister a XenStore watch. + + @param Watch An XENSTORE_WATCH object previously returned by a successful + call to XenStoreRegisterWatch (). +**/ +VOID +XenStoreUnregisterWatch ( + IN XENSTORE_WATCH *Watch + ); + +/** + Allocate and return the XenStore path string /. If name + is the NUL string, the returned value contains the path string + . + + @param DirectoryPath The NUL terminated directory prefix for new path. + @param Node The NUL terminated basename for the new path. + + @return A buffer containing the joined path. + */ +CHAR8 * +XenStoreJoin ( + IN CONST CHAR8 *DirectoryPath, + IN CONST CHAR8 *Node + ); + + +/** + Initialize the XenStore states and rings. + + @param Dev A pointer to a XENBUS_DEVICE instance. + + @return EFI_SUCCESS if everything went smoothly. +**/ +EFI_STATUS +XenStoreInit ( + XENBUS_DEVICE *Dev + ); + +/** + Deinitialize the XenStore states and rings. + + @param Dev A pointer to a XENBUS_DEVICE instance. +**/ +VOID +XenStoreDeinit ( + IN XENBUS_DEVICE *Dev + ); + +#endif /* _XEN_XENSTORE_XENSTOREVAR_H */