2014-03-04 13:58:07 +00:00
|
|
|
/* visorchipset_main.c
|
|
|
|
*
|
2015-07-16 16:40:48 +00:00
|
|
|
* Copyright (C) 2010 - 2015 UNISYS CORPORATION
|
2014-03-04 13:58:07 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
2015-07-16 16:40:48 +00:00
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
2014-03-04 13:58:07 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2015-05-05 22:37:02 +00:00
|
|
|
#include <linux/acpi.h>
|
2015-05-05 22:37:06 +00:00
|
|
|
#include <linux/cdev.h>
|
2015-05-05 22:36:14 +00:00
|
|
|
#include <linux/ctype.h>
|
2015-05-05 22:36:13 +00:00
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/mm.h>
|
2014-03-04 13:58:07 +00:00
|
|
|
#include <linux/nls.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/platform_device.h>
|
2014-05-06 13:58:23 +00:00
|
|
|
#include <linux/uuid.h>
|
2015-04-06 14:27:40 +00:00
|
|
|
#include <linux/crash_dump.h>
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-05-05 22:37:02 +00:00
|
|
|
#include "version.h"
|
|
|
|
#include "visorbus.h"
|
|
|
|
#include "visorbus_private.h"
|
2015-05-13 17:22:10 +00:00
|
|
|
#include "vmcallinterface.h"
|
2015-05-05 22:37:02 +00:00
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
#define CURRENT_FILE_PC VISOR_CHIPSET_PC_visorchipset_main_c
|
|
|
|
|
|
|
|
#define POLLJIFFIES_CONTROLVMCHANNEL_FAST 1
|
|
|
|
#define POLLJIFFIES_CONTROLVMCHANNEL_SLOW 100
|
|
|
|
|
2015-12-09 15:27:05 +00:00
|
|
|
#define MAX_CONTROLVM_PAYLOAD_BYTES (1024 * 128)
|
2015-05-05 22:36:16 +00:00
|
|
|
|
|
|
|
#define VISORCHIPSET_MMAP_CONTROLCHANOFFSET 0x00000000
|
|
|
|
|
2015-05-05 22:37:04 +00:00
|
|
|
#define UNISYS_SPAR_LEAF_ID 0x40000000
|
|
|
|
|
|
|
|
/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */
|
|
|
|
#define UNISYS_SPAR_ID_EBX 0x73696e55
|
|
|
|
#define UNISYS_SPAR_ID_ECX 0x70537379
|
|
|
|
#define UNISYS_SPAR_ID_EDX 0x34367261
|
|
|
|
|
2015-05-05 22:35:38 +00:00
|
|
|
/*
|
|
|
|
* Module parameters
|
|
|
|
*/
|
|
|
|
static int visorchipset_major;
|
|
|
|
|
2015-05-05 22:36:13 +00:00
|
|
|
static int
|
|
|
|
visorchipset_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2016-04-05 03:31:11 +00:00
|
|
|
unsigned int minor_number = iminor(inode);
|
2015-05-05 22:36:13 +00:00
|
|
|
|
|
|
|
if (minor_number)
|
|
|
|
return -ENODEV;
|
|
|
|
file->private_data = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
visorchipset_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* When the controlvm channel is idle for at least MIN_IDLE_SECONDS,
|
|
|
|
* we switch to slow polling mode. As soon as we get a controlvm
|
|
|
|
* message, we switch back to fast polling mode.
|
|
|
|
*/
|
2014-03-04 13:58:07 +00:00
|
|
|
#define MIN_IDLE_SECONDS 10
|
2015-04-13 14:28:41 +00:00
|
|
|
static unsigned long poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST;
|
2016-02-08 15:41:44 +00:00
|
|
|
/* when we got our last controlvm message */
|
|
|
|
static unsigned long most_recent_message_jiffies;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-05-05 22:36:14 +00:00
|
|
|
struct parser_context {
|
|
|
|
unsigned long allocbytes;
|
|
|
|
unsigned long param_bytes;
|
|
|
|
u8 *curr;
|
|
|
|
unsigned long bytes_remaining;
|
|
|
|
bool byte_stream;
|
|
|
|
char data[0];
|
|
|
|
};
|
|
|
|
|
2015-03-16 17:57:57 +00:00
|
|
|
static struct delayed_work periodic_controlvm_work;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-05-05 22:36:13 +00:00
|
|
|
static struct cdev file_cdev;
|
|
|
|
static struct visorchannel **file_controlvm_channel;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
static struct visorchannel *controlvm_channel;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:07 +00:00
|
|
|
/* Manages the request payload in the controlvm channel */
|
2015-04-13 14:28:39 +00:00
|
|
|
struct visor_controlvm_payload_info {
|
2015-08-11 03:07:06 +00:00
|
|
|
u8 *ptr; /* pointer to base address of payload pool */
|
2016-06-11 01:48:18 +00:00
|
|
|
u64 offset; /*
|
|
|
|
* offset from beginning of controlvm
|
2016-02-08 15:41:44 +00:00
|
|
|
* channel to beginning of payload * pool
|
|
|
|
*/
|
2014-07-31 16:00:50 +00:00
|
|
|
u32 bytes; /* number of bytes in payload pool */
|
2015-04-13 14:28:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct visor_controlvm_payload_info controlvm_payload_info;
|
2016-09-19 21:09:19 +00:00
|
|
|
static unsigned long controlvm_payload_bytes_buffered;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* The following globals are used to handle the scenario where we are unable to
|
|
|
|
* offload the payload from a controlvm message due to memory requirements. In
|
2014-03-04 13:58:07 +00:00
|
|
|
* this scenario, we simply stash the controlvm message, then attempt to
|
|
|
|
* process it again the next time controlvm_periodic_work() runs.
|
|
|
|
*/
|
2015-03-16 17:58:38 +00:00
|
|
|
static struct controlvm_message controlvm_pending_msg;
|
2015-05-05 22:36:15 +00:00
|
|
|
static bool controlvm_pending_msg_valid;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* This describes a buffer and its current state of transfer (e.g., how many
|
2014-03-04 13:58:07 +00:00
|
|
|
* bytes have already been supplied as putfile data, and how many bytes are
|
|
|
|
* remaining) for a putfile_request.
|
|
|
|
*/
|
|
|
|
struct putfile_active_buffer {
|
|
|
|
/* a payload from a controlvm message, containing a file data buffer */
|
2015-03-16 17:57:51 +00:00
|
|
|
struct parser_context *parser_ctx;
|
2014-03-04 13:58:07 +00:00
|
|
|
/* points within data area of parser_ctx to next byte of data */
|
|
|
|
size_t bytes_remaining;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define PUTFILE_REQUEST_SIG 0x0906101302281211
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* This identifies a single remote --> local CONTROLVM_TRANSMIT_FILE
|
|
|
|
* conversation. Structs of this type are dynamically linked into
|
2014-03-04 13:58:07 +00:00
|
|
|
* <Putfile_request_list>.
|
|
|
|
*/
|
|
|
|
struct putfile_request {
|
|
|
|
u64 sig; /* PUTFILE_REQUEST_SIG */
|
|
|
|
|
|
|
|
/* header from original TransmitFile request */
|
2014-10-23 18:30:26 +00:00
|
|
|
struct controlvm_message_header controlvm_header;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
/* link to next struct putfile_request */
|
|
|
|
struct list_head next_putfile_request;
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* head of putfile_buffer_entry list, which describes the data to be
|
2014-03-04 13:58:07 +00:00
|
|
|
* supplied as putfile data;
|
|
|
|
* - this list is added to when controlvm messages come in that supply
|
|
|
|
* file data
|
|
|
|
* - this list is removed from via the hotplug program that is actually
|
2016-02-08 15:41:44 +00:00
|
|
|
* consuming these buffers to write as file data
|
|
|
|
*/
|
2014-03-04 13:58:07 +00:00
|
|
|
struct list_head input_buffer_list;
|
|
|
|
spinlock_t req_list_lock; /* lock for input_buffer_list */
|
|
|
|
|
|
|
|
/* waiters for input_buffer_list to go non-empty */
|
|
|
|
wait_queue_head_t input_buffer_wq;
|
|
|
|
|
|
|
|
/* data not yet read within current putfile_buffer_entry */
|
|
|
|
struct putfile_active_buffer active_buf;
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* <0 = failed, 0 = in-progress, >0 = successful;
|
|
|
|
* note that this must be set with req_list_lock, and if you set <0,
|
|
|
|
* it is your responsibility to also free up all of the other objects
|
|
|
|
* in this struct (like input_buffer_list, active_buf.parser_ctx)
|
|
|
|
* before releasing the lock
|
|
|
|
*/
|
2014-03-04 13:58:07 +00:00
|
|
|
int completion_status;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct parahotplug_request {
|
|
|
|
struct list_head list;
|
|
|
|
int id;
|
|
|
|
unsigned long expiration;
|
2014-10-23 18:30:31 +00:00
|
|
|
struct controlvm_message msg;
|
2014-03-04 13:58:07 +00:00
|
|
|
};
|
|
|
|
|
2015-03-16 17:58:41 +00:00
|
|
|
static LIST_HEAD(parahotplug_request_list);
|
|
|
|
static DEFINE_SPINLOCK(parahotplug_request_list_lock); /* lock for above */
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
/* info for /dev/visorchipset */
|
2016-06-11 01:48:18 +00:00
|
|
|
static dev_t major_dev = -1; /*< indicates major num for device */
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2014-07-22 13:56:25 +00:00
|
|
|
/* prototypes for attributes */
|
|
|
|
static ssize_t toolaction_show(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr, char *buf);
|
2014-07-22 13:56:25 +00:00
|
|
|
static ssize_t toolaction_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count);
|
2014-07-22 13:56:25 +00:00
|
|
|
static DEVICE_ATTR_RW(toolaction);
|
|
|
|
|
2014-07-22 13:56:26 +00:00
|
|
|
static ssize_t boottotool_show(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr, char *buf);
|
2014-07-22 13:56:26 +00:00
|
|
|
static ssize_t boottotool_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr, const char *buf,
|
|
|
|
size_t count);
|
2014-07-22 13:56:26 +00:00
|
|
|
static DEVICE_ATTR_RW(boottotool);
|
|
|
|
|
2014-07-24 18:08:42 +00:00
|
|
|
static ssize_t error_show(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
char *buf);
|
2014-07-24 18:08:42 +00:00
|
|
|
static ssize_t error_store(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
const char *buf, size_t count);
|
2014-07-24 18:08:42 +00:00
|
|
|
static DEVICE_ATTR_RW(error);
|
|
|
|
|
|
|
|
static ssize_t textid_show(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
char *buf);
|
2014-07-24 18:08:42 +00:00
|
|
|
static ssize_t textid_store(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
const char *buf, size_t count);
|
2014-07-24 18:08:42 +00:00
|
|
|
static DEVICE_ATTR_RW(textid);
|
|
|
|
|
|
|
|
static ssize_t remaining_steps_show(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr, char *buf);
|
2014-07-24 18:08:42 +00:00
|
|
|
static ssize_t remaining_steps_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count);
|
2014-07-24 18:08:42 +00:00
|
|
|
static DEVICE_ATTR_RW(remaining_steps);
|
|
|
|
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
static ssize_t devicedisabled_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count);
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
static DEVICE_ATTR_WO(devicedisabled);
|
|
|
|
|
|
|
|
static ssize_t deviceenabled_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count);
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
static DEVICE_ATTR_WO(deviceenabled);
|
|
|
|
|
2014-07-22 13:56:25 +00:00
|
|
|
static struct attribute *visorchipset_install_attrs[] = {
|
|
|
|
&dev_attr_toolaction.attr,
|
2014-07-22 13:56:26 +00:00
|
|
|
&dev_attr_boottotool.attr,
|
2014-07-24 18:08:42 +00:00
|
|
|
&dev_attr_error.attr,
|
|
|
|
&dev_attr_textid.attr,
|
|
|
|
&dev_attr_remaining_steps.attr,
|
2014-07-22 13:56:25 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute_group visorchipset_install_group = {
|
|
|
|
.name = "install",
|
|
|
|
.attrs = visorchipset_install_attrs
|
|
|
|
};
|
|
|
|
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
static struct attribute *visorchipset_parahotplug_attrs[] = {
|
|
|
|
&dev_attr_devicedisabled.attr,
|
|
|
|
&dev_attr_deviceenabled.attr,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute_group visorchipset_parahotplug_group = {
|
|
|
|
.name = "parahotplug",
|
|
|
|
.attrs = visorchipset_parahotplug_attrs
|
|
|
|
};
|
|
|
|
|
2014-07-22 13:56:25 +00:00
|
|
|
static const struct attribute_group *visorchipset_dev_groups[] = {
|
|
|
|
&visorchipset_install_group,
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
&visorchipset_parahotplug_group,
|
2014-07-22 13:56:25 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2015-06-04 13:22:48 +00:00
|
|
|
static void visorchipset_dev_release(struct device *dev)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
/* /sys/devices/platform/visorchipset */
|
2015-03-16 17:58:45 +00:00
|
|
|
static struct platform_device visorchipset_platform_device = {
|
2014-03-04 13:58:07 +00:00
|
|
|
.name = "visorchipset",
|
|
|
|
.id = -1,
|
2014-07-22 13:56:25 +00:00
|
|
|
.dev.groups = visorchipset_dev_groups,
|
2015-06-04 13:22:48 +00:00
|
|
|
.dev.release = visorchipset_dev_release,
|
2014-03-04 13:58:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Function prototypes */
|
2014-10-23 18:30:26 +00:00
|
|
|
static void controlvm_respond_chipset_init(
|
2015-03-16 17:58:46 +00:00
|
|
|
struct controlvm_message_header *msg_hdr, int response,
|
2014-10-23 18:30:26 +00:00
|
|
|
enum ultra_chipset_feature features);
|
|
|
|
static void controlvm_respond_physdev_changestate(
|
2015-03-16 17:58:46 +00:00
|
|
|
struct controlvm_message_header *msg_hdr, int response,
|
2014-10-23 18:30:26 +00:00
|
|
|
struct spar_segment_state state);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-05-05 22:36:16 +00:00
|
|
|
static void parser_done(struct parser_context *ctx);
|
|
|
|
|
2015-05-05 22:36:14 +00:00
|
|
|
static struct parser_context *
|
2015-05-05 22:37:14 +00:00
|
|
|
parser_init_byte_stream(u64 addr, u32 bytes, bool local, bool *retry)
|
2015-05-05 22:36:14 +00:00
|
|
|
{
|
|
|
|
int allocbytes = sizeof(struct parser_context) + bytes;
|
2016-03-11 22:01:40 +00:00
|
|
|
struct parser_context *ctx;
|
2015-05-05 22:36:14 +00:00
|
|
|
|
|
|
|
if (retry)
|
|
|
|
*retry = false;
|
2015-05-05 22:37:13 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* alloc an 0 extra byte to ensure payload is
|
|
|
|
* '\0'-terminated
|
|
|
|
*/
|
|
|
|
allocbytes++;
|
2015-05-05 22:36:14 +00:00
|
|
|
if ((controlvm_payload_bytes_buffered + bytes)
|
|
|
|
> MAX_CONTROLVM_PAYLOAD_BYTES) {
|
|
|
|
if (retry)
|
|
|
|
*retry = true;
|
2016-03-11 22:01:40 +00:00
|
|
|
return NULL;
|
2015-05-05 22:36:14 +00:00
|
|
|
}
|
2016-02-08 15:41:45 +00:00
|
|
|
ctx = kzalloc(allocbytes, GFP_KERNEL | __GFP_NORETRY);
|
2015-05-05 22:36:14 +00:00
|
|
|
if (!ctx) {
|
|
|
|
if (retry)
|
|
|
|
*retry = true;
|
2016-03-11 22:01:40 +00:00
|
|
|
return NULL;
|
2015-05-05 22:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx->allocbytes = allocbytes;
|
|
|
|
ctx->param_bytes = bytes;
|
|
|
|
ctx->curr = NULL;
|
|
|
|
ctx->bytes_remaining = 0;
|
|
|
|
ctx->byte_stream = false;
|
|
|
|
if (local) {
|
|
|
|
void *p;
|
|
|
|
|
2016-03-11 22:01:40 +00:00
|
|
|
if (addr > virt_to_phys(high_memory - 1))
|
|
|
|
goto err_finish_ctx;
|
2016-02-08 15:41:48 +00:00
|
|
|
p = __va((unsigned long)(addr));
|
2015-05-05 22:36:14 +00:00
|
|
|
memcpy(ctx->data, p, bytes);
|
|
|
|
} else {
|
2016-03-02 00:45:02 +00:00
|
|
|
void *mapping = memremap(addr, bytes, MEMREMAP_WB);
|
2015-05-05 22:36:26 +00:00
|
|
|
|
2016-03-11 22:01:40 +00:00
|
|
|
if (!mapping)
|
|
|
|
goto err_finish_ctx;
|
2015-08-11 03:07:06 +00:00
|
|
|
memcpy(ctx->data, mapping, bytes);
|
|
|
|
memunmap(mapping);
|
2015-05-05 22:36:14 +00:00
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:13 +00:00
|
|
|
ctx->byte_stream = true;
|
2016-03-11 22:01:40 +00:00
|
|
|
controlvm_payload_bytes_buffered += ctx->param_bytes;
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
|
|
|
|
err_finish_ctx:
|
|
|
|
parser_done(ctx);
|
|
|
|
return NULL;
|
2015-05-05 22:36:14 +00:00
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:12 +00:00
|
|
|
static uuid_le
|
2015-05-05 22:36:14 +00:00
|
|
|
parser_id_get(struct parser_context *ctx)
|
|
|
|
{
|
|
|
|
struct spar_controlvm_parameters_header *phdr = NULL;
|
|
|
|
|
2016-02-08 15:41:46 +00:00
|
|
|
if (!ctx)
|
2015-05-05 22:36:14 +00:00
|
|
|
return NULL_UUID_LE;
|
|
|
|
phdr = (struct spar_controlvm_parameters_header *)(ctx->data);
|
|
|
|
return phdr->id;
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* Describes the state from the perspective of which controlvm messages have
|
|
|
|
* been received for a bus or device.
|
2015-05-05 22:36:16 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
enum PARSER_WHICH_STRING {
|
|
|
|
PARSERSTRING_INITIATOR,
|
|
|
|
PARSERSTRING_TARGET,
|
|
|
|
PARSERSTRING_CONNECTION,
|
|
|
|
PARSERSTRING_NAME, /* TODO: only PARSERSTRING_NAME is used ? */
|
|
|
|
};
|
|
|
|
|
2015-05-05 22:37:12 +00:00
|
|
|
static void
|
2015-05-05 22:36:16 +00:00
|
|
|
parser_param_start(struct parser_context *ctx,
|
|
|
|
enum PARSER_WHICH_STRING which_string)
|
2015-05-05 22:36:14 +00:00
|
|
|
{
|
|
|
|
struct spar_controlvm_parameters_header *phdr = NULL;
|
|
|
|
|
2016-02-08 15:41:46 +00:00
|
|
|
if (!ctx)
|
2016-02-08 15:41:53 +00:00
|
|
|
return;
|
|
|
|
|
2015-05-05 22:36:14 +00:00
|
|
|
phdr = (struct spar_controlvm_parameters_header *)(ctx->data);
|
|
|
|
switch (which_string) {
|
|
|
|
case PARSERSTRING_INITIATOR:
|
|
|
|
ctx->curr = ctx->data + phdr->initiator_offset;
|
|
|
|
ctx->bytes_remaining = phdr->initiator_length;
|
|
|
|
break;
|
|
|
|
case PARSERSTRING_TARGET:
|
|
|
|
ctx->curr = ctx->data + phdr->target_offset;
|
|
|
|
ctx->bytes_remaining = phdr->target_length;
|
|
|
|
break;
|
|
|
|
case PARSERSTRING_CONNECTION:
|
|
|
|
ctx->curr = ctx->data + phdr->connection_offset;
|
|
|
|
ctx->bytes_remaining = phdr->connection_length;
|
|
|
|
break;
|
|
|
|
case PARSERSTRING_NAME:
|
|
|
|
ctx->curr = ctx->data + phdr->name_offset;
|
|
|
|
ctx->bytes_remaining = phdr->name_length;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:12 +00:00
|
|
|
static void parser_done(struct parser_context *ctx)
|
2015-05-05 22:36:14 +00:00
|
|
|
{
|
|
|
|
if (!ctx)
|
|
|
|
return;
|
|
|
|
controlvm_payload_bytes_buffered -= ctx->param_bytes;
|
|
|
|
kfree(ctx);
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:12 +00:00
|
|
|
static void *
|
2015-05-05 22:36:14 +00:00
|
|
|
parser_string_get(struct parser_context *ctx)
|
|
|
|
{
|
|
|
|
u8 *pscan;
|
|
|
|
unsigned long nscan;
|
|
|
|
int value_length = -1;
|
|
|
|
void *value = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return NULL;
|
|
|
|
pscan = ctx->curr;
|
|
|
|
nscan = ctx->bytes_remaining;
|
|
|
|
if (nscan == 0)
|
|
|
|
return NULL;
|
|
|
|
if (!pscan)
|
|
|
|
return NULL;
|
|
|
|
for (i = 0, value_length = -1; i < nscan; i++)
|
|
|
|
if (pscan[i] == '\0') {
|
|
|
|
value_length = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (value_length < 0) /* '\0' was not included in the length */
|
|
|
|
value_length = nscan;
|
2016-02-08 15:41:45 +00:00
|
|
|
value = kmalloc(value_length + 1, GFP_KERNEL | __GFP_NORETRY);
|
2016-02-08 15:41:46 +00:00
|
|
|
if (!value)
|
2015-05-05 22:36:14 +00:00
|
|
|
return NULL;
|
|
|
|
if (value_length > 0)
|
|
|
|
memcpy(value, pscan, value_length);
|
2016-02-08 15:41:48 +00:00
|
|
|
((u8 *)(value))[value_length] = '\0';
|
2015-05-05 22:36:14 +00:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2014-08-01 08:29:30 +00:00
|
|
|
static ssize_t toolaction_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
2014-07-22 13:56:25 +00:00
|
|
|
{
|
2016-04-19 03:40:09 +00:00
|
|
|
u8 tool_action = 0;
|
2014-07-22 13:56:25 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
visorchannel_read(controlvm_channel,
|
2015-12-07 15:58:37 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
tool_action), &tool_action, sizeof(u8));
|
2015-03-16 17:58:48 +00:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%u\n", tool_action);
|
2014-07-22 13:56:25 +00:00
|
|
|
}
|
|
|
|
|
2014-08-01 08:29:30 +00:00
|
|
|
static ssize_t toolaction_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
2014-07-22 13:56:25 +00:00
|
|
|
{
|
2015-03-16 17:58:48 +00:00
|
|
|
u8 tool_action;
|
2014-07-25 17:55:10 +00:00
|
|
|
int ret;
|
2014-07-22 13:56:25 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtou8(buf, 10, &tool_action))
|
2014-07-25 17:55:10 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-04-05 03:31:12 +00:00
|
|
|
ret = visorchannel_write
|
|
|
|
(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
tool_action),
|
|
|
|
&tool_action, sizeof(u8));
|
2014-07-25 17:55:10 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-08-18 13:34:54 +00:00
|
|
|
return count;
|
2014-07-22 13:56:25 +00:00
|
|
|
}
|
|
|
|
|
2014-08-01 08:29:30 +00:00
|
|
|
static ssize_t boottotool_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
2014-07-22 13:56:26 +00:00
|
|
|
{
|
2015-03-16 17:58:49 +00:00
|
|
|
struct efi_spar_indication efi_spar_indication;
|
2014-07-22 13:56:26 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
visorchannel_read(controlvm_channel,
|
2015-03-16 17:58:52 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
efi_spar_ind), &efi_spar_indication,
|
|
|
|
sizeof(struct efi_spar_indication));
|
2014-07-22 13:56:26 +00:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%u\n",
|
2015-03-16 17:58:52 +00:00
|
|
|
efi_spar_indication.boot_to_tool);
|
2014-07-22 13:56:26 +00:00
|
|
|
}
|
|
|
|
|
2014-08-01 08:29:30 +00:00
|
|
|
static ssize_t boottotool_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
2014-07-22 13:56:26 +00:00
|
|
|
{
|
2014-07-25 17:55:10 +00:00
|
|
|
int val, ret;
|
2015-03-16 17:58:49 +00:00
|
|
|
struct efi_spar_indication efi_spar_indication;
|
2014-07-22 13:56:26 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtoint(buf, 10, &val))
|
2014-07-25 17:55:10 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2015-03-16 17:58:49 +00:00
|
|
|
efi_spar_indication.boot_to_tool = val;
|
2016-04-05 03:31:12 +00:00
|
|
|
ret = visorchannel_write
|
|
|
|
(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
efi_spar_ind), &(efi_spar_indication),
|
|
|
|
sizeof(struct efi_spar_indication));
|
2014-07-25 17:55:10 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-08-18 13:34:54 +00:00
|
|
|
return count;
|
2014-07-22 13:56:26 +00:00
|
|
|
}
|
2014-07-24 18:08:42 +00:00
|
|
|
|
|
|
|
static ssize_t error_show(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
char *buf)
|
2014-07-24 18:08:42 +00:00
|
|
|
{
|
2016-04-19 03:40:09 +00:00
|
|
|
u32 error = 0;
|
2014-07-24 18:08:42 +00:00
|
|
|
|
2015-03-16 17:58:52 +00:00
|
|
|
visorchannel_read(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
installation_error),
|
|
|
|
&error, sizeof(u32));
|
2014-07-24 18:08:42 +00:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%i\n", error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t error_store(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
const char *buf, size_t count)
|
2014-07-24 18:08:42 +00:00
|
|
|
{
|
|
|
|
u32 error;
|
2014-07-25 17:55:10 +00:00
|
|
|
int ret;
|
2014-07-24 18:08:42 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtou32(buf, 10, &error))
|
2014-07-25 17:55:10 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-04-05 03:31:12 +00:00
|
|
|
ret = visorchannel_write
|
|
|
|
(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
installation_error),
|
|
|
|
&error, sizeof(u32));
|
2014-07-25 17:55:10 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-08-18 13:34:54 +00:00
|
|
|
return count;
|
2014-07-24 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t textid_show(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
char *buf)
|
2014-07-24 18:08:42 +00:00
|
|
|
{
|
2016-04-19 03:40:09 +00:00
|
|
|
u32 text_id = 0;
|
2014-07-24 18:08:42 +00:00
|
|
|
|
2016-04-05 03:31:12 +00:00
|
|
|
visorchannel_read
|
|
|
|
(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
installation_text_id),
|
|
|
|
&text_id, sizeof(u32));
|
2015-03-16 17:58:50 +00:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%i\n", text_id);
|
2014-07-24 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t textid_store(struct device *dev, struct device_attribute *attr,
|
2015-03-16 17:58:52 +00:00
|
|
|
const char *buf, size_t count)
|
2014-07-24 18:08:42 +00:00
|
|
|
{
|
2015-03-16 17:58:50 +00:00
|
|
|
u32 text_id;
|
2014-07-25 17:55:10 +00:00
|
|
|
int ret;
|
2014-07-24 18:08:42 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtou32(buf, 10, &text_id))
|
2014-07-25 17:55:10 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-04-05 03:31:12 +00:00
|
|
|
ret = visorchannel_write
|
|
|
|
(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
installation_text_id),
|
|
|
|
&text_id, sizeof(u32));
|
2014-07-25 17:55:10 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-08-18 13:34:54 +00:00
|
|
|
return count;
|
2014-07-24 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t remaining_steps_show(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr, char *buf)
|
2014-07-24 18:08:42 +00:00
|
|
|
{
|
2016-04-19 03:40:09 +00:00
|
|
|
u16 remaining_steps = 0;
|
2014-07-24 18:08:42 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
visorchannel_read(controlvm_channel,
|
2015-03-16 17:58:52 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
installation_remaining_steps),
|
|
|
|
&remaining_steps, sizeof(u16));
|
2015-03-16 17:58:51 +00:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%hu\n", remaining_steps);
|
2014-07-24 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t remaining_steps_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
2014-07-24 18:08:42 +00:00
|
|
|
{
|
2015-03-16 17:58:51 +00:00
|
|
|
u16 remaining_steps;
|
2014-07-25 17:55:10 +00:00
|
|
|
int ret;
|
2014-07-24 18:08:42 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtou16(buf, 10, &remaining_steps))
|
2014-07-25 17:55:10 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-04-05 03:31:12 +00:00
|
|
|
ret = visorchannel_write
|
|
|
|
(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
installation_remaining_steps),
|
|
|
|
&remaining_steps, sizeof(u16));
|
2014-07-25 17:55:10 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-08-18 13:34:54 +00:00
|
|
|
return count;
|
2014-07-24 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 17:22:19 +00:00
|
|
|
struct visor_busdev {
|
|
|
|
u32 bus_no;
|
|
|
|
u32 dev_no;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int match_visorbus_dev_by_id(struct device *dev, void *data)
|
|
|
|
{
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2015-10-14 19:28:23 +00:00
|
|
|
struct visor_busdev *id = data;
|
2015-05-13 17:22:19 +00:00
|
|
|
u32 bus_no = id->bus_no;
|
|
|
|
u32 dev_no = id->dev_no;
|
|
|
|
|
2015-06-04 13:22:40 +00:00
|
|
|
if ((vdev->chipset_bus_no == bus_no) &&
|
|
|
|
(vdev->chipset_dev_no == dev_no))
|
2015-05-13 17:22:19 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-02-08 15:41:47 +00:00
|
|
|
|
2015-05-13 17:22:19 +00:00
|
|
|
struct visor_device *visorbus_get_device_by_id(u32 bus_no, u32 dev_no,
|
|
|
|
struct visor_device *from)
|
|
|
|
{
|
|
|
|
struct device *dev;
|
|
|
|
struct device *dev_start = NULL;
|
|
|
|
struct visor_device *vdev = NULL;
|
|
|
|
struct visor_busdev id = {
|
|
|
|
.bus_no = bus_no,
|
|
|
|
.dev_no = dev_no
|
|
|
|
};
|
|
|
|
|
|
|
|
if (from)
|
|
|
|
dev_start = &from->device;
|
|
|
|
dev = bus_find_device(&visorbus_type, dev_start, (void *)&id,
|
|
|
|
match_visorbus_dev_by_id);
|
|
|
|
if (dev)
|
|
|
|
vdev = to_visor_device(dev);
|
|
|
|
return vdev;
|
|
|
|
}
|
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
chipset_init(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
static int chipset_inited;
|
2014-10-23 18:30:24 +00:00
|
|
|
enum ultra_chipset_feature features = 0;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
|
|
|
|
|
|
|
POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
if (chipset_inited) {
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
2016-03-11 22:01:41 +00:00
|
|
|
goto out_respond;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
chipset_inited = 1;
|
|
|
|
POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* Set features to indicate we support parahotplug (if Command
|
2016-02-08 15:41:44 +00:00
|
|
|
* also supports it).
|
|
|
|
*/
|
2014-03-04 13:58:07 +00:00
|
|
|
features =
|
2014-10-23 18:30:25 +00:00
|
|
|
inmsg->cmd.init_chipset.
|
2014-03-04 13:58:07 +00:00
|
|
|
features & ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG;
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* Set the "reply" bit so Command knows this is a
|
2016-02-08 15:41:44 +00:00
|
|
|
* features-aware driver.
|
|
|
|
*/
|
2014-03-04 13:58:07 +00:00
|
|
|
features |= ULTRA_CHIPSET_FEATURE_REPLY;
|
|
|
|
|
2016-03-11 22:01:41 +00:00
|
|
|
out_respond:
|
2014-10-23 18:30:26 +00:00
|
|
|
if (inmsg->hdr.flags.response_expected)
|
2014-03-04 13:58:07 +00:00
|
|
|
controlvm_respond_chipset_init(&inmsg->hdr, rc, features);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
controlvm_init_response(struct controlvm_message *msg,
|
2015-03-16 17:58:46 +00:00
|
|
|
struct controlvm_message_header *msg_hdr, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:31 +00:00
|
|
|
memset(msg, 0, sizeof(struct controlvm_message));
|
2015-03-16 17:58:46 +00:00
|
|
|
memcpy(&msg->hdr, msg_hdr, sizeof(struct controlvm_message_header));
|
2014-10-23 18:30:26 +00:00
|
|
|
msg->hdr.payload_bytes = 0;
|
|
|
|
msg->hdr.payload_vm_offset = 0;
|
|
|
|
msg->hdr.payload_max_bytes = 0;
|
2014-03-04 13:58:07 +00:00
|
|
|
if (response < 0) {
|
2014-10-23 18:30:26 +00:00
|
|
|
msg->hdr.flags.failed = 1;
|
2016-02-08 15:41:48 +00:00
|
|
|
msg->hdr.completion_status = (u32)(-response);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:58:46 +00:00
|
|
|
controlvm_respond(struct controlvm_message_header *msg_hdr, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:31 +00:00
|
|
|
struct controlvm_message outmsg;
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2015-03-16 17:58:46 +00:00
|
|
|
controlvm_init_response(&outmsg, msg_hdr, response);
|
2015-03-04 17:14:22 +00:00
|
|
|
if (outmsg.hdr.flags.test_message == 1)
|
2014-03-04 13:58:07 +00:00
|
|
|
return;
|
2015-03-04 17:14:22 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
if (!visorchannel_signalinsert(controlvm_channel,
|
2014-03-04 13:58:07 +00:00
|
|
|
CONTROLVM_QUEUE_REQUEST, &outmsg)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:58:46 +00:00
|
|
|
controlvm_respond_chipset_init(struct controlvm_message_header *msg_hdr,
|
2014-10-23 18:30:26 +00:00
|
|
|
int response,
|
2014-10-23 18:30:24 +00:00
|
|
|
enum ultra_chipset_feature features)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:31 +00:00
|
|
|
struct controlvm_message outmsg;
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2015-03-16 17:58:46 +00:00
|
|
|
controlvm_init_response(&outmsg, msg_hdr, response);
|
2014-10-23 18:30:25 +00:00
|
|
|
outmsg.cmd.init_chipset.features = features;
|
2015-03-16 17:58:05 +00:00
|
|
|
if (!visorchannel_signalinsert(controlvm_channel,
|
2014-03-04 13:58:07 +00:00
|
|
|
CONTROLVM_QUEUE_REQUEST, &outmsg)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-23 18:30:26 +00:00
|
|
|
static void controlvm_respond_physdev_changestate(
|
2015-03-16 17:58:46 +00:00
|
|
|
struct controlvm_message_header *msg_hdr, int response,
|
2014-10-23 18:30:26 +00:00
|
|
|
struct spar_segment_state state)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:31 +00:00
|
|
|
struct controlvm_message outmsg;
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2015-03-16 17:58:46 +00:00
|
|
|
controlvm_init_response(&outmsg, msg_hdr, response);
|
2014-10-23 18:30:25 +00:00
|
|
|
outmsg.cmd.device_change_state.state = state;
|
|
|
|
outmsg.cmd.device_change_state.flags.phys_device = 1;
|
2015-03-16 17:58:05 +00:00
|
|
|
if (!visorchannel_signalinsert(controlvm_channel,
|
2014-03-04 13:58:07 +00:00
|
|
|
CONTROLVM_QUEUE_REQUEST, &outmsg)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:36:16 +00:00
|
|
|
enum crash_obj_type {
|
|
|
|
CRASH_DEV,
|
|
|
|
CRASH_BUS,
|
|
|
|
};
|
|
|
|
|
2016-03-02 00:45:04 +00:00
|
|
|
static void
|
|
|
|
save_crash_message(struct controlvm_message *msg, enum crash_obj_type typ)
|
|
|
|
{
|
|
|
|
u32 local_crash_msg_offset;
|
|
|
|
u16 local_crash_msg_count;
|
|
|
|
|
|
|
|
if (visorchannel_read(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
saved_crash_message_count),
|
|
|
|
&local_crash_msg_count, sizeof(u16)) < 0) {
|
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) {
|
|
|
|
POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC,
|
|
|
|
local_crash_msg_count,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (visorchannel_read(controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
saved_crash_message_offset),
|
|
|
|
&local_crash_msg_offset, sizeof(u32)) < 0) {
|
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typ == CRASH_BUS) {
|
|
|
|
if (visorchannel_write(controlvm_channel,
|
|
|
|
local_crash_msg_offset,
|
|
|
|
msg,
|
|
|
|
sizeof(struct controlvm_message)) < 0) {
|
|
|
|
POSTCODE_LINUX_2(SAVE_MSG_BUS_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
local_crash_msg_offset += sizeof(struct controlvm_message);
|
|
|
|
if (visorchannel_write(controlvm_channel,
|
|
|
|
local_crash_msg_offset,
|
|
|
|
msg,
|
|
|
|
sizeof(struct controlvm_message)) < 0) {
|
|
|
|
POSTCODE_LINUX_2(SAVE_MSG_DEV_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
static void
|
2015-06-01 17:00:27 +00:00
|
|
|
bus_responder(enum controlvm_id cmd_id,
|
|
|
|
struct controlvm_message_header *pending_msg_hdr,
|
2015-05-13 17:22:24 +00:00
|
|
|
int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2016-02-08 15:41:46 +00:00
|
|
|
if (!pending_msg_hdr)
|
2015-06-01 17:00:27 +00:00
|
|
|
return; /* no controlvm response needed */
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
if (pending_msg_hdr->id != (u32)cmd_id)
|
2014-03-04 13:58:07 +00:00
|
|
|
return;
|
2015-03-04 17:14:25 +00:00
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
controlvm_respond(pending_msg_hdr, response);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:58:16 +00:00
|
|
|
device_changestate_responder(enum controlvm_id cmd_id,
|
2015-06-04 13:22:42 +00:00
|
|
|
struct visor_device *p, int response,
|
2015-03-16 17:58:16 +00:00
|
|
|
struct spar_segment_state response_state)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:31 +00:00
|
|
|
struct controlvm_message outmsg;
|
2015-06-04 13:22:42 +00:00
|
|
|
u32 bus_no = p->chipset_bus_no;
|
|
|
|
u32 dev_no = p->chipset_dev_no;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-02-08 15:41:46 +00:00
|
|
|
if (!p->pending_msg_hdr)
|
2014-03-04 13:58:07 +00:00
|
|
|
return; /* no controlvm response needed */
|
2015-06-01 17:00:27 +00:00
|
|
|
if (p->pending_msg_hdr->id != cmd_id)
|
2014-03-04 13:58:07 +00:00
|
|
|
return;
|
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
controlvm_init_response(&outmsg, p->pending_msg_hdr, response);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:16 +00:00
|
|
|
outmsg.cmd.device_change_state.bus_no = bus_no;
|
|
|
|
outmsg.cmd.device_change_state.dev_no = dev_no;
|
|
|
|
outmsg.cmd.device_change_state.state = response_state;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
if (!visorchannel_signalinsert(controlvm_channel,
|
2015-03-04 17:14:25 +00:00
|
|
|
CONTROLVM_QUEUE_REQUEST, &outmsg))
|
2014-03-04 13:58:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-06-01 17:00:27 +00:00
|
|
|
device_responder(enum controlvm_id cmd_id,
|
|
|
|
struct controlvm_message_header *pending_msg_hdr,
|
2015-05-13 17:22:25 +00:00
|
|
|
int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2016-02-08 15:41:46 +00:00
|
|
|
if (!pending_msg_hdr)
|
2014-03-04 13:58:07 +00:00
|
|
|
return; /* no controlvm response needed */
|
2015-03-04 17:14:25 +00:00
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
if (pending_msg_hdr->id != (u32)cmd_id)
|
2014-03-04 13:58:07 +00:00
|
|
|
return;
|
2015-03-04 17:14:25 +00:00
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
controlvm_respond(pending_msg_hdr, response);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_epilog(struct visor_device *bus_info,
|
2015-03-16 17:58:17 +00:00
|
|
|
u32 cmd, struct controlvm_message_header *msg_hdr,
|
2015-04-13 14:28:40 +00:00
|
|
|
int response, bool need_response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-06-01 17:00:27 +00:00
|
|
|
struct controlvm_message_header *pmsg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
if (!bus_info) {
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* relying on a valid passed in response code
|
|
|
|
* be lazy and re-use msg_hdr for this failure, is this ok??
|
|
|
|
*/
|
2015-06-01 17:00:27 +00:00
|
|
|
pmsg_hdr = msg_hdr;
|
2016-06-11 01:48:21 +00:00
|
|
|
goto out_respond;
|
2015-06-01 17:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bus_info->pending_msg_hdr) {
|
|
|
|
/* only non-NULL if dev is still waiting on a response */
|
|
|
|
response = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT;
|
|
|
|
pmsg_hdr = bus_info->pending_msg_hdr;
|
2016-06-11 01:48:21 +00:00
|
|
|
goto out_respond;
|
2015-06-01 17:00:27 +00:00
|
|
|
}
|
2015-03-04 17:14:25 +00:00
|
|
|
|
2015-03-16 17:58:17 +00:00
|
|
|
if (need_response) {
|
2015-06-01 17:00:27 +00:00
|
|
|
pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
|
|
|
|
if (!pmsg_hdr) {
|
2016-03-11 22:01:43 +00:00
|
|
|
POSTCODE_LINUX_4(MALLOC_FAILURE_PC, cmd,
|
|
|
|
bus_info->chipset_bus_no,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
2016-06-11 01:48:21 +00:00
|
|
|
return;
|
2015-06-01 17:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(pmsg_hdr, msg_hdr,
|
2014-10-23 18:30:26 +00:00
|
|
|
sizeof(struct controlvm_message_header));
|
2015-06-01 17:00:27 +00:00
|
|
|
bus_info->pending_msg_hdr = pmsg_hdr;
|
2015-03-16 17:58:19 +00:00
|
|
|
}
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
if (response == CONTROLVM_RESP_SUCCESS) {
|
|
|
|
switch (cmd) {
|
|
|
|
case CONTROLVM_BUS_CREATE:
|
2016-06-11 01:48:21 +00:00
|
|
|
chipset_bus_create(bus_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
break;
|
|
|
|
case CONTROLVM_BUS_DESTROY:
|
2016-06-11 01:48:21 +00:00
|
|
|
chipset_bus_destroy(bus_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-03-11 22:01:43 +00:00
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
out_respond:
|
2016-04-05 03:31:39 +00:00
|
|
|
bus_responder(cmd, pmsg_hdr, response);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-06-04 13:22:42 +00:00
|
|
|
device_epilog(struct visor_device *dev_info,
|
2015-05-13 17:22:25 +00:00
|
|
|
struct spar_segment_state state, u32 cmd,
|
2015-03-16 17:58:17 +00:00
|
|
|
struct controlvm_message_header *msg_hdr, int response,
|
2015-04-13 14:28:40 +00:00
|
|
|
bool need_response, bool for_visorbus)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-06-01 17:00:27 +00:00
|
|
|
struct controlvm_message_header *pmsg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-06-01 17:00:27 +00:00
|
|
|
if (!dev_info) {
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* relying on a valid passed in response code
|
|
|
|
* be lazy and re-use msg_hdr for this failure, is this ok??
|
|
|
|
*/
|
2015-06-01 17:00:27 +00:00
|
|
|
pmsg_hdr = msg_hdr;
|
2016-06-11 01:48:21 +00:00
|
|
|
goto out_respond;
|
2015-06-01 17:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dev_info->pending_msg_hdr) {
|
|
|
|
/* only non-NULL if dev is still waiting on a response */
|
|
|
|
response = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT;
|
|
|
|
pmsg_hdr = dev_info->pending_msg_hdr;
|
2016-06-11 01:48:21 +00:00
|
|
|
goto out_respond;
|
2015-06-01 17:00:27 +00:00
|
|
|
}
|
|
|
|
|
2015-03-16 17:58:17 +00:00
|
|
|
if (need_response) {
|
2015-06-01 17:00:27 +00:00
|
|
|
pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
|
|
|
|
if (!pmsg_hdr) {
|
|
|
|
response = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
2016-06-11 01:48:21 +00:00
|
|
|
goto out_respond;
|
2015-06-01 17:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(pmsg_hdr, msg_hdr,
|
2014-10-23 18:30:26 +00:00
|
|
|
sizeof(struct controlvm_message_header));
|
2015-06-01 17:00:27 +00:00
|
|
|
dev_info->pending_msg_hdr = pmsg_hdr;
|
2015-03-16 17:58:19 +00:00
|
|
|
}
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
if (response >= 0) {
|
|
|
|
switch (cmd) {
|
|
|
|
case CONTROLVM_DEVICE_CREATE:
|
2016-06-11 01:48:21 +00:00
|
|
|
chipset_device_create(dev_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
break;
|
|
|
|
case CONTROLVM_DEVICE_CHANGESTATE:
|
|
|
|
/* ServerReady / ServerRunning / SegmentStateRunning */
|
2014-10-23 18:30:13 +00:00
|
|
|
if (state.alive == segment_state_running.alive &&
|
|
|
|
state.operating ==
|
|
|
|
segment_state_running.operating) {
|
2016-06-11 01:48:21 +00:00
|
|
|
chipset_device_resume(dev_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
/* ServerNotReady / ServerLost / SegmentStateStandby */
|
2014-10-23 18:30:13 +00:00
|
|
|
else if (state.alive == segment_state_standby.alive &&
|
2014-10-23 18:30:12 +00:00
|
|
|
state.operating ==
|
2014-10-23 18:30:13 +00:00
|
|
|
segment_state_standby.operating) {
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* technically this is standby case
|
2014-03-04 13:58:07 +00:00
|
|
|
* where server is lost
|
|
|
|
*/
|
2016-06-11 01:48:21 +00:00
|
|
|
chipset_device_pause(dev_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CONTROLVM_DEVICE_DESTROY:
|
2016-06-11 01:48:21 +00:00
|
|
|
chipset_device_destroy(dev_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-05 03:31:38 +00:00
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
out_respond:
|
2016-04-05 03:31:38 +00:00
|
|
|
device_responder(cmd, pmsg_hdr, response);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
bus_create(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &inmsg->cmd;
|
2015-04-13 14:28:41 +00:00
|
|
|
u32 bus_no = cmd->create_bus.bus_no;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
2015-06-04 13:22:41 +00:00
|
|
|
struct visor_device *bus_info;
|
2015-06-01 17:00:26 +00:00
|
|
|
struct visorchannel *visorchannel;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
|
2015-03-16 17:58:20 +00:00
|
|
|
if (bus_info && (bus_info->state.created == 1)) {
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
2016-03-11 22:01:42 +00:00
|
|
|
goto out_bus_epilog;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2015-03-16 17:58:20 +00:00
|
|
|
bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL);
|
|
|
|
if (!bus_info) {
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
2016-03-11 22:01:42 +00:00
|
|
|
goto out_bus_epilog;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2015-06-04 13:22:49 +00:00
|
|
|
INIT_LIST_HEAD(&bus_info->list_all);
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_info->chipset_bus_no = bus_no;
|
|
|
|
bus_info->chipset_dev_no = BUS_ROOT_DEVICE;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:20 +00:00
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-06-01 17:00:26 +00:00
|
|
|
visorchannel = visorchannel_create(cmd->create_bus.channel_addr,
|
|
|
|
cmd->create_bus.channel_bytes,
|
|
|
|
GFP_KERNEL,
|
|
|
|
cmd->create_bus.bus_data_type_uuid);
|
|
|
|
|
|
|
|
if (!visorchannel) {
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
|
|
|
kfree(bus_info);
|
|
|
|
bus_info = NULL;
|
2016-03-11 22:01:42 +00:00
|
|
|
goto out_bus_epilog;
|
2015-06-01 17:00:26 +00:00
|
|
|
}
|
|
|
|
bus_info->visorchannel = visorchannel;
|
2016-09-19 21:09:18 +00:00
|
|
|
if (uuid_le_cmp(cmd->create_bus.bus_inst_uuid, spar_siovm_uuid) == 0)
|
2016-03-02 00:45:04 +00:00
|
|
|
save_crash_message(inmsg, CRASH_BUS);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:20 +00:00
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-03-11 22:01:42 +00:00
|
|
|
out_bus_epilog:
|
2015-05-13 17:22:24 +00:00
|
|
|
bus_epilog(bus_info, CONTROLVM_BUS_CREATE, &inmsg->hdr,
|
2014-10-23 18:30:26 +00:00
|
|
|
rc, inmsg->hdr.flags.response_expected == 1);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
bus_destroy(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &inmsg->cmd;
|
2015-04-13 14:28:41 +00:00
|
|
|
u32 bus_no = cmd->destroy_bus.bus_no;
|
2015-06-04 13:22:41 +00:00
|
|
|
struct visor_device *bus_info;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
|
|
|
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
|
2015-03-16 17:58:21 +00:00
|
|
|
if (!bus_info)
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
|
2015-03-16 17:58:21 +00:00
|
|
|
else if (bus_info->state.created == 0)
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-05-13 17:22:24 +00:00
|
|
|
bus_epilog(bus_info, CONTROLVM_BUS_DESTROY, &inmsg->hdr,
|
2014-10-23 18:30:26 +00:00
|
|
|
rc, inmsg->hdr.flags.response_expected == 1);
|
2015-06-04 13:22:41 +00:00
|
|
|
|
|
|
|
/* bus_info is freed as part of the busdevice_release function */
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:57:51 +00:00
|
|
|
bus_configure(struct controlvm_message *inmsg,
|
|
|
|
struct parser_context *parser_ctx)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &inmsg->cmd;
|
2015-05-05 22:35:45 +00:00
|
|
|
u32 bus_no;
|
2015-06-04 13:22:41 +00:00
|
|
|
struct visor_device *bus_info;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
|
|
|
|
2015-03-16 17:58:22 +00:00
|
|
|
bus_no = cmd->configure_bus.bus_no;
|
|
|
|
POSTCODE_LINUX_3(BUS_CONFIGURE_ENTRY_PC, bus_no,
|
|
|
|
POSTCODE_SEVERITY_INFO);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
|
2015-03-16 17:58:22 +00:00
|
|
|
if (!bus_info) {
|
|
|
|
POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
|
2015-03-16 17:58:22 +00:00
|
|
|
} else if (bus_info->state.created == 0) {
|
|
|
|
POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
|
2016-02-08 15:41:46 +00:00
|
|
|
} else if (bus_info->pending_msg_hdr) {
|
2015-03-16 17:58:22 +00:00
|
|
|
POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT;
|
2015-03-16 17:58:22 +00:00
|
|
|
} else {
|
2016-04-05 03:31:12 +00:00
|
|
|
visorchannel_set_clientpartition
|
|
|
|
(bus_info->visorchannel,
|
|
|
|
cmd->configure_bus.guest_handle);
|
2015-03-16 17:58:22 +00:00
|
|
|
bus_info->partition_uuid = parser_id_get(parser_ctx);
|
|
|
|
parser_param_start(parser_ctx, PARSERSTRING_NAME);
|
|
|
|
bus_info->name = parser_string_get(parser_ctx);
|
|
|
|
|
|
|
|
POSTCODE_LINUX_3(BUS_CONFIGURE_EXIT_PC, bus_no,
|
|
|
|
POSTCODE_SEVERITY_INFO);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2015-05-13 17:22:24 +00:00
|
|
|
bus_epilog(bus_info, CONTROLVM_BUS_CONFIGURE, &inmsg->hdr,
|
2014-10-23 18:30:26 +00:00
|
|
|
rc, inmsg->hdr.flags.response_expected == 1);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
my_device_create(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &inmsg->cmd;
|
2015-04-13 14:28:41 +00:00
|
|
|
u32 bus_no = cmd->create_device.bus_no;
|
|
|
|
u32 dev_no = cmd->create_device.dev_no;
|
2015-06-04 13:22:42 +00:00
|
|
|
struct visor_device *dev_info = NULL;
|
2015-06-04 13:22:41 +00:00
|
|
|
struct visor_device *bus_info;
|
2015-06-01 17:00:26 +00:00
|
|
|
struct visorchannel *visorchannel;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
|
|
|
|
2015-06-04 13:22:42 +00:00
|
|
|
bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
|
|
|
|
if (!bus_info) {
|
2015-03-16 17:58:23 +00:00
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2015-06-04 13:22:42 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
|
2016-03-11 22:01:44 +00:00
|
|
|
goto out_respond;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2015-06-04 13:22:42 +00:00
|
|
|
|
|
|
|
if (bus_info->state.created == 0) {
|
2015-03-16 17:58:23 +00:00
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
|
2016-03-11 22:01:44 +00:00
|
|
|
goto out_respond;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2015-06-04 13:22:42 +00:00
|
|
|
|
|
|
|
dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL);
|
|
|
|
if (dev_info && (dev_info->state.created == 1)) {
|
2015-03-16 17:58:23 +00:00
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2015-06-04 13:22:42 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
2016-03-11 22:01:44 +00:00
|
|
|
goto out_respond;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2015-06-04 13:22:42 +00:00
|
|
|
|
2015-03-16 17:58:23 +00:00
|
|
|
dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
|
|
|
|
if (!dev_info) {
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
2016-03-11 22:01:44 +00:00
|
|
|
goto out_respond;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2014-03-14 02:20:06 +00:00
|
|
|
|
2015-06-04 13:22:42 +00:00
|
|
|
dev_info->chipset_bus_no = bus_no;
|
|
|
|
dev_info->chipset_dev_no = dev_no;
|
|
|
|
dev_info->inst = cmd->create_device.dev_inst_uuid;
|
|
|
|
|
|
|
|
/* not sure where the best place to set the 'parent' */
|
|
|
|
dev_info->device.parent = &bus_info->device;
|
|
|
|
|
2015-03-16 17:58:23 +00:00
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
|
2015-07-14 18:43:29 +00:00
|
|
|
visorchannel =
|
|
|
|
visorchannel_create_with_lock(cmd->create_device.channel_addr,
|
|
|
|
cmd->create_device.channel_bytes,
|
|
|
|
GFP_KERNEL,
|
|
|
|
cmd->create_device.data_type_uuid);
|
2015-06-01 17:00:26 +00:00
|
|
|
|
|
|
|
if (!visorchannel) {
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
|
|
|
kfree(dev_info);
|
|
|
|
dev_info = NULL;
|
2016-03-11 22:01:44 +00:00
|
|
|
goto out_respond;
|
2015-06-01 17:00:26 +00:00
|
|
|
}
|
|
|
|
dev_info->visorchannel = visorchannel;
|
|
|
|
dev_info->channel_type_guid = cmd->create_device.data_type_uuid;
|
2016-03-02 00:45:04 +00:00
|
|
|
if (uuid_le_cmp(cmd->create_device.data_type_uuid,
|
|
|
|
spar_vhba_channel_protocol_uuid) == 0)
|
|
|
|
save_crash_message(inmsg, CRASH_DEV);
|
|
|
|
|
2015-03-16 17:58:23 +00:00
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_EXIT_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_INFO);
|
2016-03-11 22:01:44 +00:00
|
|
|
out_respond:
|
2015-05-13 17:22:25 +00:00
|
|
|
device_epilog(dev_info, segment_state_running,
|
2014-03-04 13:58:07 +00:00
|
|
|
CONTROLVM_DEVICE_CREATE, &inmsg->hdr, rc,
|
2015-05-05 22:36:39 +00:00
|
|
|
inmsg->hdr.flags.response_expected == 1, 1);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
my_device_changestate(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &inmsg->cmd;
|
2015-04-13 14:28:41 +00:00
|
|
|
u32 bus_no = cmd->device_change_state.bus_no;
|
|
|
|
u32 dev_no = cmd->device_change_state.dev_no;
|
2014-10-23 18:30:25 +00:00
|
|
|
struct spar_segment_state state = cmd->device_change_state.state;
|
2015-06-04 13:22:42 +00:00
|
|
|
struct visor_device *dev_info;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
|
|
|
|
2015-06-04 13:22:42 +00:00
|
|
|
dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL);
|
2015-03-16 17:58:24 +00:00
|
|
|
if (!dev_info) {
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID;
|
2015-03-16 17:58:24 +00:00
|
|
|
} else if (dev_info->state.created == 0) {
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
2015-03-16 17:58:24 +00:00
|
|
|
if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info)
|
2015-05-13 17:22:25 +00:00
|
|
|
device_epilog(dev_info, state,
|
2015-03-16 17:58:24 +00:00
|
|
|
CONTROLVM_DEVICE_CHANGESTATE, &inmsg->hdr, rc,
|
2015-05-05 22:36:39 +00:00
|
|
|
inmsg->hdr.flags.response_expected == 1, 1);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
my_device_destroy(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &inmsg->cmd;
|
2015-04-13 14:28:41 +00:00
|
|
|
u32 bus_no = cmd->destroy_device.bus_no;
|
|
|
|
u32 dev_no = cmd->destroy_device.dev_no;
|
2015-06-04 13:22:42 +00:00
|
|
|
struct visor_device *dev_info;
|
2014-03-04 13:58:07 +00:00
|
|
|
int rc = CONTROLVM_RESP_SUCCESS;
|
|
|
|
|
2015-06-04 13:22:42 +00:00
|
|
|
dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL);
|
2015-03-16 17:58:25 +00:00
|
|
|
if (!dev_info)
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID;
|
2015-03-16 17:58:25 +00:00
|
|
|
else if (dev_info->state.created == 0)
|
2014-03-19 18:06:25 +00:00
|
|
|
rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:25 +00:00
|
|
|
if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info)
|
2015-05-13 17:22:25 +00:00
|
|
|
device_epilog(dev_info, segment_state_running,
|
2014-03-04 13:58:07 +00:00
|
|
|
CONTROLVM_DEVICE_DESTROY, &inmsg->hdr, rc,
|
2015-05-05 22:36:39 +00:00
|
|
|
inmsg->hdr.flags.response_expected == 1, 1);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* initialize_controlvm_payload_info() - init controlvm_payload_info struct
|
|
|
|
* @phys_addr: the physical address of controlvm channel
|
|
|
|
* @offset: the offset to payload
|
|
|
|
* @bytes: the size of the payload in bytes
|
|
|
|
* @info: the returning valid struct
|
|
|
|
*
|
|
|
|
* When provided with the physical address of the controlvm channel
|
2014-03-04 13:58:07 +00:00
|
|
|
* (phys_addr), the offset to the payload area we need to manage
|
|
|
|
* (offset), and the size of this payload area (bytes), fills in the
|
2016-06-11 01:48:18 +00:00
|
|
|
* controlvm_payload_info struct.
|
|
|
|
*
|
|
|
|
* Return: CONTROLVM_RESP_SUCCESS for success or a negative for failure
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static int
|
2015-05-05 22:37:04 +00:00
|
|
|
initialize_controlvm_payload_info(u64 phys_addr, u64 offset, u32 bytes,
|
2015-04-13 14:28:39 +00:00
|
|
|
struct visor_controlvm_payload_info *info)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-08-11 03:07:06 +00:00
|
|
|
u8 *payload = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-03-11 22:01:39 +00:00
|
|
|
if (!info)
|
|
|
|
return -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID;
|
|
|
|
|
2015-04-13 14:28:39 +00:00
|
|
|
memset(info, 0, sizeof(struct visor_controlvm_payload_info));
|
2016-03-11 22:01:39 +00:00
|
|
|
if ((offset == 0) || (bytes == 0))
|
|
|
|
return -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID;
|
|
|
|
|
2015-08-11 03:07:06 +00:00
|
|
|
payload = memremap(phys_addr + offset, bytes, MEMREMAP_WB);
|
2016-03-11 22:01:39 +00:00
|
|
|
if (!payload)
|
|
|
|
return -CONTROLVM_RESP_ERROR_IOREMAP_FAILED;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
info->offset = offset;
|
|
|
|
info->bytes = bytes;
|
|
|
|
info->ptr = payload;
|
|
|
|
|
2016-03-11 22:01:39 +00:00
|
|
|
return CONTROLVM_RESP_SUCCESS;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-04-13 14:28:39 +00:00
|
|
|
destroy_controlvm_payload_info(struct visor_controlvm_payload_info *info)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-03-16 17:58:27 +00:00
|
|
|
if (info->ptr) {
|
2015-08-11 03:07:06 +00:00
|
|
|
memunmap(info->ptr);
|
2014-03-04 13:58:07 +00:00
|
|
|
info->ptr = NULL;
|
|
|
|
}
|
2015-04-13 14:28:39 +00:00
|
|
|
memset(info, 0, sizeof(struct visor_controlvm_payload_info));
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
initialize_controlvm_payload(void)
|
|
|
|
{
|
2015-05-05 22:37:04 +00:00
|
|
|
u64 phys_addr = visorchannel_get_physaddr(controlvm_channel);
|
2015-03-16 17:58:28 +00:00
|
|
|
u64 payload_offset = 0;
|
|
|
|
u32 payload_bytes = 0;
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
if (visorchannel_read(controlvm_channel,
|
2014-10-23 18:30:34 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
request_payload_offset),
|
2015-03-16 17:58:28 +00:00
|
|
|
&payload_offset, sizeof(payload_offset)) < 0) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-16 17:58:05 +00:00
|
|
|
if (visorchannel_read(controlvm_channel,
|
2014-10-23 18:30:34 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
request_payload_bytes),
|
2015-03-16 17:58:28 +00:00
|
|
|
&payload_bytes, sizeof(payload_bytes)) < 0) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
initialize_controlvm_payload_info(phys_addr,
|
2015-03-16 17:58:28 +00:00
|
|
|
payload_offset, payload_bytes,
|
2015-03-16 17:58:07 +00:00
|
|
|
&controlvm_payload_info);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* visorchipset_chipset_ready() - sends chipset_ready action
|
|
|
|
*
|
|
|
|
* Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset.
|
|
|
|
*
|
|
|
|
* Return: CONTROLVM_RESP_SUCCESS
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
2015-05-13 17:21:57 +00:00
|
|
|
static int
|
2014-03-04 13:58:07 +00:00
|
|
|
visorchipset_chipset_ready(void)
|
|
|
|
{
|
2015-03-16 17:58:45 +00:00
|
|
|
kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_ONLINE);
|
2014-03-04 13:58:07 +00:00
|
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2015-05-13 17:21:57 +00:00
|
|
|
static int
|
2014-03-04 13:58:07 +00:00
|
|
|
visorchipset_chipset_selftest(void)
|
|
|
|
{
|
|
|
|
char env_selftest[20];
|
|
|
|
char *envp[] = { env_selftest, NULL };
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1);
|
2015-03-16 17:58:45 +00:00
|
|
|
kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE,
|
2014-03-04 13:58:07 +00:00
|
|
|
envp);
|
|
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* visorchipset_chipset_notready() - sends chipset_notready action
|
|
|
|
*
|
|
|
|
* Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset.
|
|
|
|
*
|
|
|
|
* Return: CONTROLVM_RESP_SUCCESS
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
2015-05-13 17:21:57 +00:00
|
|
|
static int
|
2014-03-04 13:58:07 +00:00
|
|
|
visorchipset_chipset_notready(void)
|
|
|
|
{
|
2015-03-16 17:58:45 +00:00
|
|
|
kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_OFFLINE);
|
2014-03-04 13:58:07 +00:00
|
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:58:47 +00:00
|
|
|
chipset_ready(struct controlvm_message_header *msg_hdr)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
int rc = visorchipset_chipset_ready();
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
if (rc != CONTROLVM_RESP_SUCCESS)
|
|
|
|
rc = -rc;
|
2016-04-19 12:11:52 +00:00
|
|
|
if (msg_hdr->flags.response_expected)
|
2015-03-16 17:58:47 +00:00
|
|
|
controlvm_respond(msg_hdr, rc);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:58:47 +00:00
|
|
|
chipset_selftest(struct controlvm_message_header *msg_hdr)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
int rc = visorchipset_chipset_selftest();
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
if (rc != CONTROLVM_RESP_SUCCESS)
|
|
|
|
rc = -rc;
|
2015-03-16 17:58:47 +00:00
|
|
|
if (msg_hdr->flags.response_expected)
|
|
|
|
controlvm_respond(msg_hdr, rc);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-16 17:58:47 +00:00
|
|
|
chipset_notready(struct controlvm_message_header *msg_hdr)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
int rc = visorchipset_chipset_notready();
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
if (rc != CONTROLVM_RESP_SUCCESS)
|
|
|
|
rc = -rc;
|
2015-03-16 17:58:47 +00:00
|
|
|
if (msg_hdr->flags.response_expected)
|
|
|
|
controlvm_respond(msg_hdr, rc);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-06-11 01:48:18 +00:00
|
|
|
* The general parahotplug flow works as follows. The visorchipset
|
2014-03-04 13:58:07 +00:00
|
|
|
* driver receives a DEVICE_CHANGESTATE message from Command
|
2016-06-11 01:48:18 +00:00
|
|
|
* specifying a physical device to enable or disable. The CONTROLVM
|
2014-03-04 13:58:07 +00:00
|
|
|
* message handler calls parahotplug_process_message, which then adds
|
|
|
|
* the message to a global list and kicks off a udev event which
|
|
|
|
* causes a user level script to enable or disable the specified
|
2016-06-11 01:48:18 +00:00
|
|
|
* device. The udev script then writes to
|
2014-03-04 13:58:07 +00:00
|
|
|
* /proc/visorchipset/parahotplug, which causes parahotplug_proc_write
|
|
|
|
* to get called, at which point the appropriate CONTROLVM message is
|
|
|
|
* retrieved from the list and responded to.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PARAHOTPLUG_TIMEOUT_MS 2000
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_next_id() - generate unique int to match an outstanding CONTROLVM
|
|
|
|
* message with a udev script /proc response
|
|
|
|
*
|
|
|
|
* Return: a unique integer value
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parahotplug_next_id(void)
|
|
|
|
{
|
|
|
|
static atomic_t id = ATOMIC_INIT(0);
|
2014-08-18 13:34:53 +00:00
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
return atomic_inc_return(&id);
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_next_expiration() - returns the time (in jiffies) when a
|
|
|
|
* CONTROLVM message on the list should expire
|
|
|
|
* -- PARAHOTPLUG_TIMEOUT_MS in the future
|
|
|
|
*
|
|
|
|
* Return: expected expiration time (in jiffies)
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static unsigned long
|
|
|
|
parahotplug_next_expiration(void)
|
|
|
|
{
|
2015-01-31 11:02:08 +00:00
|
|
|
return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_request_create() - create a parahotplug_request, which is
|
|
|
|
* basically a wrapper for a CONTROLVM_MESSAGE
|
|
|
|
* that we can stick on a list
|
|
|
|
* @msg: the message to insert in the request
|
|
|
|
*
|
|
|
|
* Return: the request containing the provided message
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static struct parahotplug_request *
|
2014-10-23 18:30:31 +00:00
|
|
|
parahotplug_request_create(struct controlvm_message *msg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-02-10 14:12:07 +00:00
|
|
|
struct parahotplug_request *req;
|
|
|
|
|
2015-03-16 17:58:30 +00:00
|
|
|
req = kmalloc(sizeof(*req), GFP_KERNEL | __GFP_NORETRY);
|
2015-03-16 17:58:13 +00:00
|
|
|
if (!req)
|
2014-03-04 13:58:07 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
req->id = parahotplug_next_id();
|
|
|
|
req->expiration = parahotplug_next_expiration();
|
|
|
|
req->msg = *msg;
|
|
|
|
|
|
|
|
return req;
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_request_destroy() - free a parahotplug_request
|
|
|
|
* @req: the request to deallocate
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
parahotplug_request_destroy(struct parahotplug_request *req)
|
|
|
|
{
|
|
|
|
kfree(req);
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_request_kickoff() - initiate parahotplug request
|
|
|
|
* @req: the request to initiate
|
|
|
|
*
|
|
|
|
* Cause uevent to run the user level script to do the disable/enable specified
|
|
|
|
* in the parahotplug_request.
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
parahotplug_request_kickoff(struct parahotplug_request *req)
|
|
|
|
{
|
2014-10-23 18:30:25 +00:00
|
|
|
struct controlvm_message_packet *cmd = &req->msg.cmd;
|
2014-03-04 13:58:07 +00:00
|
|
|
char env_cmd[40], env_id[40], env_state[40], env_bus[40], env_dev[40],
|
|
|
|
env_func[40];
|
|
|
|
char *envp[] = {
|
|
|
|
env_cmd, env_id, env_state, env_bus, env_dev, env_func, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
sprintf(env_cmd, "SPAR_PARAHOTPLUG=1");
|
|
|
|
sprintf(env_id, "SPAR_PARAHOTPLUG_ID=%d", req->id);
|
|
|
|
sprintf(env_state, "SPAR_PARAHOTPLUG_STATE=%d",
|
2014-10-23 18:30:25 +00:00
|
|
|
cmd->device_change_state.state.active);
|
2014-03-04 13:58:07 +00:00
|
|
|
sprintf(env_bus, "SPAR_PARAHOTPLUG_BUS=%d",
|
2014-10-23 18:30:25 +00:00
|
|
|
cmd->device_change_state.bus_no);
|
2014-03-04 13:58:07 +00:00
|
|
|
sprintf(env_dev, "SPAR_PARAHOTPLUG_DEVICE=%d",
|
2014-10-23 18:30:25 +00:00
|
|
|
cmd->device_change_state.dev_no >> 3);
|
2014-03-04 13:58:07 +00:00
|
|
|
sprintf(env_func, "SPAR_PARAHOTPLUG_FUNCTION=%d",
|
2014-10-23 18:30:25 +00:00
|
|
|
cmd->device_change_state.dev_no & 0x7);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:45 +00:00
|
|
|
kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE,
|
2014-03-04 13:58:07 +00:00
|
|
|
envp);
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_request_complete() - mark request as complete
|
|
|
|
* @id: the id of the request
|
|
|
|
* @active: indicates whether the request is assigned to active partition
|
|
|
|
*
|
2014-03-04 13:58:07 +00:00
|
|
|
* Called from the /proc handler, which means the user script has
|
2016-06-11 01:48:18 +00:00
|
|
|
* finished the enable/disable. Find the matching identifier, and
|
2014-03-04 13:58:07 +00:00
|
|
|
* respond to the CONTROLVM message with success.
|
2016-06-11 01:48:18 +00:00
|
|
|
*
|
|
|
|
* Return: 0 on success or -EINVAL on failure
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
static int
|
2014-07-31 16:00:49 +00:00
|
|
|
parahotplug_request_complete(int id, u16 active)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-05-05 22:35:45 +00:00
|
|
|
struct list_head *pos;
|
|
|
|
struct list_head *tmp;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:41 +00:00
|
|
|
spin_lock(¶hotplug_request_list_lock);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
/* Look for a request matching "id". */
|
2015-03-16 17:58:41 +00:00
|
|
|
list_for_each_safe(pos, tmp, ¶hotplug_request_list) {
|
2014-03-04 13:58:07 +00:00
|
|
|
struct parahotplug_request *req =
|
|
|
|
list_entry(pos, struct parahotplug_request, list);
|
|
|
|
if (req->id == id) {
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* Found a match. Remove it from the list and
|
2014-03-04 13:58:07 +00:00
|
|
|
* respond.
|
|
|
|
*/
|
|
|
|
list_del(pos);
|
2015-03-16 17:58:41 +00:00
|
|
|
spin_unlock(¶hotplug_request_list_lock);
|
2014-10-23 18:30:25 +00:00
|
|
|
req->msg.cmd.device_change_state.state.active = active;
|
2014-10-23 18:30:26 +00:00
|
|
|
if (req->msg.hdr.flags.response_expected)
|
2014-03-04 13:58:07 +00:00
|
|
|
controlvm_respond_physdev_changestate(
|
|
|
|
&req->msg.hdr, CONTROLVM_RESP_SUCCESS,
|
2014-10-23 18:30:25 +00:00
|
|
|
req->msg.cmd.device_change_state.state);
|
2014-03-04 13:58:07 +00:00
|
|
|
parahotplug_request_destroy(req);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-16 17:58:41 +00:00
|
|
|
spin_unlock(¶hotplug_request_list_lock);
|
2016-05-14 03:17:20 +00:00
|
|
|
return -EINVAL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_process_message() - enables or disables a PCI device by kicking
|
|
|
|
* off a udev script
|
|
|
|
* @inmsg: the message indicating whether to enable or disable
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
2014-03-13 20:39:22 +00:00
|
|
|
static void
|
2014-10-23 18:30:31 +00:00
|
|
|
parahotplug_process_message(struct controlvm_message *inmsg)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
struct parahotplug_request *req;
|
|
|
|
|
|
|
|
req = parahotplug_request_create(inmsg);
|
|
|
|
|
2015-03-16 17:58:13 +00:00
|
|
|
if (!req)
|
2014-03-04 13:58:07 +00:00
|
|
|
return;
|
|
|
|
|
2014-10-23 18:30:25 +00:00
|
|
|
if (inmsg->cmd.device_change_state.state.active) {
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* For enable messages, just respond with success
|
|
|
|
* right away. This is a bit of a hack, but there are
|
|
|
|
* issues with the early enable messages we get (with
|
|
|
|
* either the udev script not detecting that the device
|
|
|
|
* is up, or not getting called at all). Fortunately
|
|
|
|
* the messages that get lost don't matter anyway, as
|
|
|
|
*
|
|
|
|
* devices are automatically enabled at
|
|
|
|
* initialization.
|
2014-03-04 13:58:07 +00:00
|
|
|
*/
|
|
|
|
parahotplug_request_kickoff(req);
|
2016-04-05 03:31:12 +00:00
|
|
|
controlvm_respond_physdev_changestate
|
|
|
|
(&inmsg->hdr,
|
|
|
|
CONTROLVM_RESP_SUCCESS,
|
|
|
|
inmsg->cmd.device_change_state.state);
|
2014-03-04 13:58:07 +00:00
|
|
|
parahotplug_request_destroy(req);
|
|
|
|
} else {
|
2016-06-11 01:48:18 +00:00
|
|
|
/*
|
|
|
|
* For disable messages, add the request to the
|
|
|
|
* request list before kicking off the udev script. It
|
|
|
|
* won't get responded to until the script has
|
|
|
|
* indicated it's done.
|
|
|
|
*/
|
2015-03-16 17:58:41 +00:00
|
|
|
spin_lock(¶hotplug_request_list_lock);
|
|
|
|
list_add_tail(&req->list, ¶hotplug_request_list);
|
|
|
|
spin_unlock(¶hotplug_request_list_lock);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
parahotplug_request_kickoff(req);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 17:22:10 +00:00
|
|
|
static inline unsigned int
|
|
|
|
issue_vmcall_io_controlvm_addr(u64 *control_addr, u32 *control_bytes)
|
|
|
|
{
|
|
|
|
struct vmcall_io_controlvm_addr_params params;
|
|
|
|
int result = VMCALL_SUCCESS;
|
|
|
|
u64 physaddr;
|
|
|
|
|
|
|
|
physaddr = virt_to_phys(¶ms);
|
|
|
|
ISSUE_IO_VMCALL(VMCALL_IO_CONTROLVM_ADDR, physaddr, result);
|
|
|
|
if (VMCALL_SUCCESSFUL(result)) {
|
|
|
|
*control_addr = params.address;
|
|
|
|
*control_bytes = params.channel_bytes;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:04 +00:00
|
|
|
static u64 controlvm_get_channel_address(void)
|
2014-07-17 16:39:57 +00:00
|
|
|
{
|
2014-07-31 16:00:51 +00:00
|
|
|
u64 addr = 0;
|
2014-07-31 16:00:50 +00:00
|
|
|
u32 size = 0;
|
2014-07-17 16:39:57 +00:00
|
|
|
|
2015-03-04 17:14:25 +00:00
|
|
|
if (!VMCALL_SUCCESSFUL(issue_vmcall_io_controlvm_addr(&addr, &size)))
|
2014-07-17 16:39:57 +00:00
|
|
|
return 0;
|
2015-03-04 17:14:25 +00:00
|
|
|
|
2014-07-17 16:39:57 +00:00
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2014-03-04 13:58:07 +00:00
|
|
|
static void
|
|
|
|
setup_crash_devices_work_queue(struct work_struct *work)
|
|
|
|
{
|
2015-03-16 17:58:33 +00:00
|
|
|
struct controlvm_message local_crash_bus_msg;
|
|
|
|
struct controlvm_message local_crash_dev_msg;
|
2014-10-23 18:30:31 +00:00
|
|
|
struct controlvm_message msg;
|
2015-03-16 17:58:33 +00:00
|
|
|
u32 local_crash_msg_offset;
|
|
|
|
u16 local_crash_msg_count;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_ENTRY_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
|
|
|
|
/* send init chipset msg */
|
2014-10-23 18:30:26 +00:00
|
|
|
msg.hdr.id = CONTROLVM_CHIPSET_INIT;
|
2014-10-23 18:30:25 +00:00
|
|
|
msg.cmd.init_chipset.bus_count = 23;
|
|
|
|
msg.cmd.init_chipset.switch_count = 0;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
chipset_init(&msg);
|
|
|
|
|
|
|
|
/* get saved message count */
|
2015-03-16 17:58:05 +00:00
|
|
|
if (visorchannel_read(controlvm_channel,
|
2014-10-23 18:30:34 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
saved_crash_message_count),
|
2015-03-16 17:58:33 +00:00
|
|
|
&local_crash_msg_count, sizeof(u16)) < 0) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-16 17:58:33 +00:00
|
|
|
if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC,
|
2015-03-16 17:58:33 +00:00
|
|
|
local_crash_msg_count,
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get saved crash message offset */
|
2015-03-16 17:58:05 +00:00
|
|
|
if (visorchannel_read(controlvm_channel,
|
2014-10-23 18:30:34 +00:00
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
saved_crash_message_offset),
|
2015-03-16 17:58:33 +00:00
|
|
|
&local_crash_msg_offset, sizeof(u32)) < 0) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read create device message for storage bus offset */
|
2015-03-16 17:58:05 +00:00
|
|
|
if (visorchannel_read(controlvm_channel,
|
2015-03-16 17:58:33 +00:00
|
|
|
local_crash_msg_offset,
|
|
|
|
&local_crash_bus_msg,
|
2014-10-23 18:30:31 +00:00
|
|
|
sizeof(struct controlvm_message)) < 0) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_RD_BUS_FAIULRE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read create device message for storage device */
|
2015-03-16 17:58:05 +00:00
|
|
|
if (visorchannel_read(controlvm_channel,
|
2015-03-16 17:58:33 +00:00
|
|
|
local_crash_msg_offset +
|
2014-10-23 18:30:31 +00:00
|
|
|
sizeof(struct controlvm_message),
|
2015-03-16 17:58:33 +00:00
|
|
|
&local_crash_dev_msg,
|
2014-10-23 18:30:31 +00:00
|
|
|
sizeof(struct controlvm_message)) < 0) {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_RD_DEV_FAIULRE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reuse IOVM create bus message */
|
2015-05-05 22:35:57 +00:00
|
|
|
if (local_crash_bus_msg.cmd.create_bus.channel_addr) {
|
2015-03-16 17:58:33 +00:00
|
|
|
bus_create(&local_crash_bus_msg);
|
2015-03-16 17:58:19 +00:00
|
|
|
} else {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_BUS_NULL_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reuse create device message for storage device */
|
2015-05-05 22:35:57 +00:00
|
|
|
if (local_crash_dev_msg.cmd.create_device.channel_addr) {
|
2015-03-16 17:58:33 +00:00
|
|
|
my_device_create(&local_crash_dev_msg);
|
2015-03-16 17:58:19 +00:00
|
|
|
} else {
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_DEV_NULL_FAILURE_PC,
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
POSTCODE_LINUX_2(CRASH_DEV_EXIT_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
void
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_create_response(struct visor_device *bus_info, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-06-13 15:40:48 +00:00
|
|
|
if (response >= 0)
|
2015-06-01 17:00:27 +00:00
|
|
|
bus_info->state.created = 1;
|
|
|
|
|
|
|
|
bus_responder(CONTROLVM_BUS_CREATE, bus_info->pending_msg_hdr,
|
|
|
|
response);
|
|
|
|
|
|
|
|
kfree(bus_info->pending_msg_hdr);
|
|
|
|
bus_info->pending_msg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
void
|
2015-06-04 13:22:41 +00:00
|
|
|
bus_destroy_response(struct visor_device *bus_info, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-06-01 17:00:27 +00:00
|
|
|
bus_responder(CONTROLVM_BUS_DESTROY, bus_info->pending_msg_hdr,
|
|
|
|
response);
|
|
|
|
|
|
|
|
kfree(bus_info->pending_msg_hdr);
|
|
|
|
bus_info->pending_msg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
void
|
2015-06-04 13:22:42 +00:00
|
|
|
device_create_response(struct visor_device *dev_info, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-06-01 17:00:27 +00:00
|
|
|
if (response >= 0)
|
|
|
|
dev_info->state.created = 1;
|
|
|
|
|
|
|
|
device_responder(CONTROLVM_DEVICE_CREATE, dev_info->pending_msg_hdr,
|
|
|
|
response);
|
|
|
|
|
|
|
|
kfree(dev_info->pending_msg_hdr);
|
2015-07-09 17:27:41 +00:00
|
|
|
dev_info->pending_msg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
void
|
2015-06-04 13:22:42 +00:00
|
|
|
device_destroy_response(struct visor_device *dev_info, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2015-06-01 17:00:27 +00:00
|
|
|
device_responder(CONTROLVM_DEVICE_DESTROY, dev_info->pending_msg_hdr,
|
|
|
|
response);
|
|
|
|
|
|
|
|
kfree(dev_info->pending_msg_hdr);
|
|
|
|
dev_info->pending_msg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
void
|
2016-06-11 01:48:22 +00:00
|
|
|
device_pause_response(struct visor_device *dev_info,
|
|
|
|
int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE,
|
2015-05-13 17:22:25 +00:00
|
|
|
dev_info, response,
|
2014-10-23 18:30:13 +00:00
|
|
|
segment_state_standby);
|
2015-06-01 17:00:27 +00:00
|
|
|
|
|
|
|
kfree(dev_info->pending_msg_hdr);
|
|
|
|
dev_info->pending_msg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:21 +00:00
|
|
|
void
|
2015-06-04 13:22:42 +00:00
|
|
|
device_resume_response(struct visor_device *dev_info, int response)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE,
|
2015-05-13 17:22:25 +00:00
|
|
|
dev_info, response,
|
2014-10-23 18:30:13 +00:00
|
|
|
segment_state_running);
|
2015-06-01 17:00:27 +00:00
|
|
|
|
|
|
|
kfree(dev_info->pending_msg_hdr);
|
|
|
|
dev_info->pending_msg_hdr = NULL;
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* devicedisabled_store() - disables the hotplug device
|
|
|
|
* @dev: sysfs interface variable not utilized in this function
|
|
|
|
* @attr: sysfs interface variable not utilized in this function
|
|
|
|
* @buf: buffer containing the device id
|
|
|
|
* @count: the size of the buffer
|
|
|
|
*
|
|
|
|
* The parahotplug/devicedisabled interface gets called by our support script
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
* when an SR-IOV device has been shut down. The ID is passed to the script
|
|
|
|
* and then passed back when the device has been removed.
|
2016-06-11 01:48:18 +00:00
|
|
|
*
|
|
|
|
* Return: the size of the buffer for success or negative for error
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
*/
|
|
|
|
static ssize_t devicedisabled_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
{
|
2015-05-05 22:35:46 +00:00
|
|
|
unsigned int id;
|
2016-06-11 01:48:00 +00:00
|
|
|
int err;
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtouint(buf, 10, &id))
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-06-11 01:48:00 +00:00
|
|
|
err = parahotplug_request_complete(id, 0);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2016-06-11 01:48:18 +00:00
|
|
|
/**
|
|
|
|
* deviceenabled_store() - enables the hotplug device
|
|
|
|
* @dev: sysfs interface variable not utilized in this function
|
|
|
|
* @attr: sysfs interface variable not utilized in this function
|
|
|
|
* @buf: buffer containing the device id
|
|
|
|
* @count: the size of the buffer
|
|
|
|
*
|
|
|
|
* The parahotplug/deviceenabled interface gets called by our support script
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
* when an SR-IOV device has been recovered. The ID is passed to the script
|
|
|
|
* and then passed back when the device has been brought back up.
|
2016-06-11 01:48:18 +00:00
|
|
|
*
|
|
|
|
* Return: the size of the buffer for success or negative for error
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
*/
|
|
|
|
static ssize_t deviceenabled_store(struct device *dev,
|
2015-03-16 17:58:52 +00:00
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
{
|
2015-05-05 22:35:46 +00:00
|
|
|
unsigned int id;
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
|
2015-05-05 22:35:57 +00:00
|
|
|
if (kstrtouint(buf, 10, &id))
|
staging: unisys: add sysfs entries for parahotplug support
This patch adds new a new directory parahotplug to the visorchipset sysfs
directory, and two new attributes, deviceenabled, and devicedisabled, into the
new directory, to add s-Par parahotplug support.
The parahotplug interface is used to deal with SR-IOV recovery situations on
s-Par guest partitions. The command service partition will send a message to a
guest when an SR-IOV device that guest is using needs to be temporarily removed.
The message triggers a udev event that will cause a recovery script to run.
When that script has completed its work, it will write to one of the
parahotplug interfaces to send a message back to Command, indicating that the
recovery action has completed.
When a guest that is sharing an SR-IOV device is restarted, that guest will
take down the PF driver on the device, but any guests with VFs will not know
that their device needs to be reset as well. The recovery script makes it so
the device will be shut down fully and then restarted after the sharing guest
comes back up, and ensures that the timing is correct.
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-29 15:11:21 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
parahotplug_request_complete(id, 1);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:36:13 +00:00
|
|
|
static int
|
|
|
|
visorchipset_mmap(struct file *file, struct vm_area_struct *vma)
|
|
|
|
{
|
|
|
|
unsigned long physaddr = 0;
|
|
|
|
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
2015-05-05 22:36:35 +00:00
|
|
|
u64 addr = 0;
|
2015-05-05 22:36:13 +00:00
|
|
|
|
|
|
|
/* sv_enable_dfp(); */
|
|
|
|
if (offset & (PAGE_SIZE - 1))
|
|
|
|
return -ENXIO; /* need aligned offsets */
|
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case VISORCHIPSET_MMAP_CONTROLCHANOFFSET:
|
|
|
|
vma->vm_flags |= VM_IO;
|
|
|
|
if (!*file_controlvm_channel)
|
|
|
|
return -ENXIO;
|
|
|
|
|
2016-04-05 03:31:12 +00:00
|
|
|
visorchannel_read
|
|
|
|
(*file_controlvm_channel,
|
|
|
|
offsetof(struct spar_controlvm_channel_protocol,
|
|
|
|
gp_control_channel),
|
|
|
|
&addr, sizeof(addr));
|
2015-05-05 22:36:13 +00:00
|
|
|
if (!addr)
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
physaddr = (unsigned long)addr;
|
|
|
|
if (remap_pfn_range(vma, vma->vm_start,
|
|
|
|
physaddr >> PAGE_SHIFT,
|
|
|
|
vma->vm_end - vma->vm_start,
|
|
|
|
/*pgprot_noncached */
|
|
|
|
(vma->vm_page_prot))) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-13 17:22:10 +00:00
|
|
|
static inline s64 issue_vmcall_query_guest_virtual_time_offset(void)
|
|
|
|
{
|
|
|
|
u64 result = VMCALL_SUCCESS;
|
|
|
|
u64 physaddr = 0;
|
|
|
|
|
|
|
|
ISSUE_IO_VMCALL(VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET, physaddr,
|
|
|
|
result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int issue_vmcall_update_physical_time(u64 adjustment)
|
|
|
|
{
|
|
|
|
int result = VMCALL_SUCCESS;
|
|
|
|
|
|
|
|
ISSUE_IO_VMCALL(VMCALL_UPDATE_PHYSICAL_TIME, adjustment, result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:36:13 +00:00
|
|
|
static long visorchipset_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
2016-03-03 18:00:38 +00:00
|
|
|
u64 adjustment;
|
2015-05-05 22:36:13 +00:00
|
|
|
s64 vrtc_offset;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET:
|
|
|
|
/* get the physical rtc offset */
|
|
|
|
vrtc_offset = issue_vmcall_query_guest_virtual_time_offset();
|
|
|
|
if (copy_to_user((void __user *)arg, &vrtc_offset,
|
|
|
|
sizeof(vrtc_offset))) {
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
2015-05-05 22:37:04 +00:00
|
|
|
return 0;
|
2015-05-05 22:36:13 +00:00
|
|
|
case VMCALL_UPDATE_PHYSICAL_TIME:
|
|
|
|
if (copy_from_user(&adjustment, (void __user *)arg,
|
|
|
|
sizeof(adjustment))) {
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return issue_vmcall_update_physical_time(adjustment);
|
|
|
|
default:
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations visorchipset_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = visorchipset_open,
|
|
|
|
.read = NULL,
|
|
|
|
.write = NULL,
|
|
|
|
.unlocked_ioctl = visorchipset_ioctl,
|
|
|
|
.release = visorchipset_release,
|
|
|
|
.mmap = visorchipset_mmap,
|
|
|
|
};
|
|
|
|
|
2015-05-13 17:21:55 +00:00
|
|
|
static int
|
2015-05-05 22:36:13 +00:00
|
|
|
visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
file_controlvm_channel = controlvm_channel;
|
|
|
|
cdev_init(&file_cdev, &visorchipset_fops);
|
|
|
|
file_cdev.owner = THIS_MODULE;
|
|
|
|
if (MAJOR(major_dev) == 0) {
|
2015-05-05 22:36:14 +00:00
|
|
|
rc = alloc_chrdev_region(&major_dev, 0, 1, "visorchipset");
|
2015-05-05 22:36:13 +00:00
|
|
|
/* dynamic major device number registration required */
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
} else {
|
|
|
|
/* static major device number registration required */
|
2015-05-05 22:36:14 +00:00
|
|
|
rc = register_chrdev_region(major_dev, 1, "visorchipset");
|
2015-05-05 22:36:13 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1);
|
|
|
|
if (rc < 0) {
|
|
|
|
unregister_chrdev_region(major_dev, 1);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-05 03:31:37 +00:00
|
|
|
static void
|
|
|
|
visorchipset_file_cleanup(dev_t major_dev)
|
|
|
|
{
|
|
|
|
if (file_cdev.ops)
|
|
|
|
cdev_del(&file_cdev);
|
|
|
|
file_cdev.ops = NULL;
|
|
|
|
unregister_chrdev_region(major_dev, 1);
|
|
|
|
}
|
|
|
|
|
2016-09-19 21:09:21 +00:00
|
|
|
/**
|
|
|
|
* handle_command() - process a controlvm message
|
|
|
|
* @inmsg: the message to process
|
|
|
|
* @channel_addr: address of the controlvm channel
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* false - this function will return false only in the case where the
|
|
|
|
* controlvm message was NOT processed, but processing must be
|
|
|
|
* retried before reading the next controlvm message; a
|
|
|
|
* scenario where this can occur is when we need to throttle
|
|
|
|
* the allocation of memory in which to copy out controlvm
|
|
|
|
* payload data
|
|
|
|
* true - processing of the controlvm message completed,
|
|
|
|
* either successfully or with an error
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
handle_command(struct controlvm_message inmsg, u64 channel_addr)
|
|
|
|
{
|
|
|
|
struct controlvm_message_packet *cmd = &inmsg.cmd;
|
|
|
|
u64 parm_addr;
|
|
|
|
u32 parm_bytes;
|
|
|
|
struct parser_context *parser_ctx = NULL;
|
|
|
|
bool local_addr;
|
|
|
|
struct controlvm_message ackmsg;
|
|
|
|
|
|
|
|
/* create parsing context if necessary */
|
|
|
|
local_addr = (inmsg.hdr.flags.test_message == 1);
|
|
|
|
if (channel_addr == 0)
|
|
|
|
return true;
|
|
|
|
parm_addr = channel_addr + inmsg.hdr.payload_vm_offset;
|
|
|
|
parm_bytes = inmsg.hdr.payload_bytes;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parameter and channel addresses within test messages actually lie
|
|
|
|
* within our OS-controlled memory. We need to know that, because it
|
|
|
|
* makes a difference in how we compute the virtual address.
|
|
|
|
*/
|
|
|
|
if (parm_addr && parm_bytes) {
|
|
|
|
bool retry = false;
|
|
|
|
|
|
|
|
parser_ctx =
|
|
|
|
parser_init_byte_stream(parm_addr, parm_bytes,
|
|
|
|
local_addr, &retry);
|
|
|
|
if (!parser_ctx && retry)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!local_addr) {
|
|
|
|
controlvm_init_response(&ackmsg, &inmsg.hdr,
|
|
|
|
CONTROLVM_RESP_SUCCESS);
|
|
|
|
if (controlvm_channel)
|
|
|
|
visorchannel_signalinsert(controlvm_channel,
|
|
|
|
CONTROLVM_QUEUE_ACK,
|
|
|
|
&ackmsg);
|
|
|
|
}
|
|
|
|
switch (inmsg.hdr.id) {
|
|
|
|
case CONTROLVM_CHIPSET_INIT:
|
|
|
|
chipset_init(&inmsg);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_BUS_CREATE:
|
|
|
|
bus_create(&inmsg);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_BUS_DESTROY:
|
|
|
|
bus_destroy(&inmsg);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_BUS_CONFIGURE:
|
|
|
|
bus_configure(&inmsg, parser_ctx);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_DEVICE_CREATE:
|
|
|
|
my_device_create(&inmsg);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_DEVICE_CHANGESTATE:
|
|
|
|
if (cmd->device_change_state.flags.phys_device) {
|
|
|
|
parahotplug_process_message(&inmsg);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* save the hdr and cmd structures for later use
|
|
|
|
* when sending back the response to Command
|
|
|
|
*/
|
|
|
|
my_device_changestate(&inmsg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CONTROLVM_DEVICE_DESTROY:
|
|
|
|
my_device_destroy(&inmsg);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_DEVICE_CONFIGURE:
|
|
|
|
/* no op for now, just send a respond that we passed */
|
|
|
|
if (inmsg.hdr.flags.response_expected)
|
|
|
|
controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_CHIPSET_READY:
|
|
|
|
chipset_ready(&inmsg.hdr);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_CHIPSET_SELFTEST:
|
|
|
|
chipset_selftest(&inmsg.hdr);
|
|
|
|
break;
|
|
|
|
case CONTROLVM_CHIPSET_STOP:
|
|
|
|
chipset_notready(&inmsg.hdr);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (inmsg.hdr.flags.response_expected)
|
|
|
|
controlvm_respond
|
|
|
|
(&inmsg.hdr,
|
|
|
|
-CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parser_ctx) {
|
|
|
|
parser_done(parser_ctx);
|
|
|
|
parser_ctx = NULL;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-19 21:09:23 +00:00
|
|
|
/**
|
|
|
|
* read_controlvm_event() - retreives the next message from the
|
|
|
|
* CONTROLVM_QUEUE_EVENT queue in the controlvm
|
|
|
|
* channel
|
|
|
|
* @msg: pointer to the retrieved message
|
|
|
|
*
|
|
|
|
* Return: true if a valid message was retrieved or false otherwise
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
read_controlvm_event(struct controlvm_message *msg)
|
|
|
|
{
|
|
|
|
if (visorchannel_signalremove(controlvm_channel,
|
|
|
|
CONTROLVM_QUEUE_EVENT, msg)) {
|
|
|
|
/* got a message */
|
|
|
|
if (msg->hdr.flags.test_message == 1)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-19 21:09:24 +00:00
|
|
|
/**
|
|
|
|
* parahotplug_process_list() - remove any request from the list that's been on
|
|
|
|
* there too long and respond with an error
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
parahotplug_process_list(void)
|
|
|
|
{
|
|
|
|
struct list_head *pos;
|
|
|
|
struct list_head *tmp;
|
|
|
|
|
|
|
|
spin_lock(¶hotplug_request_list_lock);
|
|
|
|
|
|
|
|
list_for_each_safe(pos, tmp, ¶hotplug_request_list) {
|
|
|
|
struct parahotplug_request *req =
|
|
|
|
list_entry(pos, struct parahotplug_request, list);
|
|
|
|
|
|
|
|
if (!time_after_eq(jiffies, req->expiration))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_del(pos);
|
|
|
|
if (req->msg.hdr.flags.response_expected)
|
|
|
|
controlvm_respond_physdev_changestate(
|
|
|
|
&req->msg.hdr,
|
|
|
|
CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT,
|
|
|
|
req->msg.cmd.device_change_state.state);
|
|
|
|
parahotplug_request_destroy(req);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock(¶hotplug_request_list_lock);
|
|
|
|
}
|
|
|
|
|
2016-09-19 21:09:20 +00:00
|
|
|
static void
|
|
|
|
controlvm_periodic_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct controlvm_message inmsg;
|
|
|
|
bool got_command = false;
|
|
|
|
bool handle_command_failed = false;
|
|
|
|
|
|
|
|
while (visorchannel_signalremove(controlvm_channel,
|
|
|
|
CONTROLVM_QUEUE_RESPONSE,
|
|
|
|
&inmsg))
|
|
|
|
;
|
|
|
|
if (!got_command) {
|
|
|
|
if (controlvm_pending_msg_valid) {
|
|
|
|
/*
|
|
|
|
* we throttled processing of a prior
|
|
|
|
* msg, so try to process it again
|
|
|
|
* rather than reading a new one
|
|
|
|
*/
|
|
|
|
inmsg = controlvm_pending_msg;
|
|
|
|
controlvm_pending_msg_valid = false;
|
|
|
|
got_command = true;
|
|
|
|
} else {
|
|
|
|
got_command = read_controlvm_event(&inmsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handle_command_failed = false;
|
|
|
|
while (got_command && (!handle_command_failed)) {
|
|
|
|
most_recent_message_jiffies = jiffies;
|
|
|
|
if (handle_command(inmsg,
|
|
|
|
visorchannel_get_physaddr
|
|
|
|
(controlvm_channel)))
|
|
|
|
got_command = read_controlvm_event(&inmsg);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* this is a scenario where throttling
|
|
|
|
* is required, but probably NOT an
|
|
|
|
* error...; we stash the current
|
|
|
|
* controlvm msg so we will attempt to
|
|
|
|
* reprocess it on our next loop
|
|
|
|
*/
|
|
|
|
handle_command_failed = true;
|
|
|
|
controlvm_pending_msg = inmsg;
|
|
|
|
controlvm_pending_msg_valid = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parahotplug_worker */
|
|
|
|
parahotplug_process_list();
|
|
|
|
|
|
|
|
if (time_after(jiffies,
|
|
|
|
most_recent_message_jiffies + (HZ * MIN_IDLE_SECONDS))) {
|
|
|
|
/*
|
|
|
|
* it's been longer than MIN_IDLE_SECONDS since we
|
|
|
|
* processed our last controlvm message; slow down the
|
|
|
|
* polling
|
|
|
|
*/
|
|
|
|
if (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_SLOW)
|
|
|
|
poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW;
|
|
|
|
} else {
|
|
|
|
if (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_FAST)
|
|
|
|
poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST;
|
|
|
|
}
|
|
|
|
|
|
|
|
schedule_delayed_work(&periodic_controlvm_work, poll_jiffies);
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:02 +00:00
|
|
|
static int
|
|
|
|
visorchipset_init(struct acpi_device *acpi_device)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
2016-04-05 03:31:37 +00:00
|
|
|
int err = -ENODEV;
|
2015-05-05 22:37:04 +00:00
|
|
|
u64 addr;
|
2015-05-13 17:21:57 +00:00
|
|
|
uuid_le uuid = SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID;
|
|
|
|
|
|
|
|
addr = controlvm_get_channel_address();
|
|
|
|
if (!addr)
|
2016-04-05 03:31:37 +00:00
|
|
|
goto error;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:07 +00:00
|
|
|
memset(&controlvm_payload_info, 0, sizeof(controlvm_payload_info));
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-03-02 00:45:03 +00:00
|
|
|
controlvm_channel = visorchannel_create_with_lock(addr, 0,
|
2015-05-13 17:21:57 +00:00
|
|
|
GFP_KERNEL, uuid);
|
2016-03-02 00:45:03 +00:00
|
|
|
if (!controlvm_channel)
|
2016-04-05 03:31:37 +00:00
|
|
|
goto error;
|
|
|
|
|
2015-05-13 17:21:57 +00:00
|
|
|
if (SPAR_CONTROLVM_CHANNEL_OK_CLIENT(
|
|
|
|
visorchannel_get_header(controlvm_channel))) {
|
|
|
|
initialize_controlvm_payload();
|
2014-07-17 16:39:58 +00:00
|
|
|
} else {
|
2016-04-05 03:31:37 +00:00
|
|
|
goto error_destroy_channel;
|
2014-07-17 16:39:58 +00:00
|
|
|
}
|
|
|
|
|
2015-03-16 17:58:44 +00:00
|
|
|
major_dev = MKDEV(visorchipset_major, 0);
|
2016-04-05 03:31:37 +00:00
|
|
|
err = visorchipset_file_init(major_dev, &controlvm_channel);
|
|
|
|
if (err < 0)
|
|
|
|
goto error_destroy_payload;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-05-05 22:36:39 +00:00
|
|
|
/* if booting in a crash kernel */
|
|
|
|
if (is_kdump_kernel())
|
|
|
|
INIT_DELAYED_WORK(&periodic_controlvm_work,
|
|
|
|
setup_crash_devices_work_queue);
|
|
|
|
else
|
|
|
|
INIT_DELAYED_WORK(&periodic_controlvm_work,
|
|
|
|
controlvm_periodic_work);
|
|
|
|
|
|
|
|
most_recent_message_jiffies = jiffies;
|
|
|
|
poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST;
|
2016-02-28 12:44:16 +00:00
|
|
|
schedule_delayed_work(&periodic_controlvm_work, poll_jiffies);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2015-03-16 17:58:45 +00:00
|
|
|
visorchipset_platform_device.dev.devt = major_dev;
|
|
|
|
if (platform_device_register(&visorchipset_platform_device) < 0) {
|
2014-03-19 18:06:20 +00:00
|
|
|
POSTCODE_LINUX_2(DEVICE_REGISTER_FAILURE_PC, DIAG_SEVERITY_ERR);
|
2016-04-05 03:31:37 +00:00
|
|
|
err = -ENODEV;
|
|
|
|
goto error_cancel_work;
|
2014-03-19 18:06:20 +00:00
|
|
|
}
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(CHIPSET_INIT_SUCCESS_PC, POSTCODE_SEVERITY_INFO);
|
2015-05-05 22:36:15 +00:00
|
|
|
|
2016-04-05 03:31:37 +00:00
|
|
|
err = visorbus_init();
|
|
|
|
if (err < 0)
|
|
|
|
goto error_unregister;
|
2014-03-04 13:58:07 +00:00
|
|
|
|
2016-04-05 03:31:37 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
error_unregister:
|
|
|
|
platform_device_unregister(&visorchipset_platform_device);
|
|
|
|
|
|
|
|
error_cancel_work:
|
|
|
|
cancel_delayed_work_sync(&periodic_controlvm_work);
|
|
|
|
visorchipset_file_cleanup(major_dev);
|
|
|
|
|
|
|
|
error_destroy_payload:
|
|
|
|
destroy_controlvm_payload_info(&controlvm_payload_info);
|
|
|
|
|
|
|
|
error_destroy_channel:
|
|
|
|
visorchannel_destroy(controlvm_channel);
|
|
|
|
|
|
|
|
error:
|
|
|
|
POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, err, POSTCODE_SEVERITY_ERR);
|
|
|
|
return err;
|
2015-05-05 22:36:13 +00:00
|
|
|
}
|
|
|
|
|
2015-05-05 22:37:02 +00:00
|
|
|
static int
|
|
|
|
visorchipset_exit(struct acpi_device *acpi_device)
|
2014-03-04 13:58:07 +00:00
|
|
|
{
|
|
|
|
POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
|
2015-05-05 22:36:15 +00:00
|
|
|
visorbus_exit();
|
|
|
|
|
2016-02-28 12:44:16 +00:00
|
|
|
cancel_delayed_work_sync(&periodic_controlvm_work);
|
2015-05-05 22:36:39 +00:00
|
|
|
destroy_controlvm_payload_info(&controlvm_payload_info);
|
2014-07-15 17:30:41 +00:00
|
|
|
|
2015-03-16 17:58:05 +00:00
|
|
|
visorchannel_destroy(controlvm_channel);
|
2014-07-17 16:39:58 +00:00
|
|
|
|
2015-03-24 15:17:28 +00:00
|
|
|
visorchipset_file_cleanup(visorchipset_platform_device.dev.devt);
|
2015-06-04 13:22:48 +00:00
|
|
|
platform_device_unregister(&visorchipset_platform_device);
|
2014-03-04 13:58:07 +00:00
|
|
|
POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO);
|
2015-05-05 22:37:02 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct acpi_device_id unisys_device_ids[] = {
|
|
|
|
{"PNP0A07", 0},
|
|
|
|
{"", 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct acpi_driver unisys_acpi_driver = {
|
|
|
|
.name = "unisys_acpi",
|
|
|
|
.class = "unisys_acpi_class",
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.ids = unisys_device_ids,
|
|
|
|
.ops = {
|
|
|
|
.add = visorchipset_init,
|
|
|
|
.remove = visorchipset_exit,
|
|
|
|
},
|
|
|
|
};
|
2015-07-09 17:27:53 +00:00
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(acpi, unisys_device_ids);
|
|
|
|
|
2015-05-05 22:37:04 +00:00
|
|
|
static __init uint32_t visorutil_spar_detect(void)
|
|
|
|
{
|
|
|
|
unsigned int eax, ebx, ecx, edx;
|
|
|
|
|
2016-03-29 15:41:55 +00:00
|
|
|
if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) {
|
2015-05-05 22:37:04 +00:00
|
|
|
/* check the ID */
|
|
|
|
cpuid(UNISYS_SPAR_LEAF_ID, &eax, &ebx, &ecx, &edx);
|
|
|
|
return (ebx == UNISYS_SPAR_ID_EBX) &&
|
|
|
|
(ecx == UNISYS_SPAR_ID_ECX) &&
|
|
|
|
(edx == UNISYS_SPAR_ID_EDX);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2015-05-05 22:37:02 +00:00
|
|
|
|
|
|
|
static int init_unisys(void)
|
|
|
|
{
|
|
|
|
int result;
|
2015-06-13 15:40:49 +00:00
|
|
|
|
2015-05-05 22:37:04 +00:00
|
|
|
if (!visorutil_spar_detect())
|
2015-05-05 22:37:02 +00:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
result = acpi_bus_register_driver(&unisys_acpi_driver);
|
|
|
|
if (result)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
pr_info("Unisys Visorchipset Driver Loaded.\n");
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void exit_unisys(void)
|
|
|
|
{
|
|
|
|
acpi_bus_unregister_driver(&unisys_acpi_driver);
|
2014-03-04 13:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module_param_named(major, visorchipset_major, int, S_IRUGO);
|
2015-05-05 22:35:38 +00:00
|
|
|
MODULE_PARM_DESC(visorchipset_major,
|
|
|
|
"major device number to use for the device node");
|
|
|
|
|
2015-05-05 22:37:02 +00:00
|
|
|
module_init(init_unisys);
|
|
|
|
module_exit(exit_unisys);
|
2014-03-04 13:58:07 +00:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Unisys");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("Supervisor chipset driver for service partition: ver "
|
|
|
|
VERSION);
|
|
|
|
MODULE_VERSION(VERSION);
|