2019-05-29 17:17:54 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-02-16 23:34:30 +04:00
/*
* An implementation of file copy service .
*
* Copyright ( C ) 2014 , Microsoft , Inc .
*
* Author : K . Y . Srinivasan < ksrinivasan @ novell . com >
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/nls.h>
# include <linux/workqueue.h>
# include <linux/hyperv.h>
# include <linux/sched.h>
2019-07-25 08:03:14 +03:00
# include <asm/hyperv-tlfs.h>
2014-02-16 23:34:30 +04:00
# include "hyperv_vmbus.h"
2015-04-12 04:07:53 +03:00
# include "hv_utils_transport.h"
2014-02-16 23:34:30 +04:00
# define WIN8_SRV_MAJOR 1
# define WIN8_SRV_MINOR 1
# define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
2017-01-28 22:37:17 +03:00
# define FCOPY_VER_COUNT 1
static const int fcopy_versions [ ] = {
WIN8_SRV_VERSION
} ;
# define FW_VER_COUNT 1
static const int fw_versions [ ] = {
UTIL_FW_VERSION
} ;
2014-02-16 23:34:30 +04:00
/*
* Global state maintained for transaction that is being processed .
* For a class of integration services , including the " file copy service " ,
* the specified protocol is a " request/response " protocol which means that
* there can only be single outstanding transaction from the host at any
* given point in time . We use this to simplify memory management in this
* driver - we cache and process only one message at a time .
*
* While the request / response protocol is guaranteed by the host , we further
* ensure this by serializing packet processing in this driver - we do not
* read additional packets from the VMBUs until the current packet is fully
* handled .
*/
static struct {
2015-04-12 04:07:49 +03:00
int state ; /* hvutil_device_state */
2014-02-16 23:34:30 +04:00
int recv_len ; /* number of bytes received. */
struct hv_fcopy_hdr * fcopy_msg ; /* current message */
struct vmbus_channel * recv_channel ; /* chn we got the request */
u64 recv_req_id ; /* request ID. */
} fcopy_transaction ;
static void fcopy_respond_to_host ( int error ) ;
2015-04-12 04:07:53 +03:00
static void fcopy_send_data ( struct work_struct * dummy ) ;
2015-04-12 04:07:45 +03:00
static void fcopy_timeout_func ( struct work_struct * dummy ) ;
static DECLARE_DELAYED_WORK ( fcopy_timeout_work , fcopy_timeout_func ) ;
2015-04-12 04:07:53 +03:00
static DECLARE_WORK ( fcopy_send_work , fcopy_send_data ) ;
static const char fcopy_devname [ ] = " vmbus/hv_fcopy " ;
2014-02-16 23:34:30 +04:00
static u8 * recv_buffer ;
2015-04-12 04:07:53 +03:00
static struct hvutil_transport * hvt ;
2015-04-12 04:07:58 +03:00
/*
* This state maintains the version number registered by the daemon .
*/
static int dm_reg_value ;
2014-02-16 23:34:30 +04:00
2015-12-15 03:01:33 +03:00
static void fcopy_poll_wrapper ( void * channel )
{
/* Transaction is finished, reset the state here to avoid races. */
fcopy_transaction . state = HVUTIL_READY ;
2020-04-06 03:15:08 +03:00
tasklet_schedule ( & ( ( struct vmbus_channel * ) channel ) - > callback_event ) ;
2015-12-15 03:01:33 +03:00
}
2015-04-12 04:07:45 +03:00
static void fcopy_timeout_func ( struct work_struct * dummy )
2014-02-16 23:34:30 +04:00
{
/*
* If the timer fires , the user - mode component has not responded ;
* process the pending transaction .
*/
fcopy_respond_to_host ( HV_E_FAIL ) ;
2015-12-15 03:01:33 +03:00
hv_poll_channel ( fcopy_transaction . recv_channel , fcopy_poll_wrapper ) ;
2014-02-16 23:34:30 +04:00
}
2016-06-10 03:08:57 +03:00
static void fcopy_register_done ( void )
{
pr_debug ( " FCP: userspace daemon registered \n " ) ;
hv_poll_channel ( fcopy_transaction . recv_channel , fcopy_poll_wrapper ) ;
}
2014-02-16 23:34:30 +04:00
static int fcopy_handle_handshake ( u32 version )
{
2015-04-12 04:07:58 +03:00
u32 our_ver = FCOPY_CURRENT_VERSION ;
2014-02-16 23:34:30 +04:00
switch ( version ) {
2015-04-12 04:07:58 +03:00
case FCOPY_VERSION_0 :
/* Daemon doesn't expect us to reply */
dm_reg_value = version ;
break ;
case FCOPY_VERSION_1 :
/* Daemon expects us to reply with our own version */
2016-06-10 03:08:57 +03:00
if ( hvutil_transport_send ( hvt , & our_ver , sizeof ( our_ver ) ,
fcopy_register_done ) )
2015-04-12 04:07:58 +03:00
return - EFAULT ;
dm_reg_value = version ;
2014-02-16 23:34:30 +04:00
break ;
default :
/*
* For now we will fail the registration .
* If and when we have multiple versions to
* deal with , we will be backward compatible .
* We will add this code when needed .
*/
return - EINVAL ;
}
2016-06-10 03:08:57 +03:00
pr_debug ( " FCP: userspace daemon ver. %d connected \n " , version ) ;
2014-02-16 23:34:30 +04:00
return 0 ;
}
2015-04-12 04:07:53 +03:00
static void fcopy_send_data ( struct work_struct * dummy )
2014-02-16 23:34:30 +04:00
{
2015-08-02 02:08:12 +03:00
struct hv_start_fcopy * smsg_out = NULL ;
2014-02-16 23:34:30 +04:00
int operation = fcopy_transaction . fcopy_msg - > operation ;
struct hv_start_fcopy * smsg_in ;
2015-04-12 04:07:53 +03:00
void * out_src ;
int rc , out_len ;
2014-02-16 23:34:30 +04:00
/*
* The strings sent from the host are encoded in
2022-09-04 18:48:08 +03:00
* utf16 ; convert it to utf8 strings .
2014-02-16 23:34:30 +04:00
* The host assures us that the utf16 strings will not exceed
* the max lengths specified . We will however , reserve room
* for the string terminating character - in the utf16s_utf8s ( )
* function we limit the size of the buffer where the converted
* string is placed to W_MAX_PATH - 1 to guarantee
* that the strings can be properly terminated !
*/
switch ( operation ) {
case START_FILE_COPY :
2015-04-12 04:07:53 +03:00
out_len = sizeof ( struct hv_start_fcopy ) ;
2015-08-02 02:08:12 +03:00
smsg_out = kzalloc ( sizeof ( * smsg_out ) , GFP_KERNEL ) ;
if ( ! smsg_out )
return ;
smsg_out - > hdr . operation = operation ;
2014-02-16 23:34:30 +04:00
smsg_in = ( struct hv_start_fcopy * ) fcopy_transaction . fcopy_msg ;
utf16s_to_utf8s ( ( wchar_t * ) smsg_in - > file_name , W_MAX_PATH ,
UTF16_LITTLE_ENDIAN ,
2015-08-02 02:08:12 +03:00
( __u8 * ) & smsg_out - > file_name , W_MAX_PATH - 1 ) ;
2014-02-16 23:34:30 +04:00
utf16s_to_utf8s ( ( wchar_t * ) smsg_in - > path_name , W_MAX_PATH ,
UTF16_LITTLE_ENDIAN ,
2015-08-02 02:08:12 +03:00
( __u8 * ) & smsg_out - > path_name , W_MAX_PATH - 1 ) ;
2014-02-16 23:34:30 +04:00
2015-08-02 02:08:12 +03:00
smsg_out - > copy_flags = smsg_in - > copy_flags ;
smsg_out - > file_size = smsg_in - > file_size ;
out_src = smsg_out ;
2014-02-16 23:34:30 +04:00
break ;
2017-09-22 09:41:48 +03:00
case WRITE_TO_FILE :
out_src = fcopy_transaction . fcopy_msg ;
out_len = sizeof ( struct hv_do_fcopy ) ;
break ;
2014-02-16 23:34:30 +04:00
default :
2015-04-12 04:07:53 +03:00
out_src = fcopy_transaction . fcopy_msg ;
out_len = fcopy_transaction . recv_len ;
2014-02-16 23:34:30 +04:00
break ;
}
2015-04-12 04:07:53 +03:00
fcopy_transaction . state = HVUTIL_USERSPACE_REQ ;
2016-06-10 03:08:57 +03:00
rc = hvutil_transport_send ( hvt , out_src , out_len , NULL ) ;
2015-04-12 04:07:53 +03:00
if ( rc ) {
pr_debug ( " FCP: failed to communicate to the daemon: %d \n " , rc ) ;
if ( cancel_delayed_work_sync ( & fcopy_timeout_work ) ) {
fcopy_respond_to_host ( HV_E_FAIL ) ;
fcopy_transaction . state = HVUTIL_READY ;
}
}
2015-08-02 02:08:12 +03:00
kfree ( smsg_out ) ;
2014-02-16 23:34:30 +04:00
}
/*
* Send a response back to the host .
*/
static void
fcopy_respond_to_host ( int error )
{
struct icmsg_hdr * icmsghdr ;
u32 buf_len ;
struct vmbus_channel * channel ;
u64 req_id ;
/*
* Copy the global state for completing the transaction . Note that
* only one transaction can be active at a time . This is guaranteed
* by the file copy protocol implemented by the host . Furthermore ,
* the " transaction active " state we maintain ensures that there can
* only be one active transaction at a time .
*/
buf_len = fcopy_transaction . recv_len ;
channel = fcopy_transaction . recv_channel ;
req_id = fcopy_transaction . recv_req_id ;
icmsghdr = ( struct icmsg_hdr * )
& recv_buffer [ sizeof ( struct vmbuspipe_hdr ) ] ;
if ( channel - > onchannel_callback = = NULL )
/*
* We have raced with util driver being unloaded ;
* silently return .
*/
return ;
icmsghdr - > status = error ;
icmsghdr - > icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer , buf_len , req_id ,
VM_PKT_DATA_INBAND , 0 ) ;
}
void hv_fcopy_onchannelcallback ( void * context )
{
struct vmbus_channel * channel = context ;
u32 recvlen ;
u64 requestid ;
struct hv_fcopy_hdr * fcopy_msg ;
struct icmsg_hdr * icmsghdr ;
int fcopy_srv_version ;
2015-12-15 03:01:33 +03:00
if ( fcopy_transaction . state > HVUTIL_READY )
2014-02-16 23:34:30 +04:00
return ;
2020-11-09 13:07:04 +03:00
if ( vmbus_recvpacket ( channel , recv_buffer , HV_HYP_PAGE_SIZE * 2 , & recvlen , & requestid ) ) {
pr_err_ratelimited ( " Fcopy request received. Could not read into recv buf \n " ) ;
2014-02-16 23:34:30 +04:00
return ;
2020-11-09 13:07:04 +03:00
}
if ( ! recvlen )
return ;
/* Ensure recvlen is big enough to read header data */
if ( recvlen < ICMSG_HDR ) {
pr_err_ratelimited ( " Fcopy request received. Packet length too small: %d \n " ,
recvlen ) ;
return ;
}
2014-02-16 23:34:30 +04:00
icmsghdr = ( struct icmsg_hdr * ) & recv_buffer [
sizeof ( struct vmbuspipe_hdr ) ] ;
2020-11-09 13:07:04 +03:00
2014-02-16 23:34:30 +04:00
if ( icmsghdr - > icmsgtype = = ICMSGTYPE_NEGOTIATE ) {
2020-11-09 13:07:04 +03:00
if ( vmbus_prep_negotiate_resp ( icmsghdr ,
recv_buffer , recvlen ,
2017-01-28 22:37:17 +03:00
fw_versions , FW_VER_COUNT ,
fcopy_versions , FCOPY_VER_COUNT ,
2017-01-28 22:37:18 +03:00
NULL , & fcopy_srv_version ) ) {
pr_info ( " FCopy IC version %d.%d \n " ,
fcopy_srv_version > > 16 ,
fcopy_srv_version & 0xFFFF ) ;
}
2020-11-09 13:07:04 +03:00
} else if ( icmsghdr - > icmsgtype = = ICMSGTYPE_FCOPY ) {
/* Ensure recvlen is big enough to contain hv_fcopy_hdr */
if ( recvlen < ICMSG_HDR + sizeof ( struct hv_fcopy_hdr ) ) {
pr_err_ratelimited ( " Invalid Fcopy hdr. Packet length too small: %u \n " ,
recvlen ) ;
return ;
}
fcopy_msg = ( struct hv_fcopy_hdr * ) & recv_buffer [ ICMSG_HDR ] ;
2014-02-16 23:34:30 +04:00
/*
* Stash away this global state for completing the
* transaction ; note transactions are serialized .
*/
fcopy_transaction . recv_len = recvlen ;
fcopy_transaction . recv_req_id = requestid ;
fcopy_transaction . fcopy_msg = fcopy_msg ;
2015-04-12 04:07:49 +03:00
if ( fcopy_transaction . state < HVUTIL_READY ) {
/* Userspace is not registered yet */
fcopy_respond_to_host ( HV_E_FAIL ) ;
return ;
}
fcopy_transaction . state = HVUTIL_HOSTMSG_RECEIVED ;
2014-02-16 23:34:30 +04:00
/*
* Send the information to the user - level daemon .
*/
2015-04-12 04:07:53 +03:00
schedule_work ( & fcopy_send_work ) ;
2015-12-15 03:01:32 +03:00
schedule_delayed_work ( & fcopy_timeout_work ,
HV_UTIL_TIMEOUT * HZ ) ;
2014-02-16 23:34:30 +04:00
return ;
2020-11-09 13:07:04 +03:00
} else {
pr_err_ratelimited ( " Fcopy request received. Invalid msg type: %d \n " ,
icmsghdr - > icmsgtype ) ;
return ;
2014-02-16 23:34:30 +04:00
}
icmsghdr - > icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer , recvlen , requestid ,
VM_PKT_DATA_INBAND , 0 ) ;
}
2015-04-12 04:07:53 +03:00
/* Callback when data is received from userspace */
static int fcopy_on_msg ( void * msg , int len )
2014-02-16 23:34:30 +04:00
{
2015-04-12 04:07:53 +03:00
int * val = ( int * ) msg ;
2014-02-16 23:34:30 +04:00
2015-04-12 04:07:53 +03:00
if ( len ! = sizeof ( int ) )
2014-02-16 23:34:30 +04:00
return - EINVAL ;
2015-04-12 04:07:53 +03:00
if ( fcopy_transaction . state = = HVUTIL_DEVICE_INIT )
return fcopy_handle_handshake ( * val ) ;
2014-02-16 23:34:30 +04:00
2015-04-12 04:07:49 +03:00
if ( fcopy_transaction . state ! = HVUTIL_USERSPACE_REQ )
return - EINVAL ;
2014-02-16 23:34:30 +04:00
/*
* Complete the transaction by forwarding the result
* to the host . But first , cancel the timeout .
*/
2015-04-12 04:07:49 +03:00
if ( cancel_delayed_work_sync ( & fcopy_timeout_work ) ) {
fcopy_transaction . state = HVUTIL_USERSPACE_RECV ;
2015-04-12 04:07:53 +03:00
fcopy_respond_to_host ( * val ) ;
2015-12-15 03:01:33 +03:00
hv_poll_channel ( fcopy_transaction . recv_channel ,
fcopy_poll_wrapper ) ;
2015-04-12 04:07:42 +03:00
}
2014-02-16 23:34:30 +04:00
return 0 ;
}
2015-04-12 04:07:53 +03:00
static void fcopy_on_reset ( void )
2014-02-16 23:34:30 +04:00
{
/*
* The daemon has exited ; reset the state .
*/
2015-04-12 04:07:49 +03:00
fcopy_transaction . state = HVUTIL_DEVICE_INIT ;
2015-01-14 12:55:10 +03:00
2015-04-12 04:07:53 +03:00
if ( cancel_delayed_work_sync ( & fcopy_timeout_work ) )
2015-01-14 12:55:10 +03:00
fcopy_respond_to_host ( HV_E_FAIL ) ;
2014-02-16 23:34:30 +04:00
}
int hv_fcopy_init ( struct hv_util_service * srv )
{
recv_buffer = srv - > recv_buffer ;
2016-02-27 02:13:19 +03:00
fcopy_transaction . recv_channel = srv - > channel ;
2021-04-08 19:14:39 +03:00
fcopy_transaction . recv_channel - > max_pkt_size = HV_HYP_PAGE_SIZE * 2 ;
2014-02-16 23:34:30 +04:00
/*
* When this driver loads , the user level daemon that
* processes the host requests may not yet be running .
* Defer processing channel callbacks until the daemon
* has registered .
*/
2015-04-12 04:07:49 +03:00
fcopy_transaction . state = HVUTIL_DEVICE_INIT ;
2014-02-16 23:34:30 +04:00
2015-04-12 04:07:53 +03:00
hvt = hvutil_transport_init ( fcopy_devname , 0 , 0 ,
fcopy_on_msg , fcopy_on_reset ) ;
if ( ! hvt )
return - EFAULT ;
return 0 ;
2014-02-16 23:34:30 +04:00
}
2020-01-26 08:49:44 +03:00
static void hv_fcopy_cancel_work ( void )
{
cancel_delayed_work_sync ( & fcopy_timeout_work ) ;
cancel_work_sync ( & fcopy_send_work ) ;
}
int hv_fcopy_pre_suspend ( void )
{
struct vmbus_channel * channel = fcopy_transaction . recv_channel ;
struct hv_fcopy_hdr * fcopy_msg ;
/*
* Fake a CANCEL_FCOPY message for the user space daemon in case the
* daemon is in the middle of copying some file . It doesn ' t matter if
* there is already a message pending to be delivered to the user
* space since we force fcopy_transaction . state to be HVUTIL_READY , so
* the user space daemon ' s write ( ) will fail with EINVAL ( see
* fcopy_on_msg ( ) ) , and the daemon will reset the device by closing
* and re - opening it .
*/
fcopy_msg = kzalloc ( sizeof ( * fcopy_msg ) , GFP_KERNEL ) ;
if ( ! fcopy_msg )
return - ENOMEM ;
tasklet_disable ( & channel - > callback_event ) ;
fcopy_msg - > operation = CANCEL_FCOPY ;
hv_fcopy_cancel_work ( ) ;
/* We don't care about the return value. */
hvutil_transport_send ( hvt , fcopy_msg , sizeof ( * fcopy_msg ) , NULL ) ;
kfree ( fcopy_msg ) ;
fcopy_transaction . state = HVUTIL_READY ;
/* tasklet_enable() will be called in hv_fcopy_pre_resume(). */
return 0 ;
}
int hv_fcopy_pre_resume ( void )
{
struct vmbus_channel * channel = fcopy_transaction . recv_channel ;
tasklet_enable ( & channel - > callback_event ) ;
return 0 ;
}
2014-02-16 23:34:30 +04:00
void hv_fcopy_deinit ( void )
{
2015-04-12 04:07:49 +03:00
fcopy_transaction . state = HVUTIL_DEVICE_DYING ;
2020-01-26 08:49:44 +03:00
hv_fcopy_cancel_work ( ) ;
2015-04-12 04:07:53 +03:00
hvutil_transport_destroy ( hvt ) ;
2014-02-16 23:34:30 +04:00
}