438 lines · 10.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  Copyright (c) 2013, Microsoft Corporation. */ #include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/completion.h>#include <linux/hyperv.h>#include <linux/serio.h>#include <linux/slab.h> /* * Current version 1.0 * */#define SYNTH_KBD_VERSION_MAJOR 1#define SYNTH_KBD_VERSION_MINOR	0#define SYNTH_KBD_VERSION		(SYNTH_KBD_VERSION_MINOR | \					 (SYNTH_KBD_VERSION_MAJOR << 16))  /* * Message types in the synthetic input protocol */enum synth_kbd_msg_type {	SYNTH_KBD_PROTOCOL_REQUEST = 1,	SYNTH_KBD_PROTOCOL_RESPONSE = 2,	SYNTH_KBD_EVENT = 3,	SYNTH_KBD_LED_INDICATORS = 4,}; /* * Basic message structures. */struct synth_kbd_msg_hdr {	__le32 type;}; struct synth_kbd_msg {	struct synth_kbd_msg_hdr header;	char data[]; /* Enclosed message */}; union synth_kbd_version {	__le32 version;}; /* * Protocol messages */struct synth_kbd_protocol_request {	struct synth_kbd_msg_hdr header;	union synth_kbd_version version_requested;}; #define PROTOCOL_ACCEPTED	BIT(0)struct synth_kbd_protocol_response {	struct synth_kbd_msg_hdr header;	__le32 proto_status;}; #define IS_UNICODE	BIT(0)#define IS_BREAK	BIT(1)#define IS_E0		BIT(2)#define IS_E1		BIT(3)struct synth_kbd_keystroke {	struct synth_kbd_msg_hdr header;	__le16 make_code;	__le16 reserved0;	__le32 info; /* Additional information */};  #define HK_MAXIMUM_MESSAGE_SIZE 256 #define KBD_VSC_SEND_RING_BUFFER_SIZE	VMBUS_RING_SIZE(36 * 1024)#define KBD_VSC_RECV_RING_BUFFER_SIZE	VMBUS_RING_SIZE(36 * 1024) #define XTKBD_EMUL0     0xe0#define XTKBD_EMUL1     0xe1#define XTKBD_RELEASE   0x80  /* * Represents a keyboard device */struct hv_kbd_dev {	struct hv_device *hv_dev;	struct serio *hv_serio;	struct synth_kbd_protocol_request protocol_req;	struct synth_kbd_protocol_response protocol_resp;	/* Synchronize the request/response if needed */	struct completion wait_event;	spinlock_t lock; /* protects 'started' field */	bool started;}; static void hv_kbd_on_receive(struct hv_device *hv_dev,			      struct synth_kbd_msg *msg, u32 msg_length){	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);	struct synth_kbd_keystroke *ks_msg;	u32 msg_type = __le32_to_cpu(msg->header.type);	u32 info;	u16 scan_code; 	switch (msg_type) {	case SYNTH_KBD_PROTOCOL_RESPONSE:		/*		 * Validate the information provided by the host.		 * If the host is giving us a bogus packet,		 * drop the packet (hoping the problem		 * goes away).		 */		if (msg_length < sizeof(struct synth_kbd_protocol_response)) {			dev_err(&hv_dev->device,				"Illegal protocol response packet (len: %d)\n",				msg_length);			break;		} 		memcpy(&kbd_dev->protocol_resp, msg,			sizeof(struct synth_kbd_protocol_response));		complete(&kbd_dev->wait_event);		break; 	case SYNTH_KBD_EVENT:		/*		 * Validate the information provided by the host.		 * If the host is giving us a bogus packet,		 * drop the packet (hoping the problem		 * goes away).		 */		if (msg_length < sizeof(struct  synth_kbd_keystroke)) {			dev_err(&hv_dev->device,				"Illegal keyboard event packet (len: %d)\n",				msg_length);			break;		} 		ks_msg = (struct synth_kbd_keystroke *)msg;		info = __le32_to_cpu(ks_msg->info); 		/*		 * Inject the information through the serio interrupt.		 */		scoped_guard(spinlock_irqsave, &kbd_dev->lock) {			if (kbd_dev->started) {				if (info & IS_E0)					serio_interrupt(kbd_dev->hv_serio,							XTKBD_EMUL0, 0);				if (info & IS_E1)					serio_interrupt(kbd_dev->hv_serio,							XTKBD_EMUL1, 0);				scan_code = __le16_to_cpu(ks_msg->make_code);				if (info & IS_BREAK)					scan_code |= XTKBD_RELEASE; 				serio_interrupt(kbd_dev->hv_serio,						scan_code, 0);			}		} 		/*		 * Only trigger a wakeup on key down, otherwise		 * "echo freeze > /sys/power/state" can't really enter the		 * state because the Enter-UP can trigger a wakeup at once.		 */		if (!(info & IS_BREAK))			pm_wakeup_hard_event(&hv_dev->device); 		break; 	default:		dev_err(&hv_dev->device,			"unhandled message type %d\n", msg_type);	}} static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,					  struct vmpacket_descriptor *desc,					  u32 bytes_recvd,					  u64 req_id){	struct synth_kbd_msg *msg;	u32 msg_sz; 	switch (desc->type) {	case VM_PKT_COMP:		break; 	case VM_PKT_DATA_INBAND:		/*		 * We have a packet that has "inband" data. The API used		 * for retrieving the packet guarantees that the complete		 * packet is read. So, minimally, we should be able to		 * parse the payload header safely (assuming that the host		 * can be trusted.  Trusting the host seems to be a		 * reasonable assumption because in a virtualized		 * environment there is not whole lot you can do if you		 * don't trust the host.		 *		 * Nonetheless, let us validate if the host can be trusted		 * (in a trivial way).  The interesting aspect of this		 * validation is how do you recover if we discover that the		 * host is not to be trusted? Simply dropping the packet, I		 * don't think is an appropriate recovery.  In the interest		 * of failing fast, it may be better to crash the guest.		 * For now, I will just drop the packet!		 */ 		msg_sz = bytes_recvd - (desc->offset8 << 3);		if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) {			/*			 * Drop the packet and hope			 * the problem magically goes away.			 */			dev_err(&hv_dev->device,				"Illegal packet (type: %d, tid: %llx, size: %d)\n",				desc->type, req_id, msg_sz);			break;		} 		msg = (void *)desc + (desc->offset8 << 3);		hv_kbd_on_receive(hv_dev, msg, msg_sz);		break; 	default:		dev_err(&hv_dev->device,			"unhandled packet type %d, tid %llx len %d\n",			desc->type, req_id, bytes_recvd);		break;	}} static void hv_kbd_on_channel_callback(void *context){	struct vmpacket_descriptor *desc;	struct hv_device *hv_dev = context;	u32 bytes_recvd;	u64 req_id; 	foreach_vmbus_pkt(desc, hv_dev->channel) {		bytes_recvd = desc->len8 * 8;		req_id = desc->trans_id; 		hv_kbd_handle_received_packet(hv_dev, desc, bytes_recvd,					      req_id);	}} static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev){	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);	struct synth_kbd_protocol_request *request;	struct synth_kbd_protocol_response *response;	u32 proto_status;	int error; 	reinit_completion(&kbd_dev->wait_event); 	request = &kbd_dev->protocol_req;	memset(request, 0, sizeof(struct synth_kbd_protocol_request));	request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST);	request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION); 	error = vmbus_sendpacket(hv_dev->channel, request,				 sizeof(struct synth_kbd_protocol_request),				 (unsigned long)request,				 VM_PKT_DATA_INBAND,				 VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);	if (error)		return error; 	if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ))		return -ETIMEDOUT; 	response = &kbd_dev->protocol_resp;	proto_status = __le32_to_cpu(response->proto_status);	if (!(proto_status & PROTOCOL_ACCEPTED)) {		dev_err(&hv_dev->device,			"synth_kbd protocol request failed (version %d)\n",		        SYNTH_KBD_VERSION);		return -ENODEV;	} 	return 0;} static int hv_kbd_start(struct serio *serio){	struct hv_kbd_dev *kbd_dev = serio->port_data; 	guard(spinlock_irqsave)(&kbd_dev->lock); 	kbd_dev->started = true; 	return 0;} static void hv_kbd_stop(struct serio *serio){	struct hv_kbd_dev *kbd_dev = serio->port_data; 	guard(spinlock_irqsave)(&kbd_dev->lock); 	kbd_dev->started = false;} static int hv_kbd_probe(struct hv_device *hv_dev,			const struct hv_vmbus_device_id *dev_id){	struct hv_kbd_dev *kbd_dev;	struct serio *hv_serio;	int error; 	kbd_dev = kzalloc_obj(*kbd_dev);	hv_serio = kzalloc_obj(*hv_serio);	if (!kbd_dev || !hv_serio) {		error = -ENOMEM;		goto err_free_mem;	} 	kbd_dev->hv_dev = hv_dev;	kbd_dev->hv_serio = hv_serio;	spin_lock_init(&kbd_dev->lock);	init_completion(&kbd_dev->wait_event);	hv_set_drvdata(hv_dev, kbd_dev); 	hv_serio->dev.parent  = &hv_dev->device;	hv_serio->id.type = SERIO_8042_XL;	hv_serio->port_data = kbd_dev;	strscpy(hv_serio->name, dev_name(&hv_dev->device),		sizeof(hv_serio->name));	strscpy(hv_serio->phys, dev_name(&hv_dev->device),		sizeof(hv_serio->phys)); 	hv_serio->start = hv_kbd_start;	hv_serio->stop = hv_kbd_stop; 	error = vmbus_open(hv_dev->channel,			   KBD_VSC_SEND_RING_BUFFER_SIZE,			   KBD_VSC_RECV_RING_BUFFER_SIZE,			   NULL, 0,			   hv_kbd_on_channel_callback,			   hv_dev);	if (error)		goto err_free_mem; 	error = hv_kbd_connect_to_vsp(hv_dev);	if (error)		goto err_close_vmbus; 	serio_register_port(kbd_dev->hv_serio); 	device_init_wakeup(&hv_dev->device, true); 	return 0; err_close_vmbus:	vmbus_close(hv_dev->channel);err_free_mem:	kfree(hv_serio);	kfree(kbd_dev);	return error;} static void hv_kbd_remove(struct hv_device *hv_dev){	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 	serio_unregister_port(kbd_dev->hv_serio);	vmbus_close(hv_dev->channel);	kfree(kbd_dev); 	hv_set_drvdata(hv_dev, NULL);} static int hv_kbd_suspend(struct hv_device *hv_dev){	vmbus_close(hv_dev->channel); 	return 0;} static int hv_kbd_resume(struct hv_device *hv_dev){	int ret; 	ret = vmbus_open(hv_dev->channel,			 KBD_VSC_SEND_RING_BUFFER_SIZE,			 KBD_VSC_RECV_RING_BUFFER_SIZE,			 NULL, 0,			 hv_kbd_on_channel_callback,			 hv_dev);	if (ret == 0)		ret = hv_kbd_connect_to_vsp(hv_dev); 	return ret;} static const struct hv_vmbus_device_id id_table[] = {	/* Keyboard guid */	{ HV_KBD_GUID, },	{ },}; MODULE_DEVICE_TABLE(vmbus, id_table); static struct  hv_driver hv_kbd_drv = {	.name = KBUILD_MODNAME,	.id_table = id_table,	.probe = hv_kbd_probe,	.remove = hv_kbd_remove,	.suspend = hv_kbd_suspend,	.resume = hv_kbd_resume,	.driver = {		.probe_type = PROBE_PREFER_ASYNCHRONOUS,	},}; static int __init hv_kbd_init(void){	return vmbus_driver_register(&hv_kbd_drv);} static void __exit hv_kbd_exit(void){	vmbus_driver_unregister(&hv_kbd_drv);} MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Keyboard Driver"); module_init(hv_kbd_init);module_exit(hv_kbd_exit);