18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2010 - 2015 UNISYS CORPORATION
48c2ecf20Sopenharmony_ci * All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/acpi.h>
88c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
98c2ecf20Sopenharmony_ci#include <linux/visorbus.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "visorbus_private.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* {72120008-4AAB-11DC-8530-444553544200} */
148c2ecf20Sopenharmony_ci#define VISOR_SIOVM_GUID GUID_INIT(0x72120008, 0x4AAB, 0x11DC, 0x85, 0x30, \
158c2ecf20Sopenharmony_ci				   0x44, 0x45, 0x53, 0x54, 0x42, 0x00)
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic const guid_t visor_vhba_channel_guid = VISOR_VHBA_CHANNEL_GUID;
188c2ecf20Sopenharmony_cistatic const guid_t visor_siovm_guid = VISOR_SIOVM_GUID;
198c2ecf20Sopenharmony_cistatic const guid_t visor_controlvm_channel_guid = VISOR_CONTROLVM_CHANNEL_GUID;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define POLLJIFFIES_CONTROLVM_FAST 1
228c2ecf20Sopenharmony_ci#define POLLJIFFIES_CONTROLVM_SLOW 100
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define MAX_CONTROLVM_PAYLOAD_BYTES (1024 * 128)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define UNISYS_VISOR_LEAF_ID 0x40000000
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */
298c2ecf20Sopenharmony_ci#define UNISYS_VISOR_ID_EBX 0x73696e55
308c2ecf20Sopenharmony_ci#define UNISYS_VISOR_ID_ECX 0x70537379
318c2ecf20Sopenharmony_ci#define UNISYS_VISOR_ID_EDX 0x34367261
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * When the controlvm channel is idle for at least MIN_IDLE_SECONDS, we switch
358c2ecf20Sopenharmony_ci * to slow polling mode. As soon as we get a controlvm message, we switch back
368c2ecf20Sopenharmony_ci * to fast polling mode.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#define MIN_IDLE_SECONDS 10
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct parser_context {
418c2ecf20Sopenharmony_ci	unsigned long allocbytes;
428c2ecf20Sopenharmony_ci	unsigned long param_bytes;
438c2ecf20Sopenharmony_ci	u8 *curr;
448c2ecf20Sopenharmony_ci	unsigned long bytes_remaining;
458c2ecf20Sopenharmony_ci	bool byte_stream;
468c2ecf20Sopenharmony_ci	struct visor_controlvm_parameters_header data;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* VMCALL_CONTROLVM_ADDR: Used by all guests, not just IO. */
508c2ecf20Sopenharmony_ci#define VMCALL_CONTROLVM_ADDR 0x0501
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cienum vmcall_result {
538c2ecf20Sopenharmony_ci	VMCALL_RESULT_SUCCESS = 0,
548c2ecf20Sopenharmony_ci	VMCALL_RESULT_INVALID_PARAM = 1,
558c2ecf20Sopenharmony_ci	VMCALL_RESULT_DATA_UNAVAILABLE = 2,
568c2ecf20Sopenharmony_ci	VMCALL_RESULT_FAILURE_UNAVAILABLE = 3,
578c2ecf20Sopenharmony_ci	VMCALL_RESULT_DEVICE_ERROR = 4,
588c2ecf20Sopenharmony_ci	VMCALL_RESULT_DEVICE_NOT_READY = 5
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * struct vmcall_io_controlvm_addr_params - Structure for IO VMCALLS. Has
638c2ecf20Sopenharmony_ci *					    parameters to VMCALL_CONTROLVM_ADDR
648c2ecf20Sopenharmony_ci *					    interface.
658c2ecf20Sopenharmony_ci * @address:	   The Guest-relative physical address of the ControlVm channel.
668c2ecf20Sopenharmony_ci *		   This VMCall fills this in with the appropriate address.
678c2ecf20Sopenharmony_ci *		   Contents provided by this VMCALL (OUT).
688c2ecf20Sopenharmony_ci * @channel_bytes: The size of the ControlVm channel in bytes This VMCall fills
698c2ecf20Sopenharmony_ci *		   this in with the appropriate address. Contents provided by
708c2ecf20Sopenharmony_ci *		   this VMCALL (OUT).
718c2ecf20Sopenharmony_ci * @unused:	   Unused Bytes in the 64-Bit Aligned Struct.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistruct vmcall_io_controlvm_addr_params {
748c2ecf20Sopenharmony_ci	u64 address;
758c2ecf20Sopenharmony_ci	u32 channel_bytes;
768c2ecf20Sopenharmony_ci	u8 unused[4];
778c2ecf20Sopenharmony_ci} __packed;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct visorchipset_device {
808c2ecf20Sopenharmony_ci	struct acpi_device *acpi_device;
818c2ecf20Sopenharmony_ci	unsigned long poll_jiffies;
828c2ecf20Sopenharmony_ci	/* when we got our last controlvm message */
838c2ecf20Sopenharmony_ci	unsigned long most_recent_message_jiffies;
848c2ecf20Sopenharmony_ci	struct delayed_work periodic_controlvm_work;
858c2ecf20Sopenharmony_ci	struct visorchannel *controlvm_channel;
868c2ecf20Sopenharmony_ci	unsigned long controlvm_payload_bytes_buffered;
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * The following variables are used to handle the scenario where we are
898c2ecf20Sopenharmony_ci	 * unable to offload the payload from a controlvm message due to memory
908c2ecf20Sopenharmony_ci	 * requirements. In this scenario, we simply stash the controlvm
918c2ecf20Sopenharmony_ci	 * message, then attempt to process it again the next time
928c2ecf20Sopenharmony_ci	 * controlvm_periodic_work() runs.
938c2ecf20Sopenharmony_ci	 */
948c2ecf20Sopenharmony_ci	struct controlvm_message controlvm_pending_msg;
958c2ecf20Sopenharmony_ci	bool controlvm_pending_msg_valid;
968c2ecf20Sopenharmony_ci	struct vmcall_io_controlvm_addr_params controlvm_params;
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic struct visorchipset_device *chipset_dev;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistruct parahotplug_request {
1028c2ecf20Sopenharmony_ci	struct list_head list;
1038c2ecf20Sopenharmony_ci	int id;
1048c2ecf20Sopenharmony_ci	unsigned long expiration;
1058c2ecf20Sopenharmony_ci	struct controlvm_message msg;
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* prototypes for attributes */
1098c2ecf20Sopenharmony_cistatic ssize_t toolaction_show(struct device *dev,
1108c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
1118c2ecf20Sopenharmony_ci			       char *buf)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	u8 tool_action = 0;
1148c2ecf20Sopenharmony_ci	int err;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
1178c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
1188c2ecf20Sopenharmony_ci					 tool_action),
1198c2ecf20Sopenharmony_ci				&tool_action, sizeof(u8));
1208c2ecf20Sopenharmony_ci	if (err)
1218c2ecf20Sopenharmony_ci		return err;
1228c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", tool_action);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic ssize_t toolaction_store(struct device *dev,
1268c2ecf20Sopenharmony_ci				struct device_attribute *attr,
1278c2ecf20Sopenharmony_ci				const char *buf, size_t count)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	u8 tool_action;
1308c2ecf20Sopenharmony_ci	int err;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (kstrtou8(buf, 10, &tool_action))
1338c2ecf20Sopenharmony_ci		return -EINVAL;
1348c2ecf20Sopenharmony_ci	err = visorchannel_write(chipset_dev->controlvm_channel,
1358c2ecf20Sopenharmony_ci				 offsetof(struct visor_controlvm_channel,
1368c2ecf20Sopenharmony_ci					  tool_action),
1378c2ecf20Sopenharmony_ci				 &tool_action, sizeof(u8));
1388c2ecf20Sopenharmony_ci	if (err)
1398c2ecf20Sopenharmony_ci		return err;
1408c2ecf20Sopenharmony_ci	return count;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(toolaction);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic ssize_t boottotool_show(struct device *dev,
1458c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
1468c2ecf20Sopenharmony_ci			       char *buf)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct efi_visor_indication efi_visor_indication;
1498c2ecf20Sopenharmony_ci	int err;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
1528c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
1538c2ecf20Sopenharmony_ci					 efi_visor_ind),
1548c2ecf20Sopenharmony_ci				&efi_visor_indication,
1558c2ecf20Sopenharmony_ci				sizeof(struct efi_visor_indication));
1568c2ecf20Sopenharmony_ci	if (err)
1578c2ecf20Sopenharmony_ci		return err;
1588c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", efi_visor_indication.boot_to_tool);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic ssize_t boottotool_store(struct device *dev,
1628c2ecf20Sopenharmony_ci				struct device_attribute *attr,
1638c2ecf20Sopenharmony_ci				const char *buf, size_t count)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	int val, err;
1668c2ecf20Sopenharmony_ci	struct efi_visor_indication efi_visor_indication;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (kstrtoint(buf, 10, &val))
1698c2ecf20Sopenharmony_ci		return -EINVAL;
1708c2ecf20Sopenharmony_ci	efi_visor_indication.boot_to_tool = val;
1718c2ecf20Sopenharmony_ci	err = visorchannel_write(chipset_dev->controlvm_channel,
1728c2ecf20Sopenharmony_ci				 offsetof(struct visor_controlvm_channel,
1738c2ecf20Sopenharmony_ci					  efi_visor_ind),
1748c2ecf20Sopenharmony_ci				 &(efi_visor_indication),
1758c2ecf20Sopenharmony_ci				 sizeof(struct efi_visor_indication));
1768c2ecf20Sopenharmony_ci	if (err)
1778c2ecf20Sopenharmony_ci		return err;
1788c2ecf20Sopenharmony_ci	return count;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(boottotool);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic ssize_t error_show(struct device *dev, struct device_attribute *attr,
1838c2ecf20Sopenharmony_ci			  char *buf)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	u32 error = 0;
1868c2ecf20Sopenharmony_ci	int err;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
1898c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
1908c2ecf20Sopenharmony_ci					 installation_error),
1918c2ecf20Sopenharmony_ci				&error, sizeof(u32));
1928c2ecf20Sopenharmony_ci	if (err)
1938c2ecf20Sopenharmony_ci		return err;
1948c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", error);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic ssize_t error_store(struct device *dev, struct device_attribute *attr,
1988c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	u32 error;
2018c2ecf20Sopenharmony_ci	int err;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (kstrtou32(buf, 10, &error))
2048c2ecf20Sopenharmony_ci		return -EINVAL;
2058c2ecf20Sopenharmony_ci	err = visorchannel_write(chipset_dev->controlvm_channel,
2068c2ecf20Sopenharmony_ci				 offsetof(struct visor_controlvm_channel,
2078c2ecf20Sopenharmony_ci					  installation_error),
2088c2ecf20Sopenharmony_ci				 &error, sizeof(u32));
2098c2ecf20Sopenharmony_ci	if (err)
2108c2ecf20Sopenharmony_ci		return err;
2118c2ecf20Sopenharmony_ci	return count;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(error);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic ssize_t textid_show(struct device *dev, struct device_attribute *attr,
2168c2ecf20Sopenharmony_ci			   char *buf)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	u32 text_id = 0;
2198c2ecf20Sopenharmony_ci	int err;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
2228c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
2238c2ecf20Sopenharmony_ci					 installation_text_id),
2248c2ecf20Sopenharmony_ci				&text_id, sizeof(u32));
2258c2ecf20Sopenharmony_ci	if (err)
2268c2ecf20Sopenharmony_ci		return err;
2278c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", text_id);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic ssize_t textid_store(struct device *dev, struct device_attribute *attr,
2318c2ecf20Sopenharmony_ci			    const char *buf, size_t count)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	u32 text_id;
2348c2ecf20Sopenharmony_ci	int err;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (kstrtou32(buf, 10, &text_id))
2378c2ecf20Sopenharmony_ci		return -EINVAL;
2388c2ecf20Sopenharmony_ci	err = visorchannel_write(chipset_dev->controlvm_channel,
2398c2ecf20Sopenharmony_ci				 offsetof(struct visor_controlvm_channel,
2408c2ecf20Sopenharmony_ci					  installation_text_id),
2418c2ecf20Sopenharmony_ci				 &text_id, sizeof(u32));
2428c2ecf20Sopenharmony_ci	if (err)
2438c2ecf20Sopenharmony_ci		return err;
2448c2ecf20Sopenharmony_ci	return count;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(textid);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic ssize_t remaining_steps_show(struct device *dev,
2498c2ecf20Sopenharmony_ci				    struct device_attribute *attr, char *buf)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	u16 remaining_steps = 0;
2528c2ecf20Sopenharmony_ci	int err;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
2558c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
2568c2ecf20Sopenharmony_ci					 installation_remaining_steps),
2578c2ecf20Sopenharmony_ci				&remaining_steps, sizeof(u16));
2588c2ecf20Sopenharmony_ci	if (err)
2598c2ecf20Sopenharmony_ci		return err;
2608c2ecf20Sopenharmony_ci	return sprintf(buf, "%hu\n", remaining_steps);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic ssize_t remaining_steps_store(struct device *dev,
2648c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
2658c2ecf20Sopenharmony_ci				     const char *buf, size_t count)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	u16 remaining_steps;
2688c2ecf20Sopenharmony_ci	int err;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (kstrtou16(buf, 10, &remaining_steps))
2718c2ecf20Sopenharmony_ci		return -EINVAL;
2728c2ecf20Sopenharmony_ci	err = visorchannel_write(chipset_dev->controlvm_channel,
2738c2ecf20Sopenharmony_ci				 offsetof(struct visor_controlvm_channel,
2748c2ecf20Sopenharmony_ci					  installation_remaining_steps),
2758c2ecf20Sopenharmony_ci				 &remaining_steps, sizeof(u16));
2768c2ecf20Sopenharmony_ci	if (err)
2778c2ecf20Sopenharmony_ci		return err;
2788c2ecf20Sopenharmony_ci	return count;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(remaining_steps);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void controlvm_init_response(struct controlvm_message *msg,
2838c2ecf20Sopenharmony_ci				    struct controlvm_message_header *msg_hdr,
2848c2ecf20Sopenharmony_ci				    int response)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	memset(msg, 0, sizeof(struct controlvm_message));
2878c2ecf20Sopenharmony_ci	memcpy(&msg->hdr, msg_hdr, sizeof(struct controlvm_message_header));
2888c2ecf20Sopenharmony_ci	msg->hdr.payload_bytes = 0;
2898c2ecf20Sopenharmony_ci	msg->hdr.payload_vm_offset = 0;
2908c2ecf20Sopenharmony_ci	msg->hdr.payload_max_bytes = 0;
2918c2ecf20Sopenharmony_ci	if (response < 0) {
2928c2ecf20Sopenharmony_ci		msg->hdr.flags.failed = 1;
2938c2ecf20Sopenharmony_ci		msg->hdr.completion_status = (u32)(-response);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int controlvm_respond_chipset_init(
2988c2ecf20Sopenharmony_ci				struct controlvm_message_header *msg_hdr,
2998c2ecf20Sopenharmony_ci				int response,
3008c2ecf20Sopenharmony_ci				enum visor_chipset_feature features)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct controlvm_message outmsg;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	controlvm_init_response(&outmsg, msg_hdr, response);
3058c2ecf20Sopenharmony_ci	outmsg.cmd.init_chipset.features = features;
3068c2ecf20Sopenharmony_ci	return visorchannel_signalinsert(chipset_dev->controlvm_channel,
3078c2ecf20Sopenharmony_ci					 CONTROLVM_QUEUE_REQUEST, &outmsg);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int chipset_init(struct controlvm_message *inmsg)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	static int chipset_inited;
3138c2ecf20Sopenharmony_ci	enum visor_chipset_feature features = 0;
3148c2ecf20Sopenharmony_ci	int rc = CONTROLVM_RESP_SUCCESS;
3158c2ecf20Sopenharmony_ci	int res = 0;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (chipset_inited) {
3188c2ecf20Sopenharmony_ci		rc = -CONTROLVM_RESP_ALREADY_DONE;
3198c2ecf20Sopenharmony_ci		res = -EIO;
3208c2ecf20Sopenharmony_ci		goto out_respond;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci	chipset_inited = 1;
3238c2ecf20Sopenharmony_ci	/*
3248c2ecf20Sopenharmony_ci	 * Set features to indicate we support parahotplug (if Command also
3258c2ecf20Sopenharmony_ci	 * supports it). Set the "reply" bit so Command knows this is a
3268c2ecf20Sopenharmony_ci	 * features-aware driver.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	features = inmsg->cmd.init_chipset.features &
3298c2ecf20Sopenharmony_ci		   VISOR_CHIPSET_FEATURE_PARA_HOTPLUG;
3308c2ecf20Sopenharmony_ci	features |= VISOR_CHIPSET_FEATURE_REPLY;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ciout_respond:
3338c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected)
3348c2ecf20Sopenharmony_ci		res = controlvm_respond_chipset_init(&inmsg->hdr, rc, features);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return res;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int controlvm_respond(struct controlvm_message_header *msg_hdr,
3408c2ecf20Sopenharmony_ci			     int response, struct visor_segment_state *state)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct controlvm_message outmsg;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	controlvm_init_response(&outmsg, msg_hdr, response);
3458c2ecf20Sopenharmony_ci	if (outmsg.hdr.flags.test_message == 1)
3468c2ecf20Sopenharmony_ci		return -EINVAL;
3478c2ecf20Sopenharmony_ci	if (state) {
3488c2ecf20Sopenharmony_ci		outmsg.cmd.device_change_state.state = *state;
3498c2ecf20Sopenharmony_ci		outmsg.cmd.device_change_state.flags.phys_device = 1;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	return visorchannel_signalinsert(chipset_dev->controlvm_channel,
3528c2ecf20Sopenharmony_ci					 CONTROLVM_QUEUE_REQUEST, &outmsg);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cienum crash_obj_type {
3568c2ecf20Sopenharmony_ci	CRASH_DEV,
3578c2ecf20Sopenharmony_ci	CRASH_BUS,
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int save_crash_message(struct controlvm_message *msg,
3618c2ecf20Sopenharmony_ci			      enum crash_obj_type cr_type)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	u32 local_crash_msg_offset;
3648c2ecf20Sopenharmony_ci	u16 local_crash_msg_count;
3658c2ecf20Sopenharmony_ci	int err;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
3688c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
3698c2ecf20Sopenharmony_ci					 saved_crash_message_count),
3708c2ecf20Sopenharmony_ci				&local_crash_msg_count, sizeof(u16));
3718c2ecf20Sopenharmony_ci	if (err) {
3728c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
3738c2ecf20Sopenharmony_ci			"failed to read message count\n");
3748c2ecf20Sopenharmony_ci		return err;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci	if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) {
3778c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
3788c2ecf20Sopenharmony_ci			"invalid number of messages\n");
3798c2ecf20Sopenharmony_ci		return -EIO;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci	err = visorchannel_read(chipset_dev->controlvm_channel,
3828c2ecf20Sopenharmony_ci				offsetof(struct visor_controlvm_channel,
3838c2ecf20Sopenharmony_ci					 saved_crash_message_offset),
3848c2ecf20Sopenharmony_ci				&local_crash_msg_offset, sizeof(u32));
3858c2ecf20Sopenharmony_ci	if (err) {
3868c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
3878c2ecf20Sopenharmony_ci			"failed to read offset\n");
3888c2ecf20Sopenharmony_ci		return err;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci	switch (cr_type) {
3918c2ecf20Sopenharmony_ci	case CRASH_DEV:
3928c2ecf20Sopenharmony_ci		local_crash_msg_offset += sizeof(struct controlvm_message);
3938c2ecf20Sopenharmony_ci		err = visorchannel_write(chipset_dev->controlvm_channel,
3948c2ecf20Sopenharmony_ci					 local_crash_msg_offset, msg,
3958c2ecf20Sopenharmony_ci					 sizeof(struct controlvm_message));
3968c2ecf20Sopenharmony_ci		if (err) {
3978c2ecf20Sopenharmony_ci			dev_err(&chipset_dev->acpi_device->dev,
3988c2ecf20Sopenharmony_ci				"failed to write dev msg\n");
3998c2ecf20Sopenharmony_ci			return err;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci		break;
4028c2ecf20Sopenharmony_ci	case CRASH_BUS:
4038c2ecf20Sopenharmony_ci		err = visorchannel_write(chipset_dev->controlvm_channel,
4048c2ecf20Sopenharmony_ci					 local_crash_msg_offset, msg,
4058c2ecf20Sopenharmony_ci					 sizeof(struct controlvm_message));
4068c2ecf20Sopenharmony_ci		if (err) {
4078c2ecf20Sopenharmony_ci			dev_err(&chipset_dev->acpi_device->dev,
4088c2ecf20Sopenharmony_ci				"failed to write bus msg\n");
4098c2ecf20Sopenharmony_ci			return err;
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci		break;
4128c2ecf20Sopenharmony_ci	default:
4138c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
4148c2ecf20Sopenharmony_ci			"Invalid crash_obj_type\n");
4158c2ecf20Sopenharmony_ci		break;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int controlvm_responder(enum controlvm_id cmd_id,
4218c2ecf20Sopenharmony_ci			       struct controlvm_message_header *pending_msg_hdr,
4228c2ecf20Sopenharmony_ci			       int response)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	if (pending_msg_hdr->id != (u32)cmd_id)
4258c2ecf20Sopenharmony_ci		return -EINVAL;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return controlvm_respond(pending_msg_hdr, response, NULL);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int device_changestate_responder(enum controlvm_id cmd_id,
4318c2ecf20Sopenharmony_ci					struct visor_device *p, int response,
4328c2ecf20Sopenharmony_ci					struct visor_segment_state state)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct controlvm_message outmsg;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (p->pending_msg_hdr->id != cmd_id)
4378c2ecf20Sopenharmony_ci		return -EINVAL;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	controlvm_init_response(&outmsg, p->pending_msg_hdr, response);
4408c2ecf20Sopenharmony_ci	outmsg.cmd.device_change_state.bus_no = p->chipset_bus_no;
4418c2ecf20Sopenharmony_ci	outmsg.cmd.device_change_state.dev_no = p->chipset_dev_no;
4428c2ecf20Sopenharmony_ci	outmsg.cmd.device_change_state.state = state;
4438c2ecf20Sopenharmony_ci	return visorchannel_signalinsert(chipset_dev->controlvm_channel,
4448c2ecf20Sopenharmony_ci					 CONTROLVM_QUEUE_REQUEST, &outmsg);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int visorbus_create(struct controlvm_message *inmsg)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &inmsg->cmd;
4508c2ecf20Sopenharmony_ci	struct controlvm_message_header *pmsg_hdr;
4518c2ecf20Sopenharmony_ci	u32 bus_no = cmd->create_bus.bus_no;
4528c2ecf20Sopenharmony_ci	struct visor_device *bus_info;
4538c2ecf20Sopenharmony_ci	struct visorchannel *visorchannel;
4548c2ecf20Sopenharmony_ci	int err;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
4578c2ecf20Sopenharmony_ci	if (bus_info && bus_info->state.created == 1) {
4588c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
4598c2ecf20Sopenharmony_ci			"failed %s: already exists\n", __func__);
4608c2ecf20Sopenharmony_ci		err = -EEXIST;
4618c2ecf20Sopenharmony_ci		goto err_respond;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci	bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL);
4648c2ecf20Sopenharmony_ci	if (!bus_info) {
4658c2ecf20Sopenharmony_ci		err = -ENOMEM;
4668c2ecf20Sopenharmony_ci		goto err_respond;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bus_info->list_all);
4698c2ecf20Sopenharmony_ci	bus_info->chipset_bus_no = bus_no;
4708c2ecf20Sopenharmony_ci	bus_info->chipset_dev_no = BUS_ROOT_DEVICE;
4718c2ecf20Sopenharmony_ci	if (guid_equal(&cmd->create_bus.bus_inst_guid, &visor_siovm_guid)) {
4728c2ecf20Sopenharmony_ci		err = save_crash_message(inmsg, CRASH_BUS);
4738c2ecf20Sopenharmony_ci		if (err)
4748c2ecf20Sopenharmony_ci			goto err_free_bus_info;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1) {
4778c2ecf20Sopenharmony_ci		pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
4788c2ecf20Sopenharmony_ci		if (!pmsg_hdr) {
4798c2ecf20Sopenharmony_ci			err = -ENOMEM;
4808c2ecf20Sopenharmony_ci			goto err_free_bus_info;
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci		memcpy(pmsg_hdr, &inmsg->hdr,
4838c2ecf20Sopenharmony_ci		       sizeof(struct controlvm_message_header));
4848c2ecf20Sopenharmony_ci		bus_info->pending_msg_hdr = pmsg_hdr;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	visorchannel = visorchannel_create(cmd->create_bus.channel_addr,
4878c2ecf20Sopenharmony_ci					   GFP_KERNEL,
4888c2ecf20Sopenharmony_ci					   &cmd->create_bus.bus_data_type_guid,
4898c2ecf20Sopenharmony_ci					   false);
4908c2ecf20Sopenharmony_ci	if (!visorchannel) {
4918c2ecf20Sopenharmony_ci		err = -ENOMEM;
4928c2ecf20Sopenharmony_ci		goto err_free_pending_msg;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	bus_info->visorchannel = visorchannel;
4958c2ecf20Sopenharmony_ci	/* Response will be handled by visorbus_create_instance on success */
4968c2ecf20Sopenharmony_ci	err = visorbus_create_instance(bus_info);
4978c2ecf20Sopenharmony_ci	if (err)
4988c2ecf20Sopenharmony_ci		goto err_destroy_channel;
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cierr_destroy_channel:
5028c2ecf20Sopenharmony_ci	visorchannel_destroy(visorchannel);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cierr_free_pending_msg:
5058c2ecf20Sopenharmony_ci	kfree(bus_info->pending_msg_hdr);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cierr_free_bus_info:
5088c2ecf20Sopenharmony_ci	kfree(bus_info);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cierr_respond:
5118c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
5128c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
5138c2ecf20Sopenharmony_ci	return err;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int visorbus_destroy(struct controlvm_message *inmsg)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct controlvm_message_header *pmsg_hdr;
5198c2ecf20Sopenharmony_ci	u32 bus_no = inmsg->cmd.destroy_bus.bus_no;
5208c2ecf20Sopenharmony_ci	struct visor_device *bus_info;
5218c2ecf20Sopenharmony_ci	int err;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
5248c2ecf20Sopenharmony_ci	if (!bus_info) {
5258c2ecf20Sopenharmony_ci		err = -ENODEV;
5268c2ecf20Sopenharmony_ci		goto err_respond;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci	if (bus_info->state.created == 0) {
5298c2ecf20Sopenharmony_ci		err = -ENOENT;
5308c2ecf20Sopenharmony_ci		goto err_respond;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	if (bus_info->pending_msg_hdr) {
5338c2ecf20Sopenharmony_ci		/* only non-NULL if dev is still waiting on a response */
5348c2ecf20Sopenharmony_ci		err = -EEXIST;
5358c2ecf20Sopenharmony_ci		goto err_respond;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1) {
5388c2ecf20Sopenharmony_ci		pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
5398c2ecf20Sopenharmony_ci		if (!pmsg_hdr) {
5408c2ecf20Sopenharmony_ci			err = -ENOMEM;
5418c2ecf20Sopenharmony_ci			goto err_respond;
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci		memcpy(pmsg_hdr, &inmsg->hdr,
5448c2ecf20Sopenharmony_ci		       sizeof(struct controlvm_message_header));
5458c2ecf20Sopenharmony_ci		bus_info->pending_msg_hdr = pmsg_hdr;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	/* Response will be handled by visorbus_remove_instance */
5488c2ecf20Sopenharmony_ci	visorbus_remove_instance(bus_info);
5498c2ecf20Sopenharmony_ci	return 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cierr_respond:
5528c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
5538c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
5548c2ecf20Sopenharmony_ci	return err;
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic const guid_t *parser_id_get(struct parser_context *ctx)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	return &ctx->data.id;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void *parser_string_get(u8 *pscan, int nscan)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int value_length;
5658c2ecf20Sopenharmony_ci	void *value;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (nscan == 0)
5688c2ecf20Sopenharmony_ci		return NULL;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	value_length = strnlen(pscan, nscan);
5718c2ecf20Sopenharmony_ci	value = kzalloc(value_length + 1, GFP_KERNEL);
5728c2ecf20Sopenharmony_ci	if (!value)
5738c2ecf20Sopenharmony_ci		return NULL;
5748c2ecf20Sopenharmony_ci	if (value_length > 0)
5758c2ecf20Sopenharmony_ci		memcpy(value, pscan, value_length);
5768c2ecf20Sopenharmony_ci	return value;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic void *parser_name_get(struct parser_context *ctx)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct visor_controlvm_parameters_header *phdr;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	phdr = &ctx->data;
5848c2ecf20Sopenharmony_ci	if ((unsigned long)phdr->name_offset +
5858c2ecf20Sopenharmony_ci	    (unsigned long)phdr->name_length > ctx->param_bytes)
5868c2ecf20Sopenharmony_ci		return NULL;
5878c2ecf20Sopenharmony_ci	ctx->curr = (char *)&phdr + phdr->name_offset;
5888c2ecf20Sopenharmony_ci	ctx->bytes_remaining = phdr->name_length;
5898c2ecf20Sopenharmony_ci	return parser_string_get(ctx->curr, phdr->name_length);
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic int visorbus_configure(struct controlvm_message *inmsg,
5938c2ecf20Sopenharmony_ci			      struct parser_context *parser_ctx)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &inmsg->cmd;
5968c2ecf20Sopenharmony_ci	u32 bus_no;
5978c2ecf20Sopenharmony_ci	struct visor_device *bus_info;
5988c2ecf20Sopenharmony_ci	int err = 0;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	bus_no = cmd->configure_bus.bus_no;
6018c2ecf20Sopenharmony_ci	bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
6028c2ecf20Sopenharmony_ci	if (!bus_info) {
6038c2ecf20Sopenharmony_ci		err = -EINVAL;
6048c2ecf20Sopenharmony_ci		goto err_respond;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci	if (bus_info->state.created == 0) {
6078c2ecf20Sopenharmony_ci		err = -EINVAL;
6088c2ecf20Sopenharmony_ci		goto err_respond;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci	if (bus_info->pending_msg_hdr) {
6118c2ecf20Sopenharmony_ci		err = -EIO;
6128c2ecf20Sopenharmony_ci		goto err_respond;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci	err = visorchannel_set_clientpartition(bus_info->visorchannel,
6158c2ecf20Sopenharmony_ci					       cmd->configure_bus.guest_handle);
6168c2ecf20Sopenharmony_ci	if (err)
6178c2ecf20Sopenharmony_ci		goto err_respond;
6188c2ecf20Sopenharmony_ci	if (parser_ctx) {
6198c2ecf20Sopenharmony_ci		const guid_t *partition_guid = parser_id_get(parser_ctx);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		guid_copy(&bus_info->partition_guid, partition_guid);
6228c2ecf20Sopenharmony_ci		bus_info->name = parser_name_get(parser_ctx);
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
6258c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
6268c2ecf20Sopenharmony_ci	return 0;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cierr_respond:
6298c2ecf20Sopenharmony_ci	dev_err(&chipset_dev->acpi_device->dev,
6308c2ecf20Sopenharmony_ci		"%s exited with err: %d\n", __func__, err);
6318c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
6328c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
6338c2ecf20Sopenharmony_ci	return err;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int visorbus_device_create(struct controlvm_message *inmsg)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &inmsg->cmd;
6398c2ecf20Sopenharmony_ci	struct controlvm_message_header *pmsg_hdr;
6408c2ecf20Sopenharmony_ci	u32 bus_no = cmd->create_device.bus_no;
6418c2ecf20Sopenharmony_ci	u32 dev_no = cmd->create_device.dev_no;
6428c2ecf20Sopenharmony_ci	struct visor_device *dev_info;
6438c2ecf20Sopenharmony_ci	struct visor_device *bus_info;
6448c2ecf20Sopenharmony_ci	struct visorchannel *visorchannel;
6458c2ecf20Sopenharmony_ci	int err;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
6488c2ecf20Sopenharmony_ci	if (!bus_info) {
6498c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
6508c2ecf20Sopenharmony_ci			"failed to get bus by id: %d\n", bus_no);
6518c2ecf20Sopenharmony_ci		err = -ENODEV;
6528c2ecf20Sopenharmony_ci		goto err_respond;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci	if (bus_info->state.created == 0) {
6558c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
6568c2ecf20Sopenharmony_ci			"bus not created, id: %d\n", bus_no);
6578c2ecf20Sopenharmony_ci		err = -EINVAL;
6588c2ecf20Sopenharmony_ci		goto err_respond;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci	dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL);
6618c2ecf20Sopenharmony_ci	if (dev_info && dev_info->state.created == 1) {
6628c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
6638c2ecf20Sopenharmony_ci			"failed to get bus by id: %d/%d\n", bus_no, dev_no);
6648c2ecf20Sopenharmony_ci		err = -EEXIST;
6658c2ecf20Sopenharmony_ci		goto err_respond;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
6698c2ecf20Sopenharmony_ci	if (!dev_info) {
6708c2ecf20Sopenharmony_ci		err = -ENOMEM;
6718c2ecf20Sopenharmony_ci		goto err_respond;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci	dev_info->chipset_bus_no = bus_no;
6748c2ecf20Sopenharmony_ci	dev_info->chipset_dev_no = dev_no;
6758c2ecf20Sopenharmony_ci	guid_copy(&dev_info->inst, &cmd->create_device.dev_inst_guid);
6768c2ecf20Sopenharmony_ci	dev_info->device.parent = &bus_info->device;
6778c2ecf20Sopenharmony_ci	visorchannel = visorchannel_create(cmd->create_device.channel_addr,
6788c2ecf20Sopenharmony_ci					   GFP_KERNEL,
6798c2ecf20Sopenharmony_ci					   &cmd->create_device.data_type_guid,
6808c2ecf20Sopenharmony_ci					   true);
6818c2ecf20Sopenharmony_ci	if (!visorchannel) {
6828c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
6838c2ecf20Sopenharmony_ci			"failed to create visorchannel: %d/%d\n",
6848c2ecf20Sopenharmony_ci			bus_no, dev_no);
6858c2ecf20Sopenharmony_ci		err = -ENOMEM;
6868c2ecf20Sopenharmony_ci		goto err_free_dev_info;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci	dev_info->visorchannel = visorchannel;
6898c2ecf20Sopenharmony_ci	guid_copy(&dev_info->channel_type_guid,
6908c2ecf20Sopenharmony_ci		  &cmd->create_device.data_type_guid);
6918c2ecf20Sopenharmony_ci	if (guid_equal(&cmd->create_device.data_type_guid,
6928c2ecf20Sopenharmony_ci		       &visor_vhba_channel_guid)) {
6938c2ecf20Sopenharmony_ci		err = save_crash_message(inmsg, CRASH_DEV);
6948c2ecf20Sopenharmony_ci		if (err)
6958c2ecf20Sopenharmony_ci			goto err_destroy_visorchannel;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1) {
6988c2ecf20Sopenharmony_ci		pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
6998c2ecf20Sopenharmony_ci		if (!pmsg_hdr) {
7008c2ecf20Sopenharmony_ci			err = -ENOMEM;
7018c2ecf20Sopenharmony_ci			goto err_destroy_visorchannel;
7028c2ecf20Sopenharmony_ci		}
7038c2ecf20Sopenharmony_ci		memcpy(pmsg_hdr, &inmsg->hdr,
7048c2ecf20Sopenharmony_ci		       sizeof(struct controlvm_message_header));
7058c2ecf20Sopenharmony_ci		dev_info->pending_msg_hdr = pmsg_hdr;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci	/* create_visor_device will send response */
7088c2ecf20Sopenharmony_ci	err = create_visor_device(dev_info);
7098c2ecf20Sopenharmony_ci	if (err)
7108c2ecf20Sopenharmony_ci		goto err_destroy_visorchannel;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	return 0;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cierr_destroy_visorchannel:
7158c2ecf20Sopenharmony_ci	visorchannel_destroy(visorchannel);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cierr_free_dev_info:
7188c2ecf20Sopenharmony_ci	kfree(dev_info);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cierr_respond:
7218c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
7228c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
7238c2ecf20Sopenharmony_ci	return err;
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic int visorbus_device_changestate(struct controlvm_message *inmsg)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &inmsg->cmd;
7298c2ecf20Sopenharmony_ci	struct controlvm_message_header *pmsg_hdr;
7308c2ecf20Sopenharmony_ci	u32 bus_no = cmd->device_change_state.bus_no;
7318c2ecf20Sopenharmony_ci	u32 dev_no = cmd->device_change_state.dev_no;
7328c2ecf20Sopenharmony_ci	struct visor_segment_state state = cmd->device_change_state.state;
7338c2ecf20Sopenharmony_ci	struct visor_device *dev_info;
7348c2ecf20Sopenharmony_ci	int err = 0;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL);
7378c2ecf20Sopenharmony_ci	if (!dev_info) {
7388c2ecf20Sopenharmony_ci		err = -ENODEV;
7398c2ecf20Sopenharmony_ci		goto err_respond;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	if (dev_info->state.created == 0) {
7428c2ecf20Sopenharmony_ci		err = -EINVAL;
7438c2ecf20Sopenharmony_ci		goto err_respond;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	if (dev_info->pending_msg_hdr) {
7468c2ecf20Sopenharmony_ci		/* only non-NULL if dev is still waiting on a response */
7478c2ecf20Sopenharmony_ci		err = -EIO;
7488c2ecf20Sopenharmony_ci		goto err_respond;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1) {
7528c2ecf20Sopenharmony_ci		pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
7538c2ecf20Sopenharmony_ci		if (!pmsg_hdr) {
7548c2ecf20Sopenharmony_ci			err = -ENOMEM;
7558c2ecf20Sopenharmony_ci			goto err_respond;
7568c2ecf20Sopenharmony_ci		}
7578c2ecf20Sopenharmony_ci		memcpy(pmsg_hdr, &inmsg->hdr,
7588c2ecf20Sopenharmony_ci		       sizeof(struct controlvm_message_header));
7598c2ecf20Sopenharmony_ci		dev_info->pending_msg_hdr = pmsg_hdr;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci	if (state.alive == segment_state_running.alive &&
7628c2ecf20Sopenharmony_ci	    state.operating == segment_state_running.operating)
7638c2ecf20Sopenharmony_ci		/* Response will be sent from visorchipset_device_resume */
7648c2ecf20Sopenharmony_ci		err = visorchipset_device_resume(dev_info);
7658c2ecf20Sopenharmony_ci	/* ServerNotReady / ServerLost / SegmentStateStandby */
7668c2ecf20Sopenharmony_ci	else if (state.alive == segment_state_standby.alive &&
7678c2ecf20Sopenharmony_ci		 state.operating == segment_state_standby.operating)
7688c2ecf20Sopenharmony_ci		/*
7698c2ecf20Sopenharmony_ci		 * technically this is standby case where server is lost.
7708c2ecf20Sopenharmony_ci		 * Response will be sent from visorchipset_device_pause.
7718c2ecf20Sopenharmony_ci		 */
7728c2ecf20Sopenharmony_ci		err = visorchipset_device_pause(dev_info);
7738c2ecf20Sopenharmony_ci	if (err)
7748c2ecf20Sopenharmony_ci		goto err_respond;
7758c2ecf20Sopenharmony_ci	return 0;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cierr_respond:
7788c2ecf20Sopenharmony_ci	dev_err(&chipset_dev->acpi_device->dev, "failed: %d\n", err);
7798c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
7808c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
7818c2ecf20Sopenharmony_ci	return err;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic int visorbus_device_destroy(struct controlvm_message *inmsg)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &inmsg->cmd;
7878c2ecf20Sopenharmony_ci	struct controlvm_message_header *pmsg_hdr;
7888c2ecf20Sopenharmony_ci	u32 bus_no = cmd->destroy_device.bus_no;
7898c2ecf20Sopenharmony_ci	u32 dev_no = cmd->destroy_device.dev_no;
7908c2ecf20Sopenharmony_ci	struct visor_device *dev_info;
7918c2ecf20Sopenharmony_ci	int err;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL);
7948c2ecf20Sopenharmony_ci	if (!dev_info) {
7958c2ecf20Sopenharmony_ci		err = -ENODEV;
7968c2ecf20Sopenharmony_ci		goto err_respond;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci	if (dev_info->state.created == 0) {
7998c2ecf20Sopenharmony_ci		err = -EINVAL;
8008c2ecf20Sopenharmony_ci		goto err_respond;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci	if (dev_info->pending_msg_hdr) {
8038c2ecf20Sopenharmony_ci		/* only non-NULL if dev is still waiting on a response */
8048c2ecf20Sopenharmony_ci		err = -EIO;
8058c2ecf20Sopenharmony_ci		goto err_respond;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1) {
8088c2ecf20Sopenharmony_ci		pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL);
8098c2ecf20Sopenharmony_ci		if (!pmsg_hdr) {
8108c2ecf20Sopenharmony_ci			err = -ENOMEM;
8118c2ecf20Sopenharmony_ci			goto err_respond;
8128c2ecf20Sopenharmony_ci		}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		memcpy(pmsg_hdr, &inmsg->hdr,
8158c2ecf20Sopenharmony_ci		       sizeof(struct controlvm_message_header));
8168c2ecf20Sopenharmony_ci		dev_info->pending_msg_hdr = pmsg_hdr;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci	kfree(dev_info->name);
8198c2ecf20Sopenharmony_ci	remove_visor_device(dev_info);
8208c2ecf20Sopenharmony_ci	return 0;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_cierr_respond:
8238c2ecf20Sopenharmony_ci	if (inmsg->hdr.flags.response_expected == 1)
8248c2ecf20Sopenharmony_ci		controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err);
8258c2ecf20Sopenharmony_ci	return err;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci/*
8298c2ecf20Sopenharmony_ci * The general parahotplug flow works as follows. The visorchipset receives
8308c2ecf20Sopenharmony_ci * a DEVICE_CHANGESTATE message from Command specifying a physical device
8318c2ecf20Sopenharmony_ci * to enable or disable. The CONTROLVM message handler calls
8328c2ecf20Sopenharmony_ci * parahotplug_process_message, which then adds the message to a global list
8338c2ecf20Sopenharmony_ci * and kicks off a udev event which causes a user level script to enable or
8348c2ecf20Sopenharmony_ci * disable the specified device. The udev script then writes to
8358c2ecf20Sopenharmony_ci * /sys/devices/platform/visorchipset/parahotplug, which causes the
8368c2ecf20Sopenharmony_ci * parahotplug store functions to get called, at which point the
8378c2ecf20Sopenharmony_ci * appropriate CONTROLVM message is retrieved from the list and responded to.
8388c2ecf20Sopenharmony_ci */
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci#define PARAHOTPLUG_TIMEOUT_MS 2000
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci/*
8438c2ecf20Sopenharmony_ci * parahotplug_next_id() - generate unique int to match an outstanding
8448c2ecf20Sopenharmony_ci *                         CONTROLVM message with a udev script /sys
8458c2ecf20Sopenharmony_ci *                         response
8468c2ecf20Sopenharmony_ci *
8478c2ecf20Sopenharmony_ci * Return: a unique integer value
8488c2ecf20Sopenharmony_ci */
8498c2ecf20Sopenharmony_cistatic int parahotplug_next_id(void)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	static atomic_t id = ATOMIC_INIT(0);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	return atomic_inc_return(&id);
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci/*
8578c2ecf20Sopenharmony_ci * parahotplug_next_expiration() - returns the time (in jiffies) when a
8588c2ecf20Sopenharmony_ci *                                 CONTROLVM message on the list should expire
8598c2ecf20Sopenharmony_ci *                                 -- PARAHOTPLUG_TIMEOUT_MS in the future
8608c2ecf20Sopenharmony_ci *
8618c2ecf20Sopenharmony_ci * Return: expected expiration time (in jiffies)
8628c2ecf20Sopenharmony_ci */
8638c2ecf20Sopenharmony_cistatic unsigned long parahotplug_next_expiration(void)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci/*
8698c2ecf20Sopenharmony_ci * parahotplug_request_create() - create a parahotplug_request, which is
8708c2ecf20Sopenharmony_ci *                                basically a wrapper for a CONTROLVM_MESSAGE
8718c2ecf20Sopenharmony_ci *                                that we can stick on a list
8728c2ecf20Sopenharmony_ci * @msg: the message to insert in the request
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * Return: the request containing the provided message
8758c2ecf20Sopenharmony_ci */
8768c2ecf20Sopenharmony_cistatic struct parahotplug_request *parahotplug_request_create(
8778c2ecf20Sopenharmony_ci						struct controlvm_message *msg)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	struct parahotplug_request *req;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	req = kmalloc(sizeof(*req), GFP_KERNEL);
8828c2ecf20Sopenharmony_ci	if (!req)
8838c2ecf20Sopenharmony_ci		return NULL;
8848c2ecf20Sopenharmony_ci	req->id = parahotplug_next_id();
8858c2ecf20Sopenharmony_ci	req->expiration = parahotplug_next_expiration();
8868c2ecf20Sopenharmony_ci	req->msg = *msg;
8878c2ecf20Sopenharmony_ci	return req;
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci/*
8918c2ecf20Sopenharmony_ci * parahotplug_request_destroy() - free a parahotplug_request
8928c2ecf20Sopenharmony_ci * @req: the request to deallocate
8938c2ecf20Sopenharmony_ci */
8948c2ecf20Sopenharmony_cistatic void parahotplug_request_destroy(struct parahotplug_request *req)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	kfree(req);
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic LIST_HEAD(parahotplug_request_list);
9008c2ecf20Sopenharmony_ci/* lock for above */
9018c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(parahotplug_request_list_lock);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci/*
9048c2ecf20Sopenharmony_ci * parahotplug_request_complete() - mark request as complete
9058c2ecf20Sopenharmony_ci * @id:     the id of the request
9068c2ecf20Sopenharmony_ci * @active: indicates whether the request is assigned to active partition
9078c2ecf20Sopenharmony_ci *
9088c2ecf20Sopenharmony_ci * Called from the /sys handler, which means the user script has
9098c2ecf20Sopenharmony_ci * finished the enable/disable. Find the matching identifier, and
9108c2ecf20Sopenharmony_ci * respond to the CONTROLVM message with success.
9118c2ecf20Sopenharmony_ci *
9128c2ecf20Sopenharmony_ci * Return: 0 on success or -EINVAL on failure
9138c2ecf20Sopenharmony_ci */
9148c2ecf20Sopenharmony_cistatic int parahotplug_request_complete(int id, u16 active)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	struct list_head *pos;
9178c2ecf20Sopenharmony_ci	struct list_head *tmp;
9188c2ecf20Sopenharmony_ci	struct parahotplug_request *req;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	spin_lock(&parahotplug_request_list_lock);
9218c2ecf20Sopenharmony_ci	/* Look for a request matching "id". */
9228c2ecf20Sopenharmony_ci	list_for_each_safe(pos, tmp, &parahotplug_request_list) {
9238c2ecf20Sopenharmony_ci		req = list_entry(pos, struct parahotplug_request, list);
9248c2ecf20Sopenharmony_ci		if (req->id == id) {
9258c2ecf20Sopenharmony_ci			/*
9268c2ecf20Sopenharmony_ci			 * Found a match. Remove it from the list and
9278c2ecf20Sopenharmony_ci			 * respond.
9288c2ecf20Sopenharmony_ci			 */
9298c2ecf20Sopenharmony_ci			list_del(pos);
9308c2ecf20Sopenharmony_ci			spin_unlock(&parahotplug_request_list_lock);
9318c2ecf20Sopenharmony_ci			req->msg.cmd.device_change_state.state.active = active;
9328c2ecf20Sopenharmony_ci			if (req->msg.hdr.flags.response_expected)
9338c2ecf20Sopenharmony_ci				controlvm_respond(
9348c2ecf20Sopenharmony_ci				       &req->msg.hdr, CONTROLVM_RESP_SUCCESS,
9358c2ecf20Sopenharmony_ci				       &req->msg.cmd.device_change_state.state);
9368c2ecf20Sopenharmony_ci			parahotplug_request_destroy(req);
9378c2ecf20Sopenharmony_ci			return 0;
9388c2ecf20Sopenharmony_ci		}
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci	spin_unlock(&parahotplug_request_list_lock);
9418c2ecf20Sopenharmony_ci	return -EINVAL;
9428c2ecf20Sopenharmony_ci}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci/*
9458c2ecf20Sopenharmony_ci * devicedisabled_store() - disables the hotplug device
9468c2ecf20Sopenharmony_ci * @dev:   sysfs interface variable not utilized in this function
9478c2ecf20Sopenharmony_ci * @attr:  sysfs interface variable not utilized in this function
9488c2ecf20Sopenharmony_ci * @buf:   buffer containing the device id
9498c2ecf20Sopenharmony_ci * @count: the size of the buffer
9508c2ecf20Sopenharmony_ci *
9518c2ecf20Sopenharmony_ci * The parahotplug/devicedisabled interface gets called by our support script
9528c2ecf20Sopenharmony_ci * when an SR-IOV device has been shut down. The ID is passed to the script
9538c2ecf20Sopenharmony_ci * and then passed back when the device has been removed.
9548c2ecf20Sopenharmony_ci *
9558c2ecf20Sopenharmony_ci * Return: the size of the buffer for success or negative for error
9568c2ecf20Sopenharmony_ci */
9578c2ecf20Sopenharmony_cistatic ssize_t devicedisabled_store(struct device *dev,
9588c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
9598c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	unsigned int id;
9628c2ecf20Sopenharmony_ci	int err;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (kstrtouint(buf, 10, &id))
9658c2ecf20Sopenharmony_ci		return -EINVAL;
9668c2ecf20Sopenharmony_ci	err = parahotplug_request_complete(id, 0);
9678c2ecf20Sopenharmony_ci	if (err < 0)
9688c2ecf20Sopenharmony_ci		return err;
9698c2ecf20Sopenharmony_ci	return count;
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(devicedisabled);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci/*
9748c2ecf20Sopenharmony_ci * deviceenabled_store() - enables the hotplug device
9758c2ecf20Sopenharmony_ci * @dev:   sysfs interface variable not utilized in this function
9768c2ecf20Sopenharmony_ci * @attr:  sysfs interface variable not utilized in this function
9778c2ecf20Sopenharmony_ci * @buf:   buffer containing the device id
9788c2ecf20Sopenharmony_ci * @count: the size of the buffer
9798c2ecf20Sopenharmony_ci *
9808c2ecf20Sopenharmony_ci * The parahotplug/deviceenabled interface gets called by our support script
9818c2ecf20Sopenharmony_ci * when an SR-IOV device has been recovered. The ID is passed to the script
9828c2ecf20Sopenharmony_ci * and then passed back when the device has been brought back up.
9838c2ecf20Sopenharmony_ci *
9848c2ecf20Sopenharmony_ci * Return: the size of the buffer for success or negative for error
9858c2ecf20Sopenharmony_ci */
9868c2ecf20Sopenharmony_cistatic ssize_t deviceenabled_store(struct device *dev,
9878c2ecf20Sopenharmony_ci				   struct device_attribute *attr,
9888c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	unsigned int id;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	if (kstrtouint(buf, 10, &id))
9938c2ecf20Sopenharmony_ci		return -EINVAL;
9948c2ecf20Sopenharmony_ci	parahotplug_request_complete(id, 1);
9958c2ecf20Sopenharmony_ci	return count;
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(deviceenabled);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cistatic struct attribute *visorchipset_install_attrs[] = {
10008c2ecf20Sopenharmony_ci	&dev_attr_toolaction.attr,
10018c2ecf20Sopenharmony_ci	&dev_attr_boottotool.attr,
10028c2ecf20Sopenharmony_ci	&dev_attr_error.attr,
10038c2ecf20Sopenharmony_ci	&dev_attr_textid.attr,
10048c2ecf20Sopenharmony_ci	&dev_attr_remaining_steps.attr,
10058c2ecf20Sopenharmony_ci	NULL
10068c2ecf20Sopenharmony_ci};
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_cistatic const struct attribute_group visorchipset_install_group = {
10098c2ecf20Sopenharmony_ci	.name = "install",
10108c2ecf20Sopenharmony_ci	.attrs = visorchipset_install_attrs
10118c2ecf20Sopenharmony_ci};
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic struct attribute *visorchipset_parahotplug_attrs[] = {
10148c2ecf20Sopenharmony_ci	&dev_attr_devicedisabled.attr,
10158c2ecf20Sopenharmony_ci	&dev_attr_deviceenabled.attr,
10168c2ecf20Sopenharmony_ci	NULL
10178c2ecf20Sopenharmony_ci};
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic const struct attribute_group visorchipset_parahotplug_group = {
10208c2ecf20Sopenharmony_ci	.name = "parahotplug",
10218c2ecf20Sopenharmony_ci	.attrs = visorchipset_parahotplug_attrs
10228c2ecf20Sopenharmony_ci};
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_cistatic const struct attribute_group *visorchipset_dev_groups[] = {
10258c2ecf20Sopenharmony_ci	&visorchipset_install_group,
10268c2ecf20Sopenharmony_ci	&visorchipset_parahotplug_group,
10278c2ecf20Sopenharmony_ci	NULL
10288c2ecf20Sopenharmony_ci};
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci/*
10318c2ecf20Sopenharmony_ci * parahotplug_request_kickoff() - initiate parahotplug request
10328c2ecf20Sopenharmony_ci * @req: the request to initiate
10338c2ecf20Sopenharmony_ci *
10348c2ecf20Sopenharmony_ci * Cause uevent to run the user level script to do the disable/enable specified
10358c2ecf20Sopenharmony_ci * in the parahotplug_request.
10368c2ecf20Sopenharmony_ci */
10378c2ecf20Sopenharmony_cistatic int parahotplug_request_kickoff(struct parahotplug_request *req)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &req->msg.cmd;
10408c2ecf20Sopenharmony_ci	char env_cmd[40], env_id[40], env_state[40], env_bus[40], env_dev[40],
10418c2ecf20Sopenharmony_ci	     env_func[40];
10428c2ecf20Sopenharmony_ci	char *envp[] = { env_cmd, env_id, env_state, env_bus, env_dev,
10438c2ecf20Sopenharmony_ci			 env_func, NULL
10448c2ecf20Sopenharmony_ci	};
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	sprintf(env_cmd, "VISOR_PARAHOTPLUG=1");
10478c2ecf20Sopenharmony_ci	sprintf(env_id, "VISOR_PARAHOTPLUG_ID=%d", req->id);
10488c2ecf20Sopenharmony_ci	sprintf(env_state, "VISOR_PARAHOTPLUG_STATE=%d",
10498c2ecf20Sopenharmony_ci		cmd->device_change_state.state.active);
10508c2ecf20Sopenharmony_ci	sprintf(env_bus, "VISOR_PARAHOTPLUG_BUS=%d",
10518c2ecf20Sopenharmony_ci		cmd->device_change_state.bus_no);
10528c2ecf20Sopenharmony_ci	sprintf(env_dev, "VISOR_PARAHOTPLUG_DEVICE=%d",
10538c2ecf20Sopenharmony_ci		cmd->device_change_state.dev_no >> 3);
10548c2ecf20Sopenharmony_ci	sprintf(env_func, "VISOR_PARAHOTPLUG_FUNCTION=%d",
10558c2ecf20Sopenharmony_ci		cmd->device_change_state.dev_no & 0x7);
10568c2ecf20Sopenharmony_ci	return kobject_uevent_env(&chipset_dev->acpi_device->dev.kobj,
10578c2ecf20Sopenharmony_ci				  KOBJ_CHANGE, envp);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci/*
10618c2ecf20Sopenharmony_ci * parahotplug_process_message() - enables or disables a PCI device by kicking
10628c2ecf20Sopenharmony_ci *                                 off a udev script
10638c2ecf20Sopenharmony_ci * @inmsg: the message indicating whether to enable or disable
10648c2ecf20Sopenharmony_ci */
10658c2ecf20Sopenharmony_cistatic int parahotplug_process_message(struct controlvm_message *inmsg)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct parahotplug_request *req;
10688c2ecf20Sopenharmony_ci	int err;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	req = parahotplug_request_create(inmsg);
10718c2ecf20Sopenharmony_ci	if (!req)
10728c2ecf20Sopenharmony_ci		return -ENOMEM;
10738c2ecf20Sopenharmony_ci	/*
10748c2ecf20Sopenharmony_ci	 * For enable messages, just respond with success right away, we don't
10758c2ecf20Sopenharmony_ci	 * need to wait to see if the enable was successful.
10768c2ecf20Sopenharmony_ci	 */
10778c2ecf20Sopenharmony_ci	if (inmsg->cmd.device_change_state.state.active) {
10788c2ecf20Sopenharmony_ci		err = parahotplug_request_kickoff(req);
10798c2ecf20Sopenharmony_ci		if (err)
10808c2ecf20Sopenharmony_ci			goto err_respond;
10818c2ecf20Sopenharmony_ci		controlvm_respond(&inmsg->hdr, CONTROLVM_RESP_SUCCESS,
10828c2ecf20Sopenharmony_ci				  &inmsg->cmd.device_change_state.state);
10838c2ecf20Sopenharmony_ci		parahotplug_request_destroy(req);
10848c2ecf20Sopenharmony_ci		return 0;
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci	/*
10878c2ecf20Sopenharmony_ci	 * For disable messages, add the request to the request list before
10888c2ecf20Sopenharmony_ci	 * kicking off the udev script. It won't get responded to until the
10898c2ecf20Sopenharmony_ci	 * script has indicated it's done.
10908c2ecf20Sopenharmony_ci	 */
10918c2ecf20Sopenharmony_ci	spin_lock(&parahotplug_request_list_lock);
10928c2ecf20Sopenharmony_ci	list_add_tail(&req->list, &parahotplug_request_list);
10938c2ecf20Sopenharmony_ci	spin_unlock(&parahotplug_request_list_lock);
10948c2ecf20Sopenharmony_ci	err = parahotplug_request_kickoff(req);
10958c2ecf20Sopenharmony_ci	if (err)
10968c2ecf20Sopenharmony_ci		goto err_respond;
10978c2ecf20Sopenharmony_ci	return 0;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cierr_respond:
11008c2ecf20Sopenharmony_ci	controlvm_respond(&inmsg->hdr, err,
11018c2ecf20Sopenharmony_ci			  &inmsg->cmd.device_change_state.state);
11028c2ecf20Sopenharmony_ci	return err;
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci/*
11068c2ecf20Sopenharmony_ci * chipset_ready_uevent() - sends chipset_ready action
11078c2ecf20Sopenharmony_ci *
11088c2ecf20Sopenharmony_ci * Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset.
11098c2ecf20Sopenharmony_ci *
11108c2ecf20Sopenharmony_ci * Return: 0 on success, negative on failure
11118c2ecf20Sopenharmony_ci */
11128c2ecf20Sopenharmony_cistatic int chipset_ready_uevent(struct controlvm_message_header *msg_hdr)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	int res;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	res = kobject_uevent(&chipset_dev->acpi_device->dev.kobj, KOBJ_ONLINE);
11178c2ecf20Sopenharmony_ci	if (msg_hdr->flags.response_expected)
11188c2ecf20Sopenharmony_ci		controlvm_respond(msg_hdr, res, NULL);
11198c2ecf20Sopenharmony_ci	return res;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/*
11238c2ecf20Sopenharmony_ci * chipset_selftest_uevent() - sends chipset_selftest action
11248c2ecf20Sopenharmony_ci *
11258c2ecf20Sopenharmony_ci * Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset.
11268c2ecf20Sopenharmony_ci *
11278c2ecf20Sopenharmony_ci * Return: 0 on success, negative on failure
11288c2ecf20Sopenharmony_ci */
11298c2ecf20Sopenharmony_cistatic int chipset_selftest_uevent(struct controlvm_message_header *msg_hdr)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	char env_selftest[20];
11328c2ecf20Sopenharmony_ci	char *envp[] = { env_selftest, NULL };
11338c2ecf20Sopenharmony_ci	int res;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1);
11368c2ecf20Sopenharmony_ci	res = kobject_uevent_env(&chipset_dev->acpi_device->dev.kobj,
11378c2ecf20Sopenharmony_ci				 KOBJ_CHANGE, envp);
11388c2ecf20Sopenharmony_ci	if (msg_hdr->flags.response_expected)
11398c2ecf20Sopenharmony_ci		controlvm_respond(msg_hdr, res, NULL);
11408c2ecf20Sopenharmony_ci	return res;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci/*
11448c2ecf20Sopenharmony_ci * chipset_notready_uevent() - sends chipset_notready action
11458c2ecf20Sopenharmony_ci *
11468c2ecf20Sopenharmony_ci * Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset.
11478c2ecf20Sopenharmony_ci *
11488c2ecf20Sopenharmony_ci * Return: 0 on success, negative on failure
11498c2ecf20Sopenharmony_ci */
11508c2ecf20Sopenharmony_cistatic int chipset_notready_uevent(struct controlvm_message_header *msg_hdr)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	int res = kobject_uevent(&chipset_dev->acpi_device->dev.kobj,
11538c2ecf20Sopenharmony_ci				 KOBJ_OFFLINE);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	if (msg_hdr->flags.response_expected)
11568c2ecf20Sopenharmony_ci		controlvm_respond(msg_hdr, res, NULL);
11578c2ecf20Sopenharmony_ci	return res;
11588c2ecf20Sopenharmony_ci}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_cistatic int unisys_vmcall(unsigned long tuple, unsigned long param)
11618c2ecf20Sopenharmony_ci{
11628c2ecf20Sopenharmony_ci	int result = 0;
11638c2ecf20Sopenharmony_ci	unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx;
11648c2ecf20Sopenharmony_ci	unsigned long reg_ebx;
11658c2ecf20Sopenharmony_ci	unsigned long reg_ecx;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	reg_ebx = param & 0xFFFFFFFF;
11688c2ecf20Sopenharmony_ci	reg_ecx = param >> 32;
11698c2ecf20Sopenharmony_ci	cpuid(0x00000001, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx);
11708c2ecf20Sopenharmony_ci	if (!(cpuid_ecx & 0x80000000))
11718c2ecf20Sopenharmony_ci		return -EPERM;
11728c2ecf20Sopenharmony_ci	__asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) :
11738c2ecf20Sopenharmony_ci			     "a"(tuple), "b"(reg_ebx), "c"(reg_ecx));
11748c2ecf20Sopenharmony_ci	if (result)
11758c2ecf20Sopenharmony_ci		goto error;
11768c2ecf20Sopenharmony_ci	return 0;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci/* Need to convert from VMCALL error codes to Linux */
11798c2ecf20Sopenharmony_cierror:
11808c2ecf20Sopenharmony_ci	switch (result) {
11818c2ecf20Sopenharmony_ci	case VMCALL_RESULT_INVALID_PARAM:
11828c2ecf20Sopenharmony_ci		return -EINVAL;
11838c2ecf20Sopenharmony_ci	case VMCALL_RESULT_DATA_UNAVAILABLE:
11848c2ecf20Sopenharmony_ci		return -ENODEV;
11858c2ecf20Sopenharmony_ci	default:
11868c2ecf20Sopenharmony_ci		return -EFAULT;
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_cistatic int controlvm_channel_create(struct visorchipset_device *dev)
11918c2ecf20Sopenharmony_ci{
11928c2ecf20Sopenharmony_ci	struct visorchannel *chan;
11938c2ecf20Sopenharmony_ci	u64 addr;
11948c2ecf20Sopenharmony_ci	int err;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	err = unisys_vmcall(VMCALL_CONTROLVM_ADDR,
11978c2ecf20Sopenharmony_ci			    virt_to_phys(&dev->controlvm_params));
11988c2ecf20Sopenharmony_ci	if (err)
11998c2ecf20Sopenharmony_ci		return err;
12008c2ecf20Sopenharmony_ci	addr = dev->controlvm_params.address;
12018c2ecf20Sopenharmony_ci	chan = visorchannel_create(addr, GFP_KERNEL,
12028c2ecf20Sopenharmony_ci				   &visor_controlvm_channel_guid, true);
12038c2ecf20Sopenharmony_ci	if (!chan)
12048c2ecf20Sopenharmony_ci		return -ENOMEM;
12058c2ecf20Sopenharmony_ci	dev->controlvm_channel = chan;
12068c2ecf20Sopenharmony_ci	return 0;
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cistatic void setup_crash_devices_work_queue(struct work_struct *work)
12108c2ecf20Sopenharmony_ci{
12118c2ecf20Sopenharmony_ci	struct controlvm_message local_crash_bus_msg;
12128c2ecf20Sopenharmony_ci	struct controlvm_message local_crash_dev_msg;
12138c2ecf20Sopenharmony_ci	struct controlvm_message msg = {
12148c2ecf20Sopenharmony_ci		.hdr.id = CONTROLVM_CHIPSET_INIT,
12158c2ecf20Sopenharmony_ci		.cmd.init_chipset = {
12168c2ecf20Sopenharmony_ci			.bus_count = 23,
12178c2ecf20Sopenharmony_ci			.switch_count = 0,
12188c2ecf20Sopenharmony_ci		},
12198c2ecf20Sopenharmony_ci	};
12208c2ecf20Sopenharmony_ci	u32 local_crash_msg_offset;
12218c2ecf20Sopenharmony_ci	u16 local_crash_msg_count;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/* send init chipset msg */
12248c2ecf20Sopenharmony_ci	chipset_init(&msg);
12258c2ecf20Sopenharmony_ci	/* get saved message count */
12268c2ecf20Sopenharmony_ci	if (visorchannel_read(chipset_dev->controlvm_channel,
12278c2ecf20Sopenharmony_ci			      offsetof(struct visor_controlvm_channel,
12288c2ecf20Sopenharmony_ci				       saved_crash_message_count),
12298c2ecf20Sopenharmony_ci			      &local_crash_msg_count, sizeof(u16)) < 0) {
12308c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
12318c2ecf20Sopenharmony_ci			"failed to read channel\n");
12328c2ecf20Sopenharmony_ci		return;
12338c2ecf20Sopenharmony_ci	}
12348c2ecf20Sopenharmony_ci	if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) {
12358c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev, "invalid count\n");
12368c2ecf20Sopenharmony_ci		return;
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci	/* get saved crash message offset */
12398c2ecf20Sopenharmony_ci	if (visorchannel_read(chipset_dev->controlvm_channel,
12408c2ecf20Sopenharmony_ci			      offsetof(struct visor_controlvm_channel,
12418c2ecf20Sopenharmony_ci				       saved_crash_message_offset),
12428c2ecf20Sopenharmony_ci			      &local_crash_msg_offset, sizeof(u32)) < 0) {
12438c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
12448c2ecf20Sopenharmony_ci			"failed to read channel\n");
12458c2ecf20Sopenharmony_ci		return;
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci	/* read create device message for storage bus offset */
12488c2ecf20Sopenharmony_ci	if (visorchannel_read(chipset_dev->controlvm_channel,
12498c2ecf20Sopenharmony_ci			      local_crash_msg_offset,
12508c2ecf20Sopenharmony_ci			      &local_crash_bus_msg,
12518c2ecf20Sopenharmony_ci			      sizeof(struct controlvm_message)) < 0) {
12528c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
12538c2ecf20Sopenharmony_ci			"failed to read channel\n");
12548c2ecf20Sopenharmony_ci		return;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci	/* read create device message for storage device */
12578c2ecf20Sopenharmony_ci	if (visorchannel_read(chipset_dev->controlvm_channel,
12588c2ecf20Sopenharmony_ci			      local_crash_msg_offset +
12598c2ecf20Sopenharmony_ci			      sizeof(struct controlvm_message),
12608c2ecf20Sopenharmony_ci			      &local_crash_dev_msg,
12618c2ecf20Sopenharmony_ci			      sizeof(struct controlvm_message)) < 0) {
12628c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
12638c2ecf20Sopenharmony_ci			"failed to read channel\n");
12648c2ecf20Sopenharmony_ci		return;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci	/* reuse IOVM create bus message */
12678c2ecf20Sopenharmony_ci	if (!local_crash_bus_msg.cmd.create_bus.channel_addr) {
12688c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
12698c2ecf20Sopenharmony_ci			"no valid create_bus message\n");
12708c2ecf20Sopenharmony_ci		return;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci	visorbus_create(&local_crash_bus_msg);
12738c2ecf20Sopenharmony_ci	/* reuse create device message for storage device */
12748c2ecf20Sopenharmony_ci	if (!local_crash_dev_msg.cmd.create_device.channel_addr) {
12758c2ecf20Sopenharmony_ci		dev_err(&chipset_dev->acpi_device->dev,
12768c2ecf20Sopenharmony_ci			"no valid create_device message\n");
12778c2ecf20Sopenharmony_ci		return;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci	visorbus_device_create(&local_crash_dev_msg);
12808c2ecf20Sopenharmony_ci}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_civoid visorbus_response(struct visor_device *bus_info, int response,
12838c2ecf20Sopenharmony_ci		       int controlvm_id)
12848c2ecf20Sopenharmony_ci{
12858c2ecf20Sopenharmony_ci	if (!bus_info->pending_msg_hdr)
12868c2ecf20Sopenharmony_ci		return;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	controlvm_responder(controlvm_id, bus_info->pending_msg_hdr, response);
12898c2ecf20Sopenharmony_ci	kfree(bus_info->pending_msg_hdr);
12908c2ecf20Sopenharmony_ci	bus_info->pending_msg_hdr = NULL;
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_civoid visorbus_device_changestate_response(struct visor_device *dev_info,
12948c2ecf20Sopenharmony_ci					  int response,
12958c2ecf20Sopenharmony_ci					  struct visor_segment_state state)
12968c2ecf20Sopenharmony_ci{
12978c2ecf20Sopenharmony_ci	if (!dev_info->pending_msg_hdr)
12988c2ecf20Sopenharmony_ci		return;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, dev_info,
13018c2ecf20Sopenharmony_ci				     response, state);
13028c2ecf20Sopenharmony_ci	kfree(dev_info->pending_msg_hdr);
13038c2ecf20Sopenharmony_ci	dev_info->pending_msg_hdr = NULL;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic void parser_done(struct parser_context *ctx)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	chipset_dev->controlvm_payload_bytes_buffered -= ctx->param_bytes;
13098c2ecf20Sopenharmony_ci	kfree(ctx);
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_cistatic struct parser_context *parser_init_stream(u64 addr, u32 bytes,
13138c2ecf20Sopenharmony_ci						 bool *retry)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	unsigned long allocbytes;
13168c2ecf20Sopenharmony_ci	struct parser_context *ctx;
13178c2ecf20Sopenharmony_ci	void *mapping;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	*retry = false;
13208c2ecf20Sopenharmony_ci	/* alloc an extra byte to ensure payload is \0 terminated */
13218c2ecf20Sopenharmony_ci	allocbytes = (unsigned long)bytes + 1 + (sizeof(struct parser_context) -
13228c2ecf20Sopenharmony_ci		     sizeof(struct visor_controlvm_parameters_header));
13238c2ecf20Sopenharmony_ci	if ((chipset_dev->controlvm_payload_bytes_buffered + bytes) >
13248c2ecf20Sopenharmony_ci	     MAX_CONTROLVM_PAYLOAD_BYTES) {
13258c2ecf20Sopenharmony_ci		*retry = true;
13268c2ecf20Sopenharmony_ci		return NULL;
13278c2ecf20Sopenharmony_ci	}
13288c2ecf20Sopenharmony_ci	ctx = kzalloc(allocbytes, GFP_KERNEL);
13298c2ecf20Sopenharmony_ci	if (!ctx) {
13308c2ecf20Sopenharmony_ci		*retry = true;
13318c2ecf20Sopenharmony_ci		return NULL;
13328c2ecf20Sopenharmony_ci	}
13338c2ecf20Sopenharmony_ci	ctx->allocbytes = allocbytes;
13348c2ecf20Sopenharmony_ci	ctx->param_bytes = bytes;
13358c2ecf20Sopenharmony_ci	mapping = memremap(addr, bytes, MEMREMAP_WB);
13368c2ecf20Sopenharmony_ci	if (!mapping)
13378c2ecf20Sopenharmony_ci		goto err_finish_ctx;
13388c2ecf20Sopenharmony_ci	memcpy(&ctx->data, mapping, bytes);
13398c2ecf20Sopenharmony_ci	memunmap(mapping);
13408c2ecf20Sopenharmony_ci	ctx->byte_stream = true;
13418c2ecf20Sopenharmony_ci	chipset_dev->controlvm_payload_bytes_buffered += ctx->param_bytes;
13428c2ecf20Sopenharmony_ci	return ctx;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cierr_finish_ctx:
13458c2ecf20Sopenharmony_ci	kfree(ctx);
13468c2ecf20Sopenharmony_ci	return NULL;
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci/*
13508c2ecf20Sopenharmony_ci * handle_command() - process a controlvm message
13518c2ecf20Sopenharmony_ci * @inmsg:        the message to process
13528c2ecf20Sopenharmony_ci * @channel_addr: address of the controlvm channel
13538c2ecf20Sopenharmony_ci *
13548c2ecf20Sopenharmony_ci * Return:
13558c2ecf20Sopenharmony_ci *	0	- Successfully processed the message
13568c2ecf20Sopenharmony_ci *	-EAGAIN - ControlVM message was not processed and should be retried
13578c2ecf20Sopenharmony_ci *		  reading the next controlvm message; a scenario where this can
13588c2ecf20Sopenharmony_ci *		  occur is when we need to throttle the allocation of memory in
13598c2ecf20Sopenharmony_ci *		  which to copy out controlvm payload data.
13608c2ecf20Sopenharmony_ci *	< 0	- error: ControlVM message was processed but an error occurred.
13618c2ecf20Sopenharmony_ci */
13628c2ecf20Sopenharmony_cistatic int handle_command(struct controlvm_message inmsg, u64 channel_addr)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	struct controlvm_message_packet *cmd = &inmsg.cmd;
13658c2ecf20Sopenharmony_ci	u64 parm_addr;
13668c2ecf20Sopenharmony_ci	u32 parm_bytes;
13678c2ecf20Sopenharmony_ci	struct parser_context *parser_ctx = NULL;
13688c2ecf20Sopenharmony_ci	struct controlvm_message ackmsg;
13698c2ecf20Sopenharmony_ci	int err = 0;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	/* create parsing context if necessary */
13728c2ecf20Sopenharmony_ci	parm_addr = channel_addr + inmsg.hdr.payload_vm_offset;
13738c2ecf20Sopenharmony_ci	parm_bytes = inmsg.hdr.payload_bytes;
13748c2ecf20Sopenharmony_ci	/*
13758c2ecf20Sopenharmony_ci	 * Parameter and channel addresses within test messages actually lie
13768c2ecf20Sopenharmony_ci	 * within our OS-controlled memory. We need to know that, because it
13778c2ecf20Sopenharmony_ci	 * makes a difference in how we compute the virtual address.
13788c2ecf20Sopenharmony_ci	 */
13798c2ecf20Sopenharmony_ci	if (parm_bytes) {
13808c2ecf20Sopenharmony_ci		bool retry;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci		parser_ctx = parser_init_stream(parm_addr, parm_bytes, &retry);
13838c2ecf20Sopenharmony_ci		if (!parser_ctx && retry)
13848c2ecf20Sopenharmony_ci			return -EAGAIN;
13858c2ecf20Sopenharmony_ci	}
13868c2ecf20Sopenharmony_ci	controlvm_init_response(&ackmsg, &inmsg.hdr, CONTROLVM_RESP_SUCCESS);
13878c2ecf20Sopenharmony_ci	err = visorchannel_signalinsert(chipset_dev->controlvm_channel,
13888c2ecf20Sopenharmony_ci					CONTROLVM_QUEUE_ACK, &ackmsg);
13898c2ecf20Sopenharmony_ci	if (err)
13908c2ecf20Sopenharmony_ci		return err;
13918c2ecf20Sopenharmony_ci	switch (inmsg.hdr.id) {
13928c2ecf20Sopenharmony_ci	case CONTROLVM_CHIPSET_INIT:
13938c2ecf20Sopenharmony_ci		err = chipset_init(&inmsg);
13948c2ecf20Sopenharmony_ci		break;
13958c2ecf20Sopenharmony_ci	case CONTROLVM_BUS_CREATE:
13968c2ecf20Sopenharmony_ci		err = visorbus_create(&inmsg);
13978c2ecf20Sopenharmony_ci		break;
13988c2ecf20Sopenharmony_ci	case CONTROLVM_BUS_DESTROY:
13998c2ecf20Sopenharmony_ci		err = visorbus_destroy(&inmsg);
14008c2ecf20Sopenharmony_ci		break;
14018c2ecf20Sopenharmony_ci	case CONTROLVM_BUS_CONFIGURE:
14028c2ecf20Sopenharmony_ci		err = visorbus_configure(&inmsg, parser_ctx);
14038c2ecf20Sopenharmony_ci		break;
14048c2ecf20Sopenharmony_ci	case CONTROLVM_DEVICE_CREATE:
14058c2ecf20Sopenharmony_ci		err = visorbus_device_create(&inmsg);
14068c2ecf20Sopenharmony_ci		break;
14078c2ecf20Sopenharmony_ci	case CONTROLVM_DEVICE_CHANGESTATE:
14088c2ecf20Sopenharmony_ci		if (cmd->device_change_state.flags.phys_device) {
14098c2ecf20Sopenharmony_ci			err = parahotplug_process_message(&inmsg);
14108c2ecf20Sopenharmony_ci		} else {
14118c2ecf20Sopenharmony_ci			/*
14128c2ecf20Sopenharmony_ci			 * save the hdr and cmd structures for later use when
14138c2ecf20Sopenharmony_ci			 * sending back the response to Command
14148c2ecf20Sopenharmony_ci			 */
14158c2ecf20Sopenharmony_ci			err = visorbus_device_changestate(&inmsg);
14168c2ecf20Sopenharmony_ci			break;
14178c2ecf20Sopenharmony_ci		}
14188c2ecf20Sopenharmony_ci		break;
14198c2ecf20Sopenharmony_ci	case CONTROLVM_DEVICE_DESTROY:
14208c2ecf20Sopenharmony_ci		err = visorbus_device_destroy(&inmsg);
14218c2ecf20Sopenharmony_ci		break;
14228c2ecf20Sopenharmony_ci	case CONTROLVM_DEVICE_CONFIGURE:
14238c2ecf20Sopenharmony_ci		/* no op just send a respond that we passed */
14248c2ecf20Sopenharmony_ci		if (inmsg.hdr.flags.response_expected)
14258c2ecf20Sopenharmony_ci			controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS,
14268c2ecf20Sopenharmony_ci					  NULL);
14278c2ecf20Sopenharmony_ci		break;
14288c2ecf20Sopenharmony_ci	case CONTROLVM_CHIPSET_READY:
14298c2ecf20Sopenharmony_ci		err = chipset_ready_uevent(&inmsg.hdr);
14308c2ecf20Sopenharmony_ci		break;
14318c2ecf20Sopenharmony_ci	case CONTROLVM_CHIPSET_SELFTEST:
14328c2ecf20Sopenharmony_ci		err = chipset_selftest_uevent(&inmsg.hdr);
14338c2ecf20Sopenharmony_ci		break;
14348c2ecf20Sopenharmony_ci	case CONTROLVM_CHIPSET_STOP:
14358c2ecf20Sopenharmony_ci		err = chipset_notready_uevent(&inmsg.hdr);
14368c2ecf20Sopenharmony_ci		break;
14378c2ecf20Sopenharmony_ci	default:
14388c2ecf20Sopenharmony_ci		err = -ENOMSG;
14398c2ecf20Sopenharmony_ci		if (inmsg.hdr.flags.response_expected)
14408c2ecf20Sopenharmony_ci			controlvm_respond(&inmsg.hdr,
14418c2ecf20Sopenharmony_ci					  -CONTROLVM_RESP_ID_UNKNOWN, NULL);
14428c2ecf20Sopenharmony_ci		break;
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci	if (parser_ctx) {
14458c2ecf20Sopenharmony_ci		parser_done(parser_ctx);
14468c2ecf20Sopenharmony_ci		parser_ctx = NULL;
14478c2ecf20Sopenharmony_ci	}
14488c2ecf20Sopenharmony_ci	return err;
14498c2ecf20Sopenharmony_ci}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci/*
14528c2ecf20Sopenharmony_ci * read_controlvm_event() - retreives the next message from the
14538c2ecf20Sopenharmony_ci *                          CONTROLVM_QUEUE_EVENT queue in the controlvm
14548c2ecf20Sopenharmony_ci *                          channel
14558c2ecf20Sopenharmony_ci * @msg: pointer to the retrieved message
14568c2ecf20Sopenharmony_ci *
14578c2ecf20Sopenharmony_ci * Return: 0 if valid message was retrieved or -error
14588c2ecf20Sopenharmony_ci */
14598c2ecf20Sopenharmony_cistatic int read_controlvm_event(struct controlvm_message *msg)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	int err = visorchannel_signalremove(chipset_dev->controlvm_channel,
14628c2ecf20Sopenharmony_ci					    CONTROLVM_QUEUE_EVENT, msg);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	if (err)
14658c2ecf20Sopenharmony_ci		return err;
14668c2ecf20Sopenharmony_ci	/* got a message */
14678c2ecf20Sopenharmony_ci	if (msg->hdr.flags.test_message == 1)
14688c2ecf20Sopenharmony_ci		return -EINVAL;
14698c2ecf20Sopenharmony_ci	return 0;
14708c2ecf20Sopenharmony_ci}
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci/*
14738c2ecf20Sopenharmony_ci * parahotplug_process_list() - remove any request from the list that's been on
14748c2ecf20Sopenharmony_ci *                              there too long and respond with an error
14758c2ecf20Sopenharmony_ci */
14768c2ecf20Sopenharmony_cistatic void parahotplug_process_list(void)
14778c2ecf20Sopenharmony_ci{
14788c2ecf20Sopenharmony_ci	struct list_head *pos;
14798c2ecf20Sopenharmony_ci	struct list_head *tmp;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	spin_lock(&parahotplug_request_list_lock);
14828c2ecf20Sopenharmony_ci	list_for_each_safe(pos, tmp, &parahotplug_request_list) {
14838c2ecf20Sopenharmony_ci		struct parahotplug_request *req =
14848c2ecf20Sopenharmony_ci		    list_entry(pos, struct parahotplug_request, list);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci		if (!time_after_eq(jiffies, req->expiration))
14878c2ecf20Sopenharmony_ci			continue;
14888c2ecf20Sopenharmony_ci		list_del(pos);
14898c2ecf20Sopenharmony_ci		if (req->msg.hdr.flags.response_expected)
14908c2ecf20Sopenharmony_ci			controlvm_respond(
14918c2ecf20Sopenharmony_ci				&req->msg.hdr,
14928c2ecf20Sopenharmony_ci				CONTROLVM_RESP_DEVICE_UDEV_TIMEOUT,
14938c2ecf20Sopenharmony_ci				&req->msg.cmd.device_change_state.state);
14948c2ecf20Sopenharmony_ci		parahotplug_request_destroy(req);
14958c2ecf20Sopenharmony_ci	}
14968c2ecf20Sopenharmony_ci	spin_unlock(&parahotplug_request_list_lock);
14978c2ecf20Sopenharmony_ci}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_cistatic void controlvm_periodic_work(struct work_struct *work)
15008c2ecf20Sopenharmony_ci{
15018c2ecf20Sopenharmony_ci	struct controlvm_message inmsg;
15028c2ecf20Sopenharmony_ci	int count = 0;
15038c2ecf20Sopenharmony_ci	int err;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	/* Drain the RESPONSE queue make it empty */
15068c2ecf20Sopenharmony_ci	do {
15078c2ecf20Sopenharmony_ci		err = visorchannel_signalremove(chipset_dev->controlvm_channel,
15088c2ecf20Sopenharmony_ci						CONTROLVM_QUEUE_RESPONSE,
15098c2ecf20Sopenharmony_ci						&inmsg);
15108c2ecf20Sopenharmony_ci	} while ((!err) && (++count < CONTROLVM_MESSAGE_MAX));
15118c2ecf20Sopenharmony_ci	if (err != -EAGAIN)
15128c2ecf20Sopenharmony_ci		goto schedule_out;
15138c2ecf20Sopenharmony_ci	if (chipset_dev->controlvm_pending_msg_valid) {
15148c2ecf20Sopenharmony_ci		/*
15158c2ecf20Sopenharmony_ci		 * we throttled processing of a prior msg, so try to process
15168c2ecf20Sopenharmony_ci		 * it again rather than reading a new one
15178c2ecf20Sopenharmony_ci		 */
15188c2ecf20Sopenharmony_ci		inmsg = chipset_dev->controlvm_pending_msg;
15198c2ecf20Sopenharmony_ci		chipset_dev->controlvm_pending_msg_valid = false;
15208c2ecf20Sopenharmony_ci		err = 0;
15218c2ecf20Sopenharmony_ci	} else {
15228c2ecf20Sopenharmony_ci		err = read_controlvm_event(&inmsg);
15238c2ecf20Sopenharmony_ci	}
15248c2ecf20Sopenharmony_ci	while (!err) {
15258c2ecf20Sopenharmony_ci		chipset_dev->most_recent_message_jiffies = jiffies;
15268c2ecf20Sopenharmony_ci		err = handle_command(inmsg,
15278c2ecf20Sopenharmony_ci				     visorchannel_get_physaddr
15288c2ecf20Sopenharmony_ci				     (chipset_dev->controlvm_channel));
15298c2ecf20Sopenharmony_ci		if (err == -EAGAIN) {
15308c2ecf20Sopenharmony_ci			chipset_dev->controlvm_pending_msg = inmsg;
15318c2ecf20Sopenharmony_ci			chipset_dev->controlvm_pending_msg_valid = true;
15328c2ecf20Sopenharmony_ci			break;
15338c2ecf20Sopenharmony_ci		}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci		err = read_controlvm_event(&inmsg);
15368c2ecf20Sopenharmony_ci	}
15378c2ecf20Sopenharmony_ci	/* parahotplug_worker */
15388c2ecf20Sopenharmony_ci	parahotplug_process_list();
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci/*
15418c2ecf20Sopenharmony_ci * The controlvm messages are sent in a bulk. If we start receiving messages, we
15428c2ecf20Sopenharmony_ci * want the polling to be fast. If we do not receive any message for
15438c2ecf20Sopenharmony_ci * MIN_IDLE_SECONDS, we can slow down the polling.
15448c2ecf20Sopenharmony_ci */
15458c2ecf20Sopenharmony_cischedule_out:
15468c2ecf20Sopenharmony_ci	if (time_after(jiffies, chipset_dev->most_recent_message_jiffies +
15478c2ecf20Sopenharmony_ci				(HZ * MIN_IDLE_SECONDS))) {
15488c2ecf20Sopenharmony_ci		/*
15498c2ecf20Sopenharmony_ci		 * it's been longer than MIN_IDLE_SECONDS since we processed
15508c2ecf20Sopenharmony_ci		 * our last controlvm message; slow down the polling
15518c2ecf20Sopenharmony_ci		 */
15528c2ecf20Sopenharmony_ci		if (chipset_dev->poll_jiffies != POLLJIFFIES_CONTROLVM_SLOW)
15538c2ecf20Sopenharmony_ci			chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_SLOW;
15548c2ecf20Sopenharmony_ci	} else {
15558c2ecf20Sopenharmony_ci		if (chipset_dev->poll_jiffies != POLLJIFFIES_CONTROLVM_FAST)
15568c2ecf20Sopenharmony_ci			chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST;
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci	schedule_delayed_work(&chipset_dev->periodic_controlvm_work,
15598c2ecf20Sopenharmony_ci			      chipset_dev->poll_jiffies);
15608c2ecf20Sopenharmony_ci}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_cistatic int visorchipset_init(struct acpi_device *acpi_device)
15638c2ecf20Sopenharmony_ci{
15648c2ecf20Sopenharmony_ci	int err = -ENOMEM;
15658c2ecf20Sopenharmony_ci	struct visorchannel *controlvm_channel;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	chipset_dev = kzalloc(sizeof(*chipset_dev), GFP_KERNEL);
15688c2ecf20Sopenharmony_ci	if (!chipset_dev)
15698c2ecf20Sopenharmony_ci		goto error;
15708c2ecf20Sopenharmony_ci	err = controlvm_channel_create(chipset_dev);
15718c2ecf20Sopenharmony_ci	if (err)
15728c2ecf20Sopenharmony_ci		goto error_free_chipset_dev;
15738c2ecf20Sopenharmony_ci	acpi_device->driver_data = chipset_dev;
15748c2ecf20Sopenharmony_ci	chipset_dev->acpi_device = acpi_device;
15758c2ecf20Sopenharmony_ci	chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST;
15768c2ecf20Sopenharmony_ci	err = sysfs_create_groups(&chipset_dev->acpi_device->dev.kobj,
15778c2ecf20Sopenharmony_ci				  visorchipset_dev_groups);
15788c2ecf20Sopenharmony_ci	if (err < 0)
15798c2ecf20Sopenharmony_ci		goto error_destroy_channel;
15808c2ecf20Sopenharmony_ci	controlvm_channel = chipset_dev->controlvm_channel;
15818c2ecf20Sopenharmony_ci	if (!visor_check_channel(visorchannel_get_header(controlvm_channel),
15828c2ecf20Sopenharmony_ci				 &chipset_dev->acpi_device->dev,
15838c2ecf20Sopenharmony_ci				 &visor_controlvm_channel_guid,
15848c2ecf20Sopenharmony_ci				 "controlvm",
15858c2ecf20Sopenharmony_ci				 sizeof(struct visor_controlvm_channel),
15868c2ecf20Sopenharmony_ci				 VISOR_CONTROLVM_CHANNEL_VERSIONID,
15878c2ecf20Sopenharmony_ci				 VISOR_CHANNEL_SIGNATURE)) {
15888c2ecf20Sopenharmony_ci		err = -ENODEV;
15898c2ecf20Sopenharmony_ci		goto error_delete_groups;
15908c2ecf20Sopenharmony_ci	}
15918c2ecf20Sopenharmony_ci	/* if booting in a crash kernel */
15928c2ecf20Sopenharmony_ci	if (is_kdump_kernel())
15938c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work,
15948c2ecf20Sopenharmony_ci				  setup_crash_devices_work_queue);
15958c2ecf20Sopenharmony_ci	else
15968c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work,
15978c2ecf20Sopenharmony_ci				  controlvm_periodic_work);
15988c2ecf20Sopenharmony_ci	chipset_dev->most_recent_message_jiffies = jiffies;
15998c2ecf20Sopenharmony_ci	chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST;
16008c2ecf20Sopenharmony_ci	schedule_delayed_work(&chipset_dev->periodic_controlvm_work,
16018c2ecf20Sopenharmony_ci			      chipset_dev->poll_jiffies);
16028c2ecf20Sopenharmony_ci	err = visorbus_init();
16038c2ecf20Sopenharmony_ci	if (err < 0)
16048c2ecf20Sopenharmony_ci		goto error_cancel_work;
16058c2ecf20Sopenharmony_ci	return 0;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_cierror_cancel_work:
16088c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&chipset_dev->periodic_controlvm_work);
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cierror_delete_groups:
16118c2ecf20Sopenharmony_ci	sysfs_remove_groups(&chipset_dev->acpi_device->dev.kobj,
16128c2ecf20Sopenharmony_ci			    visorchipset_dev_groups);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_cierror_destroy_channel:
16158c2ecf20Sopenharmony_ci	visorchannel_destroy(chipset_dev->controlvm_channel);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_cierror_free_chipset_dev:
16188c2ecf20Sopenharmony_ci	kfree(chipset_dev);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_cierror:
16218c2ecf20Sopenharmony_ci	dev_err(&acpi_device->dev, "failed with error %d\n", err);
16228c2ecf20Sopenharmony_ci	return err;
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistatic int visorchipset_exit(struct acpi_device *acpi_device)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	visorbus_exit();
16288c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&chipset_dev->periodic_controlvm_work);
16298c2ecf20Sopenharmony_ci	sysfs_remove_groups(&chipset_dev->acpi_device->dev.kobj,
16308c2ecf20Sopenharmony_ci			    visorchipset_dev_groups);
16318c2ecf20Sopenharmony_ci	visorchannel_destroy(chipset_dev->controlvm_channel);
16328c2ecf20Sopenharmony_ci	kfree(chipset_dev);
16338c2ecf20Sopenharmony_ci	return 0;
16348c2ecf20Sopenharmony_ci}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_cistatic const struct acpi_device_id unisys_device_ids[] = {
16378c2ecf20Sopenharmony_ci	{"PNP0A07", 0},
16388c2ecf20Sopenharmony_ci	{"", 0},
16398c2ecf20Sopenharmony_ci};
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_cistatic struct acpi_driver unisys_acpi_driver = {
16428c2ecf20Sopenharmony_ci	.name = "unisys_acpi",
16438c2ecf20Sopenharmony_ci	.class = "unisys_acpi_class",
16448c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
16458c2ecf20Sopenharmony_ci	.ids = unisys_device_ids,
16468c2ecf20Sopenharmony_ci	.ops = {
16478c2ecf20Sopenharmony_ci		.add = visorchipset_init,
16488c2ecf20Sopenharmony_ci		.remove = visorchipset_exit,
16498c2ecf20Sopenharmony_ci	},
16508c2ecf20Sopenharmony_ci};
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, unisys_device_ids);
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_cistatic __init int visorutil_spar_detect(void)
16558c2ecf20Sopenharmony_ci{
16568c2ecf20Sopenharmony_ci	unsigned int eax, ebx, ecx, edx;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) {
16598c2ecf20Sopenharmony_ci		/* check the ID */
16608c2ecf20Sopenharmony_ci		cpuid(UNISYS_VISOR_LEAF_ID, &eax, &ebx, &ecx, &edx);
16618c2ecf20Sopenharmony_ci		return  (ebx == UNISYS_VISOR_ID_EBX) &&
16628c2ecf20Sopenharmony_ci			(ecx == UNISYS_VISOR_ID_ECX) &&
16638c2ecf20Sopenharmony_ci			(edx == UNISYS_VISOR_ID_EDX);
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci	return 0;
16668c2ecf20Sopenharmony_ci}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_cistatic int __init init_unisys(void)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	int result;
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	if (!visorutil_spar_detect())
16738c2ecf20Sopenharmony_ci		return -ENODEV;
16748c2ecf20Sopenharmony_ci	result = acpi_bus_register_driver(&unisys_acpi_driver);
16758c2ecf20Sopenharmony_ci	if (result)
16768c2ecf20Sopenharmony_ci		return -ENODEV;
16778c2ecf20Sopenharmony_ci	pr_info("Unisys Visorchipset Driver Loaded.\n");
16788c2ecf20Sopenharmony_ci	return 0;
16798c2ecf20Sopenharmony_ci};
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_cistatic void __exit exit_unisys(void)
16828c2ecf20Sopenharmony_ci{
16838c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&unisys_acpi_driver);
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_cimodule_init(init_unisys);
16878c2ecf20Sopenharmony_cimodule_exit(exit_unisys);
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Unisys");
16908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
16918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("s-Par visorbus driver for virtual device buses");
1692