mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-29 12:35:53 +01:00
1613 lines
42 KiB
C
1613 lines
42 KiB
C
|
/** @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 hierarchy
|
||
|
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:
|
||
|
|
||
|
SPDX-License-Identifier: MIT
|
||
|
**/
|
||
|
|
||
|
#include "XenStore.h"
|
||
|
|
||
|
#include <Library/PrintLib.h>
|
||
|
|
||
|
#include <IndustryStandard/Xen/hvm/params.h>
|
||
|
|
||
|
#include "EventChannel.h"
|
||
|
#include <Library/XenHypercallLib.h>
|
||
|
|
||
|
//
|
||
|
// Private Data Structures
|
||
|
//
|
||
|
|
||
|
typedef struct {
|
||
|
CONST VOID *Data;
|
||
|
UINT32 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 callback 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 ((VOID *)&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;
|
||
|
UINTN BufSize;
|
||
|
|
||
|
/* +1 for '/' and +1 for '\0' */
|
||
|
BufSize = AsciiStrLen (DirectoryPath) + AsciiStrLen (Node) + 2;
|
||
|
Buf = AllocatePool (BufSize);
|
||
|
ASSERT (Buf != NULL);
|
||
|
|
||
|
if (Node[0] == '\0') {
|
||
|
AsciiSPrint (Buf, BufSize, "%a", DirectoryPath);
|
||
|
} else {
|
||
|
AsciiSPrint (Buf, BufSize, "%a/%a", DirectoryPath, 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 UINT32 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 ((DEBUG_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 UINT32 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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((
|
||
|
DEBUG_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 ((
|
||
|
DEBUG_WARN,
|
||
|
"XenStore: Watch handle %a not found\n",
|
||
|
Message->u.Watch.Vector[XS_WATCH_TOKEN]
|
||
|
));
|
||
|
FreePool ((VOID *)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" }
|
||
|
};
|
||
|
|
||
|
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 ((DEBUG_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 ((
|
||
|
DEBUG_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 multi-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 CONST 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;
|
||
|
|
||
|
if (Transaction == XST_NIL) {
|
||
|
Message.tx_id = 0;
|
||
|
} else {
|
||
|
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 ((DEBUG_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 ((DEBUG_ERROR, "XenStoreTalkv failed %d\n", Status));
|
||
|
goto Error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Status = XenStoreReadReply ((enum xsd_sockmsg_type *)&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 ((enum xsd_sockmsg_type)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 CONST 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 = (UINT32)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 = (UINT32)AsciiStrSize (Path);
|
||
|
WriteRequest[1].Data = (VOID *)Token;
|
||
|
WriteRequest[1].Len = (UINT32)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 = (UINT32)AsciiStrSize (Path);
|
||
|
WriteRequest[1].Data = (VOID *)Token;
|
||
|
WriteRequest[1].Len = (UINT32)AsciiStrSize (Token);
|
||
|
|
||
|
return XenStoreTalkv (XST_NIL, XS_UNWATCH, WriteRequest, 2, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
STATIC
|
||
|
XENSTORE_STATUS
|
||
|
XenStoreWaitWatch (
|
||
|
VOID *Token
|
||
|
)
|
||
|
{
|
||
|
XENSTORE_MESSAGE *Message;
|
||
|
LIST_ENTRY *Entry = NULL;
|
||
|
LIST_ENTRY *Last = NULL;
|
||
|
XENSTORE_STATUS Status;
|
||
|
|
||
|
while (TRUE) {
|
||
|
EfiAcquireLock (&xs.WatchEventsLock);
|
||
|
if (IsListEmpty (&xs.WatchEvents) ||
|
||
|
(Last == GetFirstNode (&xs.WatchEvents)))
|
||
|
{
|
||
|
EfiReleaseLock (&xs.WatchEventsLock);
|
||
|
Status = XenStoreProcessMessage ();
|
||
|
if ((Status != XENSTORE_STATUS_SUCCESS) && (Status != XENSTORE_STATUS_EAGAIN)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (Entry = GetFirstNode (&xs.WatchEvents);
|
||
|
Entry != Last && !IsNull (&xs.WatchEvents, Entry);
|
||
|
Entry = GetNextNode (&xs.WatchEvents, Entry))
|
||
|
{
|
||
|
Message = XENSTORE_MESSAGE_FROM_LINK (Entry);
|
||
|
if (Message->u.Watch.Handle == Token) {
|
||
|
RemoveEntryList (Entry);
|
||
|
EfiReleaseLock (&xs.WatchEventsLock);
|
||
|
FreePool ((VOID *)Message->u.Watch.Vector);
|
||
|
FreePool (Message);
|
||
|
return XENSTORE_STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Last = GetFirstNode (&xs.WatchEvents);
|
||
|
EfiReleaseLock (&xs.WatchEventsLock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
NotifyEventChannelCheckForEvent (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
XENSTORE_PRIVATE *xsp;
|
||
|
|
||
|
xsp = (XENSTORE_PRIVATE *)Context;
|
||
|
if (TestAndClearBit (xsp->EventChannel, xsp->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 *xsp
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_EVENT TimerEvent;
|
||
|
struct xenstore_domain_interface *XenStore = xsp->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 ((
|
||
|
DEBUG_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,
|
||
|
xsp,
|
||
|
&xsp->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 = (evtchn_port_t)XenHypercallHvmGetParam (HVM_PARAM_STORE_EVTCHN);
|
||
|
XenStoreGpfn = (UINTN)XenHypercallHvmGetParam (HVM_PARAM_STORE_PFN);
|
||
|
xs.XenStore = (VOID *)(XenStoreGpfn << EFI_PAGE_SHIFT);
|
||
|
DEBUG ((
|
||
|
DEBUG_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);
|
||
|
|
||
|
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 ((DEBUG_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 ((DEBUG_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 ((VOID *)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 CONST 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 CONST 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 ((VOID *)TempStr);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
XenStoreRead (
|
||
|
IN CONST 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 CONST 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 = (UINT32)AsciiStrSize (Path);
|
||
|
WriteRequest[1].Data = (VOID *)Str;
|
||
|
WriteRequest[1].Len = (UINT32)AsciiStrLen (Str);
|
||
|
|
||
|
Status = XenStoreTalkv (Transaction, XS_WRITE, WriteRequest, 2, NULL, NULL);
|
||
|
FreePool (Path);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
XenStoreRemove (
|
||
|
IN CONST 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 = (UINT32)AsciiStrDecimalToUintn (IdStr);
|
||
|
FreePool (IdStr);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
XenStoreTransactionEnd (
|
||
|
IN CONST XENSTORE_TRANSACTION *Transaction,
|
||
|
IN BOOLEAN Abort
|
||
|
)
|
||
|
{
|
||
|
CHAR8 AbortStr[2];
|
||
|
|
||
|
AbortStr[0] = Abort ? 'F' : 'T';
|
||
|
AbortStr[1] = '\0';
|
||
|
|
||
|
return XenStoreSingle (Transaction, XS_TRANSACTION_END, AbortStr, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenStoreVSPrint (
|
||
|
IN CONST 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;
|
||
|
VA_LIST Marker2;
|
||
|
|
||
|
VA_COPY (Marker2, Marker);
|
||
|
BufSize = SPrintLengthAsciiFormat (FormatString, Marker2) + 1;
|
||
|
VA_END (Marker2);
|
||
|
Buf = AllocateZeroPool (BufSize);
|
||
|
AsciiVSPrint (Buf, BufSize, FormatString, Marker);
|
||
|
Status = XenStoreWrite (Transaction, DirectoryPath, Node, Buf);
|
||
|
FreePool (Buf);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenStoreSPrint (
|
||
|
IN CONST 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 ((VOID *)Message->u.Watch.Vector);
|
||
|
FreePool (Message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EfiReleaseLock (&xs.WatchEventsLock);
|
||
|
|
||
|
FreePool (Watch->Node);
|
||
|
FreePool (Watch);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// XENBUS protocol
|
||
|
//
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusWaitForWatch (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN VOID *Token
|
||
|
)
|
||
|
{
|
||
|
return XenStoreWaitWatch (Token);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusXenStoreRead (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST XENSTORE_TRANSACTION *Transaction,
|
||
|
IN CONST CHAR8 *Node,
|
||
|
OUT VOID **Value
|
||
|
)
|
||
|
{
|
||
|
return XenStoreRead (Transaction, This->Node, Node, NULL, Value);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusXenStoreBackendRead (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST XENSTORE_TRANSACTION *Transaction,
|
||
|
IN CONST CHAR8 *Node,
|
||
|
OUT VOID **Value
|
||
|
)
|
||
|
{
|
||
|
return XenStoreRead (Transaction, This->Backend, Node, NULL, Value);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusXenStoreRemove (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST XENSTORE_TRANSACTION *Transaction,
|
||
|
IN const char *Node
|
||
|
)
|
||
|
{
|
||
|
return XenStoreRemove (Transaction, This->Node, Node);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusXenStoreTransactionStart (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
OUT XENSTORE_TRANSACTION *Transaction
|
||
|
)
|
||
|
{
|
||
|
return XenStoreTransactionStart (Transaction);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusXenStoreTransactionEnd (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST XENSTORE_TRANSACTION *Transaction,
|
||
|
IN BOOLEAN Abort
|
||
|
)
|
||
|
{
|
||
|
return XenStoreTransactionEnd (Transaction, Abort);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusXenStoreSPrint (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST 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
|
||
|
EFIAPI
|
||
|
XenBusRegisterWatch (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST CHAR8 *Node,
|
||
|
OUT VOID **Token
|
||
|
)
|
||
|
{
|
||
|
return XenStoreRegisterWatch (This->Node, Node, (XENSTORE_WATCH **)Token);
|
||
|
}
|
||
|
|
||
|
XENSTORE_STATUS
|
||
|
EFIAPI
|
||
|
XenBusRegisterWatchBackend (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN CONST CHAR8 *Node,
|
||
|
OUT VOID **Token
|
||
|
)
|
||
|
{
|
||
|
return XenStoreRegisterWatch (This->Backend, Node, (XENSTORE_WATCH **)Token);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
XenBusUnregisterWatch (
|
||
|
IN XENBUS_PROTOCOL *This,
|
||
|
IN VOID *Token
|
||
|
)
|
||
|
{
|
||
|
XenStoreUnregisterWatch ((XENSTORE_WATCH *)Token);
|
||
|
}
|