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(¶hotplug_request_list_lock); 9218c2ecf20Sopenharmony_ci /* Look for a request matching "id". */ 9228c2ecf20Sopenharmony_ci list_for_each_safe(pos, tmp, ¶hotplug_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(¶hotplug_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(¶hotplug_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(¶hotplug_request_list_lock); 10928c2ecf20Sopenharmony_ci list_add_tail(&req->list, ¶hotplug_request_list); 10938c2ecf20Sopenharmony_ci spin_unlock(¶hotplug_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(¶hotplug_request_list_lock); 14828c2ecf20Sopenharmony_ci list_for_each_safe(pos, tmp, ¶hotplug_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(¶hotplug_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