123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- // 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);
|