mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Move the visorbus driver out of staging (drivers/staging/unisys/visorbus) and to drivers/visorbus. Modify the configuration and makefiles so they now reference the new location. The s-Par header file visorbus.h that is referenced by all s-Par drivers, is being moved into include/linux. Signed-off-by: David Kershner <david.kershner@unisys.com> Reviewed-by: Tim Sell <timothy.sell@unisys.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
		
			
				
	
	
		
			434 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			434 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
/*
 | 
						|
 * Copyright (C) 2010 - 2015 UNISYS CORPORATION
 | 
						|
 * All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 *  This provides s-Par channel communication primitives, which are
 | 
						|
 *  independent of the mechanism used to access the channel data.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/uuid.h>
 | 
						|
#include <linux/io.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/visorbus.h>
 | 
						|
 | 
						|
#include "visorbus_private.h"
 | 
						|
#include "controlvmchannel.h"
 | 
						|
 | 
						|
#define VISOR_DRV_NAME "visorchannel"
 | 
						|
 | 
						|
#define VISOR_CONSOLEVIDEO_CHANNEL_GUID \
 | 
						|
	GUID_INIT(0x3cd6e705, 0xd6a2, 0x4aa5, \
 | 
						|
		  0xad, 0x5c, 0x7b, 0x8, 0x88, 0x9d, 0xff, 0xe2)
 | 
						|
 | 
						|
static const guid_t visor_video_guid = VISOR_CONSOLEVIDEO_CHANNEL_GUID;
 | 
						|
 | 
						|
struct visorchannel {
 | 
						|
	u64 physaddr;
 | 
						|
	ulong nbytes;
 | 
						|
	void *mapped;
 | 
						|
	bool requested;
 | 
						|
	struct channel_header chan_hdr;
 | 
						|
	guid_t guid;
 | 
						|
	/*
 | 
						|
	 * channel creator knows if more than one thread will be inserting or
 | 
						|
	 * removing
 | 
						|
	 */
 | 
						|
	bool needs_lock;
 | 
						|
	/* protect head writes in chan_hdr */
 | 
						|
	spinlock_t insert_lock;
 | 
						|
	/* protect tail writes in chan_hdr */
 | 
						|
	spinlock_t remove_lock;
 | 
						|
	guid_t type;
 | 
						|
	guid_t inst;
 | 
						|
};
 | 
						|
 | 
						|
void visorchannel_destroy(struct visorchannel *channel)
 | 
						|
{
 | 
						|
	if (!channel)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (channel->mapped) {
 | 
						|
		memunmap(channel->mapped);
 | 
						|
		if (channel->requested)
 | 
						|
			release_mem_region(channel->physaddr, channel->nbytes);
 | 
						|
	}
 | 
						|
	kfree(channel);
 | 
						|
}
 | 
						|
 | 
						|
u64 visorchannel_get_physaddr(struct visorchannel *channel)
 | 
						|
{
 | 
						|
	return channel->physaddr;
 | 
						|
}
 | 
						|
 | 
						|
ulong visorchannel_get_nbytes(struct visorchannel *channel)
 | 
						|
{
 | 
						|
	return channel->nbytes;
 | 
						|
}
 | 
						|
 | 
						|
char *visorchannel_guid_id(const guid_t *guid, char *s)
 | 
						|
{
 | 
						|
	sprintf(s, "%pUL", guid);
 | 
						|
	return s;
 | 
						|
}
 | 
						|
 | 
						|
char *visorchannel_id(struct visorchannel *channel, char *s)
 | 
						|
{
 | 
						|
	return visorchannel_guid_id(&channel->guid, s);
 | 
						|
}
 | 
						|
 | 
						|
char *visorchannel_zoneid(struct visorchannel *channel, char *s)
 | 
						|
{
 | 
						|
	return visorchannel_guid_id(&channel->chan_hdr.zone_guid, s);
 | 
						|
}
 | 
						|
 | 
						|
u64 visorchannel_get_clientpartition(struct visorchannel *channel)
 | 
						|
{
 | 
						|
	return channel->chan_hdr.partition_handle;
 | 
						|
}
 | 
						|
 | 
						|
int visorchannel_set_clientpartition(struct visorchannel *channel,
 | 
						|
				     u64 partition_handle)
 | 
						|
{
 | 
						|
	channel->chan_hdr.partition_handle = partition_handle;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * visorchannel_get_guid() - queries the GUID of the designated channel
 | 
						|
 * @channel: the channel to query
 | 
						|
 *
 | 
						|
 * Return: the GUID of the provided channel
 | 
						|
 */
 | 
						|
const guid_t *visorchannel_get_guid(struct visorchannel *channel)
 | 
						|
{
 | 
						|
	return &channel->guid;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(visorchannel_get_guid);
 | 
						|
 | 
						|
int visorchannel_read(struct visorchannel *channel, ulong offset, void *dest,
 | 
						|
		      ulong nbytes)
 | 
						|
{
 | 
						|
	if (offset + nbytes > channel->nbytes)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	memcpy(dest, channel->mapped + offset, nbytes);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int visorchannel_write(struct visorchannel *channel, ulong offset, void *dest,
 | 
						|
		       ulong nbytes)
 | 
						|
{
 | 
						|
	size_t chdr_size = sizeof(struct channel_header);
 | 
						|
	size_t copy_size;
 | 
						|
 | 
						|
	if (offset + nbytes > channel->nbytes)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	if (offset < chdr_size) {
 | 
						|
		copy_size = min(chdr_size - offset, nbytes);
 | 
						|
		memcpy(((char *)(&channel->chan_hdr)) + offset,
 | 
						|
		       dest, copy_size);
 | 
						|
	}
 | 
						|
	memcpy(channel->mapped + offset, dest, nbytes);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void *visorchannel_get_header(struct visorchannel *channel)
 | 
						|
{
 | 
						|
	return &channel->chan_hdr;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a
 | 
						|
 * channel header
 | 
						|
 */
 | 
						|
static int sig_queue_offset(struct channel_header *chan_hdr, int q)
 | 
						|
{
 | 
						|
	return ((chan_hdr)->ch_space_offset +
 | 
						|
	       ((q) * sizeof(struct signal_queue_header)));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return offset of a specific queue entry (data) from the beginning of a
 | 
						|
 * channel header
 | 
						|
 */
 | 
						|
static int sig_data_offset(struct channel_header *chan_hdr, int q,
 | 
						|
			   struct signal_queue_header *sig_hdr, int slot)
 | 
						|
{
 | 
						|
	return (sig_queue_offset(chan_hdr, q) + sig_hdr->sig_base_offset +
 | 
						|
	       (slot * sig_hdr->signal_size));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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) \
 | 
						|
	visorchannel_write(channel, \
 | 
						|
			   sig_queue_offset(&channel->chan_hdr, queue) + \
 | 
						|
			   offsetof(struct signal_queue_header, FIELD), \
 | 
						|
			   &((sig_hdr)->FIELD), \
 | 
						|
			   sizeof((sig_hdr)->FIELD))
 | 
						|
 | 
						|
static int sig_read_header(struct visorchannel *channel, u32 queue,
 | 
						|
			   struct signal_queue_header *sig_hdr)
 | 
						|
{
 | 
						|
	if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	/* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */
 | 
						|
	return visorchannel_read(channel,
 | 
						|
				 sig_queue_offset(&channel->chan_hdr, queue),
 | 
						|
				 sig_hdr, sizeof(struct signal_queue_header));
 | 
						|
}
 | 
						|
 | 
						|
static int sig_read_data(struct visorchannel *channel, u32 queue,
 | 
						|
			 struct signal_queue_header *sig_hdr, u32 slot,
 | 
						|
			 void *data)
 | 
						|
{
 | 
						|
	int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue,
 | 
						|
						 sig_hdr, slot);
 | 
						|
 | 
						|
	return visorchannel_read(channel, signal_data_offset,
 | 
						|
				 data, sig_hdr->signal_size);
 | 
						|
}
 | 
						|
 | 
						|
static int sig_write_data(struct visorchannel *channel, u32 queue,
 | 
						|
			  struct signal_queue_header *sig_hdr, u32 slot,
 | 
						|
			  void *data)
 | 
						|
{
 | 
						|
	int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue,
 | 
						|
						 sig_hdr, slot);
 | 
						|
 | 
						|
	return visorchannel_write(channel, signal_data_offset,
 | 
						|
				  data, sig_hdr->signal_size);
 | 
						|
}
 | 
						|
 | 
						|
static int signalremove_inner(struct visorchannel *channel, u32 queue,
 | 
						|
			      void *msg)
 | 
						|
{
 | 
						|
	struct signal_queue_header sig_hdr;
 | 
						|
	int error;
 | 
						|
 | 
						|
	error = sig_read_header(channel, queue, &sig_hdr);
 | 
						|
	if (error)
 | 
						|
		return error;
 | 
						|
	/* No signals to remove; have caller try again. */
 | 
						|
	if (sig_hdr.head == sig_hdr.tail)
 | 
						|
		return -EAGAIN;
 | 
						|
	sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots;
 | 
						|
	error = sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg);
 | 
						|
	if (error)
 | 
						|
		return error;
 | 
						|
	sig_hdr.num_received++;
 | 
						|
	/*
 | 
						|
	 * For each data field in SIGNAL_QUEUE_HEADER that was modified, update
 | 
						|
	 * host memory. Required for channel sync.
 | 
						|
	 */
 | 
						|
	mb();
 | 
						|
	error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail);
 | 
						|
	if (error)
 | 
						|
		return error;
 | 
						|
	error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received);
 | 
						|
	if (error)
 | 
						|
		return error;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * visorchannel_signalremove() - removes a message from the designated
 | 
						|
 *                               channel/queue
 | 
						|
 * @channel: the channel the message will be removed from
 | 
						|
 * @queue:   the queue the message will be removed from
 | 
						|
 * @msg:     the message to remove
 | 
						|
 *
 | 
						|
 * Return: integer error code indicating the status of the removal
 | 
						|
 */
 | 
						|
int visorchannel_signalremove(struct visorchannel *channel, u32 queue,
 | 
						|
			      void *msg)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (channel->needs_lock) {
 | 
						|
		spin_lock_irqsave(&channel->remove_lock, flags);
 | 
						|
		rc = signalremove_inner(channel, queue, msg);
 | 
						|
		spin_unlock_irqrestore(&channel->remove_lock, flags);
 | 
						|
	} else {
 | 
						|
		rc = signalremove_inner(channel, queue, msg);
 | 
						|
	}
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(visorchannel_signalremove);
 | 
						|
 | 
						|
static bool queue_empty(struct visorchannel *channel, u32 queue)
 | 
						|
{
 | 
						|
	struct signal_queue_header sig_hdr;
 | 
						|
 | 
						|
	if (sig_read_header(channel, queue, &sig_hdr))
 | 
						|
		return true;
 | 
						|
	return (sig_hdr.head == sig_hdr.tail);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * visorchannel_signalempty() - checks if the designated channel/queue contains
 | 
						|
 *				any messages
 | 
						|
 * @channel: the channel to query
 | 
						|
 * @queue:   the queue in the channel to query
 | 
						|
 *
 | 
						|
 * Return: boolean indicating whether any messages in the designated
 | 
						|
 *         channel/queue are present
 | 
						|
 */
 | 
						|
bool visorchannel_signalempty(struct visorchannel *channel, u32 queue)
 | 
						|
{
 | 
						|
	bool rc;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (!channel->needs_lock)
 | 
						|
		return queue_empty(channel, queue);
 | 
						|
	spin_lock_irqsave(&channel->remove_lock, flags);
 | 
						|
	rc = queue_empty(channel, queue);
 | 
						|
	spin_unlock_irqrestore(&channel->remove_lock, flags);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(visorchannel_signalempty);
 | 
						|
 | 
						|
static int signalinsert_inner(struct visorchannel *channel, u32 queue,
 | 
						|
			      void *msg)
 | 
						|
{
 | 
						|
	struct signal_queue_header sig_hdr;
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = sig_read_header(channel, queue, &sig_hdr);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	sig_hdr.head = (sig_hdr.head + 1) % sig_hdr.max_slots;
 | 
						|
	if (sig_hdr.head == sig_hdr.tail) {
 | 
						|
		sig_hdr.num_overflows++;
 | 
						|
		err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_overflows);
 | 
						|
		if (err)
 | 
						|
			return err;
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
	err = sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	sig_hdr.num_sent++;
 | 
						|
	/*
 | 
						|
	 * For each data field in SIGNAL_QUEUE_HEADER that was modified, update
 | 
						|
	 * host memory. Required for channel sync.
 | 
						|
	 */
 | 
						|
	mb();
 | 
						|
	err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, head);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * visorchannel_create() - creates the struct visorchannel abstraction for a
 | 
						|
 *                         data area in memory, but does NOT modify this data
 | 
						|
 *                         area
 | 
						|
 * @physaddr:      physical address of start of channel
 | 
						|
 * @gfp:           gfp_t to use when allocating memory for the data struct
 | 
						|
 * @guid:          GUID that identifies channel type;
 | 
						|
 * @needs_lock:    must specify true if you have multiple threads of execution
 | 
						|
 *                 that will be calling visorchannel methods of this
 | 
						|
 *                 visorchannel at the same time
 | 
						|
 *
 | 
						|
 * Return: pointer to visorchannel that was created if successful,
 | 
						|
 *         otherwise NULL
 | 
						|
 */
 | 
						|
struct visorchannel *visorchannel_create(u64 physaddr, gfp_t gfp,
 | 
						|
					 const guid_t *guid, bool needs_lock)
 | 
						|
{
 | 
						|
	struct visorchannel *channel;
 | 
						|
	int err;
 | 
						|
	size_t size = sizeof(struct channel_header);
 | 
						|
 | 
						|
	if (physaddr == 0)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	channel = kzalloc(sizeof(*channel), gfp);
 | 
						|
	if (!channel)
 | 
						|
		return NULL;
 | 
						|
	channel->needs_lock = needs_lock;
 | 
						|
	spin_lock_init(&channel->insert_lock);
 | 
						|
	spin_lock_init(&channel->remove_lock);
 | 
						|
	/*
 | 
						|
	 * Video driver constains the efi framebuffer so it will get a conflict
 | 
						|
	 * resource when requesting its full mem region. Since we are only
 | 
						|
	 * using the efi framebuffer for video we can ignore this. Remember that
 | 
						|
	 * we haven't requested it so we don't try to release later on.
 | 
						|
	 */
 | 
						|
	channel->requested = request_mem_region(physaddr, size, VISOR_DRV_NAME);
 | 
						|
	if (!channel->requested && !guid_equal(guid, &visor_video_guid))
 | 
						|
		/* we only care about errors if this is not the video channel */
 | 
						|
		goto err_destroy_channel;
 | 
						|
	channel->mapped = memremap(physaddr, size, MEMREMAP_WB);
 | 
						|
	if (!channel->mapped) {
 | 
						|
		release_mem_region(physaddr, size);
 | 
						|
		goto err_destroy_channel;
 | 
						|
	}
 | 
						|
	channel->physaddr = physaddr;
 | 
						|
	channel->nbytes = size;
 | 
						|
	err = visorchannel_read(channel, 0, &channel->chan_hdr, size);
 | 
						|
	if (err)
 | 
						|
		goto err_destroy_channel;
 | 
						|
	size = (ulong)channel->chan_hdr.size;
 | 
						|
	memunmap(channel->mapped);
 | 
						|
	if (channel->requested)
 | 
						|
		release_mem_region(channel->physaddr, channel->nbytes);
 | 
						|
	channel->mapped = NULL;
 | 
						|
	channel->requested = request_mem_region(channel->physaddr, size,
 | 
						|
						VISOR_DRV_NAME);
 | 
						|
	if (!channel->requested && !guid_equal(guid, &visor_video_guid))
 | 
						|
		/* we only care about errors if this is not the video channel */
 | 
						|
		goto err_destroy_channel;
 | 
						|
	channel->mapped = memremap(channel->physaddr, size, MEMREMAP_WB);
 | 
						|
	if (!channel->mapped) {
 | 
						|
		release_mem_region(channel->physaddr, size);
 | 
						|
		goto err_destroy_channel;
 | 
						|
	}
 | 
						|
	channel->nbytes = size;
 | 
						|
	guid_copy(&channel->guid, guid);
 | 
						|
	return channel;
 | 
						|
 | 
						|
err_destroy_channel:
 | 
						|
	visorchannel_destroy(channel);
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * visorchannel_signalinsert() - inserts a message into the designated
 | 
						|
 *                               channel/queue
 | 
						|
 * @channel: the channel the message will be added to
 | 
						|
 * @queue:   the queue the message will be added to
 | 
						|
 * @msg:     the message to insert
 | 
						|
 *
 | 
						|
 * Return: integer error code indicating the status of the insertion
 | 
						|
 */
 | 
						|
int visorchannel_signalinsert(struct visorchannel *channel, u32 queue,
 | 
						|
			      void *msg)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (channel->needs_lock) {
 | 
						|
		spin_lock_irqsave(&channel->insert_lock, flags);
 | 
						|
		rc = signalinsert_inner(channel, queue, msg);
 | 
						|
		spin_unlock_irqrestore(&channel->insert_lock, flags);
 | 
						|
	} else {
 | 
						|
		rc = signalinsert_inner(channel, queue, msg);
 | 
						|
	}
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(visorchannel_signalinsert);
 |