/** @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); | |
} |