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
44static const int fcopy_versions[] = {
45 WIN8_SRV_VERSION
46};
47
48#define FW_VER_COUNT 1
49static const int fw_versions[] = {
50 UTIL_FW_VERSION
51};
52
53static 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
73retry_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(buf: ring_path, size: sizeof(ring_path), fmt: "%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
108static unsigned char *desc;
109
110static int target_fd;
111static char target_fname[PATH_MAX];
112static unsigned long long filesize;
113
114static 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(buf: target_fname, size: sizeof(target_fname), fmt: "%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;
169done:
170 if (error)
171 target_fname[0] = '\0';
172 return error;
173}
174
175/* copy the data into the file */
176static 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
200static int hv_copy_finished(void)
201{
202 close(target_fd);
203 target_fname[0] = '\0';
204
205 return 0;
206}
207
208static 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
216static 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 */
305fw_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
328static 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
342static 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(dest: file_name, src: smsg_in->file_name, W_MAX_PATH - 1);
352 wcstoutf8(dest: path_name, src: smsg_in->path_name, W_MAX_PATH - 1);
353
354 return hv_fcopy_create_file(file_name, path_name, flags: smsg_in->copy_flags);
355}
356
357static 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(smsg_in: (struct hv_start_fcopy *)fcopy_msg);
375 case WRITE_TO_FILE:
376 return hv_copy_data(cpmsg: (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 */
385static 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(icmsghdrp: icmsghdr, buf: desc + offset, buflen: pktlen, fw_version: fw_versions,
401 FW_VER_COUNT, srv_version: fcopy_versions, FCOPY_VER_COUNT,
402 NULL, nego_srv_version: &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, recvlen: pktlen);
417 }
418
419 icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
420 ret = rte_vmbus_chan_send(txbr, type: 0x6, data: desc + offset, dlen: pktlen, flags: 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
429static 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
450int 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, chan_no: uio_name);
501 snprintf(buf: uio_dev_path, size: sizeof(uio_dev_path), fmt: "/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(fd: &fcopy_fd, size: 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(br: &txbr, buf: ring, blen: ring_size);
518 vmbus_br_setup(br: &rxbr, buf: (char *)ring + ring_size, blen: 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: &rxbr, data: desc, len: &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: &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 }
553close:
554 close(fcopy_fd);
555free_desc:
556 free(desc);
557exit:
558 return ret;
559}
560

source code of linux/tools/hv/hv_fcopy_uio_daemon.c