1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * An implementation of host to guest copy functionality for Linux.
4 *
5 * Copyright (C) 2023, Microsoft, Inc.
6 *
7 * Author : K. Y. Srinivasan <kys@microsoft.com>
8 * Author : Saurabh Sengar <ssengar@microsoft.com>
9 *
10 */
11
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <getopt.h>
16 #include <locale.h>
17 #include <stdbool.h>
18 #include <stddef.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <syslog.h>
24 #include <unistd.h>
25 #include <wchar.h>
26 #include <sys/stat.h>
27 #include <linux/hyperv.h>
28 #include <linux/limits.h>
29 #include "vmbus_bufring.h"
30
31 #define ICMSGTYPE_NEGOTIATE 0
32 #define ICMSGTYPE_FCOPY 7
33
34 #define WIN8_SRV_MAJOR 1
35 #define WIN8_SRV_MINOR 1
36 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
37
38 #define FCOPY_DEVICE_PATH(subdir) \
39 "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/" #subdir
40 #define FCOPY_UIO_PATH FCOPY_DEVICE_PATH(uio)
41 #define FCOPY_CHANNELS_PATH FCOPY_DEVICE_PATH(channels)
42
43 #define FCOPY_VER_COUNT 1
44 static const int fcopy_versions[] = {
45 WIN8_SRV_VERSION
46 };
47
48 #define FW_VER_COUNT 1
49 static const int fw_versions[] = {
50 UTIL_FW_VERSION
51 };
52
get_ring_buffer_size(void)53 static uint32_t get_ring_buffer_size(void)
54 {
55 char ring_path[PATH_MAX];
56 DIR *dir;
57 struct dirent *entry;
58 struct stat st;
59 uint32_t ring_size = 0;
60 int retry_count = 0;
61
62 /* Find the channel directory */
63 dir = opendir(FCOPY_CHANNELS_PATH);
64 if (!dir) {
65 usleep(100 * 1000); /* Avoid race with kernel, wait 100ms and retry once */
66 dir = opendir(FCOPY_CHANNELS_PATH);
67 if (!dir) {
68 syslog(LOG_ERR, "Failed to open channels directory: %s", strerror(errno));
69 return 0;
70 }
71 }
72
73 retry_once:
74 while ((entry = readdir(dir)) != NULL) {
75 if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 &&
76 strcmp(entry->d_name, "..") != 0) {
77 snprintf(ring_path, sizeof(ring_path), "%s/%s/ring",
78 FCOPY_CHANNELS_PATH, entry->d_name);
79
80 if (stat(ring_path, &st) == 0) {
81 /*
82 * stat returns size of Tx, Rx rings combined,
83 * so take half of it for individual ring size.
84 */
85 ring_size = (uint32_t)st.st_size / 2;
86 syslog(LOG_INFO, "Ring buffer size from %s: %u bytes",
87 ring_path, ring_size);
88 break;
89 }
90 }
91 }
92
93 if (!ring_size && retry_count == 0) {
94 retry_count = 1;
95 rewinddir(dir);
96 usleep(100 * 1000); /* Wait 100ms and retry once */
97 goto retry_once;
98 }
99
100 closedir(dir);
101
102 if (!ring_size)
103 syslog(LOG_ERR, "Could not determine ring size");
104
105 return ring_size;
106 }
107
108 static unsigned char *desc;
109
110 static int target_fd;
111 static char target_fname[PATH_MAX];
112 static unsigned long long filesize;
113
hv_fcopy_create_file(char * file_name,char * path_name,__u32 flags)114 static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags)
115 {
116 int error = HV_E_FAIL;
117 char *q, *p;
118
119 filesize = 0;
120 p = path_name;
121 if (snprintf(target_fname, sizeof(target_fname), "%s/%s",
122 path_name, file_name) >= sizeof(target_fname)) {
123 syslog(LOG_ERR, "target file name is too long: %s/%s", path_name, file_name);
124 goto done;
125 }
126
127 /*
128 * Check to see if the path is already in place; if not,
129 * create if required.
130 */
131 while ((q = strchr(p, '/')) != NULL) {
132 if (q == p) {
133 p++;
134 continue;
135 }
136 *q = '\0';
137 if (access(path_name, F_OK)) {
138 if (flags & CREATE_PATH) {
139 if (mkdir(path_name, 0755)) {
140 syslog(LOG_ERR, "Failed to create %s",
141 path_name);
142 goto done;
143 }
144 } else {
145 syslog(LOG_ERR, "Invalid path: %s", path_name);
146 goto done;
147 }
148 }
149 p = q + 1;
150 *q = '/';
151 }
152
153 if (!access(target_fname, F_OK)) {
154 syslog(LOG_INFO, "File: %s exists", target_fname);
155 if (!(flags & OVER_WRITE)) {
156 error = HV_ERROR_ALREADY_EXISTS;
157 goto done;
158 }
159 }
160
161 target_fd = open(target_fname,
162 O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
163 if (target_fd == -1) {
164 syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
165 goto done;
166 }
167
168 error = 0;
169 done:
170 if (error)
171 target_fname[0] = '\0';
172 return error;
173 }
174
175 /* copy the data into the file */
hv_copy_data(struct hv_do_fcopy * cpmsg)176 static int hv_copy_data(struct hv_do_fcopy *cpmsg)
177 {
178 ssize_t len;
179 int ret = 0;
180
181 len = pwrite(target_fd, cpmsg->data, cpmsg->size, cpmsg->offset);
182
183 filesize += cpmsg->size;
184 if (len != cpmsg->size) {
185 switch (errno) {
186 case ENOSPC:
187 ret = HV_ERROR_DISK_FULL;
188 break;
189 default:
190 ret = HV_E_FAIL;
191 break;
192 }
193 syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
194 filesize, (long)len, strerror(errno));
195 }
196
197 return ret;
198 }
199
hv_copy_finished(void)200 static int hv_copy_finished(void)
201 {
202 close(target_fd);
203 target_fname[0] = '\0';
204
205 return 0;
206 }
207
print_usage(char * argv[])208 static void print_usage(char *argv[])
209 {
210 fprintf(stderr, "Usage: %s [options]\n"
211 "Options are:\n"
212 " -n, --no-daemon stay in foreground, don't daemonize\n"
213 " -h, --help print this help\n", argv[0]);
214 }
215
vmbus_prep_negotiate_resp(struct icmsg_hdr * icmsghdrp,unsigned char * buf,unsigned int buflen,const int * fw_version,int fw_vercnt,const int * srv_version,int srv_vercnt,int * nego_fw_version,int * nego_srv_version)216 static bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, unsigned char *buf,
217 unsigned int buflen, const int *fw_version, int fw_vercnt,
218 const int *srv_version, int srv_vercnt,
219 int *nego_fw_version, int *nego_srv_version)
220 {
221 int icframe_major, icframe_minor;
222 int icmsg_major, icmsg_minor;
223 int fw_major, fw_minor;
224 int srv_major, srv_minor;
225 int i, j;
226 bool found_match = false;
227 struct icmsg_negotiate *negop;
228
229 /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
230 if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) {
231 syslog(LOG_ERR, "Invalid icmsg negotiate");
232 return false;
233 }
234
235 icmsghdrp->icmsgsize = 0x10;
236 negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR];
237
238 icframe_major = negop->icframe_vercnt;
239 icframe_minor = 0;
240
241 icmsg_major = negop->icmsg_vercnt;
242 icmsg_minor = 0;
243
244 /* Validate negop packet */
245 if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
246 icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
247 ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) {
248 syslog(LOG_ERR, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
249 icframe_major, icmsg_major);
250 goto fw_error;
251 }
252
253 /*
254 * Select the framework version number we will
255 * support.
256 */
257
258 for (i = 0; i < fw_vercnt; i++) {
259 fw_major = (fw_version[i] >> 16);
260 fw_minor = (fw_version[i] & 0xFFFF);
261
262 for (j = 0; j < negop->icframe_vercnt; j++) {
263 if (negop->icversion_data[j].major == fw_major &&
264 negop->icversion_data[j].minor == fw_minor) {
265 icframe_major = negop->icversion_data[j].major;
266 icframe_minor = negop->icversion_data[j].minor;
267 found_match = true;
268 break;
269 }
270 }
271
272 if (found_match)
273 break;
274 }
275
276 if (!found_match)
277 goto fw_error;
278
279 found_match = false;
280
281 for (i = 0; i < srv_vercnt; i++) {
282 srv_major = (srv_version[i] >> 16);
283 srv_minor = (srv_version[i] & 0xFFFF);
284
285 for (j = negop->icframe_vercnt;
286 (j < negop->icframe_vercnt + negop->icmsg_vercnt);
287 j++) {
288 if (negop->icversion_data[j].major == srv_major &&
289 negop->icversion_data[j].minor == srv_minor) {
290 icmsg_major = negop->icversion_data[j].major;
291 icmsg_minor = negop->icversion_data[j].minor;
292 found_match = true;
293 break;
294 }
295 }
296
297 if (found_match)
298 break;
299 }
300
301 /*
302 * Respond with the framework and service
303 * version numbers we can support.
304 */
305 fw_error:
306 if (!found_match) {
307 negop->icframe_vercnt = 0;
308 negop->icmsg_vercnt = 0;
309 } else {
310 negop->icframe_vercnt = 1;
311 negop->icmsg_vercnt = 1;
312 }
313
314 if (nego_fw_version)
315 *nego_fw_version = (icframe_major << 16) | icframe_minor;
316
317 if (nego_srv_version)
318 *nego_srv_version = (icmsg_major << 16) | icmsg_minor;
319
320 negop->icversion_data[0].major = icframe_major;
321 negop->icversion_data[0].minor = icframe_minor;
322 negop->icversion_data[1].major = icmsg_major;
323 negop->icversion_data[1].minor = icmsg_minor;
324
325 return found_match;
326 }
327
wcstoutf8(char * dest,const __u16 * src,size_t dest_size)328 static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size)
329 {
330 size_t len = 0;
331
332 while (len < dest_size && *src) {
333 if (src[len] < 0x80)
334 dest[len++] = (char)(*src++);
335 else
336 dest[len++] = 'X';
337 }
338
339 dest[len] = '\0';
340 }
341
hv_fcopy_start(struct hv_start_fcopy * smsg_in)342 static int hv_fcopy_start(struct hv_start_fcopy *smsg_in)
343 {
344 /*
345 * file_name and path_name should have same length with appropriate
346 * member of hv_start_fcopy.
347 */
348 char file_name[W_MAX_PATH], path_name[W_MAX_PATH];
349
350 setlocale(LC_ALL, "en_US.utf8");
351 wcstoutf8(file_name, smsg_in->file_name, W_MAX_PATH - 1);
352 wcstoutf8(path_name, smsg_in->path_name, W_MAX_PATH - 1);
353
354 return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags);
355 }
356
hv_fcopy_send_data(struct hv_fcopy_hdr * fcopy_msg,int recvlen)357 static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen)
358 {
359 int operation = fcopy_msg->operation;
360
361 /*
362 * The strings sent from the host are encoded in
363 * utf16; convert it to utf8 strings.
364 * The host assures us that the utf16 strings will not exceed
365 * the max lengths specified. We will however, reserve room
366 * for the string terminating character - in the utf16s_utf8s()
367 * function we limit the size of the buffer where the converted
368 * string is placed to W_MAX_PATH -1 to guarantee
369 * that the strings can be properly terminated!
370 */
371
372 switch (operation) {
373 case START_FILE_COPY:
374 return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg);
375 case WRITE_TO_FILE:
376 return hv_copy_data((struct hv_do_fcopy *)fcopy_msg);
377 case COMPLETE_FCOPY:
378 return hv_copy_finished();
379 }
380
381 return HV_E_FAIL;
382 }
383
384 /* process the packet recv from host */
fcopy_pkt_process(struct vmbus_br * txbr)385 static int fcopy_pkt_process(struct vmbus_br *txbr)
386 {
387 int ret, offset, pktlen;
388 int fcopy_srv_version;
389 const struct vmbus_chanpkt_hdr *pkt;
390 struct hv_fcopy_hdr *fcopy_msg;
391 struct icmsg_hdr *icmsghdr;
392
393 pkt = (const struct vmbus_chanpkt_hdr *)desc;
394 offset = pkt->hlen << 3;
395 pktlen = (pkt->tlen << 3) - offset;
396 icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)];
397 icmsghdr->status = HV_E_FAIL;
398
399 if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
400 if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions,
401 FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT,
402 NULL, &fcopy_srv_version)) {
403 syslog(LOG_INFO, "FCopy IC version %d.%d",
404 fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF);
405 icmsghdr->status = 0;
406 }
407 } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
408 /* Ensure recvlen is big enough to contain hv_fcopy_hdr */
409 if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
410 syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u",
411 pktlen);
412 return -ENOBUFS;
413 }
414
415 fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR];
416 icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen);
417 }
418
419 icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
420 ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0);
421 if (ret) {
422 syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret);
423 return ret;
424 }
425
426 return 0;
427 }
428
fcopy_get_first_folder(char * path,char * chan_no)429 static void fcopy_get_first_folder(char *path, char *chan_no)
430 {
431 DIR *dir = opendir(path);
432 struct dirent *entry;
433
434 if (!dir) {
435 syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno));
436 return;
437 }
438
439 while ((entry = readdir(dir)) != NULL) {
440 if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 &&
441 strcmp(entry->d_name, "..") != 0) {
442 strcpy(chan_no, entry->d_name);
443 break;
444 }
445 }
446
447 closedir(dir);
448 }
449
main(int argc,char * argv[])450 int main(int argc, char *argv[])
451 {
452 int fcopy_fd = -1, tmp = 1;
453 int daemonize = 1, long_index = 0, opt, ret = -EINVAL;
454 struct vmbus_br txbr, rxbr;
455 void *ring;
456 uint32_t ring_size, len;
457 char uio_name[NAME_MAX] = {0};
458 char uio_dev_path[PATH_MAX] = {0};
459
460 static struct option long_options[] = {
461 {"help", no_argument, 0, 'h' },
462 {"no-daemon", no_argument, 0, 'n' },
463 {0, 0, 0, 0 }
464 };
465
466 while ((opt = getopt_long(argc, argv, "hn", long_options,
467 &long_index)) != -1) {
468 switch (opt) {
469 case 'n':
470 daemonize = 0;
471 break;
472 case 'h':
473 default:
474 print_usage(argv);
475 goto exit;
476 }
477 }
478
479 if (daemonize && daemon(1, 0)) {
480 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
481 goto exit;
482 }
483
484 openlog("HV_UIO_FCOPY", 0, LOG_USER);
485 syslog(LOG_INFO, "starting; pid is:%d", getpid());
486
487 ring_size = get_ring_buffer_size();
488 if (!ring_size) {
489 ret = -ENODEV;
490 goto exit;
491 }
492
493 desc = malloc(ring_size * sizeof(unsigned char));
494 if (!desc) {
495 syslog(LOG_ERR, "malloc failed for desc buffer");
496 ret = -ENOMEM;
497 goto exit;
498 }
499
500 fcopy_get_first_folder(FCOPY_UIO_PATH, uio_name);
501 snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name);
502 fcopy_fd = open(uio_dev_path, O_RDWR);
503
504 if (fcopy_fd < 0) {
505 syslog(LOG_ERR, "open %s failed; error: %d %s",
506 uio_dev_path, errno, strerror(errno));
507 ret = fcopy_fd;
508 goto free_desc;
509 }
510
511 ring = vmbus_uio_map(&fcopy_fd, ring_size);
512 if (!ring) {
513 ret = errno;
514 syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret));
515 goto close;
516 }
517 vmbus_br_setup(&txbr, ring, ring_size);
518 vmbus_br_setup(&rxbr, (char *)ring + ring_size, ring_size);
519
520 rxbr.vbr->imask = 0;
521
522 while (1) {
523 /*
524 * In this loop we process fcopy messages after the
525 * handshake is complete.
526 */
527 ret = pread(fcopy_fd, &tmp, sizeof(int), 0);
528 if (ret < 0) {
529 if (errno == EINTR || errno == EAGAIN)
530 continue;
531 syslog(LOG_ERR, "pread failed: %s", strerror(errno));
532 goto close;
533 }
534
535 len = ring_size;
536 ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len);
537 if (unlikely(ret <= 0)) {
538 /* This indicates a failure to communicate (or worse) */
539 syslog(LOG_ERR, "VMBus channel recv error: %d", ret);
540 } else {
541 ret = fcopy_pkt_process(&txbr);
542 if (ret < 0)
543 goto close;
544
545 /* Signal host */
546 if ((write(fcopy_fd, &tmp, sizeof(int))) != sizeof(int)) {
547 ret = errno;
548 syslog(LOG_ERR, "Signal to host failed: %s\n", strerror(ret));
549 goto close;
550 }
551 }
552 }
553 close:
554 close(fcopy_fd);
555 free_desc:
556 free(desc);
557 exit:
558 return ret;
559 }
560