1 // SPDX-License-Identifier: GPL-2.0-only
3 * An implementation of host to guest copy functionality for Linux.
5 * Copyright (C) 2023, Microsoft, Inc.
7 * Author : K. Y. Srinivasan <kys@microsoft.com>
8 * Author : Saurabh Sengar <ssengar@microsoft.com>
27 #include <linux/hyperv.h>
28 #include <linux/limits.h>
29 #include "vmbus_bufring.h"
31 #define ICMSGTYPE_NEGOTIATE 0
32 #define ICMSGTYPE_FCOPY 7
34 #define WIN8_SRV_MAJOR 1
35 #define WIN8_SRV_MINOR 1
36 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
38 #define MAX_FOLDER_NAME 15
39 #define MAX_PATH_LEN 15
40 #define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio"
42 #define FCOPY_VER_COUNT 1
43 static const int fcopy_versions
[] = {
47 #define FW_VER_COUNT 1
48 static const int fw_versions
[] = {
52 #define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */
54 unsigned char desc
[HV_RING_SIZE
];
57 static char target_fname
[PATH_MAX
];
58 static unsigned long long filesize
;
60 static int hv_fcopy_create_file(char *file_name
, char *path_name
, __u32 flags
)
62 int error
= HV_E_FAIL
;
67 snprintf(target_fname
, sizeof(target_fname
), "%s/%s",
68 path_name
, file_name
);
71 * Check to see if the path is already in place; if not,
74 while ((q
= strchr(p
, '/')) != NULL
) {
80 if (access(path_name
, F_OK
)) {
81 if (flags
& CREATE_PATH
) {
82 if (mkdir(path_name
, 0755)) {
83 syslog(LOG_ERR
, "Failed to create %s",
88 syslog(LOG_ERR
, "Invalid path: %s", path_name
);
96 if (!access(target_fname
, F_OK
)) {
97 syslog(LOG_INFO
, "File: %s exists", target_fname
);
98 if (!(flags
& OVER_WRITE
)) {
99 error
= HV_ERROR_ALREADY_EXISTS
;
104 target_fd
= open(target_fname
,
105 O_RDWR
| O_CREAT
| O_TRUNC
| O_CLOEXEC
, 0744);
106 if (target_fd
== -1) {
107 syslog(LOG_INFO
, "Open Failed: %s", strerror(errno
));
114 target_fname
[0] = '\0';
118 /* copy the data into the file */
119 static int hv_copy_data(struct hv_do_fcopy
*cpmsg
)
124 len
= pwrite(target_fd
, cpmsg
->data
, cpmsg
->size
, cpmsg
->offset
);
126 filesize
+= cpmsg
->size
;
127 if (len
!= cpmsg
->size
) {
130 ret
= HV_ERROR_DISK_FULL
;
136 syslog(LOG_ERR
, "pwrite failed to write %llu bytes: %ld (%s)",
137 filesize
, (long)len
, strerror(errno
));
143 static int hv_copy_finished(void)
146 target_fname
[0] = '\0';
151 static void print_usage(char *argv
[])
153 fprintf(stderr
, "Usage: %s [options]\n"
155 " -n, --no-daemon stay in foreground, don't daemonize\n"
156 " -h, --help print this help\n", argv
[0]);
159 static bool vmbus_prep_negotiate_resp(struct icmsg_hdr
*icmsghdrp
, unsigned char *buf
,
160 unsigned int buflen
, const int *fw_version
, int fw_vercnt
,
161 const int *srv_version
, int srv_vercnt
,
162 int *nego_fw_version
, int *nego_srv_version
)
164 int icframe_major
, icframe_minor
;
165 int icmsg_major
, icmsg_minor
;
166 int fw_major
, fw_minor
;
167 int srv_major
, srv_minor
;
169 bool found_match
= false;
170 struct icmsg_negotiate
*negop
;
172 /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
173 if (buflen
< ICMSG_HDR
+ offsetof(struct icmsg_negotiate
, reserved
)) {
174 syslog(LOG_ERR
, "Invalid icmsg negotiate");
178 icmsghdrp
->icmsgsize
= 0x10;
179 negop
= (struct icmsg_negotiate
*)&buf
[ICMSG_HDR
];
181 icframe_major
= negop
->icframe_vercnt
;
184 icmsg_major
= negop
->icmsg_vercnt
;
187 /* Validate negop packet */
188 if (icframe_major
> IC_VERSION_NEGOTIATION_MAX_VER_COUNT
||
189 icmsg_major
> IC_VERSION_NEGOTIATION_MAX_VER_COUNT
||
190 ICMSG_NEGOTIATE_PKT_SIZE(icframe_major
, icmsg_major
) > buflen
) {
191 syslog(LOG_ERR
, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
192 icframe_major
, icmsg_major
);
197 * Select the framework version number we will
201 for (i
= 0; i
< fw_vercnt
; i
++) {
202 fw_major
= (fw_version
[i
] >> 16);
203 fw_minor
= (fw_version
[i
] & 0xFFFF);
205 for (j
= 0; j
< negop
->icframe_vercnt
; j
++) {
206 if (negop
->icversion_data
[j
].major
== fw_major
&&
207 negop
->icversion_data
[j
].minor
== fw_minor
) {
208 icframe_major
= negop
->icversion_data
[j
].major
;
209 icframe_minor
= negop
->icversion_data
[j
].minor
;
224 for (i
= 0; i
< srv_vercnt
; i
++) {
225 srv_major
= (srv_version
[i
] >> 16);
226 srv_minor
= (srv_version
[i
] & 0xFFFF);
228 for (j
= negop
->icframe_vercnt
;
229 (j
< negop
->icframe_vercnt
+ negop
->icmsg_vercnt
);
231 if (negop
->icversion_data
[j
].major
== srv_major
&&
232 negop
->icversion_data
[j
].minor
== srv_minor
) {
233 icmsg_major
= negop
->icversion_data
[j
].major
;
234 icmsg_minor
= negop
->icversion_data
[j
].minor
;
245 * Respond with the framework and service
246 * version numbers we can support.
250 negop
->icframe_vercnt
= 0;
251 negop
->icmsg_vercnt
= 0;
253 negop
->icframe_vercnt
= 1;
254 negop
->icmsg_vercnt
= 1;
258 *nego_fw_version
= (icframe_major
<< 16) | icframe_minor
;
260 if (nego_srv_version
)
261 *nego_srv_version
= (icmsg_major
<< 16) | icmsg_minor
;
263 negop
->icversion_data
[0].major
= icframe_major
;
264 negop
->icversion_data
[0].minor
= icframe_minor
;
265 negop
->icversion_data
[1].major
= icmsg_major
;
266 negop
->icversion_data
[1].minor
= icmsg_minor
;
271 static void wcstoutf8(char *dest
, const __u16
*src
, size_t dest_size
)
275 while (len
< dest_size
) {
277 dest
[len
++] = (char)(*src
++);
285 static int hv_fcopy_start(struct hv_start_fcopy
*smsg_in
)
287 setlocale(LC_ALL
, "en_US.utf8");
288 size_t file_size
, path_size
;
289 char *file_name
, *path_name
;
290 char *in_file_name
= (char *)smsg_in
->file_name
;
291 char *in_path_name
= (char *)smsg_in
->path_name
;
293 file_size
= wcstombs(NULL
, (const wchar_t *restrict
)in_file_name
, 0) + 1;
294 path_size
= wcstombs(NULL
, (const wchar_t *restrict
)in_path_name
, 0) + 1;
296 file_name
= (char *)malloc(file_size
* sizeof(char));
297 path_name
= (char *)malloc(path_size
* sizeof(char));
299 if (!file_name
|| !path_name
) {
302 syslog(LOG_ERR
, "Can't allocate memory for file name and/or path name");
306 wcstoutf8(file_name
, (__u16
*)in_file_name
, file_size
);
307 wcstoutf8(path_name
, (__u16
*)in_path_name
, path_size
);
309 return hv_fcopy_create_file(file_name
, path_name
, smsg_in
->copy_flags
);
312 static int hv_fcopy_send_data(struct hv_fcopy_hdr
*fcopy_msg
, int recvlen
)
314 int operation
= fcopy_msg
->operation
;
317 * The strings sent from the host are encoded in
318 * utf16; convert it to utf8 strings.
319 * The host assures us that the utf16 strings will not exceed
320 * the max lengths specified. We will however, reserve room
321 * for the string terminating character - in the utf16s_utf8s()
322 * function we limit the size of the buffer where the converted
323 * string is placed to W_MAX_PATH -1 to guarantee
324 * that the strings can be properly terminated!
328 case START_FILE_COPY
:
329 return hv_fcopy_start((struct hv_start_fcopy
*)fcopy_msg
);
331 return hv_copy_data((struct hv_do_fcopy
*)fcopy_msg
);
333 return hv_copy_finished();
339 /* process the packet recv from host */
340 static int fcopy_pkt_process(struct vmbus_br
*txbr
)
342 int ret
, offset
, pktlen
;
343 int fcopy_srv_version
;
344 const struct vmbus_chanpkt_hdr
*pkt
;
345 struct hv_fcopy_hdr
*fcopy_msg
;
346 struct icmsg_hdr
*icmsghdr
;
348 pkt
= (const struct vmbus_chanpkt_hdr
*)desc
;
349 offset
= pkt
->hlen
<< 3;
350 pktlen
= (pkt
->tlen
<< 3) - offset
;
351 icmsghdr
= (struct icmsg_hdr
*)&desc
[offset
+ sizeof(struct vmbuspipe_hdr
)];
352 icmsghdr
->status
= HV_E_FAIL
;
354 if (icmsghdr
->icmsgtype
== ICMSGTYPE_NEGOTIATE
) {
355 if (vmbus_prep_negotiate_resp(icmsghdr
, desc
+ offset
, pktlen
, fw_versions
,
356 FW_VER_COUNT
, fcopy_versions
, FCOPY_VER_COUNT
,
357 NULL
, &fcopy_srv_version
)) {
358 syslog(LOG_INFO
, "FCopy IC version %d.%d",
359 fcopy_srv_version
>> 16, fcopy_srv_version
& 0xFFFF);
360 icmsghdr
->status
= 0;
362 } else if (icmsghdr
->icmsgtype
== ICMSGTYPE_FCOPY
) {
363 /* Ensure recvlen is big enough to contain hv_fcopy_hdr */
364 if (pktlen
< ICMSG_HDR
+ sizeof(struct hv_fcopy_hdr
)) {
365 syslog(LOG_ERR
, "Invalid Fcopy hdr. Packet length too small: %u",
370 fcopy_msg
= (struct hv_fcopy_hdr
*)&desc
[offset
+ ICMSG_HDR
];
371 icmsghdr
->status
= hv_fcopy_send_data(fcopy_msg
, pktlen
);
374 icmsghdr
->icflags
= ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE
;
375 ret
= rte_vmbus_chan_send(txbr
, 0x6, desc
+ offset
, pktlen
, 0);
377 syslog(LOG_ERR
, "Write to ringbuffer failed err: %d", ret
);
384 static void fcopy_get_first_folder(char *path
, char *chan_no
)
386 DIR *dir
= opendir(path
);
387 struct dirent
*entry
;
390 syslog(LOG_ERR
, "Failed to open directory (errno=%s).\n", strerror(errno
));
394 while ((entry
= readdir(dir
)) != NULL
) {
395 if (entry
->d_type
== DT_DIR
&& strcmp(entry
->d_name
, ".") != 0 &&
396 strcmp(entry
->d_name
, "..") != 0) {
397 strcpy(chan_no
, entry
->d_name
);
405 int main(int argc
, char *argv
[])
407 int fcopy_fd
= -1, tmp
= 1;
408 int daemonize
= 1, long_index
= 0, opt
, ret
= -EINVAL
;
409 struct vmbus_br txbr
, rxbr
;
411 uint32_t len
= HV_RING_SIZE
;
412 char uio_name
[MAX_FOLDER_NAME
] = {0};
413 char uio_dev_path
[MAX_PATH_LEN
] = {0};
415 static struct option long_options
[] = {
416 {"help", no_argument
, 0, 'h' },
417 {"no-daemon", no_argument
, 0, 'n' },
421 while ((opt
= getopt_long(argc
, argv
, "hn", long_options
,
422 &long_index
)) != -1) {
434 if (daemonize
&& daemon(1, 0)) {
435 syslog(LOG_ERR
, "daemon() failed; error: %s", strerror(errno
));
439 openlog("HV_UIO_FCOPY", 0, LOG_USER
);
440 syslog(LOG_INFO
, "starting; pid is:%d", getpid());
442 fcopy_get_first_folder(FCOPY_UIO
, uio_name
);
443 snprintf(uio_dev_path
, sizeof(uio_dev_path
), "/dev/%s", uio_name
);
444 fcopy_fd
= open(uio_dev_path
, O_RDWR
);
447 syslog(LOG_ERR
, "open %s failed; error: %d %s",
448 uio_dev_path
, errno
, strerror(errno
));
453 ring
= vmbus_uio_map(&fcopy_fd
, HV_RING_SIZE
);
456 syslog(LOG_ERR
, "mmap ringbuffer failed; error: %d %s", ret
, strerror(ret
));
459 vmbus_br_setup(&txbr
, ring
, HV_RING_SIZE
);
460 vmbus_br_setup(&rxbr
, (char *)ring
+ HV_RING_SIZE
, HV_RING_SIZE
);
466 * In this loop we process fcopy messages after the
467 * handshake is complete.
469 ret
= pread(fcopy_fd
, &tmp
, sizeof(int), 0);
471 syslog(LOG_ERR
, "pread failed: %s", strerror(errno
));
476 ret
= rte_vmbus_chan_recv_raw(&rxbr
, desc
, &len
);
477 if (unlikely(ret
<= 0)) {
478 /* This indicates a failure to communicate (or worse) */
479 syslog(LOG_ERR
, "VMBus channel recv error: %d", ret
);
481 ret
= fcopy_pkt_process(&txbr
);
486 if ((write(fcopy_fd
, &tmp
, sizeof(int))) != sizeof(int)) {
488 syslog(LOG_ERR
, "Signal to host failed: %s\n", strerror(ret
));