From: Ken Cox Date: Tue, 4 Mar 2014 13:58:06 +0000 (-0600) Subject: staging: visorchannel module X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=e423812a9e430913e41c6565922142fe22f83ad7;p=deliverable%2Flinux.git staging: visorchannel module The visorchannel module is a support library that abstracts reading and writing a channel in memory. Signed-off-by: Ken Cox Cc: Ben Romer Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index 60bbca0bde71..15f16d6b73e3 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -10,5 +10,6 @@ menuconfig UNISYSSPAR if UNISYSSPAR source "drivers/staging/unisys/visorutil/Kconfig" +source "drivers/staging/unisys/visorchannel/Kconfig" endif # UNISYSSPAR diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index 129c28fb50f0..a457d8b49b36 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -2,4 +2,5 @@ # Makefile for Unisys SPAR drivers # obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/ +obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel/ diff --git a/drivers/staging/unisys/common-spar/include/channels/channel.h b/drivers/staging/unisys/common-spar/include/channels/channel.h new file mode 100644 index 000000000000..83906b4e8755 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/channel.h @@ -0,0 +1,645 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __CHANNEL_H__ +#define __CHANNEL_H__ + +/* +* Whenever this file is changed a corresponding change must be made in +* the Console/ServicePart/visordiag_early/supervisor_channel.h file +* which is needed for Linux kernel compiles. These two files must be +* in sync. +*/ + +/* define the following to prevent include nesting in kernel header + * files of similar abreviated content + */ +#define __SUPERVISOR_CHANNEL_H__ + +#include "commontypes.h" + +#define SIGNATURE_16(A, B) ((A) | (B<<8)) +#define SIGNATURE_32(A, B, C, D) \ + (SIGNATURE_16(A, B) | (SIGNATURE_16(C, D) << 16)) +#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ + (SIGNATURE_32(A, B, C, D) | ((U64)(SIGNATURE_32(E, F, G, H)) << 32)) + +#ifndef lengthof +#define lengthof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) +#endif +#ifndef COVERQ +#define COVERQ(v, d) (((v)+(d)-1) / (d)) +#endif +#ifndef COVER +#define COVER(v, d) ((d)*COVERQ(v, d)) +#endif + +#ifndef GUID0 +#define GUID0 {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} } +#endif + +/* The C language is inconsistent with respect to where it allows literal + * constants, especially literal constant structs. Literal constant structs + * are allowed for initialization only, whereas other types of literal + * constants are allowed anywhere. We get around this inconsistency by + * declaring a "static const" variable for each GUID. This variable can be + * used in expressions where the literal constant would not be allowed. + */ +static const GUID Guid0 = GUID0; + +#define ULTRA_CHANNEL_PROTOCOL_SIGNATURE SIGNATURE_32('E', 'C', 'N', 'L') + +typedef enum { + CHANNELSRV_UNINITIALIZED = 0, /* channel is in an undefined state */ + CHANNELSRV_READY = 1 /* channel has been initialized by server */ +} CHANNEL_SERVERSTATE; + +typedef enum { + CHANNELCLI_DETACHED = 0, + CHANNELCLI_DISABLED = 1, /* client can see channel but is NOT + * allowed to use it unless given TBD + * explicit request (should actually be + * < DETACHED) */ + CHANNELCLI_ATTACHING = 2, /* legacy EFI client request + * for EFI server to attach */ + CHANNELCLI_ATTACHED = 3, /* idle, but client may want + * to use channel any time */ + CHANNELCLI_BUSY = 4, /* client either wants to use or is + * using channel */ + CHANNELCLI_OWNED = 5 /* "no worries" state - client can + * access channel anytime */ +} CHANNEL_CLIENTSTATE; +static inline const U8 * +ULTRA_CHANNELCLI_STRING(U32 v) +{ + switch (v) { + case CHANNELCLI_DETACHED: + return (const U8 *) ("DETACHED"); + case CHANNELCLI_DISABLED: + return (const U8 *) ("DISABLED"); + case CHANNELCLI_ATTACHING: + return (const U8 *) ("ATTACHING"); + case CHANNELCLI_ATTACHED: + return (const U8 *) ("ATTACHED"); + case CHANNELCLI_BUSY: + return (const U8 *) ("BUSY"); + case CHANNELCLI_OWNED: + return (const U8 *) ("OWNED"); + default: + break; + } + return (const U8 *) ("?"); +} + +#define ULTRA_CHANNELSRV_IS_READY(x) ((x) == CHANNELSRV_READY) +#define ULTRA_CHANNEL_SERVER_READY(pChannel) \ + (ULTRA_CHANNELSRV_IS_READY((pChannel)->SrvState)) + +#define ULTRA_VALID_CHANNELCLI_TRANSITION(o, n) \ + (((((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_DISABLED)) || \ + (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_DISABLED)) || \ + (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_DISABLED)) || \ + (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_DETACHED)) || \ + (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_DETACHED)) || \ + (((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_ATTACHING)) || \ + (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_ATTACHED)) || \ + (((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_ATTACHED)) || \ + (((o) == CHANNELCLI_BUSY) && ((n) == CHANNELCLI_ATTACHED)) || \ + (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_BUSY)) || \ + (((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_OWNED)) || \ + (((o) == CHANNELCLI_DISABLED) && ((n) == CHANNELCLI_OWNED)) || \ + (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_OWNED)) || \ + (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_OWNED)) || \ + (((o) == CHANNELCLI_BUSY) && ((n) == CHANNELCLI_OWNED)) || (0)) \ + ? (1) : (0)) + +#define ULTRA_CHANNEL_CLIENT_CHK_TRANSITION(old, new, chanId, logCtx, \ + file, line) \ + do { \ + if (!ULTRA_VALID_CHANNELCLI_TRANSITION(old, new)) \ + UltraLogEvent(logCtx, \ + CHANNELSTATE_DIAG_EVENTID_TRANSITERR, \ + CHANNELSTATE_DIAG_SEVERITY, \ + CHANNELSTATE_DIAG_SUBSYS, \ + __func__, __LINE__, \ + "%s Channel StateTransition INVALID! (%s) %s(%d)-->%s(%d) @%s:%d\n", \ + chanId, "CliState", \ + ULTRA_CHANNELCLI_STRING(old), \ + old, \ + ULTRA_CHANNELCLI_STRING(new), \ + new, \ + PathName_Last_N_Nodes((U8 *)file, 4), \ + line); \ + } while (0) + +#define ULTRA_CHANNEL_CLIENT_TRANSITION(pChan, chanId, field, \ + newstate, logCtx) \ + do { \ + ULTRA_CHANNEL_CLIENT_CHK_TRANSITION( \ + (((CHANNEL_HEADER *)(pChan))->field), newstate, \ + chanId, logCtx, __FILE__, __LINE__); \ + UltraLogEvent(logCtx, CHANNELSTATE_DIAG_EVENTID_TRANSITOK, \ + CHANNELSTATE_DIAG_SEVERITY, \ + CHANNELSTATE_DIAG_SUBSYS, \ + __func__, __LINE__, \ + "%s Channel StateTransition (%s) %s(%d)-->%s(%d) @%s:%d\n", \ + chanId, #field, \ + ULTRA_CHANNELCLI_STRING(((CHANNEL_HEADER *) \ + (pChan))->field), \ + ((CHANNEL_HEADER *)(pChan))->field, \ + ULTRA_CHANNELCLI_STRING(newstate), \ + newstate, \ + PathName_Last_N_Nodes(__FILE__, 4), __LINE__); \ + ((CHANNEL_HEADER *)(pChan))->field = newstate; \ + MEMORYBARRIER; \ + } while (0) + +#define ULTRA_CHANNEL_CLIENT_ACQUIRE_OS(pChan, chanId, logCtx) \ + ULTRA_channel_client_acquire_os(pChan, chanId, logCtx, \ + (char *)__FILE__, __LINE__, \ + (char *)__func__) +#define ULTRA_CHANNEL_CLIENT_RELEASE_OS(pChan, chanId, logCtx) \ + ULTRA_channel_client_release_os(pChan, chanId, logCtx, \ + (char *)__FILE__, __LINE__, (char *)__func__) + +/* Values for ULTRA_CHANNEL_PROTOCOL.CliErrorBoot: */ +/* throttling invalid boot channel statetransition error due to client + * disabled */ +#define ULTRA_CLIERRORBOOT_THROTTLEMSG_DISABLED 0x01 + +/* throttling invalid boot channel statetransition error due to client + * not attached */ +#define ULTRA_CLIERRORBOOT_THROTTLEMSG_NOTATTACHED 0x02 + +/* throttling invalid boot channel statetransition error due to busy channel */ +#define ULTRA_CLIERRORBOOT_THROTTLEMSG_BUSY 0x04 + +/* Values for ULTRA_CHANNEL_PROTOCOL.CliErrorOS: */ +/* throttling invalid guest OS channel statetransition error due to + * client disabled */ +#define ULTRA_CLIERROROS_THROTTLEMSG_DISABLED 0x01 + +/* throttling invalid guest OS channel statetransition error due to + * client not attached */ +#define ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED 0x02 + +/* throttling invalid guest OS channel statetransition error due to + * busy channel */ +#define ULTRA_CLIERROROS_THROTTLEMSG_BUSY 0x04 + +/* Values for ULTRA_CHANNEL_PROTOCOL.Features: This define exists so +* that windows guest can look at the FeatureFlags in the io channel, +* and configure the windows driver to use interrupts or not based on +* this setting. This flag is set in uislib after the +* ULTRA_VHBA_init_channel is called. All feature bits for all +* channels should be defined here. The io channel feature bits are +* defined right here */ +#define ULTRA_IO_DRIVER_ENABLES_INTS (0x1ULL << 1) +#define ULTRA_IO_CHANNEL_IS_POLLING (0x1ULL << 3) +#define ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS (0x1ULL << 4) +#define ULTRA_IO_DRIVER_DISABLES_INTS (0x1ULL << 5) +#define ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING (0x1ULL << 6) + +#pragma pack(push, 1) /* both GCC and VC now allow this pragma */ +/* Common Channel Header */ +typedef struct _CHANNEL_HEADER { + U64 Signature; /* Signature */ + U32 LegacyState; /* DEPRECATED - being replaced by */ + /* / SrvState, CliStateBoot, and CliStateOS below */ + U32 HeaderSize; /* sizeof(CHANNEL_HEADER) */ + U64 Size; /* Total size of this channel in bytes */ + U64 Features; /* Flags to modify behavior */ + GUID Type; /* Channel type: data, bus, control, etc. */ + U64 PartitionHandle; /* ID of guest partition */ + U64 Handle; /* Device number of this channel in client */ + U64 oChannelSpace; /* Offset in bytes to channel specific area */ + U32 VersionId; /* CHANNEL_HEADER Version ID */ + U32 PartitionIndex; /* Index of guest partition */ + GUID ZoneGuid; /* Guid of Channel's zone */ + U32 oClientString; /* offset from channel header to + * nul-terminated ClientString (0 if + * ClientString not present) */ + U32 CliStateBoot; /* CHANNEL_CLIENTSTATE of pre-boot + * EFI client of this channel */ + U32 CmdStateCli; /* CHANNEL_COMMANDSTATE (overloaded in + * Windows drivers, see ServerStateUp, + * ServerStateDown, etc) */ + U32 CliStateOS; /* CHANNEL_CLIENTSTATE of Guest OS + * client of this channel */ + U32 ChannelCharacteristics; /* CHANNEL_CHARACTERISTIC_ */ + U32 CmdStateSrv; /* CHANNEL_COMMANDSTATE (overloaded in + * Windows drivers, see ServerStateUp, + * ServerStateDown, etc) */ + U32 SrvState; /* CHANNEL_SERVERSTATE */ + U8 CliErrorBoot; /* bits to indicate err states for + * boot clients, so err messages can + * be throttled */ + U8 CliErrorOS; /* bits to indicate err states for OS + * clients, so err messages can be + * throttled */ + U8 Filler[1]; /* Pad out to 128 byte cacheline */ + /* Please add all new single-byte values below here */ + U8 RecoverChannel; +} CHANNEL_HEADER, *pCHANNEL_HEADER, ULTRA_CHANNEL_PROTOCOL; + +#define ULTRA_CHANNEL_ENABLE_INTS (0x1ULL << 0) + +/* Subheader for the Signal Type variation of the Common Channel */ +typedef struct _SIGNAL_QUEUE_HEADER { + /* 1st cache line */ + U32 VersionId; /* SIGNAL_QUEUE_HEADER Version ID */ + U32 Type; /* Queue type: storage, network */ + U64 Size; /* Total size of this queue in bytes */ + U64 oSignalBase; /* Offset to signal queue area */ + U64 FeatureFlags; /* Flags to modify behavior */ + U64 NumSignalsSent; /* Total # of signals placed in this queue */ + U64 NumOverflows; /* Total # of inserts failed due to + * full queue */ + U32 SignalSize; /* Total size of a signal for this queue */ + U32 MaxSignalSlots; /* Max # of slots in queue, 1 slot is + * always empty */ + U32 MaxSignals; /* Max # of signals in queue + * (MaxSignalSlots-1) */ + U32 Head; /* Queue head signal # */ + /* 2nd cache line */ + U64 NumSignalsReceived; /* Total # of signals removed from this queue */ + U32 Tail; /* Queue tail signal # (on separate + * cache line) */ + U32 Reserved1; /* Reserved field */ + U64 Reserved2; /* Resrved field */ + U64 ClientQueue; + U64 NumInterruptsReceived; /* Total # of Interrupts received. This + * is incremented by the ISR in the + * guest windows driver */ + U64 NumEmptyCnt; /* Number of times that SignalRemove + * is called and returned Empty + * Status. */ + U32 ErrorFlags; /* Error bits set during SignalReinit + * to denote trouble with client's + * fields */ + U8 Filler[12]; /* Pad out to 64 byte cacheline */ +} SIGNAL_QUEUE_HEADER, *pSIGNAL_QUEUE_HEADER; + +#pragma pack(pop) + +#define SignalInit(chan, QHDRFLD, QDATAFLD, QDATATYPE, ver, typ) \ + do { \ + MEMSET(&chan->QHDRFLD, 0, sizeof(chan->QHDRFLD)); \ + chan->QHDRFLD.VersionId = ver; \ + chan->QHDRFLD.Type = typ; \ + chan->QHDRFLD.Size = sizeof(chan->QDATAFLD); \ + chan->QHDRFLD.SignalSize = sizeof(QDATATYPE); \ + chan->QHDRFLD.oSignalBase = (UINTN)(chan->QDATAFLD)- \ + (UINTN)(&chan->QHDRFLD); \ + chan->QHDRFLD.MaxSignalSlots = \ + sizeof(chan->QDATAFLD)/sizeof(QDATATYPE); \ + chan->QHDRFLD.MaxSignals = chan->QHDRFLD.MaxSignalSlots-1; \ + } while (0) + +/* Generic function useful for validating any type of channel when it is + * received by the client that will be accessing the channel. + * Note that is only needed for callers in the EFI environment, and + * is used to pass the EFI_DIAG_CAPTURE_PROTOCOL needed to log messages. + */ +static inline int +ULTRA_check_channel_client(void *pChannel, + GUID expectedTypeGuid, + char *channelName, + U64 expectedMinBytes, + U32 expectedVersionId, + U64 expectedSignature, + char *fileName, int lineNumber, void *logCtx) +{ + if (MEMCMP(&expectedTypeGuid, &Guid0, sizeof(GUID)) != 0) + /* caller wants us to verify type GUID */ + if (MEMCMP(&(((CHANNEL_HEADER *) (pChannel))->Type), + &expectedTypeGuid, sizeof(GUID)) != 0) { + CHANNEL_GUID_MISMATCH(expectedTypeGuid, channelName, + "type", expectedTypeGuid, + ((CHANNEL_HEADER *) + (pChannel))->Type, fileName, + lineNumber, logCtx); + return 0; + } + if (expectedMinBytes > 0) /* caller wants us to verify + * channel size */ + if (((CHANNEL_HEADER *) (pChannel))->Size < expectedMinBytes) { + CHANNEL_U64_MISMATCH(expectedTypeGuid, channelName, + "size", expectedMinBytes, + ((CHANNEL_HEADER *) + (pChannel))->Size, fileName, + lineNumber, logCtx); + return 0; + } + if (expectedVersionId > 0) /* caller wants us to verify + * channel version */ + if (((CHANNEL_HEADER *) (pChannel))->VersionId != + expectedVersionId) { + CHANNEL_U32_MISMATCH(expectedTypeGuid, channelName, + "version", expectedVersionId, + ((CHANNEL_HEADER *) + (pChannel))->VersionId, fileName, + lineNumber, logCtx); + return 0; + } + if (expectedSignature > 0) /* caller wants us to verify + * channel signature */ + if (((CHANNEL_HEADER *) (pChannel))->Signature != + expectedSignature) { + CHANNEL_U64_MISMATCH(expectedTypeGuid, channelName, + "signature", expectedSignature, + ((CHANNEL_HEADER *) + (pChannel))->Signature, fileName, + lineNumber, logCtx); + return 0; + } + return 1; +} + +/* Generic function useful for validating any type of channel when it is about + * to be initialized by the server of the channel. + * Note that is only needed for callers in the EFI environment, and + * is used to pass the EFI_DIAG_CAPTURE_PROTOCOL needed to log messages. + */ +static inline int +ULTRA_check_channel_server(GUID typeGuid, + char *channelName, + U64 expectedMinBytes, + U64 actualBytes, + char *fileName, int lineNumber, void *logCtx) +{ + if (expectedMinBytes > 0) /* caller wants us to verify + * channel size */ + if (actualBytes < expectedMinBytes) { + CHANNEL_U64_MISMATCH(typeGuid, channelName, "size", + expectedMinBytes, actualBytes, + fileName, lineNumber, logCtx); + return 0; + } + return 1; +} + +/* Given a file pathname (with '/' or '\' separating directory nodes), + * returns a pointer to the beginning of a node within that pathname such + * that the number of nodes from that pointer to the end of the string is + * NOT more than . Note that if the pathname has less than nodes + * in it, the return pointer will be to the beginning of the string. + */ +static inline U8 * +PathName_Last_N_Nodes(U8 *s, unsigned int n) +{ + U8 *p = s; + unsigned int node_count = 0; + while (*p != '\0') { + if ((*p == '/') || (*p == '\\')) + node_count++; + p++; + } + if (node_count <= n) + return s; + while (n > 0) { + p--; + if (p == s) + break; /* should never happen, unless someone + * is changing the string while we are + * looking at it!! */ + if ((*p == '/') || (*p == '\\')) + n--; + } + return p + 1; +} + +static inline int +ULTRA_channel_client_acquire_os(void *pChannel, U8 *chanId, void *logCtx, + char *file, int line, char *func) +{ + CHANNEL_HEADER *pChan = (CHANNEL_HEADER *) (pChannel); + + if (pChan->CliStateOS == CHANNELCLI_DISABLED) { + if ((pChan-> + CliErrorOS & ULTRA_CLIERROROS_THROTTLEMSG_DISABLED) == 0) { + /* we are NOT throttling this message */ + pChan->CliErrorOS |= + ULTRA_CLIERROROS_THROTTLEMSG_DISABLED; + /* throttle until acquire successful */ + + UltraLogEvent(logCtx, + CHANNELSTATE_DIAG_EVENTID_TRANSITERR, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel StateTransition INVALID! - acquire failed because OS client DISABLED @%s:%d\n", + chanId, PathName_Last_N_Nodes( + (U8 *) file, 4), line); + } + return 0; + } + if ((pChan->CliStateOS != CHANNELCLI_OWNED) + && (pChan->CliStateBoot == CHANNELCLI_DISABLED)) { + /* Our competitor is DISABLED, so we can transition to OWNED */ + UltraLogEvent(logCtx, CHANNELSTATE_DIAG_EVENTID_TRANSITOK, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel StateTransition (%s) %s(%d)-->%s(%d) @%s:%d\n", + chanId, "CliStateOS", + ULTRA_CHANNELCLI_STRING(pChan->CliStateOS), + pChan->CliStateOS, + ULTRA_CHANNELCLI_STRING(CHANNELCLI_OWNED), + CHANNELCLI_OWNED, + PathName_Last_N_Nodes((U8 *) file, 4), line); + pChan->CliStateOS = CHANNELCLI_OWNED; + MEMORYBARRIER; + } + if (pChan->CliStateOS == CHANNELCLI_OWNED) { + if (pChan->CliErrorOS != 0) { + /* we are in an error msg throttling state; + * come out of it */ + UltraLogEvent(logCtx, + CHANNELSTATE_DIAG_EVENTID_TRANSITOK, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel OS client acquire now successful @%s:%d\n", + chanId, PathName_Last_N_Nodes((U8 *) file, + 4), line); + pChan->CliErrorOS = 0; + } + return 1; + } + + /* We have to do it the "hard way". We transition to BUSY, + * and can use the channel iff our competitor has not also + * transitioned to BUSY. */ + if (pChan->CliStateOS != CHANNELCLI_ATTACHED) { + if ((pChan-> + CliErrorOS & ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED) == + 0) { + /* we are NOT throttling this message */ + pChan->CliErrorOS |= + ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED; + /* throttle until acquire successful */ + UltraLogEvent(logCtx, + CHANNELSTATE_DIAG_EVENTID_TRANSITERR, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel StateTransition INVALID! - acquire failed because OS client NOT ATTACHED (state=%s(%d)) @%s:%d\n", + chanId, + ULTRA_CHANNELCLI_STRING(pChan->CliStateOS), + pChan->CliStateOS, + PathName_Last_N_Nodes((U8 *) file, 4), + line); + } + return 0; + } + pChan->CliStateOS = CHANNELCLI_BUSY; + MEMORYBARRIER; + if (pChan->CliStateBoot == CHANNELCLI_BUSY) { + if ((pChan->CliErrorOS & ULTRA_CLIERROROS_THROTTLEMSG_BUSY) == + 0) { + /* we are NOT throttling this message */ + pChan->CliErrorOS |= ULTRA_CLIERROROS_THROTTLEMSG_BUSY; + /* throttle until acquire successful */ + UltraLogEvent(logCtx, + CHANNELSTATE_DIAG_EVENTID_TRANSITBUSY, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel StateTransition failed - host OS acquire failed because boot BUSY @%s:%d\n", + chanId, PathName_Last_N_Nodes((U8 *) file, + 4), line); + } + pChan->CliStateOS = CHANNELCLI_ATTACHED; /* reset busy */ + MEMORYBARRIER; + return 0; + } + if (pChan->CliErrorOS != 0) { + /* we are in an error msg throttling state; come out of it */ + UltraLogEvent(logCtx, CHANNELSTATE_DIAG_EVENTID_TRANSITOK, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel OS client acquire now successful @%s:%d\n", + chanId, PathName_Last_N_Nodes((U8 *) file, 4), + line); + pChan->CliErrorOS = 0; + } + return 1; +} + +static inline void +ULTRA_channel_client_release_os(void *pChannel, U8 *chanId, void *logCtx, + char *file, int line, char *func) +{ + CHANNEL_HEADER *pChan = (CHANNEL_HEADER *) (pChannel); + if (pChan->CliErrorOS != 0) { + /* we are in an error msg throttling state; come out of it */ + UltraLogEvent(logCtx, CHANNELSTATE_DIAG_EVENTID_TRANSITOK, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel OS client error state cleared @%s:%d\n", + chanId, PathName_Last_N_Nodes((U8 *) file, 4), + line); + pChan->CliErrorOS = 0; + } + if (pChan->CliStateOS == CHANNELCLI_OWNED) + return; + if (pChan->CliStateOS != CHANNELCLI_BUSY) { + UltraLogEvent(logCtx, CHANNELSTATE_DIAG_EVENTID_TRANSITERR, + CHANNELSTATE_DIAG_SEVERITY, + CHANNELSTATE_DIAG_SUBSYS, func, line, + "%s Channel StateTransition INVALID! - release failed because OS client NOT BUSY (state=%s(%d)) @%s:%d\n", + chanId, + ULTRA_CHANNELCLI_STRING(pChan->CliStateOS), + pChan->CliStateOS, + PathName_Last_N_Nodes((U8 *) file, 4), line); + /* return; */ + } + pChan->CliStateOS = CHANNELCLI_ATTACHED; /* release busy */ +} + +/* +* Routine Description: +* Tries to insert the prebuilt signal pointed to by pSignal into the nth +* Queue of the Channel pointed to by pChannel +* +* Parameters: +* pChannel: (IN) points to the IO Channel +* Queue: (IN) nth Queue of the IO Channel +* pSignal: (IN) pointer to the signal +* +* Assumptions: +* - pChannel, Queue and pSignal are valid. +* - If insertion fails due to a full queue, the caller will determine the +* retry policy (e.g. wait & try again, report an error, etc.). +* +* Return value: 1 if the insertion succeeds, 0 if the queue was +* full. +*/ + +unsigned char SignalInsert(pCHANNEL_HEADER pChannel, U32 Queue, void *pSignal); + +/* +* Routine Description: +* Removes one signal from Channel pChannel's nth Queue at the +* time of the call and copies it into the memory pointed to by +* pSignal. +* +* Parameters: +* pChannel: (IN) points to the IO Channel +* Queue: (IN) nth Queue of the IO Channel +* pSignal: (IN) pointer to where the signals are to be copied +* +* Assumptions: +* - pChannel and Queue are valid. +* - pSignal points to a memory area large enough to hold queue's SignalSize +* +* Return value: 1 if the removal succeeds, 0 if the queue was +* empty. +*/ + +unsigned char SignalRemove(pCHANNEL_HEADER pChannel, U32 Queue, void *pSignal); + +/* +* Routine Description: +* Removes all signals present in Channel pChannel's nth Queue at the +* time of the call and copies them into the memory pointed to by +* pSignal. Returns the # of signals copied as the value of the routine. +* +* Parameters: +* pChannel: (IN) points to the IO Channel +* Queue: (IN) nth Queue of the IO Channel +* pSignal: (IN) pointer to where the signals are to be copied +* +* Assumptions: +* - pChannel and Queue are valid. +* - pSignal points to a memory area large enough to hold Queue's MaxSignals +* # of signals, each of which is Queue's SignalSize. +* +* Return value: +* # of signals copied. +*/ +unsigned int SignalRemoveAll(pCHANNEL_HEADER pChannel, U32 Queue, + void *pSignal); + +/* +* Routine Description: +* Determine whether a signal queue is empty. +* +* Parameters: +* pChannel: (IN) points to the IO Channel +* Queue: (IN) nth Queue of the IO Channel +* +* Return value: +* 1 if the signal queue is empty, 0 otherwise. +*/ +unsigned char SignalQueueIsEmpty(pCHANNEL_HEADER pChannel, U32 Queue); + +#endif diff --git a/drivers/staging/unisys/common-spar/include/version.h b/drivers/staging/unisys/common-spar/include/version.h new file mode 100644 index 000000000000..00b0ebb09eae --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/version.h @@ -0,0 +1,46 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* version.h */ + +/* Common version/release info needed by all components goes here. + * (This file must compile cleanly in all environments.) + * Ultimately, this will be combined with defines generated dynamically as + * part of the sysgen, and some of the defines below may in fact end up + * being replaced with dynamically generated ones. + */ +#ifndef __VERSION_H__ +#define __VERSION_H__ + +#define SPARVER1 "1" +#define SPARVER2 "0" +#define SPARVER3 "0" +#define SPARVER4 "0" + +#define VERSION SPARVER1 "." SPARVER2 "." SPARVER3 "." SPARVER4 +#define VERSIONDATE __DATE__ + +/* Here are various version forms needed in Windows environments. + */ +#define VISOR_PRODUCTVERSION SPARVERCOMMA +#define VISOR_PRODUCTVERSION_STR SPARVER1 "." SPARVER2 "." SPARVER3 "." \ + SPARVER4 +#define VISOR_OBJECTVERSION_STR SPARVER1 "," SPARVER2 "," SPARVER3 "," \ + SPARVER4 + +#define COPYRIGHT "Unisys Corporation" +#define COPYRIGHTDATE "2010 - 2013" + +#endif diff --git a/drivers/staging/unisys/include/commontypes.h b/drivers/staging/unisys/include/commontypes.h new file mode 100644 index 000000000000..ae46bed71431 --- /dev/null +++ b/drivers/staging/unisys/include/commontypes.h @@ -0,0 +1,166 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef _COMMONTYPES_H_ +#define _COMMONTYPES_H_ + +/* define the following to prevent include nesting in kernel header files of + * similar abreviated content */ +#define _SUPERVISOR_COMMONTYPES_H_ + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +#define U8 uint8_t +#define U16 uint16_t +#define U32 uint32_t +#define U64 uint64_t +#define S8 int8_t +#define S16 int16_t +#define S32 int32_t +#define S64 int64_t + +#ifdef __KERNEL__ + +#ifdef CONFIG_X86_32 +#define UINTN U32 +#else +#define UINTN U64 +#endif + +#else + +#include +#if __WORDSIZE == 32 +#define UINTN U32 +#elif __WORDSIZE == 64 +#define UINTN U64 +#else +#error Unsupported __WORDSIZE +#endif + +#endif + +typedef struct { + U32 data1; + U16 data2; + U16 data3; + U8 data4[8]; +} __attribute__ ((__packed__)) GUID; + +#ifndef GUID0 +#define GUID0 {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} } +#endif +typedef U64 GUEST_PHYSICAL_ADDRESS; + +#define MEMSET(ptr, val, len) memset(ptr, val, len) +#define MEMCMP(m1, m2, len) memcmp(m1, m2, len) +#define STRLEN(s) ((UINTN)strlen((const char *)s)) +#define STRCPY(d, s) (strcpy((char *)d, (const char *)s)) + +#define INLINE inline +#define OFFSETOF offsetof + +#ifdef __KERNEL__ +#define MEMORYBARRIER mb() +#define MEMCPY(dest, src, len) memcpy(dest, src, len) + +#define CHANNEL_GUID_MISMATCH(chType, chName, field, expected, actual, fil, \ + lin, logCtx) \ + do { \ + char s1[50], s2[50], s3[50]; \ + pr_err("Channel mismatch on channel=%s(%s) field=%s expected=%s actual=%s @%s:%d\n", \ + chName, GUID_format2(&chType, s1), field, \ + GUID_format2(&expected, s2), GUID_format2(&actual, s3), \ + fil, lin); \ + } while (0) +#define CHANNEL_U32_MISMATCH(chType, chName, field, expected, actual, fil, \ + lin, logCtx) \ + do { \ + char s1[50]; \ + pr_err("Channel mismatch on channel=%s(%s) field=%s expected=0x%-8.8lx actual=0x%-8.8lx @%s:%d\n", \ + chName, GUID_format2(&chType, s1), field, \ + (unsigned long)expected, (unsigned long)actual, \ + fil, lin); \ + } while (0) + +#define CHANNEL_U64_MISMATCH(chType, chName, field, expected, actual, fil, \ + lin, logCtx) \ + do { \ + char s1[50]; \ + pr_err("Channel mismatch on channel=%s(%s) field=%s expected=0x%-8.8Lx actual=0x%-8.8Lx @%s:%d\n", \ + chName, GUID_format2(&chType, s1), field, \ + (unsigned long long)expected, \ + (unsigned long long)actual, \ + fil, lin); \ + } while (0) + +#define UltraLogEvent(logCtx, EventId, Severity, SubsystemMask, pFunctionName, \ + LineNumber, Str, args...) \ + pr_info(Str, ## args) + +#else +#define MEMCPY(dest, src, len) memcpy(dest, src, len) + +#define MEMORYBARRIER mb() + +#define CHANNEL_GUID_MISMATCH(chType, chName, field, expected, actual, fil, \ + lin, logCtx) \ + do { \ + char s1[50], s2[50], s3[50]; \ + syslog(LOG_USER | LOG_ERR, \ + "Channel mismatch on channel=%s(%s) field=%s expected=%s actual=%s @%s:%d", \ + chName, GUID_format2(&chType, s1), field, \ + GUID_format2(&expected, s2), GUID_format2(&actual, s3), \ + fil, lin); \ + } while (0) + +#define CHANNEL_U32_MISMATCH(chType, chName, field, expected, actual, fil, \ + lin, logCtx) \ + do { \ + char s1[50]; \ + syslog(LOG_USER | LOG_ERR, \ + "Channel mismatch on channel=%s(%s) field=%s expected=0x%-8.8lx actual=0x%-8.8lx @%s:%d", \ + chName, GUID_format2(&chType, s1), field, \ + (unsigned long)expected, (unsigned long)actual, \ + fil, lin); \ + } while (0) + +#define CHANNEL_U64_MISMATCH(chType, chName, field, expected, actual, fil, \ + lin, logCtx) \ + do { \ + char s1[50]; \ + syslog(LOG_USER | LOG_ERR, \ + "Channel mismatch on channel=%s(%s) field=%s expected=0x%-8.8Lx actual=0x%-8.8Lx @%s:%d", \ + chName, GUID_format2(&chType, s1), field, \ + (unsigned long long)expected, \ + (unsigned long long)actual, \ + fil, lin); \ + } while (0) + +#define UltraLogEvent(logCtx, EventId, Severity, SubsystemMask, pFunctionName, \ + LineNumber, Str, args...) \ + syslog(LOG_USER | LOG_INFO, Str, ## args) +#endif + +#define VolatileBarrier() MEMORYBARRIER + +#endif +#include "guidutils.h" diff --git a/drivers/staging/unisys/include/guidutils.h b/drivers/staging/unisys/include/guidutils.h new file mode 100644 index 000000000000..75caf929cd6d --- /dev/null +++ b/drivers/staging/unisys/include/guidutils.h @@ -0,0 +1,203 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* guidutils.h + * + * These are GUID manipulation inlines that can be used from either + * kernel-mode or user-mode. + * + */ +#ifndef __GUIDUTILS_H__ +#define __GUIDUTILS_H__ + +#ifdef __KERNEL__ +#include +#include +#include +#define GUID_STRTOUL kstrtoul +#else +#include +#include +#include +#include + +#define GUID_STRTOUL strtoul +#endif + +static inline char * +GUID_format1(const GUID *guid, char *s) +{ + sprintf(s, "{%-8.8lx-%-4.4x-%-4.4x-%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x}", + (ulong) guid->data1, + guid->data2, + guid->data3, + guid->data4[0], + guid->data4[1], + guid->data4[2], + guid->data4[3], + guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]); + return s; +} + +/** Format a GUID in Microsoft's 'what in the world were they thinking' + * format. + */ +static inline char * +GUID_format2(const GUID *guid, char *s) +{ + sprintf(s, "{%-8.8lx-%-4.4x-%-4.4x-%-2.2x%-2.2x-%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x}", + (ulong) guid->data1, + guid->data2, + guid->data3, + guid->data4[0], + guid->data4[1], + guid->data4[2], + guid->data4[3], + guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]); + return s; +} + +/** + * Like GUID_format2 but without the curly braces and the + * hex digits in upper case + */ +static inline char * +GUID_format3(const GUID *guid, char *s) +{ + sprintf(s, "%-8.8lX-%-4.4X-%-4.4X-%-2.2X%-2.2X-%-2.2X%-2.2X%-2.2X%-2.2X%-2.2X%-2.2X", + (ulong) guid->data1, + guid->data2, + guid->data3, + guid->data4[0], + guid->data4[1], + guid->data4[2], + guid->data4[3], + guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]); + return s; +} + +/** Parse a guid string in any of these forms: + * {11111111-2222-3333-4455-66778899aabb} + * {11111111-2222-3333-445566778899aabb} + * 11111111-2222-3333-4455-66778899aabb + * 11111111-2222-3333-445566778899aabb + */ +static inline GUID +GUID_scan(U8 *p) +{ + GUID guid = GUID0; + U8 x[33]; + int count = 0; + int c, i = 0; + U8 cdata1[9]; + U8 cdata2[5]; + U8 cdata3[5]; + U8 cdata4[3]; + int dashcount = 0; + int brace = 0; + unsigned long uldata; + + if (!p) + return guid; + if (*p == '{') { + p++; + brace = 1; + } + while (count < 32) { + if (*p == '}') + return guid; + if (*p == '\0') + return guid; + c = toupper(*p); + p++; + if (c == '-') { + switch (dashcount) { + case 0: + if (i != 8) + return guid; + break; + case 1: + if (i != 4) + return guid; + break; + case 2: + if (i != 4) + return guid; + break; + case 3: + if (i != 4) + return guid; + break; + default: + return guid; + } + dashcount++; + i = 0; + continue; + } + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) + i++; + else + return guid; + x[count++] = c; + } + x[count] = '\0'; + if (brace) { + if (*p == '}') + p++; + else + return guid; + } + if (dashcount == 3 || dashcount == 4) + ; + else + return guid; + memset(cdata1, 0, sizeof(cdata1)); + memset(cdata2, 0, sizeof(cdata2)); + memset(cdata3, 0, sizeof(cdata3)); + memset(cdata4, 0, sizeof(cdata4)); + memcpy(cdata1, x + 0, 8); + memcpy(cdata2, x + 8, 4); + memcpy(cdata3, x + 12, 4); + + if (GUID_STRTOUL((char *) cdata1, 16, &uldata) == 0) + guid.data1 = (U32)uldata; + if (GUID_STRTOUL((char *) cdata2, 16, &uldata) == 0) + guid.data2 = (U16)uldata; + if (GUID_STRTOUL((char *) cdata3, 16, &uldata) == 0) + guid.data3 = (U16)uldata; + + for (i = 0; i < 8; i++) { + memcpy(cdata4, x + 16 + (i * 2), 2); + if (GUID_STRTOUL((char *) cdata4, 16, &uldata) == 0) + guid.data4[i] = (U8) uldata; + } + + return guid; +} + +static inline char * +GUID_sanitize(char *inputGuidStr, char *outputGuidStr) +{ + GUID g; + GUID guid0 = GUID0; + *outputGuidStr = '\0'; + g = GUID_scan((U8 *) inputGuidStr); + if (memcmp(&g, &guid0, sizeof(GUID)) == 0) + return outputGuidStr; /* bad GUID format */ + return GUID_format1(&g, outputGuidStr); +} + +#endif diff --git a/drivers/staging/unisys/visorchannel/Kconfig b/drivers/staging/unisys/visorchannel/Kconfig new file mode 100644 index 000000000000..41c3b4b997eb --- /dev/null +++ b/drivers/staging/unisys/visorchannel/Kconfig @@ -0,0 +1,10 @@ +# +# Unisys visorchannel configuration +# + +config UNISYS_VISORCHANNEL + tristate "Unisys visorchannel driver" + depends on UNISYSSPAR && UNISYS_VISORUTIL + ---help--- + If you say Y here, you will enable the Unisys visorchannel driver. + diff --git a/drivers/staging/unisys/visorchannel/Makefile b/drivers/staging/unisys/visorchannel/Makefile new file mode 100644 index 000000000000..f0060be55bc5 --- /dev/null +++ b/drivers/staging/unisys/visorchannel/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Unisys visorchannel +# + +obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel.o + +visorchannel-y := visorchannel_main.o visorchannel_funcs.o + +ccflags-y += -Idrivers/staging/unisys/include +ccflags-y += -Idrivers/staging/unisys/common-spar/include +ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels +ccflags-y += -Idrivers/staging/unisys/visorutil +ccflags-y += -DCONFIG_SPAR_GUEST -DGUESTDRIVERBUILD -DNOAUTOVERSION + diff --git a/drivers/staging/unisys/visorchannel/globals.h b/drivers/staging/unisys/visorchannel/globals.h new file mode 100644 index 000000000000..668f832ca566 --- /dev/null +++ b/drivers/staging/unisys/visorchannel/globals.h @@ -0,0 +1,29 @@ +/* globals.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VISORCHANNEL_GLOBALS_H__ +#define __VISORCHANNEL_GLOBALS_H__ + +#include "uniklog.h" +#include "timskmod.h" +#include "memregion.h" +#include "version.h" + +#define MYDRVNAME "visorchannel" + + +#endif diff --git a/drivers/staging/unisys/visorchannel/visorchannel.h b/drivers/staging/unisys/visorchannel/visorchannel.h new file mode 100644 index 000000000000..45466862494c --- /dev/null +++ b/drivers/staging/unisys/visorchannel/visorchannel.h @@ -0,0 +1,106 @@ +/* visorchannel.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VISORCHANNEL_H__ +#define __VISORCHANNEL_H__ + +#include "commontypes.h" +#include "memregion.h" +#include "channel.h" +#ifndef HOSTADDRESS +#define HOSTADDRESS U64 +#endif +#ifndef BOOL +#define BOOL int +#endif + +/* VISORCHANNEL is an opaque structure to users. + * Fields are declared only in the implementation .c files. + */ +typedef struct VISORCHANNEL_Tag VISORCHANNEL; + +/* Note that for visorchannel_create() and visorchannel_create_overlapped(), + * and arguments may be 0 if we are a channel CLIENT. + * In this case, the values can simply be read from the channel header. + */ +VISORCHANNEL *visorchannel_create(HOSTADDRESS physaddr, + ulong channelBytes, GUID guid); +VISORCHANNEL *visorchannel_create_overlapped(ulong channelBytes, + VISORCHANNEL *parent, ulong off, + GUID guid); +VISORCHANNEL *visorchannel_create_with_lock(HOSTADDRESS physaddr, + ulong channelBytes, GUID guid); +VISORCHANNEL *visorchannel_create_overlapped_with_lock(ulong channelBytes, + VISORCHANNEL *parent, + ulong off, GUID guid); +void visorchannel_destroy(VISORCHANNEL *channel); +int visorchannel_read(VISORCHANNEL *channel, ulong offset, + void *local, ulong nbytes); +int visorchannel_write(VISORCHANNEL *channel, ulong offset, + void *local, ulong nbytes); +int visorchannel_clear(VISORCHANNEL *channel, ulong offset, + U8 ch, ulong nbytes); +BOOL visorchannel_signalremove(VISORCHANNEL *channel, U32 queue, void *msg); +BOOL visorchannel_signalinsert(VISORCHANNEL *channel, U32 queue, void *msg); +int visorchannel_signalqueue_slots_avail(VISORCHANNEL *channel, U32 queue); +int visorchannel_signalqueue_max_slots(VISORCHANNEL *channel, U32 queue); + +HOSTADDRESS visorchannel_get_physaddr(VISORCHANNEL *channel); +ulong visorchannel_get_nbytes(VISORCHANNEL *channel); +char *visorchannel_id(VISORCHANNEL *channel, char *s); +char *visorchannel_zoneid(VISORCHANNEL *channel, char *s); +U64 visorchannel_get_clientpartition(VISORCHANNEL *channel); +GUID visorchannel_get_GUID(VISORCHANNEL *channel); +MEMREGION *visorchannel_get_memregion(VISORCHANNEL *channel); +char *visorchannel_GUID_id(GUID *guid, char *s); +void visorchannel_debug(VISORCHANNEL *channel, int nQueues, + struct seq_file *seq, U32 off); +void visorchannel_dump_section(VISORCHANNEL *chan, char *s, + int off, int len, struct seq_file *seq); +void *visorchannel_get_header(VISORCHANNEL *channel); + +#define VISORCHANNEL_CHANGE_SERVER_STATE(chan, chanId, newstate) \ + do { \ + U8 *p = (U8 *)visorchannel_get_header(chan); \ + if (p) { \ + ULTRA_CHANNEL_SERVER_TRANSITION(p, chanId, SrvState, \ + newstate, logCtx); \ + visorchannel_write \ + (chan, \ + offsetof(ULTRA_CHANNEL_PROTOCOL, SrvState), \ + p + \ + offsetof(ULTRA_CHANNEL_PROTOCOL, SrvState), \ + sizeof(U32)); \ + } \ + } while (0) + +#define VISORCHANNEL_CHANGE_CLIENT_STATE(chan, chanId, newstate) \ + do { \ + U8 *p = (U8 *)visorchannel_get_header(chan); \ + if (p) { \ + ULTRA_CHANNEL_CLIENT_TRANSITION(p, chanId, CliStateOS, \ + newstate, logCtx); \ + visorchannel_write \ + (chan, \ + offsetof(ULTRA_CHANNEL_PROTOCOL, CliStateOS), \ + p + \ + offsetof(ULTRA_CHANNEL_PROTOCOL, CliStateOS), \ + sizeof(U32)); \ + } \ + } while (0) + +#endif diff --git a/drivers/staging/unisys/visorchannel/visorchannel_funcs.c b/drivers/staging/unisys/visorchannel/visorchannel_funcs.c new file mode 100644 index 000000000000..509c77bbb3a8 --- /dev/null +++ b/drivers/staging/unisys/visorchannel/visorchannel_funcs.c @@ -0,0 +1,765 @@ +/* visorchannel_funcs.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * This provides Supervisor channel communication primitives, which are + * independent of the mechanism used to access the channel data. All channel + * data is accessed using the memregion abstraction. (memregion has both + * a CM2 implementation and a direct memory implementation.) + */ + +#include "globals.h" +#include "visorchannel.h" +#include "guidutils.h" + +#define MYDRVNAME "visorchannel" + +struct VISORCHANNEL_Tag { + MEMREGION *memregion; /* from memregion_create() */ + CHANNEL_HEADER chan_hdr; + GUID guid; + ulong size; + BOOL needs_lock; + spinlock_t insert_lock; + spinlock_t remove_lock; + + struct { + SIGNAL_QUEUE_HEADER req_queue; + SIGNAL_QUEUE_HEADER rsp_queue; + SIGNAL_QUEUE_HEADER event_queue; + SIGNAL_QUEUE_HEADER ack_queue; + } safe_uis_queue; +}; + +/* Creates the VISORCHANNEL abstraction for a data area in memory, but does + * NOT modify this data area. + */ +static VISORCHANNEL * +visorchannel_create_guts(HOSTADDRESS physaddr, ulong channelBytes, + VISORCHANNEL *parent, ulong off, GUID guid, + BOOL needs_lock) +{ + VISORCHANNEL *p = NULL; + void *rc = NULL; + + p = kmalloc(sizeof(VISORCHANNEL), GFP_KERNEL|__GFP_NORETRY); + if (p == NULL) + FAIL("allocation failed", 0); + p->memregion = NULL; + p->needs_lock = needs_lock; + spin_lock_init(&p->insert_lock); + spin_lock_init(&p->remove_lock); + + /* prepare chan_hdr (abstraction to read/write channel memory) */ + if (parent == NULL) + p->memregion = + memregion_create(physaddr, sizeof(CHANNEL_HEADER)); + else + p->memregion = + memregion_create_overlapped + (parent->memregion, off, sizeof(CHANNEL_HEADER)); + if (p->memregion == NULL) + FAIL("memregion_create failed", 0); + if (memregion_read(p->memregion, 0, &p->chan_hdr, + sizeof(CHANNEL_HEADER)) < 0) + FAIL("memregion_read failed", 0); + if (channelBytes == 0) + /* we had better be a CLIENT of this channel */ + channelBytes = (ulong) p->chan_hdr.Size; + if (STRUCTSEQUAL(guid, Guid0)) + /* we had better be a CLIENT of this channel */ + guid = p->chan_hdr.Type; + if (memregion_resize(p->memregion, channelBytes) < 0) + FAIL("memregion_resize failed", 0); + p->size = channelBytes; + p->guid = guid; + + RETPTR(p); + +Away: + + if (rc == NULL) { + if (p != NULL) { + visorchannel_destroy(p); + p = NULL; + } + } + return rc; +} + +VISORCHANNEL * +visorchannel_create(HOSTADDRESS physaddr, ulong channelBytes, GUID guid) +{ + return visorchannel_create_guts(physaddr, channelBytes, NULL, 0, guid, + FALSE); +} +EXPORT_SYMBOL_GPL(visorchannel_create); + +VISORCHANNEL * +visorchannel_create_with_lock(HOSTADDRESS physaddr, ulong channelBytes, + GUID guid) +{ + return visorchannel_create_guts(physaddr, channelBytes, NULL, 0, guid, + TRUE); +} +EXPORT_SYMBOL_GPL(visorchannel_create_with_lock); + +VISORCHANNEL * +visorchannel_create_overlapped(ulong channelBytes, + VISORCHANNEL *parent, ulong off, GUID guid) +{ + return visorchannel_create_guts(0, channelBytes, parent, off, guid, + FALSE); +} +EXPORT_SYMBOL_GPL(visorchannel_create_overlapped); + +VISORCHANNEL * +visorchannel_create_overlapped_with_lock(ulong channelBytes, + VISORCHANNEL *parent, ulong off, + GUID guid) +{ + return visorchannel_create_guts(0, channelBytes, parent, off, guid, + TRUE); +} +EXPORT_SYMBOL_GPL(visorchannel_create_overlapped_with_lock); + +void +visorchannel_destroy(VISORCHANNEL *channel) +{ + if (channel == NULL) + return; + if (channel->memregion != NULL) { + memregion_destroy(channel->memregion); + channel->memregion = NULL; + } + kfree(channel); +} +EXPORT_SYMBOL_GPL(visorchannel_destroy); + +HOSTADDRESS +visorchannel_get_physaddr(VISORCHANNEL *channel) +{ + return memregion_get_physaddr(channel->memregion); +} +EXPORT_SYMBOL_GPL(visorchannel_get_physaddr); + +ulong +visorchannel_get_nbytes(VISORCHANNEL *channel) +{ + return channel->size; +} +EXPORT_SYMBOL_GPL(visorchannel_get_nbytes); + +char * +visorchannel_GUID_id(GUID *guid, char *s) +{ + return GUID_format1(guid, s); +} +EXPORT_SYMBOL_GPL(visorchannel_GUID_id); + +char * +visorchannel_id(VISORCHANNEL *channel, char *s) +{ + return visorchannel_GUID_id(&channel->guid, s); +} +EXPORT_SYMBOL_GPL(visorchannel_id); + +char * +visorchannel_zoneid(VISORCHANNEL *channel, char *s) +{ + return visorchannel_GUID_id(&channel->chan_hdr.ZoneGuid, s); +} +EXPORT_SYMBOL_GPL(visorchannel_zoneid); + +HOSTADDRESS +visorchannel_get_clientpartition(VISORCHANNEL *channel) +{ + return channel->chan_hdr.PartitionHandle; +} +EXPORT_SYMBOL_GPL(visorchannel_get_clientpartition); + +GUID +visorchannel_get_GUID(VISORCHANNEL *channel) +{ + return channel->guid; +} +EXPORT_SYMBOL_GPL(visorchannel_get_GUID); + +MEMREGION * +visorchannel_get_memregion(VISORCHANNEL *channel) +{ + return channel->memregion; +} +EXPORT_SYMBOL_GPL(visorchannel_get_memregion); + +pSIGNAL_QUEUE_HEADER +visorchannel_get_safe_queue(VISORCHANNEL *pchannel, U32 queue) +{ + switch (queue) { + case 0: + return &pchannel->safe_uis_queue.req_queue; + case 1: + return &pchannel->safe_uis_queue.rsp_queue; + case 2: + return &pchannel->safe_uis_queue.event_queue; + case 3: + return &pchannel->safe_uis_queue.ack_queue; + default: + ERRDRV("Invalid queue value %d\n", queue); + return NULL; + } +} /* end visorchannel_get_safe_queue */ + +int +visorchannel_read(VISORCHANNEL *channel, ulong offset, + void *local, ulong nbytes) +{ + int rc = memregion_read(channel->memregion, offset, local, nbytes); + if ((rc >= 0) && (offset == 0) && (nbytes >= sizeof(CHANNEL_HEADER))) + memcpy(&channel->chan_hdr, local, sizeof(CHANNEL_HEADER)); + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_read); + +int +visorchannel_write(VISORCHANNEL *channel, ulong offset, + void *local, ulong nbytes) +{ + if (offset == 0 && nbytes >= sizeof(CHANNEL_HEADER)) + memcpy(&channel->chan_hdr, local, sizeof(CHANNEL_HEADER)); + return memregion_write(channel->memregion, offset, local, nbytes); +} +EXPORT_SYMBOL_GPL(visorchannel_write); + +int +visorchannel_clear(VISORCHANNEL *channel, ulong offset, U8 ch, ulong nbytes) +{ + int rc = -1; + int bufsize = 65536; + int written = 0; + U8 *buf = vmalloc(bufsize); + + if (buf == NULL) { + ERRDRV("%s failed memory allocation", __func__); + RETINT(-1); + } + memset(buf, ch, bufsize); + while (nbytes > 0) { + ulong thisbytes = bufsize; + int x = -1; + if (nbytes < thisbytes) + thisbytes = nbytes; + x = memregion_write(channel->memregion, offset + written, + buf, thisbytes); + if (x < 0) + RETINT(x); + written += thisbytes; + nbytes -= thisbytes; + } + RETINT(0); + +Away: + if (buf != NULL) { + vfree(buf); + buf = NULL; + } + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_clear); + +void * +visorchannel_get_header(VISORCHANNEL *channel) +{ + return (void *) &(channel->chan_hdr); +} +EXPORT_SYMBOL_GPL(visorchannel_get_header); + +/** Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a + * channel header + */ +#define SIG_QUEUE_OFFSET(chan_hdr, q) \ + ((chan_hdr)->oChannelSpace + ((q) * sizeof(SIGNAL_QUEUE_HEADER))) + +/** Return offset of a specific queue entry (data) from the beginning of a + * channel header + */ +#define SIG_DATA_OFFSET(chan_hdr, q, sig_hdr, slot) \ + (SIG_QUEUE_OFFSET(chan_hdr, q) + (sig_hdr)->oSignalBase + \ + ((slot) * (sig_hdr)->SignalSize)) + +/** Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back + * into host memory + */ +#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD) \ + (memregion_write(channel->memregion, \ + SIG_QUEUE_OFFSET(&channel->chan_hdr, queue)+\ + offsetof(SIGNAL_QUEUE_HEADER, FIELD), \ + &((sig_hdr)->FIELD), \ + sizeof((sig_hdr)->FIELD)) >= 0) + +static BOOL +sig_read_header(VISORCHANNEL *channel, U32 queue, + SIGNAL_QUEUE_HEADER *sig_hdr) +{ + BOOL rc = FALSE; + + if (channel->chan_hdr.oChannelSpace < sizeof(CHANNEL_HEADER)) + FAIL("oChannelSpace too small", FALSE); + + /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */ + + if (memregion_read(channel->memregion, + SIG_QUEUE_OFFSET(&channel->chan_hdr, queue), + sig_hdr, sizeof(SIGNAL_QUEUE_HEADER)) < 0) { + ERRDRV("queue=%d SIG_QUEUE_OFFSET=%d", + queue, (int)SIG_QUEUE_OFFSET(&channel->chan_hdr, queue)); + FAIL("memregion_read of signal queue failed", FALSE); + } + RETBOOL(TRUE); +Away: + return rc; +} + +static BOOL +sig_do_data(VISORCHANNEL *channel, U32 queue, + SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data, BOOL is_write) +{ + BOOL rc = FALSE; + int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue, + sig_hdr, slot); + if (is_write) { + if (memregion_write(channel->memregion, signal_data_offset, + data, sig_hdr->SignalSize) < 0) + FAIL("memregion_write of signal data failed", FALSE); + } else { + if (memregion_read(channel->memregion, signal_data_offset, + data, sig_hdr->SignalSize) < 0) + FAIL("memregion_read of signal data failed", FALSE); + } + RETBOOL(TRUE); +Away: + return rc; +} + +static inline BOOL +sig_read_data(VISORCHANNEL *channel, U32 queue, + SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data) +{ + return sig_do_data(channel, queue, sig_hdr, slot, data, FALSE); +} + +static inline BOOL +sig_write_data(VISORCHANNEL *channel, U32 queue, + SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data) +{ + return sig_do_data(channel, queue, sig_hdr, slot, data, TRUE); +} + +static inline unsigned char +safe_sig_queue_validate(pSIGNAL_QUEUE_HEADER psafe_sqh, + pSIGNAL_QUEUE_HEADER punsafe_sqh, + U32 *phead, U32 *ptail) +{ + if ((*phead >= psafe_sqh->MaxSignalSlots) + || (*ptail >= psafe_sqh->MaxSignalSlots)) { + /* Choose 0 or max, maybe based on current tail value */ + *phead = 0; + *ptail = 0; + + /* Sync with client as necessary */ + punsafe_sqh->Head = *phead; + punsafe_sqh->Tail = *ptail; + + ERRDRV("safe_sig_queue_validate: head = 0x%x, tail = 0x%x, MaxSlots = 0x%x", + *phead, *ptail, psafe_sqh->MaxSignalSlots); + return 0; + } + return 1; +} /* end safe_sig_queue_validate */ + +BOOL +visorchannel_signalremove(VISORCHANNEL *channel, U32 queue, void *msg) +{ + BOOL rc = FALSE; + SIGNAL_QUEUE_HEADER sig_hdr; + + if (channel->needs_lock) + spin_lock(&channel->remove_lock); + + if (!sig_read_header(channel, queue, &sig_hdr)) + RETBOOL(FALSE); + if (sig_hdr.Head == sig_hdr.Tail) + RETBOOL(FALSE); /* no signals to remove */ + sig_hdr.Tail = (sig_hdr.Tail + 1) % sig_hdr.MaxSignalSlots; + if (!sig_read_data(channel, queue, &sig_hdr, sig_hdr.Tail, msg)) + FAIL("sig_read_data failed", FALSE); + sig_hdr.NumSignalsReceived++; + + /* For each data field in SIGNAL_QUEUE_HEADER that was modified, + * update host memory. + */ + MEMORYBARRIER; + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, Tail)) + FAIL("memregion_write of Tail failed", FALSE); + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumSignalsReceived)) + FAIL("memregion_write of NumSignalsReceived failed", FALSE); + + RETBOOL(TRUE); + +Away: + if (channel->needs_lock) + spin_unlock(&channel->remove_lock); + + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_signalremove); + +BOOL +visorchannel_safesignalremove(VISORCHANNEL *channel, U32 queue, void *msg) +{ + BOOL rc = FALSE; + SIGNAL_QUEUE_HEADER *psafe_sqh, unsafe_sqh; + int stat; + + if (channel->needs_lock) + spin_lock(&channel->remove_lock); + + if (!sig_read_header(channel, queue, &unsafe_sqh)) + RETBOOL(FALSE); + + psafe_sqh = visorchannel_get_safe_queue(channel, queue); + if (psafe_sqh == NULL) { + ERRDRV("safesignalremove: get_safe_queue failed\n"); + RETBOOL(FALSE); + } + + stat = + safe_sig_queue_validate(psafe_sqh, &unsafe_sqh, &unsafe_sqh.Head, + &unsafe_sqh.Tail); + if (stat == 0) { + ERRDRV("safe_signal_remove: safe_sig_queue_validate failed, queue = %d", + queue); + RETBOOL(FALSE); + } + + if (unsafe_sqh.Head == unsafe_sqh.Tail) + RETBOOL(FALSE); /* no signals to remove */ + unsafe_sqh.Tail = (unsafe_sqh.Tail + 1) % psafe_sqh->MaxSignalSlots; + if (!sig_read_data(channel, queue, psafe_sqh, unsafe_sqh.Tail, msg)) + FAIL("sig_read_data failed", FALSE); + unsafe_sqh.NumSignalsReceived++; + + /* For each data field in SIGNAL_QUEUE_HEADER that was modified, + * update host memory. + */ + MEMORYBARRIER; + if (!SIG_WRITE_FIELD(channel, queue, &unsafe_sqh, Tail)) + FAIL("memregion_write of Tail failed", FALSE); + if (!SIG_WRITE_FIELD(channel, queue, &unsafe_sqh, NumSignalsReceived)) + FAIL("memregion_write of NumSignalsReceived failed", FALSE); + + RETBOOL(TRUE); + +Away: + if (channel->needs_lock) + spin_unlock(&channel->remove_lock); + + return rc; +} /* end visorchannel_safesignalremove */ + +BOOL +visorchannel_signalinsert(VISORCHANNEL *channel, U32 queue, void *msg) +{ + BOOL rc = FALSE; + SIGNAL_QUEUE_HEADER sig_hdr; + + if (channel->needs_lock) + spin_lock(&channel->insert_lock); + + if (!sig_read_header(channel, queue, &sig_hdr)) + RETBOOL(FALSE); + + sig_hdr.Head = ((sig_hdr.Head + 1) % sig_hdr.MaxSignalSlots); + if (sig_hdr.Head == sig_hdr.Tail) { +#if 0 + ERRDRV("visorchannel queue #%d overflow (max slots=%d)", + queue, sig_hdr.MaxSignalSlots); +#endif + sig_hdr.NumOverflows++; + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumOverflows)) + FAIL("memregion_write of NumOverflows failed", FALSE); + RETBOOL(FALSE); + } + + if (!sig_write_data(channel, queue, &sig_hdr, sig_hdr.Head, msg)) + FAIL("sig_write_data failed", FALSE); + sig_hdr.NumSignalsSent++; + + /* For each data field in SIGNAL_QUEUE_HEADER that was modified, + * update host memory. + */ + MEMORYBARRIER; + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, Head)) + FAIL("memregion_write of Head failed", FALSE); + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumSignalsSent)) + FAIL("memregion_write of NumSignalsSent failed", FALSE); + + RETBOOL(TRUE); + +Away: + if (channel->needs_lock) + spin_unlock(&channel->insert_lock); + + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_signalinsert); + + +int +visorchannel_signalqueue_slots_avail(VISORCHANNEL *channel, U32 queue) +{ + SIGNAL_QUEUE_HEADER sig_hdr; + U32 slots_avail, slots_used; + U32 head, tail; + + if (!sig_read_header(channel, queue, &sig_hdr)) + return 0; + head = sig_hdr.Head; + tail = sig_hdr.Tail; + if (head < tail) + head = head + sig_hdr.MaxSignalSlots; + slots_used = (head - tail); + slots_avail = sig_hdr.MaxSignals - slots_used; + return (int) slots_avail; +} +EXPORT_SYMBOL_GPL(visorchannel_signalqueue_slots_avail); + +int +visorchannel_signalqueue_max_slots(VISORCHANNEL *channel, U32 queue) +{ + SIGNAL_QUEUE_HEADER sig_hdr; + if (!sig_read_header(channel, queue, &sig_hdr)) + return 0; + return (int) sig_hdr.MaxSignals; +} +EXPORT_SYMBOL_GPL(visorchannel_signalqueue_max_slots); + +BOOL +visorchannel_safesignalinsert(VISORCHANNEL *channel, U32 queue, void *msg) +{ + BOOL rc = FALSE; + SIGNAL_QUEUE_HEADER *psafe_sqh, unsafe_sqh; + int stat; + + if (channel->needs_lock) + spin_lock(&channel->insert_lock); + + if (!sig_read_header(channel, queue, &unsafe_sqh)) + RETBOOL(FALSE); + + psafe_sqh = visorchannel_get_safe_queue(channel, queue); + if (psafe_sqh == NULL) { + ERRDRV("safesignalinsert: get_safe_queue failed\n"); + RETBOOL(FALSE); + } + + unsafe_sqh.Head = ((unsafe_sqh.Head + 1) % psafe_sqh->MaxSignalSlots); + + stat = + safe_sig_queue_validate(psafe_sqh, &unsafe_sqh, &unsafe_sqh.Head, + &unsafe_sqh.Tail); + if (stat == 0) { + ERRDRV("safe_signal_insert: safe_sig_queue_validate failed, queue = %d", + queue); + RETBOOL(FALSE); + } + + if (unsafe_sqh.Head == unsafe_sqh.Tail) { +#if 0 + ERRDRV("visorchannel queue #%d overflow (max slots=%d)", + queue, psafe_sqh->MaxSignalSlots); +#endif + unsafe_sqh.NumOverflows++; + if (!SIG_WRITE_FIELD(channel, queue, &unsafe_sqh, NumOverflows)) + FAIL("memregion_write of NumOverflows failed", FALSE); + RETBOOL(FALSE); + } + + if (!sig_write_data(channel, queue, psafe_sqh, unsafe_sqh.Head, msg)) + FAIL("sig_write_data failed", FALSE); + unsafe_sqh.NumSignalsSent++; + + /* For each data field in SIGNAL_QUEUE_HEADER that was modified, + * update host memory. + */ + MEMORYBARRIER; + if (!SIG_WRITE_FIELD(channel, queue, &unsafe_sqh, Head)) + FAIL("memregion_write of Head failed", FALSE); + if (!SIG_WRITE_FIELD(channel, queue, &unsafe_sqh, NumSignalsSent)) + FAIL("memregion_write of NumSignalsSent failed", FALSE); + + RETBOOL(TRUE); + +Away: + if (channel->needs_lock) + spin_unlock(&channel->insert_lock); + + return rc; +} /* end visorchannel_safesignalinsert */ + +static void +sigqueue_debug(SIGNAL_QUEUE_HEADER *q, int which, struct seq_file *seq) +{ + seq_printf(seq, "Signal Queue #%d\n", which); + seq_printf(seq, " VersionId = %lu\n", (ulong) q->VersionId); + seq_printf(seq, " Type = %lu\n", (ulong) q->Type); + seq_printf(seq, " oSignalBase = %llu\n", + (long long) q->oSignalBase); + seq_printf(seq, " SignalSize = %lu\n", (ulong) q->SignalSize); + seq_printf(seq, " MaxSignalSlots = %lu\n", + (ulong) q->MaxSignalSlots); + seq_printf(seq, " MaxSignals = %lu\n", (ulong) q->MaxSignals); + seq_printf(seq, " FeatureFlags = %-16.16Lx\n", + (long long) q->FeatureFlags); + seq_printf(seq, " NumSignalsSent = %llu\n", + (long long) q->NumSignalsSent); + seq_printf(seq, " NumSignalsReceived = %llu\n", + (long long) q->NumSignalsReceived); + seq_printf(seq, " NumOverflows = %llu\n", + (long long) q->NumOverflows); + seq_printf(seq, " Head = %lu\n", (ulong) q->Head); + seq_printf(seq, " Tail = %lu\n", (ulong) q->Tail); +} + +void +visorchannel_debug(VISORCHANNEL *channel, int nQueues, + struct seq_file *seq, U32 off) +{ + HOSTADDRESS addr = 0; + ulong nbytes = 0, nbytes_region = 0; + MEMREGION *memregion = NULL; + CHANNEL_HEADER hdr; + CHANNEL_HEADER *phdr = &hdr; + char s[99]; + int i = 0; + int errcode = 0; + + if (channel == NULL) { + ERRDRV("%s no channel", __func__); + return; + } + memregion = channel->memregion; + if (memregion == NULL) { + ERRDRV("%s no memregion", __func__); + return; + } + addr = memregion_get_physaddr(memregion); + nbytes_region = memregion_get_nbytes(memregion); + errcode = visorchannel_read(channel, off, + phdr, sizeof(CHANNEL_HEADER)); + if (errcode < 0) { + seq_printf(seq, + "Read of channel header failed with errcode=%d)\n", + errcode); + if (off == 0) { + phdr = &channel->chan_hdr; + seq_puts(seq, "(following data may be stale)\n"); + } else + return; + } + nbytes = (ulong) (phdr->Size); + seq_printf(seq, "--- Begin channel @0x%-16.16Lx for 0x%lx bytes (region=0x%lx bytes) ---\n", + addr + off, nbytes, nbytes_region); + seq_printf(seq, "Type = %s\n", GUID_format2(&phdr->Type, s)); + seq_printf(seq, "ZoneGuid = %s\n", + GUID_format2(&phdr->ZoneGuid, s)); + seq_printf(seq, "Signature = 0x%-16.16Lx\n", + (long long) phdr->Signature); + seq_printf(seq, "LegacyState = %lu\n", (ulong) phdr->LegacyState); + seq_printf(seq, "SrvState = %lu\n", (ulong) phdr->SrvState); + seq_printf(seq, "CliStateBoot = %lu\n", (ulong) phdr->CliStateBoot); + seq_printf(seq, "CliStateOS = %lu\n", (ulong) phdr->CliStateOS); + seq_printf(seq, "HeaderSize = %lu\n", (ulong) phdr->HeaderSize); + seq_printf(seq, "Size = %llu\n", (long long) phdr->Size); + seq_printf(seq, "Features = 0x%-16.16llx\n", + (long long) phdr->Features); + seq_printf(seq, "PartitionHandle = 0x%-16.16llx\n", + (long long) phdr->PartitionHandle); + seq_printf(seq, "Handle = 0x%-16.16llx\n", + (long long) phdr->Handle); + seq_printf(seq, "VersionId = %lu\n", (ulong) phdr->VersionId); + seq_printf(seq, "oChannelSpace = %llu\n", + (long long) phdr->oChannelSpace); + if ((phdr->oChannelSpace == 0) || (errcode < 0)) + ; + else + for (i = 0; i < nQueues; i++) { + SIGNAL_QUEUE_HEADER q; + errcode = visorchannel_read(channel, + off + phdr->oChannelSpace + + (i * sizeof(q)), + &q, sizeof(q)); + if (errcode < 0) { + seq_printf(seq, + "failed to read signal queue #%d from channel @0x%-16.16Lx errcode=%d\n", + i, addr, errcode); + continue; + } + sigqueue_debug(&q, i, seq); + } + seq_printf(seq, "--- End channel @0x%-16.16Lx for 0x%lx bytes ---\n", + addr + off, nbytes); +} +EXPORT_SYMBOL_GPL(visorchannel_debug); + +void +visorchannel_dump_section(VISORCHANNEL *chan, char *s, + int off, int len, struct seq_file *seq) +{ + char *buf = NULL, *fmtbuf = NULL; + int fmtbufsize = 0; + int i = 0; + int errcode = 0; + + fmtbufsize = 100 * COVQ(len, 16); + buf = kmalloc(len, GFP_KERNEL|__GFP_NORETRY); + fmtbuf = kmalloc(fmtbufsize, GFP_KERNEL|__GFP_NORETRY); + if (buf == NULL || fmtbuf == NULL) + goto Away; + + errcode = visorchannel_read(chan, off, buf, len); + if (errcode < 0) { + ERRDRV("%s failed to read %s from channel errcode=%d", + s, __func__, errcode); + goto Away; + } + seq_printf(seq, "channel %s:\n", s); + hexDumpToBuffer(fmtbuf, fmtbufsize, " ", buf, len, 16); + for (i = 0; fmtbuf[i] != '\0'; i++) + seq_printf(seq, "%c", fmtbuf[i]); + +Away: + if (buf != NULL) { + kfree(buf); + buf = NULL; + } + if (fmtbuf != NULL) { + kfree(fmtbuf); + fmtbuf = NULL; + } +} +EXPORT_SYMBOL_GPL(visorchannel_dump_section); diff --git a/drivers/staging/unisys/visorchannel/visorchannel_main.c b/drivers/staging/unisys/visorchannel/visorchannel_main.c new file mode 100644 index 000000000000..482ee0ac1c16 --- /dev/null +++ b/drivers/staging/unisys/visorchannel/visorchannel_main.c @@ -0,0 +1,49 @@ +/* visorchannel_main.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * This is a module "wrapper" around visorchannel_funcs. + */ + +#include "globals.h" +#include "channel.h" +#include "visorchannel.h" +#include "guidutils.h" + +#define MYDRVNAME "visorchannel" + +static int __init +visorchannel_init(void) +{ + INFODRV("driver version %s loaded", VERSION); + return 0; +} + +static void +visorchannel_exit(void) +{ + INFODRV("driver unloaded"); +} + +module_init(visorchannel_init); +module_exit(visorchannel_exit); + +MODULE_AUTHOR("Unisys"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Supervisor channel driver for service partition: ver " + VERSION); +MODULE_VERSION(VERSION);