- Add memory allocation check in hv_fcopy_start (94e86b17)

- suppress the invalid warning for packed member alignment (207e03b0)
- Add new fcopy application based on uio driver (82b0945c)
- Add vmbus_bufring (45bab4d7)
- kvp: Handle IPv4 and Ipv6 combination for keyfile format (f971f6dd)
- kvp: Some small fixes for handling NM keyfiles (c3803203)
- kvp: Support for keyfile based connection profile (42999c90)
- kvp: remove unnecessary (void*) conversions (22589542)
- Remove an extraneous "the" (f15f39fa)
- change http to https in hv_kvp_daemon.c (fa52a4b2)
- replace the copy of include/linux/hyperv.h with include/uapi/linux/hyperv.h (6de74d10)
- merge individual udev rules files into a single rules file
- package only files, not directories already owned by filesystem.rpm
- remove braces from rpm spec macros
- remove obsolete Group tag
- replace RPM_BUILD_ROOT with buildroot
- use a meaningful name for the UAPI include file
- use a meaningful variable name for ifcfg in hv_set_ifconfig.sh

OBS-URL: https://build.opensuse.org/package/show/Virtualization/hyper-v?expand=0&rev=159
This commit is contained in:
Olaf Hering 2024-10-22 13:05:39 +00:00 committed by Git OBS Bridge
commit cbd6b4685f
18 changed files with 5312 additions and 0 deletions

23
.gitattributes vendored Normal file
View File

@ -0,0 +1,23 @@
## Default LFS
*.7z filter=lfs diff=lfs merge=lfs -text
*.bsp filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.gem filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text
*.lz filter=lfs diff=lfs merge=lfs -text
*.lzma filter=lfs diff=lfs merge=lfs -text
*.obscpio filter=lfs diff=lfs merge=lfs -text
*.oxt filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.rpm filter=lfs diff=lfs merge=lfs -text
*.tbz filter=lfs diff=lfs merge=lfs -text
*.tbz2 filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.txz filter=lfs diff=lfs merge=lfs -text
*.whl filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.osc

540
hyper-v.changes Normal file
View File

@ -0,0 +1,540 @@
-------------------------------------------------------------------
Tue Oct 22 22:22:22 UTC 2024 - ohering@suse.de
- Add memory allocation check in hv_fcopy_start (94e86b17)
- suppress the invalid warning for packed member alignment (207e03b0)
- Add new fcopy application based on uio driver (82b0945c)
- Add vmbus_bufring (45bab4d7)
- kvp: Handle IPv4 and Ipv6 combination for keyfile format (f971f6dd)
- kvp: Some small fixes for handling NM keyfiles (c3803203)
- kvp: Support for keyfile based connection profile (42999c90)
- kvp: remove unnecessary (void*) conversions (22589542)
- Remove an extraneous "the" (f15f39fa)
- change http to https in hv_kvp_daemon.c (fa52a4b2)
- replace the copy of include/linux/hyperv.h with include/uapi/linux/hyperv.h (6de74d10)
- merge individual udev rules files into a single rules file
- package only files, not directories already owned by filesystem.rpm
- remove braces from rpm spec macros
- remove obsolete Group tag
- replace RPM_BUILD_ROOT with buildroot
- use a meaningful name for the UAPI include file
- use a meaningful variable name for ifcfg in hv_set_ifconfig.sh
-------------------------------------------------------------------
Tue Apr 23 10:01:10 UTC 2024 - pgajdos@suse.com
- remove dependency on /usr/bin/python3 using
%python3_fix_shebang macro, [bsc#1212476]
-------------------------------------------------------------------
Thu Feb 22 07:56:09 UTC 2024 - Dominique Leuenberger <dimstar@opensuse.org>
- Use %patch -P N instead of deprecated %patchN.
-------------------------------------------------------------------
Thu Aug 20 12:12:12 UTC 2020 - ohering@suse.de
- Allow daemon restart again (bsc#1116957)
Since some time the kernel moved from netlink sockets to char
devices, which is supposed to handle a connection reopen for the
kernel<->daemon interface
-------------------------------------------------------------------
Wed Jul 22 12:12:12 UTC 2020 - ohering@suse.de
- Remove dependency to network-online.target now that gethostname
is used in kvp_daemon (bsc#1174443, bsc#1174444)
- Reopen the devices if read() or write() returns errors (9fc3c01a)
- Use either python2 or python3 for lsvmbus (bsc#1093910)
- Remove sysv init scripts
-------------------------------------------------------------------
Wed Jul 22 09:12:44 UTC 2020 - Guillaume GARDET <guillaume.gardet@opensuse.org>
- Enable build on aarch64
-------------------------------------------------------------------
Thu Feb 20 16:16:16 UTC 2020 - ohering@suse.de
- Revert previous non-upstream change for async name resolution
Just use gethostname via hyper-v.kvp.gethostname.patch (bsc#1100758)
-------------------------------------------------------------------
Fri Nov 29 16:41:25 UTC 2019 - ohering@suse.de
- Update lsvmbus interpreter from python(1) to python3(1) again
because only SLE12 lacked proper python3 support (bsc#1093910)
-------------------------------------------------------------------
Wed Nov 27 09:08:07 UTC 2019 - ohering@suse.de
- async name resolution in kvp_daemon (bsc#1100758)
- kvp: eliminate 'may be used uninitialized' warning (89eb4d8d)
- fix typos in toolchain (2d35c660)
- fixed Python pep8/flake8 warnings for lsvmbus (5912e791)
- Replace GPLv2 boilerplate/reference with SPDX (43aa3132)
- Fix a warning of buffer overflow with gcc 8.0.1 (4fcba780)
-------------------------------------------------------------------
Tue Oct 9 09:27:45 UTC 2018 - ohering@suse.de
- fcopy: set 'error' in case an unknown operation was requested (c2d68afb)
-------------------------------------------------------------------
Mon Oct 8 06:48:04 UTC 2018 - ohering@suse.de
- Update lsvmbus interpreter from python3(1) to python(1)
because SLE12 lacks python3 support (bsc#1093910)
-------------------------------------------------------------------
Tue Sep 25 08:33:36 UTC 2018 - ohering@suse.de
- vss: fix loop device detection (07136793)
- Fix IP reporting by KVP daemon with SRIOV (4ba63412)
- Fix a bug in the key delete code (86503bd3)
- fix compiler warnings about major/target_fname (1330fc35)
- PRIVATE hyper-v.compare-with-upstream.sh
-------------------------------------------------------------------
Thu Aug 2 08:46:52 UTC 2018 - schwab@suse.de
- hyper-v.tools.hv.hv_vss_daemon.c: Include <sys/sysmacros.h> for major
-------------------------------------------------------------------
Tue May 22 06:56:57 UTC 2018 - ohering@suse.de
- Update lsvmbus interpreter from env(1) to python3(1) (bsc#1093910)
-------------------------------------------------------------------
Mon Jan 8 10:45:35 UTC 2018 - ohering@suse.de
- update buffer handling in hv_fcopy_daemon
- remove unnecessary header files and netlink related code
- Avoid reading past allocated blocks from KVP file (bnc#1087658)
- fix snprintf warning in kvp_daemon
- properly handle long paths
- kvp: configurable external scripts path
- vss: Thaw the filesystem and continue if freeze call has timed out
- vss: Skip freezing filesystems backed by loop
-------------------------------------------------------------------
Wed Jun 28 12:04:38 UTC 2017 - ohering@suse.de
- Check VSS daemon is listening before a hot backup (bnc#1029693)
- fix a compile warning in snprintf
- kvp: ensure kvp device fd is closed on exec
- lsvmbus: add pci pass-through UUID
-------------------------------------------------------------------
Wed May 4 13:29:54 UTC 2016 - ohering@suse.de
- add Conflicts with kernel < 4.2 (fate#320485)
- vss: fix the write()'s argument: error -> vss_msg
- remove repeated HV_FCOPY string
- report ENOSPC errors in hv_fcopy_daemon
- fcopy: full handshake support
- vss: full handshake support
- vss: use misc char device to communicate with kernel
- kvp: use misc char device to communicate with kernel
-------------------------------------------------------------------
Fri Sep 18 10:11:01 UTC 2015 - ohering@suse.de
- add a python script lsvmbus to list VMBus devices (fate#315887)
-------------------------------------------------------------------
Thu Apr 30 11:25:47 UTC 2015 - ohering@suse.de
- hv: hypervvssd: call endmntent before call setmntent again
- remove unused bytes_written from kvp_update_file()
- address compiler warnings for hv_kvp_daemon.c
- kvp_daemon: make IPv6-only-injection work
- address compiler warnings for hv_fcopy_daemon.c
- do not add redundant '/' in hv_start_fcopy()
-------------------------------------------------------------------
Thu Apr 16 15:23:18 UTC 2015 - ohering@suse.de
- Add runlevel script for fcopy daemon in SLE11 (bnc#926534)
-------------------------------------------------------------------
Mon Jan 12 09:37:40 UTC 2015 - ohering@suse.de
- Check return value of setsockopt call
- Improve error logging in VSS daemon.
- Check return value of poll call
- Properly pack the data for file copy functionality
- make struct hv_do_fcopy match Hyper-V host messages
-------------------------------------------------------------------
Wed Dec 17 09:06:33 UTC 2014 - ohering@suse.de
- Start hv_kvp_daemon after network-online.target (bnc#910353)
- ignore ENOBUFS and ENOMEM in the KVP daemon
- vssdaemon: skip all filesystems mounted readonly (bnc#909864)
- vssdaemon: report freeze errors
-------------------------------------------------------------------
Tue Nov 25 17:46:30 UTC 2014 - ohering@suse.de
- introduce -n/--no-daemon option (fate#317533)
-------------------------------------------------------------------
Sun Nov 09 04:39:00 UTC 2014 - Led <ledest@gmail.com>
- fix bashisms in pre/post scripts
-------------------------------------------------------------------
Tue Sep 30 15:41:02 UTC 2014 - ohering@suse.de
- vssdaemon: ignore the EBUSY on multiple freezing the same
partition (bnc#899204)
-------------------------------------------------------------------
Tue Jul 1 17:32:30 CEST 2014 - ohering@suse.de
- fix file overwriting of hv_fcopy_daemon
-------------------------------------------------------------------
Mon May 26 15:50:21 CEST 2014 - ohering@suse.de
- copy mask2pfxlen from /etc/sysconfig/network/scripts/functions
to hv_set_ifconfig (bnc#879256)
-------------------------------------------------------------------
Wed May 21 12:05:31 CEST 2014 - ohering@suse.de
- Start hyper-v helpers manually with helper script in inst-sys
-------------------------------------------------------------------
Thu Apr 10 19:19:04 CEST 2014 - ohering@suse.de
- Handle the case when the target file exists correctly in hv_fcopy_daemon
- vssdaemon: Ignore VFAT mounts during the Freeze operation
-------------------------------------------------------------------
Sun Mar 2 18:11:31 CET 2014 - ohering@suse.de
- Package /usr/lib/udev to fix build error
-------------------------------------------------------------------
Wed Feb 12 10:32:54 CET 2014 - ohering@suse.de
- Update hv_fcopy_daemon, kernel name changed to /dev/vmbus/hv_fcopy
-------------------------------------------------------------------
Thu Jan 23 17:00:54 CET 2014 - ohering@suse.de
- Start daemons on demand via udev rules because the guest services
are optional.
- If systemd is used daemons live in libdir, they are not usercallable apps
-------------------------------------------------------------------
Thu Jan 16 11:09:34 CET 2014 - ohering@suse.de
- Add hv_fcopy_daemon for post 13.1 releases (fate#315887)
hyper-v.tools.hv.hv_fcopy_daemon.c
-------------------------------------------------------------------
Wed Dec 18 14:46:37 CET 2013 - ohering@suse.de
- Use native systemd services for post 13.1 releases
-------------------------------------------------------------------
Sun Sep 8 10:36:29 CEST 2013 - ohering@suse.de
- Update Supplements: modalias(dmi:*)
-------------------------------------------------------------------
Mon Aug 12 16:56:08 CEST 2013 - ohering@suse.de
- Skip restart_on_update with old hyper-v.rpms while old
kernel is running (bnc#770763)
-------------------------------------------------------------------
Wed Aug 7 19:04:35 CEST 2013 - ohering@suse.de
- cache FQDN in kvp_daemon to avoid timeouts (bnc#828714)
- use full nlmsghdr in netlink_send
- correct payload size in netlink_send
- use single send+recv buffer
- log errors to syslog in kvp_set_ip_info
- check return value of system in hv_kvp_daemon
- in kvp_set_ip_info free mac_addr right after usage
- check return value of daemon to fix compiler warning.
-------------------------------------------------------------------
Thu Aug 1 14:21:57 CEST 2013 - ohering@suse.de
- Fix send/recv buffer allocation (bnc#828714)
-------------------------------------------------------------------
Wed Jul 24 10:18:34 CEST 2013 - ohering@suse.de
- set BOOTPROTO=static if any IPADDR/IP6ADDR was passed to
hv_set_ifconfig and dhcp is disabled
-------------------------------------------------------------------
Mon Jul 15 16:16:06 CEST 2013 - ohering@suse.de
- update hv_kvp_daemon (merge 0783d72fa from v3.9-rc1)
Fix how ifcfg-* file is created
-------------------------------------------------------------------
Mon Jul 15 15:24:00 CEST 2013 - ohering@suse.de
- update hv_kvp_daemon (changes up to 3.11-rc1):
Improve error logging in KVP daemon.
Fix file descriptor leaks
Check retrun value of strchr call
Check return value of poll call
Check return value of setsockopt call
daemon should check type of received Netlink msg
daemon setsockopt should use options macros
daemon should subscribe only to CN_KVP_IDX group
-------------------------------------------------------------------
Mon Jul 15 12:04:05 CEST 2013 - ohering@suse.de
- Fix a bug in IPV6 subnet enumeration (bnc#828714)
-------------------------------------------------------------------
Tue Mar 26 18:03:47 CET 2013 - ohering@suse.de
- Update hv_vss_daemon (bnc#811033)
-------------------------------------------------------------------
Fri Mar 22 17:23:36 CET 2013 - ohering@suse.de
- add hv_vss_daemon (fate#314921)
helper to support host initiated backup
-------------------------------------------------------------------
Fri Mar 22 16:56:57 CET 2013 - ohering@suse.de
- build hv_kvp_daemon with -D_GNU_SOURCE to get O_CLOEXEC
-------------------------------------------------------------------
Fri Mar 22 16:19:38 CET 2013 - ohering@suse.de
- update hv_kvp_daemon
Use CLOEXEC when opening kvp_pool files
Fix permissions of created directory and files
Fix /var subdirectory (move state files from /var/opt to /var/lib)
Fix string types
-------------------------------------------------------------------
Tue Nov 27 11:19:32 CET 2012 - ohering@suse.de
- update hv_set_ifconfig, use single index for static ipv4/ipv6 [bnc#790469]
-------------------------------------------------------------------
Sun Nov 25 17:50:40 CET 2012 - ohering@suse.de
- update hv_set_ifconfig further to work with our ifcfg [bnc#790469]
-------------------------------------------------------------------
Thu Nov 22 18:14:12 CET 2012 - ohering@suse.de
- update hv_get_dhcp_info to work with our ifcfg [bnc#790469]
- remove cat usage from hv_get_dns_info
- add quoting to hv_set_ifconfig to make it more robust
-------------------------------------------------------------------
Mon Nov 12 17:18:25 CET 2012 - ohering@suse.de
- remove code to build kmp, it was not enabled because the
drivers are now in kernel since a long time. [bnc#676890]
-------------------------------------------------------------------
Thu Nov 8 14:30:05 CET 2012 - ohering@suse.de
- Netlink source address validation allows DoS [bnc#791605, CVE-2012-5532]
bugfix for recvfrom check from bnc#761200
-------------------------------------------------------------------
Fri Oct 26 17:13:40 CEST 2012 - ohering@suse.de
- update hv_set_ifconfig to work with our ifcfg
-------------------------------------------------------------------
Sat Oct 13 11:40:30 CEST 2012 - ohering@suse.de
- update hv_kvp_daemon
Return the full kernel version
Don't return loopback addresses
-------------------------------------------------------------------
Thu Oct 4 15:14:05 CEST 2012 - ohering@suse.de
- bump to version 4
- update kv_kvp_daemon to 3.7-rc1 state [fate#314441]
support KVP IP Injection, helper scripts go to /usr/lib/hyper-v/bin:
hv_get_dhcp_info, hv_get_dns_info, hv_set_ifconfig
- remove usage of absolute paths in runlevel script
-------------------------------------------------------------------
Tue Sep 4 14:55:38 CEST 2012 - ohering@suse.de
- remove restart_on_update in postun section
the daemon can not be restarted at this point, and the new daemon
may not be 100 percent compatible with the currently running
kernel [bnc#770763]
-------------------------------------------------------------------
Wed May 16 20:44:36 CEST 2012 - ohering@suse.de
- check origin of netlink messages, use recvfrom() [bnc#761200, CVE-2012-2669]
-------------------------------------------------------------------
Wed May 2 12:13:03 CEST 2012 - ohering@suse.de
- update insserv part, see comments in OBS submit request #112701
-------------------------------------------------------------------
Tue Mar 27 08:50:43 CEST 2012 - ohering@suse.de
- add kvptest.ps1.txt to docs, a PowerShell script to verify KVP
-------------------------------------------------------------------
Mon Mar 26 19:05:48 CEST 2012 - ohering@suse.de
- update hv_kvp_daemon to 3.4-rc1 state
use a copy linux/hyperv.h to compile the daemon
-------------------------------------------------------------------
Thu Dec 8 17:46:21 CET 2011 - ohering@suse.de
- do not package modprobe.conf rule in main package
prevents loading of ata_piix which is required for cdrom access
-------------------------------------------------------------------
Fri Nov 18 21:49:57 CET 2011 - ohering@suse.de
- add Supplements to install package if dmi matches [bnc#731198]
-------------------------------------------------------------------
Thu Sep 1 18:51:03 CEST 2011 - ohering@suse.de
- disable hyper-v-kmp
hv_storvsc handles now IDE and SCSI disks and ata_piix is
compiled into the kernel in SLE12, so the modprobe.conf rules
have no meaning anymore
-------------------------------------------------------------------
Tue Jul 26 21:24:25 CEST 2011 - ohering@suse.de
- update hv_kvp_daemon: Cleanup kvp_get_domain_name(). If
getaddrinfo() fails, deal with it properly (this can happen if no
IP address has been assigned). Also, don't specify a specific
service in the call to getaddrinfo() to make this code as generic
as possible.
-------------------------------------------------------------------
Sun Jul 24 17:22:41 CEST 2011 - ohering@suse.de
- update hv_kvp_daemon: The current win7 host does not like it when
we return the complete kernel release information. Conform to
what the host expects.
-------------------------------------------------------------------
Fri Jun 17 15:41:33 CEST 2011 - ohering@suse.de
- update preun, stop_on_removal requires an argument
-------------------------------------------------------------------
Fri Jun 17 11:09:30 CEST 2011 - ohering@suse.de
- remove get_release_number.sh, which was added by accident
- bump version number to 2
-------------------------------------------------------------------
Fri Jun 17 10:58:42 CEST 2011 - ohering@suse.de
- catch errors from daemon()
-------------------------------------------------------------------
Fri Jun 17 10:33:50 CEST 2011 - ohering@suse.de
- update postun to run restart_on_update
- use rpmmacro for daemon name
-------------------------------------------------------------------
Fri Jun 17 08:10:12 CEST 2011 - ohering@suse.de
- update postin to work in / again when running insserv macro
-------------------------------------------------------------------
Thu Jun 16 15:33:49 CEST 2011 - ohering@suse.de
- Update version number to 1 so kernel.rpm can obsolete
the old kmp packages
-------------------------------------------------------------------
Thu Jun 16 11:12:06 CEST 2011 - ohering@suse.de
- enable hv_kvp_daemon if run in a hv guest [fate#312213]
-------------------------------------------------------------------
Thu Apr 21 17:18:20 CEST 2011 - ohering@suse.de
- add hv_kvp_daemon to provide system infos to hypervisor [bnc#685189]
this enables the hyper-v main package
-------------------------------------------------------------------
Sat Apr 16 15:13:36 CEST 2011 - ohering@suse.de
- Require at least kernel-default 2.6.32.27 for an empty KMP.
-------------------------------------------------------------------
Fri Apr 15 16:50:25 CEST 2011 - ohering@suse.de
- actually keep the KMP packages with just the modprobe.conf file
-------------------------------------------------------------------
Thu Mar 31 11:24:03 CEST 2011 - ohering@suse.de
- make building of kernel modules optional [bnc#676890]
the hv*.ko drivers exist in two places, kernel-default and this
KMP package. Both can get out of sync, then mkinitrd will use
(the possible outdated) drivers from this KMP package.
disable building drivers per default, keep only the
hyperv_pvdrivers.conf
- mark hyperv_pvdrivers.conf as config to preserve local
modifications done by the sysadmin
-------------------------------------------------------------------
Thu Feb 24 12:09:25 CET 2011 - meissner@suse.de
- also add hv_timesource to Modules.supported [bnc#650748]
-------------------------------------------------------------------
Tue Jan 4 10:28:21 CET 2011 - meissner@suse.de
- add hv_utils to Modules.supported [bnc#650748]
-------------------------------------------------------------------
Wed Apr 28 11:40:33 CEST 2010 - kukuk@suse.de
- Next update of hyperv_pvdrivers.conf [bnc#600212]
-------------------------------------------------------------------
Tue Apr 27 13:20:55 CEST 2010 - kukuk@suse.de
- hyperv_pvdrivers.conf: fix syntax error
-------------------------------------------------------------------
Tue Apr 27 07:10:05 CEST 2010 - ksrinivasan@novell.com
- Update hyperv_pvdrivers.conf config file
-------------------------------------------------------------------
Mon Apr 26 18:15:40 CEST 2010 - kukuk@suse.de
- Add hyperv_pvdrivers.conf modprobe config file
-------------------------------------------------------------------
Fri Mar 5 13:40:56 CET 2010 - mmarek@suse.cz
- Packaged drivers/staging/hv as a standalone KMP (bnc#585651).

View File

@ -0,0 +1,6 @@
d=~/work/src/kernel/kerncvs.kernel.master
diff -u hyper-v.tools.hv.hv_vss_daemon.c ${d}/tools/hv/hv_vss_daemon.c
diff -u hyper-v.tools.hv.hv_kvp_daemon.c ${d}/tools/hv/hv_kvp_daemon.c
diff -u hyper-v.tools.hv.hv_fcopy_daemon.c ${d}/tools/hv/hv_fcopy_daemon.c
diff -u hyper-v.include.linux.hyperv.h ${d}/include/uapi/linux/hyperv.h
diff -u hyper-v.lsvmbus.py ${d}/tools/hv/lsvmbus

View File

@ -0,0 +1,411 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
*
* Copyright (c) 2011, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
* K. Y. Srinivasan <kys@microsoft.com>
*
*/
#ifndef _UAPI_HYPERV_H
#define _UAPI_HYPERV_H
#include <linux/types.h>
/*
* Framework version for util services.
*/
#define UTIL_FW_MINOR 0
#define UTIL_WS2K8_FW_MAJOR 1
#define UTIL_WS2K8_FW_VERSION (UTIL_WS2K8_FW_MAJOR << 16 | UTIL_FW_MINOR)
#define UTIL_FW_MAJOR 3
#define UTIL_FW_VERSION (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
/*
* Implementation of host controlled snapshot of the guest.
*/
#define VSS_OP_REGISTER 128
/*
Daemon code with full handshake support.
*/
#define VSS_OP_REGISTER1 129
enum hv_vss_op {
VSS_OP_CREATE = 0,
VSS_OP_DELETE,
VSS_OP_HOT_BACKUP,
VSS_OP_GET_DM_INFO,
VSS_OP_BU_COMPLETE,
/*
* Following operations are only supported with IC version >= 5.0
*/
VSS_OP_FREEZE, /* Freeze the file systems in the VM */
VSS_OP_THAW, /* Unfreeze the file systems */
VSS_OP_AUTO_RECOVER,
VSS_OP_COUNT /* Number of operations, must be last */
};
/*
* Header for all VSS messages.
*/
struct hv_vss_hdr {
__u8 operation;
__u8 reserved[7];
} __attribute__((packed));
/*
* Flag values for the hv_vss_check_feature. Linux supports only
* one value.
*/
#define VSS_HBU_NO_AUTO_RECOVERY 0x00000005
struct hv_vss_check_feature {
__u32 flags;
} __attribute__((packed));
struct hv_vss_check_dm_info {
__u32 flags;
} __attribute__((packed));
/*
* struct hv_vss_msg encodes the fields that the Linux VSS
* driver accesses. However, FREEZE messages from Hyper-V contain
* additional LUN information that Linux doesn't use and are not
* represented in struct hv_vss_msg. A received FREEZE message may
* be as large as 6,260 bytes, so the driver must allocate at least
* that much space, not sizeof(struct hv_vss_msg). Other messages
* such as AUTO_RECOVER may be as large as 12,500 bytes. However,
* because the Linux VSS driver responds that it doesn't support
* auto-recovery, it should not receive such messages.
*/
struct hv_vss_msg {
union {
struct hv_vss_hdr vss_hdr;
int error;
};
union {
struct hv_vss_check_feature vss_cf;
struct hv_vss_check_dm_info dm_info;
};
} __attribute__((packed));
/*
* Implementation of a host to guest copy facility.
*/
#define FCOPY_VERSION_0 0
#define FCOPY_VERSION_1 1
#define FCOPY_CURRENT_VERSION FCOPY_VERSION_1
#define W_MAX_PATH 260
enum hv_fcopy_op {
START_FILE_COPY = 0,
WRITE_TO_FILE,
COMPLETE_FCOPY,
CANCEL_FCOPY,
};
struct hv_fcopy_hdr {
__u32 operation;
__u8 service_id0[16]; /* currently unused */
__u8 service_id1[16]; /* currently unused */
} __attribute__((packed));
#define OVER_WRITE 0x1
#define CREATE_PATH 0x2
struct hv_start_fcopy {
struct hv_fcopy_hdr hdr;
__u16 file_name[W_MAX_PATH];
__u16 path_name[W_MAX_PATH];
__u32 copy_flags;
__u64 file_size;
} __attribute__((packed));
/*
* The file is chunked into fragments.
*/
#define DATA_FRAGMENT (6 * 1024)
struct hv_do_fcopy {
struct hv_fcopy_hdr hdr;
__u32 pad;
__u64 offset;
__u32 size;
__u8 data[DATA_FRAGMENT];
} __attribute__((packed));
/*
* An implementation of HyperV key value pair (KVP) functionality for Linux.
*
*
* Copyright (C) 2010, Novell, Inc.
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
*
*/
/*
* Maximum value size - used for both key names and value data, and includes
* any applicable NULL terminators.
*
* Note: This limit is somewhat arbitrary, but falls easily within what is
* supported for all native guests (back to Win 2000) and what is reasonable
* for the IC KVP exchange functionality. Note that Windows Me/98/95 are
* limited to 255 character key names.
*
* MSDN recommends not storing data values larger than 2048 bytes in the
* registry.
*
* Note: This value is used in defining the KVP exchange message - this value
* cannot be modified without affecting the message size and compatibility.
*/
/*
* bytes, including any null terminators
*/
#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048)
/*
* Maximum key size - the registry limit for the length of an entry name
* is 256 characters, including the null terminator
*/
#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512)
/*
* In Linux, we implement the KVP functionality in two components:
* 1) The kernel component which is packaged as part of the hv_utils driver
* is responsible for communicating with the host and responsible for
* implementing the host/guest protocol. 2) A user level daemon that is
* responsible for data gathering.
*
* Host/Guest Protocol: The host iterates over an index and expects the guest
* to assign a key name to the index and also return the value corresponding to
* the key. The host will have atmost one KVP transaction outstanding at any
* given point in time. The host side iteration stops when the guest returns
* an error. Microsoft has specified the following mapping of key names to
* host specified index:
*
* Index Key Name
* 0 FullyQualifiedDomainName
* 1 IntegrationServicesVersion
* 2 NetworkAddressIPv4
* 3 NetworkAddressIPv6
* 4 OSBuildNumber
* 5 OSName
* 6 OSMajorVersion
* 7 OSMinorVersion
* 8 OSVersion
* 9 ProcessorArchitecture
*
* The Windows host expects the Key Name and Key Value to be encoded in utf16.
*
* Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
* data gathering functionality in a user mode daemon. The user level daemon
* is also responsible for binding the key name to the index as well. The
* kernel and user-level daemon communicate using a connector channel.
*
* The user mode component first registers with the
* kernel component. Subsequently, the kernel component requests, data
* for the specified keys. In response to this message the user mode component
* fills in the value corresponding to the specified key. We overload the
* sequence field in the cn_msg header to define our KVP message types.
*
*
* The kernel component simply acts as a conduit for communication between the
* Windows host and the user-level daemon. The kernel component passes up the
* index received from the Host to the user-level daemon. If the index is
* valid (supported), the corresponding key as well as its
* value (both are strings) is returned. If the index is invalid
* (not supported), a NULL key string is returned.
*/
/*
* Registry value types.
*/
#define REG_SZ 1
#define REG_U32 4
#define REG_U64 8
/*
* As we look at expanding the KVP functionality to include
* IP injection functionality, we need to maintain binary
* compatibility with older daemons.
*
* The KVP opcodes are defined by the host and it was unfortunate
* that I chose to treat the registration operation as part of the
* KVP operations defined by the host.
* Here is the level of compatibility
* (between the user level daemon and the kernel KVP driver) that we
* will implement:
*
* An older daemon will always be supported on a newer driver.
* A given user level daemon will require a minimal version of the
* kernel driver.
* If we cannot handle the version differences, we will fail gracefully
* (this can happen when we have a user level daemon that is more
* advanced than the KVP driver.
*
* We will use values used in this handshake for determining if we have
* workable user level daemon and the kernel driver. We begin by taking the
* registration opcode out of the KVP opcode namespace. We will however,
* maintain compatibility with the existing user-level daemon code.
*/
/*
* Daemon code not supporting IP injection (legacy daemon).
*/
#define KVP_OP_REGISTER 4
/*
* Daemon code supporting IP injection.
* The KVP opcode field is used to communicate the
* registration information; so define a namespace that
* will be distinct from the host defined KVP opcode.
*/
#define KVP_OP_REGISTER1 100
enum hv_kvp_exchg_op {
KVP_OP_GET = 0,
KVP_OP_SET,
KVP_OP_DELETE,
KVP_OP_ENUMERATE,
KVP_OP_GET_IP_INFO,
KVP_OP_SET_IP_INFO,
KVP_OP_COUNT /* Number of operations, must be last. */
};
enum hv_kvp_exchg_pool {
KVP_POOL_EXTERNAL = 0,
KVP_POOL_GUEST,
KVP_POOL_AUTO,
KVP_POOL_AUTO_EXTERNAL,
KVP_POOL_AUTO_INTERNAL,
KVP_POOL_COUNT /* Number of pools, must be last. */
};
/*
* Some Hyper-V status codes.
*/
#define HV_S_OK 0x00000000
#define HV_E_FAIL 0x80004005
#define HV_S_CONT 0x80070103
#define HV_ERROR_NOT_SUPPORTED 0x80070032
#define HV_ERROR_MACHINE_LOCKED 0x800704F7
#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
#define HV_INVALIDARG 0x80070057
#define HV_GUID_NOTFOUND 0x80041002
#define HV_ERROR_ALREADY_EXISTS 0x80070050
#define HV_ERROR_DISK_FULL 0x80070070
#define ADDR_FAMILY_NONE 0x00
#define ADDR_FAMILY_IPV4 0x01
#define ADDR_FAMILY_IPV6 0x02
#define MAX_ADAPTER_ID_SIZE 128
#define MAX_IP_ADDR_SIZE 1024
#define MAX_GATEWAY_SIZE 512
struct hv_kvp_ipaddr_value {
__u16 adapter_id[MAX_ADAPTER_ID_SIZE];
__u8 addr_family;
__u8 dhcp_enabled;
__u16 ip_addr[MAX_IP_ADDR_SIZE];
__u16 sub_net[MAX_IP_ADDR_SIZE];
__u16 gate_way[MAX_GATEWAY_SIZE];
__u16 dns_addr[MAX_IP_ADDR_SIZE];
} __attribute__((packed));
struct hv_kvp_hdr {
__u8 operation;
__u8 pool;
__u16 pad;
} __attribute__((packed));
struct hv_kvp_exchg_msg_value {
__u32 value_type;
__u32 key_size;
__u32 value_size;
__u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
union {
__u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
__u32 value_u32;
__u64 value_u64;
};
} __attribute__((packed));
struct hv_kvp_msg_enumerate {
__u32 index;
struct hv_kvp_exchg_msg_value data;
} __attribute__((packed));
struct hv_kvp_msg_get {
struct hv_kvp_exchg_msg_value data;
};
struct hv_kvp_msg_set {
struct hv_kvp_exchg_msg_value data;
};
struct hv_kvp_msg_delete {
__u32 key_size;
__u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
};
struct hv_kvp_register {
__u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
};
struct hv_kvp_msg {
union {
struct hv_kvp_hdr kvp_hdr;
int error;
};
union {
struct hv_kvp_msg_get kvp_get;
struct hv_kvp_msg_set kvp_set;
struct hv_kvp_msg_delete kvp_delete;
struct hv_kvp_msg_enumerate kvp_enum_data;
struct hv_kvp_ipaddr_value kvp_ip_val;
struct hv_kvp_register kvp_register;
} body;
} __attribute__((packed));
struct hv_kvp_ip_msg {
__u8 operation;
__u8 pool;
struct hv_kvp_ipaddr_value kvp_ip_val;
} __attribute__((packed));
#endif /* _UAPI_HYPERV_H */

View File

@ -0,0 +1,23 @@
--- a/hyper-v.tools.hv.hv_kvp_daemon.c
+++ b/hyper-v.tools.hv.hv_kvp_daemon.c
@@ -1335,6 +1335,7 @@ kvp_get_domain_name(char *buffer, int le
struct addrinfo hints, *info ;
int error = 0;
+ return;
gethostname(buffer, length);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
@@ -1572,6 +1573,12 @@ int main(int argc, char *argv[])
switch (hv_msg->body.kvp_enum_data.index) {
case FullyQualifiedDomainName:
+ /*
+ * The API is undocumented.
+ * The Host can not possibly care about DNS within the guest network
+ * The time it takes to get the hostname is much shorter than a DNS lookup.
+ */
+ gethostname(full_domain_name, sizeof(full_domain_name));
strcpy(key_value, full_domain_name);
strcpy(key_name, "FullyQualifiedDomainName");
break;

49
hyper-v.kvptest.ps1.txt Normal file
View File

@ -0,0 +1,49 @@
# Windows PowerShell script to test Key Value Pair functionality
#
# http://blogs.msdn.com/b/virtual_pc_guy/archive/2008/11/18/hyper-v-script-looking-at-kvp-guestintrinsicexchangeitems.aspx
#
# Per default execution of scripts is disabled.
# http://technet.microsoft.com/en-us/library/ee176949.aspx
# The command 'Set-ExecutionPolicy RemoteSigned' will enable it.
#
# Filter for parsing XML data
filter Import-CimXml
{
# Create new XML object from input
$CimXml = [Xml]$_
$CimObj = New-Object -TypeName System.Object
# Iterate over the data and pull out just the value name and data for each entry
foreach ($CimProperty in $CimXml.SelectNodes("/INSTANCE/PROPERTY[@NAME='Name']"))
{
$CimObj | Add-Member -MemberType NoteProperty -Name $CimProperty.NAME -Value $CimProperty.VALUE
}
foreach ($CimProperty in $CimXml.SelectNodes("/INSTANCE/PROPERTY[@NAME='Data']"))
{
$CimObj | Add-Member -MemberType NoteProperty -Name $CimProperty.NAME -Value $CimProperty.VALUE
}
# Display output
$CimObj
}
# Prompt for the Hyper-V Server to use
$HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
# Prompt for the virtual machine to use
$VMName = Read-Host "Specify the name of the virtual machine"
# Get the virtual machine object
$query = "Select * From Msvm_ComputerSystem Where ElementName='" + $VMName + "'"
$Vm = gwmi -namespace root\virtualization -query $query -computername $HyperVServer
# Get the KVP Object
$query = "Associators of {$Vm} Where AssocClass=Msvm_SystemDevice ResultClass=Msvm_KvpExchangeComponent"
$Kvp = gwmi -namespace root\virtualization -query $query -computername $HyperVServer
Write-Host
Write-Host "Guest KVP information for" $VMName
# Filter the results
$Kvp.GuestIntrinsicExchangeItems | Import-CimXml

112
hyper-v.lsvmbus.py Normal file
View File

@ -0,0 +1,112 @@
#!/usr/bin/python3
# SPDX-License-Identifier: GPL-2.0
import os
from optparse import OptionParser
help_msg = "print verbose messages. Try -vv, -vvv for more verbose messages"
parser = OptionParser()
parser.add_option(
"-v", "--verbose", dest="verbose", help=help_msg, action="count")
(options, args) = parser.parse_args()
verbose = 0
if options.verbose is not None:
verbose = options.verbose
vmbus_sys_path = '/sys/bus/vmbus/devices'
if not os.path.isdir(vmbus_sys_path):
print("%s doesn't exist: exiting..." % vmbus_sys_path)
exit(-1)
vmbus_dev_dict = {
'{0e0b6031-5213-4934-818b-38d90ced39db}': '[Operating system shutdown]',
'{9527e630-d0ae-497b-adce-e80ab0175caf}': '[Time Synchronization]',
'{57164f39-9115-4e78-ab55-382f3bd5422d}': '[Heartbeat]',
'{a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}': '[Data Exchange]',
'{35fa2e29-ea23-4236-96ae-3a6ebacba440}': '[Backup (volume checkpoint)]',
'{34d14be3-dee4-41c8-9ae7-6b174977c192}': '[Guest services]',
'{525074dc-8985-46e2-8057-a307dc18a502}': '[Dynamic Memory]',
'{cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}': 'Synthetic mouse',
'{f912ad6d-2b17-48ea-bd65-f927a61c7684}': 'Synthetic keyboard',
'{da0a7802-e377-4aac-8e77-0558eb1073f8}': 'Synthetic framebuffer adapter',
'{f8615163-df3e-46c5-913f-f2d2f965ed0e}': 'Synthetic network adapter',
'{32412632-86cb-44a2-9b5c-50d1417354f5}': 'Synthetic IDE Controller',
'{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}': 'Synthetic SCSI Controller',
'{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}': 'Synthetic fiber channel adapter',
'{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}': 'Synthetic RDMA adapter',
'{44c4f61d-4444-4400-9d52-802e27ede19f}': 'PCI Express pass-through',
'{276aacf4-ac15-426c-98dd-7521ad3f01fe}': '[Reserved system device]',
'{f8e65716-3cb3-4a06-9a60-1889c5cccab5}': '[Reserved system device]',
'{3375baf4-9e15-4b30-b765-67acb10d607b}': '[Reserved system device]',
}
def get_vmbus_dev_attr(dev_name, attr):
try:
f = open('%s/%s/%s' % (vmbus_sys_path, dev_name, attr), 'r')
lines = f.readlines()
f.close()
except IOError:
lines = []
return lines
class VMBus_Dev:
pass
vmbus_dev_list = []
for f in os.listdir(vmbus_sys_path):
vmbus_id = get_vmbus_dev_attr(f, 'id')[0].strip()
class_id = get_vmbus_dev_attr(f, 'class_id')[0].strip()
device_id = get_vmbus_dev_attr(f, 'device_id')[0].strip()
dev_desc = vmbus_dev_dict.get(class_id, 'Unknown')
chn_vp_mapping = get_vmbus_dev_attr(f, 'channel_vp_mapping')
chn_vp_mapping = [c.strip() for c in chn_vp_mapping]
chn_vp_mapping = sorted(
chn_vp_mapping, key=lambda c: int(c.split(':')[0]))
chn_vp_mapping = [
'\tRel_ID=%s, target_cpu=%s' %
(c.split(':')[0], c.split(':')[1]) for c in chn_vp_mapping
]
d = VMBus_Dev()
d.sysfs_path = '%s/%s' % (vmbus_sys_path, f)
d.vmbus_id = vmbus_id
d.class_id = class_id
d.device_id = device_id
d.dev_desc = dev_desc
d.chn_vp_mapping = '\n'.join(chn_vp_mapping)
if d.chn_vp_mapping:
d.chn_vp_mapping += '\n'
vmbus_dev_list.append(d)
vmbus_dev_list = sorted(vmbus_dev_list, key=lambda d: int(d.vmbus_id))
format0 = '%2s: %s'
format1 = '%2s: Class_ID = %s - %s\n%s'
format2 = '%2s: Class_ID = %s - %s\n\tDevice_ID = %s\n\tSysfs path: %s\n%s'
for d in vmbus_dev_list:
if verbose == 0:
print(('VMBUS ID ' + format0) % (d.vmbus_id, d.dev_desc))
elif verbose == 1:
print(
('VMBUS ID ' + format1) %
(d.vmbus_id, d.class_id, d.dev_desc, d.chn_vp_mapping)
)
else:
print(
('VMBUS ID ' + format2) %
(
d.vmbus_id, d.class_id, d.dev_desc,
d.device_id, d.sysfs_path, d.chn_vp_mapping
)
)

316
hyper-v.spec Normal file
View File

@ -0,0 +1,316 @@
#
# spec file for package hyper-v
#
# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%define hv_kvp_daemon hv_kvp_daemon
%define hv_vss_daemon hv_vss_daemon
%define hv_fcopy_daemon hv_fcopy_daemon
%define vmbus_bufring vmbus_bufring
%define hv_fcopy_uio_daemon hv_fcopy_uio_daemon
%define include_uapi_linux_hyperv include_uapi_linux_hyperv
%define chardev_kvp vmbus/hv_kvp
%define chardev_vss vmbus/hv_vss
%define chardev_fcopy vmbus/hv_fcopy
%define helper_dir /usr/lib/%name
Name: hyper-v
ExclusiveArch: %ix86 x86_64 aarch64
%{?systemd_requires}
BuildRequires: python-rpm-macros
BuildRequires: pkgconfig(systemd)
# Due to usage of char device instead of netlink
Conflicts: kernel < 4.2
Summary: Microsoft Hyper-V tools
License: GPL-2.0-only
Supplements: modalias(dmi:*svnMicrosoftCorporation*pnVirtualMachine*rnVirtualMachine*)
Supplements: modalias(pci:v00001414d00005353sv*sd*bc*sc*i*)
URL: http://www.kernel.org
# Arbitrary version number
Version: 9
Release: 0
Source0: hyper-v.lsvmbus.py
Source5: hyper-v.kvptest.ps1.txt
Source7: hyper-v.compare-with-upstream.sh
Source8: hyper-v.tools.hv.vmbus_bufring.h
Source9: hyper-v.include.linux.hyperv.h
Source10: hyper-v.tools.hv.hv_kvp_daemon.c
Source11: hyper-v.tools.hv.vmbus_bufring.c
Source12: hyper-v.tools.hv.hv_vss_daemon.c
Source13: hyper-v.tools.hv.hv_fcopy_uio_daemon.c
Source14: hyper-v.tools.hv.hv_fcopy_daemon.c
Source20: hyper-v.tools.hv.hv_get_dhcp_info.sh
Source21: hyper-v.tools.hv.hv_get_dns_info.sh
Source22: hyper-v.tools.hv.hv_set_ifconfig.sh
Patch0: hyper-v.kvp.gethostname.patch
%description
This package contains the Microsoft Hyper-V tools.
%prep
%setup -Tc
cp -avL %{S:5} kvptest.ps1.txt
cp -vL %{S:8} %vmbus_bufring.h
cp -vL %{S:9} %include_uapi_linux_hyperv.h
cp -vL %{S:10} .
cp -vL %{S:11} %vmbus_bufring.c
cp -vL %{S:12} %hv_vss_daemon.c
cp -vL %{S:13} %hv_fcopy_uio_daemon.c
cp -vL %{S:14} %hv_fcopy_daemon.c
%patch -P 0 -p1
mv `basename %{S:10}` %hv_kvp_daemon.c
%build
sed -i~ '/#include <linux.hyperv.h>/d' %hv_kvp_daemon.c
sed -i~ '/#include <linux.hyperv.h>/d' %hv_vss_daemon.c
sed -i~ '/#include <linux.hyperv.h>/d' %hv_fcopy_uio_daemon.c
sed -i~ '/#include <linux.hyperv.h>/d' %hv_fcopy_daemon.c
gcc \
$RPM_OPT_FLAGS \
-Wno-unused-variable \
-Wno-pointer-sign \
-D_GNU_SOURCE \
-g \
%hv_kvp_daemon.c \
-include %include_uapi_linux_hyperv.h \
-DCN_KVP_IDX=0x9 \
-DCN_KVP_VAL=0x1 \
-DKVP_SCRIPTS_PATH= \
-pthread \
-o %hv_kvp_daemon
gcc \
$RPM_OPT_FLAGS \
-Wno-unused-variable \
-Wno-pointer-sign \
-D_GNU_SOURCE \
-g \
%hv_vss_daemon.c \
-include %include_uapi_linux_hyperv.h \
-DCN_VSS_IDX=0xa \
-DCN_VSS_VAL=0x1 \
-o %hv_vss_daemon
gcc \
$RPM_OPT_FLAGS \
-Wno-unused-variable \
-Wno-pointer-sign \
-D_GNU_SOURCE \
-g \
%hv_fcopy_daemon.c \
-include %include_uapi_linux_hyperv.h \
-o %hv_fcopy_daemon
gcc \
$RPM_OPT_FLAGS \
-Wno-unused-variable \
-Wno-pointer-sign \
-Wno-address-of-packed-member \
-D_GNU_SOURCE \
-g \
%vmbus_bufring.c \
%hv_fcopy_uio_daemon.c \
-include %include_uapi_linux_hyperv.h \
-o %hv_fcopy_uio_daemon
%install
# It is not a callable app anyway, so move it out of the way
bindir=%helper_dir/bin
mkdir -p %buildroot${bindir}
mkdir -p %buildroot%_sbindir
install -m755 %hv_kvp_daemon %buildroot${bindir}
install -m755 %hv_vss_daemon %buildroot${bindir}
install -m755 %hv_fcopy_daemon %buildroot${bindir}
install -m755 %hv_fcopy_uio_daemon %buildroot${bindir}
cp -avL %{S:0} %buildroot%_sbindir/lsvmbus
chmod 0755 %buildroot%_sbindir/lsvmbus
cp -avL %{S:20} %buildroot${bindir}/hv_get_dhcp_info
cp -avL %{S:21} %buildroot${bindir}/hv_get_dns_info
cp -avL %{S:22} %buildroot${bindir}/hv_set_ifconfig
chmod 755 %buildroot${bindir}/*
d=%buildroot%_unitdir
mkdir -vp ${d}
#
tee ${d}/%hv_kvp_daemon.service <<EOF
# started via %_udevrulesdir/%name.rules
[Unit]
Description=Hyper-V KVP Daemon
After=local-fs.target
ConditionVirtualization=microsoft
ConditionPathExists=/dev/%chardev_kvp
[Service]
Environment="PATH=${bindir}:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=${bindir}/%hv_kvp_daemon --no-daemon
Restart=on-failure
[Install]
WantedBy=default.target
EOF
#
tee ${d}/%hv_vss_daemon.service <<EOF
# started via %_udevrulesdir/%name.rules
[Unit]
Description=Hyper-V VSS Daemon
ConditionVirtualization=microsoft
ConditionPathExists=/dev/%chardev_vss
[Service]
ExecStart=${bindir}/%hv_vss_daemon --no-daemon
Restart=on-failure
[Install]
WantedBy=default.target
EOF
#
tee ${d}/%hv_fcopy_daemon.service <<EOF
# started via %_udevrulesdir/%name.rules
[Unit]
Description=Hyper-V host to guest file copy daemon
ConditionVirtualization=microsoft
ConditionPathExists=/dev/%chardev_fcopy
[Service]
ExecStart=${bindir}/%hv_fcopy_daemon --no-daemon
Restart=on-failure
[Install]
WantedBy=default.target
EOF
#
tee ${d}/%hv_fcopy_uio_daemon.service <<EOF
# started via %_udevrulesdir/%name.rules
[Unit]
Description=Hyper-V host to guest UIO file copy daemon
ConditionVirtualization=microsoft
ConditionPathExists=/sys/bus/vmbus/drivers/uio_hv_generic/new_id
ConditionPathExists=!/dev/%chardev_fcopy
[Service]
ExecStartPre=/bin/sh -c 'set -e;echo 34d14be3-dee4-41c8-9ae7-6b174977c192 > /sys/bus/vmbus/drivers/uio_hv_generic/new_id'
ExecStart=${bindir}/%hv_fcopy_uio_daemon --no-daemon
Restart=on-failure
[Install]
WantedBy=default.target
EOF
#
#
#
d=%buildroot%_udevrulesdir
mkdir -vp ${d}
tee ${d}/%name.rules <<EOF
ACTION=="add", KERNEL=="%chardev_kvp", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_kvp_daemon.service"
ACTION=="add", KERNEL=="%chardev_vss", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_vss_daemon.service"
ACTION=="add", KERNEL=="%chardev_fcopy", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_fcopy_daemon.service"
ACTION=="add", SUBSYSTEM=="vmbus", ATTRS{class_id}=="{34d14be3-dee4-41c8-9ae7-6b174977c192}", RUN{builtin}+="kmod load uio_hv_generic", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_fcopy_uio_daemon.service"
EOF
#
#
helper=inst_sys.sh
tee %buildroot${bindir}/${helper} <<'_EOF_'
#!/bin/bash
# Starting daemons via RUN== in udev rules is not supported.
# In inst-sys systemd is not used, so start all daemons manually.
bindir=%helper_dir/bin
declare -a helpers
test -c /dev/%chardev_kvp && helpers+=(%hv_kvp_daemon)
test -c /dev/%chardev_vss && helpers+=(%hv_vss_daemon)
if test -c /dev/%chardev_fcopy
then
helpers+=(%hv_fcopy_daemon)
else
modprobe -v uio_hv_generic
if test -f /sys/bus/vmbus/drivers/uio_hv_generic/new_id
then
echo '34d14be3-dee4-41c8-9ae7-6b174977c192' > /sys/bus/vmbus/drivers/uio_hv_generic/new_id
helpers+=(%hv_fcopy_uio_daemon)
fi
fi
if test -d /sys/bus/vmbus/devices
then
export PATH=${bindir}:$PATH
echo -n "Starting hyper-v helpers:"
for i in "${helpers[@]}"
do
if mkdir /run/$i
then
echo -n " $i"
$i < /dev/null &
fi
done
echo " ... done"
fi
_EOF_
chmod 755 %buildroot${bindir}/${helper}
#
%?python3_fix_shebang
%files
%doc kvptest.ps1.txt
%_unitdir/*
%_udevrulesdir/*
%_sbindir/*
%helper_dir
%pre
# hv_kvp_daemon in SLES11 SP2 stored temporary state files in /var/opt
# move them to /var/lib and remove old directory, if possible.
if test -d /var/opt/hyperv
then
if mkdir -p -v -m 0755 /var/lib/hyperv
then
cd /var/lib/hyperv
for oldfile in /var/opt/hyperv/ifcfg-* /var/opt/hyperv/.kvp_pool_*
do
if test -e "${oldfile}"
then
mv -vfb "${oldfile}" . || :
fi
done
cd - >/dev/null
fi
rmdir -v /var/opt/hyperv || :
fi
: nothing to do in case of systemd
%post
board_vendor=
product_name=
if cd /sys/class/dmi/id 2>/dev/null
then
if test -r board_vendor
then
board_vendor="`cat board_vendor`"
fi
if test -r product_name
then
product_name="`cat product_name`"
fi
cd - >/dev/null
fi
if test "${board_vendor}" = "Microsoft Corporation" -a "${product_name}" = "Virtual Machine"
then
: nothing to do in case of systemd
fi
%preun
: nothing to do in case of systemd
%postun
# no restart on update because the daemon can not be restarted
: nothing to do in case of systemd
%changelog

View File

@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* An implementation of host to guest copy functionality for Linux.
*
* Copyright (C) 2014, Microsoft, Inc.
*
* Author : K. Y. Srinivasan <kys@microsoft.com>
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/hyperv.h>
#include <linux/limits.h>
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
static int target_fd;
static char target_fname[PATH_MAX];
static unsigned long long filesize;
static int hv_start_fcopy(struct hv_start_fcopy *smsg)
{
int error = HV_E_FAIL;
char *q, *p;
filesize = 0;
p = (char *)smsg->path_name;
snprintf(target_fname, sizeof(target_fname), "%s/%s",
(char *)smsg->path_name, (char *)smsg->file_name);
syslog(LOG_INFO, "Target file name: %s", target_fname);
/*
* Check to see if the path is already in place; if not,
* create if required.
*/
while ((q = strchr(p, '/')) != NULL) {
if (q == p) {
p++;
continue;
}
*q = '\0';
if (access((char *)smsg->path_name, F_OK)) {
if (smsg->copy_flags & CREATE_PATH) {
if (mkdir((char *)smsg->path_name, 0755)) {
syslog(LOG_ERR, "Failed to create %s",
(char *)smsg->path_name);
goto done;
}
} else {
syslog(LOG_ERR, "Invalid path: %s",
(char *)smsg->path_name);
goto done;
}
}
p = q + 1;
*q = '/';
}
if (!access(target_fname, F_OK)) {
syslog(LOG_INFO, "File: %s exists", target_fname);
if (!(smsg->copy_flags & OVER_WRITE)) {
error = HV_ERROR_ALREADY_EXISTS;
goto done;
}
}
target_fd = open(target_fname,
O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
if (target_fd == -1) {
syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
goto done;
}
error = 0;
done:
if (error)
target_fname[0] = '\0';
return error;
}
static int hv_copy_data(struct hv_do_fcopy *cpmsg)
{
ssize_t bytes_written;
int ret = 0;
bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
cpmsg->offset);
filesize += cpmsg->size;
if (bytes_written != cpmsg->size) {
switch (errno) {
case ENOSPC:
ret = HV_ERROR_DISK_FULL;
break;
default:
ret = HV_E_FAIL;
break;
}
syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
filesize, (long)bytes_written, strerror(errno));
}
return ret;
}
/*
* Reset target_fname to "" in the two below functions for hibernation: if
* the fcopy operation is aborted by hibernation, the daemon should remove the
* partially-copied file; to achieve this, the hv_utils driver always fakes a
* CANCEL_FCOPY message upon suspend, and later when the VM resumes back,
* the daemon calls hv_copy_cancel() to remove the file; if a file is copied
* successfully before suspend, hv_copy_finished() must reset target_fname to
* avoid that the file can be incorrectly removed upon resume, since the faked
* CANCEL_FCOPY message is spurious in this case.
*/
static int hv_copy_finished(void)
{
close(target_fd);
target_fname[0] = '\0';
return 0;
}
static int hv_copy_cancel(void)
{
close(target_fd);
if (strlen(target_fname) > 0) {
unlink(target_fname);
target_fname[0] = '\0';
}
return 0;
}
void print_usage(char *argv[])
{
fprintf(stderr, "Usage: %s [options]\n"
"Options are:\n"
" -n, --no-daemon stay in foreground, don't daemonize\n"
" -h, --help print this help\n", argv[0]);
}
int main(int argc, char *argv[])
{
int fcopy_fd = -1;
int error;
int daemonize = 1, long_index = 0, opt;
int version = FCOPY_CURRENT_VERSION;
union {
struct hv_fcopy_hdr hdr;
struct hv_start_fcopy start;
struct hv_do_fcopy copy;
__u32 kernel_modver;
} buffer = { };
int in_handshake;
static struct option long_options[] = {
{"help", no_argument, 0, 'h' },
{"no-daemon", no_argument, 0, 'n' },
{0, 0, 0, 0 }
};
while ((opt = getopt_long(argc, argv, "hn", long_options,
&long_index)) != -1) {
switch (opt) {
case 'n':
daemonize = 0;
break;
case 'h':
default:
print_usage(argv);
exit(EXIT_FAILURE);
}
}
if (daemonize && daemon(1, 0)) {
syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
openlog("HV_FCOPY", 0, LOG_USER);
syslog(LOG_INFO, "starting; pid is:%d", getpid());
reopen_fcopy_fd:
if (fcopy_fd != -1)
close(fcopy_fd);
/* Remove any possible partially-copied file on error */
hv_copy_cancel();
in_handshake = 1;
fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
if (fcopy_fd < 0) {
syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
/*
* Register with the kernel.
*/
if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
exit(EXIT_FAILURE);
}
while (1) {
/*
* In this loop we process fcopy messages after the
* handshake is complete.
*/
ssize_t len;
len = pread(fcopy_fd, &buffer, sizeof(buffer), 0);
if (len < 0) {
syslog(LOG_ERR, "pread failed: %s", strerror(errno));
goto reopen_fcopy_fd;
}
if (in_handshake) {
if (len != sizeof(buffer.kernel_modver)) {
syslog(LOG_ERR, "invalid version negotiation");
exit(EXIT_FAILURE);
}
in_handshake = 0;
syslog(LOG_INFO, "kernel module version: %u",
buffer.kernel_modver);
continue;
}
switch (buffer.hdr.operation) {
case START_FILE_COPY:
error = hv_start_fcopy(&buffer.start);
break;
case WRITE_TO_FILE:
error = hv_copy_data(&buffer.copy);
break;
case COMPLETE_FCOPY:
error = hv_copy_finished();
break;
case CANCEL_FCOPY:
error = hv_copy_cancel();
break;
default:
error = HV_E_FAIL;
syslog(LOG_ERR, "Unknown operation: %d",
buffer.hdr.operation);
}
/*
* pwrite() may return an error due to the faked CANCEL_FCOPY
* message upon hibernation. Ignore the error by resetting the
* dev file, i.e. closing and re-opening it.
*/
if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
goto reopen_fcopy_fd;
}
}
}

View File

@ -0,0 +1,497 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* An implementation of host to guest copy functionality for Linux.
*
* Copyright (C) 2023, Microsoft, Inc.
*
* Author : K. Y. Srinivasan <kys@microsoft.com>
* Author : Saurabh Sengar <ssengar@microsoft.com>
*
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <wchar.h>
#include <sys/stat.h>
#include <linux/hyperv.h>
#include <linux/limits.h>
#include "vmbus_bufring.h"
#define ICMSGTYPE_NEGOTIATE 0
#define ICMSGTYPE_FCOPY 7
#define WIN8_SRV_MAJOR 1
#define WIN8_SRV_MINOR 1
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
#define MAX_FOLDER_NAME 15
#define MAX_PATH_LEN 15
#define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio"
#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
};
#define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */
unsigned char desc[HV_RING_SIZE];
static int target_fd;
static char target_fname[PATH_MAX];
static unsigned long long filesize;
static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags)
{
int error = HV_E_FAIL;
char *q, *p;
filesize = 0;
p = path_name;
snprintf(target_fname, sizeof(target_fname), "%s/%s",
path_name, file_name);
/*
* Check to see if the path is already in place; if not,
* create if required.
*/
while ((q = strchr(p, '/')) != NULL) {
if (q == p) {
p++;
continue;
}
*q = '\0';
if (access(path_name, F_OK)) {
if (flags & CREATE_PATH) {
if (mkdir(path_name, 0755)) {
syslog(LOG_ERR, "Failed to create %s",
path_name);
goto done;
}
} else {
syslog(LOG_ERR, "Invalid path: %s", path_name);
goto done;
}
}
p = q + 1;
*q = '/';
}
if (!access(target_fname, F_OK)) {
syslog(LOG_INFO, "File: %s exists", target_fname);
if (!(flags & OVER_WRITE)) {
error = HV_ERROR_ALREADY_EXISTS;
goto done;
}
}
target_fd = open(target_fname,
O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
if (target_fd == -1) {
syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
goto done;
}
error = 0;
done:
if (error)
target_fname[0] = '\0';
return error;
}
/* copy the data into the file */
static int hv_copy_data(struct hv_do_fcopy *cpmsg)
{
ssize_t len;
int ret = 0;
len = pwrite(target_fd, cpmsg->data, cpmsg->size, cpmsg->offset);
filesize += cpmsg->size;
if (len != cpmsg->size) {
switch (errno) {
case ENOSPC:
ret = HV_ERROR_DISK_FULL;
break;
default:
ret = HV_E_FAIL;
break;
}
syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
filesize, (long)len, strerror(errno));
}
return ret;
}
static int hv_copy_finished(void)
{
close(target_fd);
target_fname[0] = '\0';
return 0;
}
static void print_usage(char *argv[])
{
fprintf(stderr, "Usage: %s [options]\n"
"Options are:\n"
" -n, --no-daemon stay in foreground, don't daemonize\n"
" -h, --help print this help\n", argv[0]);
}
static bool 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)
{
int icframe_major, icframe_minor;
int icmsg_major, icmsg_minor;
int fw_major, fw_minor;
int srv_major, srv_minor;
int i, j;
bool found_match = false;
struct icmsg_negotiate *negop;
/* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) {
syslog(LOG_ERR, "Invalid icmsg negotiate");
return false;
}
icmsghdrp->icmsgsize = 0x10;
negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR];
icframe_major = negop->icframe_vercnt;
icframe_minor = 0;
icmsg_major = negop->icmsg_vercnt;
icmsg_minor = 0;
/* Validate negop packet */
if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) {
syslog(LOG_ERR, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
icframe_major, icmsg_major);
goto fw_error;
}
/*
* Select the framework version number we will
* support.
*/
for (i = 0; i < fw_vercnt; i++) {
fw_major = (fw_version[i] >> 16);
fw_minor = (fw_version[i] & 0xFFFF);
for (j = 0; j < negop->icframe_vercnt; j++) {
if (negop->icversion_data[j].major == fw_major &&
negop->icversion_data[j].minor == fw_minor) {
icframe_major = negop->icversion_data[j].major;
icframe_minor = negop->icversion_data[j].minor;
found_match = true;
break;
}
}
if (found_match)
break;
}
if (!found_match)
goto fw_error;
found_match = false;
for (i = 0; i < srv_vercnt; i++) {
srv_major = (srv_version[i] >> 16);
srv_minor = (srv_version[i] & 0xFFFF);
for (j = negop->icframe_vercnt;
(j < negop->icframe_vercnt + negop->icmsg_vercnt);
j++) {
if (negop->icversion_data[j].major == srv_major &&
negop->icversion_data[j].minor == srv_minor) {
icmsg_major = negop->icversion_data[j].major;
icmsg_minor = negop->icversion_data[j].minor;
found_match = true;
break;
}
}
if (found_match)
break;
}
/*
* Respond with the framework and service
* version numbers we can support.
*/
fw_error:
if (!found_match) {
negop->icframe_vercnt = 0;
negop->icmsg_vercnt = 0;
} else {
negop->icframe_vercnt = 1;
negop->icmsg_vercnt = 1;
}
if (nego_fw_version)
*nego_fw_version = (icframe_major << 16) | icframe_minor;
if (nego_srv_version)
*nego_srv_version = (icmsg_major << 16) | icmsg_minor;
negop->icversion_data[0].major = icframe_major;
negop->icversion_data[0].minor = icframe_minor;
negop->icversion_data[1].major = icmsg_major;
negop->icversion_data[1].minor = icmsg_minor;
return found_match;
}
static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size)
{
size_t len = 0;
while (len < dest_size) {
if (src[len] < 0x80)
dest[len++] = (char)(*src++);
else
dest[len++] = 'X';
}
dest[len] = '\0';
}
static int hv_fcopy_start(struct hv_start_fcopy *smsg_in)
{
setlocale(LC_ALL, "en_US.utf8");
size_t file_size, path_size;
char *file_name, *path_name;
char *in_file_name = (char *)smsg_in->file_name;
char *in_path_name = (char *)smsg_in->path_name;
file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1;
path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1;
file_name = (char *)malloc(file_size * sizeof(char));
path_name = (char *)malloc(path_size * sizeof(char));
if (!file_name || !path_name) {
free(file_name);
free(path_name);
syslog(LOG_ERR, "Can't allocate memory for file name and/or path name");
return HV_E_FAIL;
}
wcstoutf8(file_name, (__u16 *)in_file_name, file_size);
wcstoutf8(path_name, (__u16 *)in_path_name, path_size);
return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags);
}
static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen)
{
int operation = fcopy_msg->operation;
/*
* The strings sent from the host are encoded in
* utf16; convert it to utf8 strings.
* 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:
return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg);
case WRITE_TO_FILE:
return hv_copy_data((struct hv_do_fcopy *)fcopy_msg);
case COMPLETE_FCOPY:
return hv_copy_finished();
}
return HV_E_FAIL;
}
/* process the packet recv from host */
static int fcopy_pkt_process(struct vmbus_br *txbr)
{
int ret, offset, pktlen;
int fcopy_srv_version;
const struct vmbus_chanpkt_hdr *pkt;
struct hv_fcopy_hdr *fcopy_msg;
struct icmsg_hdr *icmsghdr;
pkt = (const struct vmbus_chanpkt_hdr *)desc;
offset = pkt->hlen << 3;
pktlen = (pkt->tlen << 3) - offset;
icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)];
icmsghdr->status = HV_E_FAIL;
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions,
FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT,
NULL, &fcopy_srv_version)) {
syslog(LOG_INFO, "FCopy IC version %d.%d",
fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF);
icmsghdr->status = 0;
}
} else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
/* Ensure recvlen is big enough to contain hv_fcopy_hdr */
if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u",
pktlen);
return -ENOBUFS;
}
fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR];
icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen);
}
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0);
if (ret) {
syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret);
return ret;
}
return 0;
}
static void fcopy_get_first_folder(char *path, char *chan_no)
{
DIR *dir = opendir(path);
struct dirent *entry;
if (!dir) {
syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno));
return;
}
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 &&
strcmp(entry->d_name, "..") != 0) {
strcpy(chan_no, entry->d_name);
break;
}
}
closedir(dir);
}
int main(int argc, char *argv[])
{
int fcopy_fd = -1, tmp = 1;
int daemonize = 1, long_index = 0, opt, ret = -EINVAL;
struct vmbus_br txbr, rxbr;
void *ring;
uint32_t len = HV_RING_SIZE;
char uio_name[MAX_FOLDER_NAME] = {0};
char uio_dev_path[MAX_PATH_LEN] = {0};
static struct option long_options[] = {
{"help", no_argument, 0, 'h' },
{"no-daemon", no_argument, 0, 'n' },
{0, 0, 0, 0 }
};
while ((opt = getopt_long(argc, argv, "hn", long_options,
&long_index)) != -1) {
switch (opt) {
case 'n':
daemonize = 0;
break;
case 'h':
default:
print_usage(argv);
goto exit;
}
}
if (daemonize && daemon(1, 0)) {
syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
goto exit;
}
openlog("HV_UIO_FCOPY", 0, LOG_USER);
syslog(LOG_INFO, "starting; pid is:%d", getpid());
fcopy_get_first_folder(FCOPY_UIO, uio_name);
snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name);
fcopy_fd = open(uio_dev_path, O_RDWR);
if (fcopy_fd < 0) {
syslog(LOG_ERR, "open %s failed; error: %d %s",
uio_dev_path, errno, strerror(errno));
ret = fcopy_fd;
goto exit;
}
ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE);
if (!ring) {
ret = errno;
syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret));
goto close;
}
vmbus_br_setup(&txbr, ring, HV_RING_SIZE);
vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE);
rxbr.vbr->imask = 0;
while (1) {
/*
* In this loop we process fcopy messages after the
* handshake is complete.
*/
ret = pread(fcopy_fd, &tmp, sizeof(int), 0);
if (ret < 0) {
syslog(LOG_ERR, "pread failed: %s", strerror(errno));
continue;
}
len = HV_RING_SIZE;
ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len);
if (unlikely(ret <= 0)) {
/* This indicates a failure to communicate (or worse) */
syslog(LOG_ERR, "VMBus channel recv error: %d", ret);
} else {
ret = fcopy_pkt_process(&txbr);
if (ret < 0)
goto close;
/* Signal host */
if ((write(fcopy_fd, &tmp, sizeof(int))) != sizeof(int)) {
ret = errno;
syslog(LOG_ERR, "Signal to host failed: %s\n", strerror(ret));
goto close;
}
}
}
close:
close(fcopy_fd);
exit:
return ret;
}

View File

@ -0,0 +1,28 @@
#!/bin/bash
# This script retrieves the DHCP state of a given interface.
# In the interest of keeping the KVP daemon code free of distro specific
# information; the kvp daemon code invokes this external script to gather
# DHCP setting for the specific interface.
#
# Input: Name of the interface
#
# Output: The script prints the string "Enabled" to stdout to indicate
# that DHCP is enabled on the interface. If DHCP is not enabled,
# the script prints the string "Disabled" to stdout.
#
# Each Distro is expected to implement this script in a distro specific
# fashion. For instance, on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for retrieving DHCP
# information.
if_file="/etc/sysconfig/network/ifcfg-$1"
dhcp=$(grep -- '^BOOTPROTO=.*dhcp' "$if_file" 2>/dev/null)
if [ "$dhcp" != "" ];
then
echo "Enabled"
else
echo "Disabled"
fi

View File

@ -0,0 +1,16 @@
#!/bin/bash
# This script parses /etc/resolv.conf to retrive DNS information.
# In the interest of keeping the KVP daemon code free of distro specific
# information; the kvp daemon code invokes this external script to gather
# DNS information.
# This script is expected to print the nameserver values to stdout.
# Each Distro is expected to implement this script in a distro specific
# fashion. For instance on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for retrieving DNS
# entries.
if test -r /etc/resolv.conf
then
awk -- '/^nameserver/ { print $2 }' /etc/resolv.conf
fi

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,247 @@
#!/bin/bash
#
# In the interest of keeping the KVP daemon code free of distro specific
# information; the kvp daemon code invokes this external script to configure
# the interface.
#
# The only argument to this script is the configuration file that is to
# be used to configure the interface.
#
# Here is the ifcfg format of the ip configuration file:
#
# HWADDR=macaddr
# DEVICE=interface name
# BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured
# or "none" if no boot-time protocol should be used)
#
# IPADDR0=ipaddr1
# IPADDR1=ipaddr2
# IPADDRx=ipaddry (where y = x + 1)
#
# NETMASK0=netmask1
# NETMASKx=netmasky (where y = x + 1)
#
# GATEWAY=ipaddr1
# GATEWAYx=ipaddry (where y = x + 1)
#
# DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
#
# IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
# IPV6NETMASK.
#
# Here is the keyfile format of the ip configuration file:
#
# [ethernet]
# mac-address=macaddr
# [connection]
# interface-name=interface name
#
# [ipv4]
# method=<protocol> (where <protocol> is "auto" if DHCP is configured
# or "manual" if no boot-time protocol should be used)
#
# address1=ipaddr1/plen
# address2=ipaddr2/plen
#
# gateway=gateway1;gateway2
#
# dns=dns1;
#
# [ipv6]
# address1=ipaddr1/plen
# address2=ipaddr2/plen
#
# gateway=gateway1;gateway2
#
# dns=dns1;dns2
#
# The host can specify multiple ipv4 and ipv6 addresses to be
# configured for the interface. Furthermore, the configuration
# needs to be persistent. A subsequent GET call on the interface
# is expected to return the configuration that is set via the SET
# call.
#
if_cfg=$1
nm_cfg=$2
if ! test -f "${if_cfg}"
then
: expect configuration datafile as first argument
exit 1
fi
# send subshell output to syslog
(
# copied from /etc/sysconfig/network/scripts/functions
mask2pfxlen() {
local i octet width=0 mask=(${1//./ })
test ${#mask[@]} -eq 4 || return 1
for octet in 0 1 2 3
do
test "${mask[octet]}" -ge 0 -a "${mask[octet]}" -le 255 || return 1
for i in 128 192 224 240 248 252 254 255
do
test ${mask[octet]} -ge $i && ((width++))
done
done
echo $width
return 0
}
pfxlen2mask() {
test -n "$1" || return 1
local o i n=0 adr=() len=$(($1))
for o in 0 1 2 3
do
adr[$o]=0
for i in 128 64 32 16 8 4 2 1
do
((n++ < len)) && ((adr[$o] = ${adr[$o]} + $i))
done
done
echo ${adr[0]}.${adr[1]}.${adr[2]}.${adr[3]}
return 0
}
# remove known config variables from environment
unset HWADDR
unset BOOTPROTO
unset DEVICE
unset ${!IPADDR*}
unset ${!NETMASK*}
unset ${!GATEWAY*}
unset ${!IPV6ADDR*}
unset ${!IPV6NETMASK*}
unset ${!IPV6_DEFAULTGW*}
unset ${!DNS*}
. "${if_cfg}"
#
if test -z "${DEVICE}"
then
echo "Missing DEVICE= in ${if_cfg}"
exit 1
fi
#
t_ifcfg=`mktemp`
t_ifroute=`mktemp`
_exit() {
rm -f "${t_ifcfg}" "${t_ifroute}"
}
trap _exit EXIT
#
if test -z "${t_ifcfg}" || test -z "${t_ifroute}"
then
exit 1
fi
#
# Create ifcfg-* file
(
echo "STARTMODE=auto"
#
if test -n "${HWADDR}"
then
: # ignore HWADDR, it just repeats the existing MAC value
fi
#
if test "${BOOTPROTO}" = "dhcp"
then
echo "BOOTPROTO=dhcp"
elif test -n "${!IPADDR*}${!IPV6ADDR*}"
then
echo "BOOTPROTO=static"
fi
# single index for all ipv4 and ipv6 adresses in final ifcfg file
i=0
idx=""
# loop through all ipv4 adresses
for var in ${!IPADDR*}
do
index=${var#IPADDR}
pfx=
# find corresponding NETMASK variable
eval nm=\$NETMASK${index}
# if specified, calculate prefix
if test -n "${nm}"
then
pfx=`mask2pfxlen "${nm}" 2>/dev/null`
fi
# if not specified, force prefix
if test -z "${pfx}"
then
pfx="32"
fi
# construct actual value
eval val=\$IPADDR${index}
# write config variable
echo "IPADDR${idx}='${val}/${pfx}'"
idx="_$((++i))"
done
# loop through all ipv6 adresses
for var in ${!IPV6ADDR*}
do
index=${var#IPV6ADDR}
# find corresponding IPV6NETMASK variable
eval pfx=\$IPV6NETMASK${index}
# if not specified, force prefix
if test -z "${pfx}"
then
pfx=128
fi
# construct actual value
eval val=\$IPV6ADDR${index}
# write config variable
echo "IPADDR${idx}='${val}/${pfx}'"
idx="_$((++i))"
done
) >> "${t_ifcfg}"
# Create ifroute-* file
(
if test -n "${GATEWAY}"
then
echo "default $GATEWAY - $DEVICE"
fi
if test -n "${IPV6_DEFAULTGW}"
then
echo "default $IPV6_DEFAULTGW - $DEVICE"
fi
) >> "${t_ifroute}"
# Only a single default gateway is supported
unset GATEWAY IPV6_DEFAULTGW
if test -n "${!GATEWAY*}${!IPV6_DEFAULTGW*}"
then
echo "WARNING: multiple gateways not supported: ${!GATEWAY*} ${!IPV6_DEFAULTGW*}"
fi
# collect DNS info
_DNS_=
for var in ${!DNS*}
do
eval val=\$${var}
if test -n "${_DNS_}"
then
_DNS_="${_DNS_} ${val}"
else
_DNS_=${val}
fi
done
#
echo "$0: working on network interface ifcfg-${DEVICE}"
mkdir -vp "/etc/sysconfig/network"
cp -fb ${t_ifcfg} "/etc/sysconfig/network/ifcfg-${DEVICE}"
cp -fb ${t_ifroute} "/etc/sysconfig/network/ifroute-${DEVICE}"
if test -w /etc/sysconfig/network/config
then
sed -i "s@^NETCONFIG_DNS_STATIC_SERVERS=.*@NETCONFIG_DNS_STATIC_SERVERS='$_DNS_'@" /etc/sysconfig/network/config
netconfig update -m dns
fi
echo "$0: working on network interface ifcfg-${DEVICE}"
nm_filename="${nm_cfg##*/}"
mkdir -vp "/etc/NetworkManager/system-connections"
umask 0177
sed '/\[connection\]/a autoconnect=true' "${nm_cfg}" > "/etc/NetworkManager/system-connections/${nm_filename}"
ifdown "${DEVICE}"
ifup "${DEVICE}"
) 2>&1 | logger -t "${0##*/}[$PPID / $$]"

View File

@ -0,0 +1,355 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* An implementation of the host initiated guest snapshot for Hyper-V.
*
* Copyright (C) 2013, Microsoft, Inc.
* Author : K. Y. Srinivasan <kys@microsoft.com>
*/
#include <sys/types.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <fcntl.h>
#include <stdio.h>
#include <mntent.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/hyperv.h>
#include <syslog.h>
#include <getopt.h>
#include <stdbool.h>
#include <dirent.h>
static bool fs_frozen;
/* Don't use syslog() in the function since that can cause write to disk */
static int vss_do_freeze(char *dir, unsigned int cmd)
{
int ret, fd = open(dir, O_RDONLY);
if (fd < 0)
return 1;
ret = ioctl(fd, cmd, 0);
/*
* If a partition is mounted more than once, only the first
* FREEZE/THAW can succeed and the later ones will get
* EBUSY/EINVAL respectively: there could be 2 cases:
* 1) a user may mount the same partition to different directories
* by mistake or on purpose;
* 2) The subvolume of btrfs appears to have the same partition
* mounted more than once.
*/
if (ret) {
if ((cmd == FIFREEZE && errno == EBUSY) ||
(cmd == FITHAW && errno == EINVAL)) {
close(fd);
return 0;
}
}
close(fd);
return !!ret;
}
static bool is_dev_loop(const char *blkname)
{
char *buffer;
DIR *dir;
struct dirent *entry;
bool ret = false;
buffer = malloc(PATH_MAX);
if (!buffer) {
syslog(LOG_ERR, "Can't allocate memory!");
exit(1);
}
snprintf(buffer, PATH_MAX, "%s/loop", blkname);
if (!access(buffer, R_OK | X_OK)) {
ret = true;
goto free_buffer;
} else if (errno != ENOENT) {
syslog(LOG_ERR, "Can't access: %s; error:%d %s!",
buffer, errno, strerror(errno));
}
snprintf(buffer, PATH_MAX, "%s/slaves", blkname);
dir = opendir(buffer);
if (!dir) {
if (errno != ENOENT)
syslog(LOG_ERR, "Can't opendir: %s; error:%d %s!",
buffer, errno, strerror(errno));
goto free_buffer;
}
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0)
continue;
snprintf(buffer, PATH_MAX, "%s/slaves/%s", blkname,
entry->d_name);
if (is_dev_loop(buffer)) {
ret = true;
break;
}
}
closedir(dir);
free_buffer:
free(buffer);
return ret;
}
static int vss_operate(int operation)
{
char match[] = "/dev/";
FILE *mounts;
struct mntent *ent;
struct stat sb;
char errdir[1024] = {0};
char blkdir[23]; /* /sys/dev/block/XXX:XXX */
unsigned int cmd;
int error = 0, root_seen = 0, save_errno = 0;
switch (operation) {
case VSS_OP_FREEZE:
cmd = FIFREEZE;
break;
case VSS_OP_THAW:
cmd = FITHAW;
break;
default:
return -1;
}
mounts = setmntent("/proc/mounts", "r");
if (mounts == NULL)
return -1;
while ((ent = getmntent(mounts))) {
if (strncmp(ent->mnt_fsname, match, strlen(match)))
continue;
if (stat(ent->mnt_fsname, &sb)) {
syslog(LOG_ERR, "Can't stat: %s; error:%d %s!",
ent->mnt_fsname, errno, strerror(errno));
} else {
sprintf(blkdir, "/sys/dev/block/%d:%d",
major(sb.st_rdev), minor(sb.st_rdev));
if (is_dev_loop(blkdir))
continue;
}
if (hasmntopt(ent, MNTOPT_RO) != NULL)
continue;
if (strcmp(ent->mnt_type, "vfat") == 0)
continue;
if (strcmp(ent->mnt_dir, "/") == 0) {
root_seen = 1;
continue;
}
error |= vss_do_freeze(ent->mnt_dir, cmd);
if (operation == VSS_OP_FREEZE) {
if (error)
goto err;
fs_frozen = true;
}
}
endmntent(mounts);
if (root_seen) {
error |= vss_do_freeze("/", cmd);
if (operation == VSS_OP_FREEZE) {
if (error)
goto err;
fs_frozen = true;
}
}
if (operation == VSS_OP_THAW && !error)
fs_frozen = false;
goto out;
err:
save_errno = errno;
if (ent) {
strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1);
endmntent(mounts);
}
vss_operate(VSS_OP_THAW);
fs_frozen = false;
/* Call syslog after we thaw all filesystems */
if (ent)
syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
errdir, save_errno, strerror(save_errno));
else
syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
strerror(save_errno));
out:
return error;
}
void print_usage(char *argv[])
{
fprintf(stderr, "Usage: %s [options]\n"
"Options are:\n"
" -n, --no-daemon stay in foreground, don't daemonize\n"
" -h, --help print this help\n", argv[0]);
}
int main(int argc, char *argv[])
{
int vss_fd = -1, len;
int error;
struct pollfd pfd;
int op;
struct hv_vss_msg vss_msg[1];
int daemonize = 1, long_index = 0, opt;
int in_handshake;
__u32 kernel_modver;
static struct option long_options[] = {
{"help", no_argument, 0, 'h' },
{"no-daemon", no_argument, 0, 'n' },
{0, 0, 0, 0 }
};
while ((opt = getopt_long(argc, argv, "hn", long_options,
&long_index)) != -1) {
switch (opt) {
case 'n':
daemonize = 0;
break;
case 'h':
print_usage(argv);
exit(0);
default:
print_usage(argv);
exit(EXIT_FAILURE);
}
}
if (daemonize && daemon(1, 0))
return 1;
openlog("Hyper-V VSS", 0, LOG_USER);
syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
reopen_vss_fd:
if (vss_fd != -1)
close(vss_fd);
if (fs_frozen) {
if (vss_operate(VSS_OP_THAW) || fs_frozen) {
syslog(LOG_ERR, "failed to thaw file system: err=%d",
errno);
exit(EXIT_FAILURE);
}
}
in_handshake = 1;
vss_fd = open("/dev/vmbus/hv_vss", O_RDWR);
if (vss_fd < 0) {
syslog(LOG_ERR, "open /dev/vmbus/hv_vss failed; error: %d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
/*
* Register ourselves with the kernel.
*/
vss_msg->vss_hdr.operation = VSS_OP_REGISTER1;
len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
if (len < 0) {
syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
errno, strerror(errno));
close(vss_fd);
exit(EXIT_FAILURE);
}
pfd.fd = vss_fd;
while (1) {
pfd.events = POLLIN;
pfd.revents = 0;
if (poll(&pfd, 1, -1) < 0) {
syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
if (errno == EINVAL) {
close(vss_fd);
exit(EXIT_FAILURE);
}
else
continue;
}
len = read(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
if (in_handshake) {
if (len != sizeof(kernel_modver)) {
syslog(LOG_ERR, "invalid version negotiation");
exit(EXIT_FAILURE);
}
kernel_modver = *(__u32 *)vss_msg;
in_handshake = 0;
syslog(LOG_INFO, "VSS: kernel module version: %d",
kernel_modver);
continue;
}
if (len != sizeof(struct hv_vss_msg)) {
syslog(LOG_ERR, "read failed; error:%d %s",
errno, strerror(errno));
goto reopen_vss_fd;
}
op = vss_msg->vss_hdr.operation;
error = HV_S_OK;
switch (op) {
case VSS_OP_FREEZE:
case VSS_OP_THAW:
error = vss_operate(op);
syslog(LOG_INFO, "VSS: op=%s: %s\n",
op == VSS_OP_FREEZE ? "FREEZE" : "THAW",
error ? "failed" : "succeeded");
if (error) {
error = HV_E_FAIL;
syslog(LOG_ERR, "op=%d failed!", op);
syslog(LOG_ERR, "report it with these files:");
syslog(LOG_ERR, "/etc/fstab and /proc/mounts");
}
break;
case VSS_OP_HOT_BACKUP:
syslog(LOG_INFO, "VSS: op=CHECK HOT BACKUP\n");
break;
default:
syslog(LOG_ERR, "Illegal op:%d\n", op);
}
/*
* The write() may return an error due to the faked VSS_OP_THAW
* message upon hibernation. Ignore the error by resetting the
* dev file, i.e. closing and re-opening it.
*/
vss_msg->error = error;
len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
if (len != sizeof(struct hv_vss_msg)) {
syslog(LOG_ERR, "write failed; error: %d %s", errno,
strerror(errno));
goto reopen_vss_fd;
}
}
close(vss_fd);
exit(0);
}

View File

@ -0,0 +1,318 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2009-2012,2016,2023 Microsoft Corp.
* Copyright (c) 2012 NetApp Inc.
* Copyright (c) 2012 Citrix Inc.
* All rights reserved.
*/
#include <errno.h>
#include <fcntl.h>
#include <emmintrin.h>
#include <linux/limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <unistd.h>
#include "vmbus_bufring.h"
/**
* Compiler barrier.
*
* Guarantees that operation reordering does not occur at compile time
* for operations directly before and after the barrier.
*/
#define rte_compiler_barrier() ({ asm volatile ("" : : : "memory"); })
#define VMBUS_RQST_ERROR 0xFFFFFFFFFFFFFFFF
#define ALIGN(val, align) ((typeof(val))((val) & (~((typeof(val))((align) - 1)))))
void *vmbus_uio_map(int *fd, int size)
{
void *map;
map = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
if (map == MAP_FAILED)
return NULL;
return map;
}
/* Increase bufring index by inc with wraparound */
static inline uint32_t vmbus_br_idxinc(uint32_t idx, uint32_t inc, uint32_t sz)
{
idx += inc;
if (idx >= sz)
idx -= sz;
return idx;
}
void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen)
{
br->vbr = buf;
br->windex = br->vbr->windex;
br->dsize = blen - sizeof(struct vmbus_bufring);
}
static inline __always_inline void
rte_smp_mb(void)
{
asm volatile("lock addl $0, -128(%%rsp); " ::: "memory");
}
static inline int
rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
{
uint8_t res;
asm volatile("lock ; "
"cmpxchgl %[src], %[dst];"
"sete %[res];"
: [res] "=a" (res), /* output */
[dst] "=m" (*dst)
: [src] "r" (src), /* input */
"a" (exp),
"m" (*dst)
: "memory"); /* no-clobber list */
return res;
}
static inline uint32_t
vmbus_txbr_copyto(const struct vmbus_br *tbr, uint32_t windex,
const void *src0, uint32_t cplen)
{
uint8_t *br_data = tbr->vbr->data;
uint32_t br_dsize = tbr->dsize;
const uint8_t *src = src0;
/* XXX use double mapping like Linux kernel? */
if (cplen > br_dsize - windex) {
uint32_t fraglen = br_dsize - windex;
/* Wrap-around detected */
memcpy(br_data + windex, src, fraglen);
memcpy(br_data, src + fraglen, cplen - fraglen);
} else {
memcpy(br_data + windex, src, cplen);
}
return vmbus_br_idxinc(windex, cplen, br_dsize);
}
/*
* Write scattered channel packet to TX bufring.
*
* The offset of this channel packet is written as a 64bits value
* immediately after this channel packet.
*
* The write goes through three stages:
* 1. Reserve space in ring buffer for the new data.
* Writer atomically moves priv_write_index.
* 2. Copy the new data into the ring.
* 3. Update the tail of the ring (visible to host) that indicates
* next read location. Writer updates write_index
*/
static int
vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen)
{
struct vmbus_bufring *vbr = tbr->vbr;
uint32_t ring_size = tbr->dsize;
uint32_t old_windex, next_windex, windex, total;
uint64_t save_windex;
int i;
total = 0;
for (i = 0; i < iovlen; i++)
total += iov[i].iov_len;
total += sizeof(save_windex);
/* Reserve space in ring */
do {
uint32_t avail;
/* Get current free location */
old_windex = tbr->windex;
/* Prevent compiler reordering this with calculation */
rte_compiler_barrier();
avail = vmbus_br_availwrite(tbr, old_windex);
/* If not enough space in ring, then tell caller. */
if (avail <= total)
return -EAGAIN;
next_windex = vmbus_br_idxinc(old_windex, total, ring_size);
/* Atomic update of next write_index for other threads */
} while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex));
/* Space from old..new is now reserved */
windex = old_windex;
for (i = 0; i < iovlen; i++)
windex = vmbus_txbr_copyto(tbr, windex, iov[i].iov_base, iov[i].iov_len);
/* Set the offset of the current channel packet. */
save_windex = ((uint64_t)old_windex) << 32;
windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
sizeof(save_windex));
/* The region reserved should match region used */
if (windex != next_windex)
return -EINVAL;
/* Ensure that data is available before updating host index */
rte_compiler_barrier();
/* Checkin for our reservation. wait for our turn to update host */
while (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex))
_mm_pause();
return 0;
}
int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data,
uint32_t dlen, uint32_t flags)
{
struct vmbus_chanpkt pkt;
unsigned int pktlen, pad_pktlen;
const uint32_t hlen = sizeof(pkt);
uint64_t pad = 0;
struct iovec iov[3];
int error;
pktlen = hlen + dlen;
pad_pktlen = ALIGN(pktlen, sizeof(uint64_t));
pkt.hdr.type = type;
pkt.hdr.flags = flags;
pkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT;
pkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT;
pkt.hdr.xactid = VMBUS_RQST_ERROR;
iov[0].iov_base = &pkt;
iov[0].iov_len = hlen;
iov[1].iov_base = data;
iov[1].iov_len = dlen;
iov[2].iov_base = &pad;
iov[2].iov_len = pad_pktlen - pktlen;
error = vmbus_txbr_write(txbr, iov, 3);
return error;
}
static inline uint32_t
vmbus_rxbr_copyfrom(const struct vmbus_br *rbr, uint32_t rindex,
void *dst0, size_t cplen)
{
const uint8_t *br_data = rbr->vbr->data;
uint32_t br_dsize = rbr->dsize;
uint8_t *dst = dst0;
if (cplen > br_dsize - rindex) {
uint32_t fraglen = br_dsize - rindex;
/* Wrap-around detected. */
memcpy(dst, br_data + rindex, fraglen);
memcpy(dst + fraglen, br_data, cplen - fraglen);
} else {
memcpy(dst, br_data + rindex, cplen);
}
return vmbus_br_idxinc(rindex, cplen, br_dsize);
}
/* Copy data from receive ring but don't change index */
static int
vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen)
{
uint32_t avail;
/*
* The requested data and the 64bits channel packet
* offset should be there at least.
*/
avail = vmbus_br_availread(rbr);
if (avail < dlen + sizeof(uint64_t))
return -EAGAIN;
vmbus_rxbr_copyfrom(rbr, rbr->vbr->rindex, data, dlen);
return 0;
}
/*
* Copy data from receive ring and change index
* NOTE:
* We assume (dlen + skip) == sizeof(channel packet).
*/
static int
vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip)
{
struct vmbus_bufring *vbr = rbr->vbr;
uint32_t br_dsize = rbr->dsize;
uint32_t rindex;
if (vmbus_br_availread(rbr) < dlen + skip + sizeof(uint64_t))
return -EAGAIN;
/* Record where host was when we started read (for debug) */
rbr->windex = rbr->vbr->windex;
/*
* Copy channel packet from RX bufring.
*/
rindex = vmbus_br_idxinc(rbr->vbr->rindex, skip, br_dsize);
rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);
/*
* Discard this channel packet's 64bits offset, which is useless to us.
*/
rindex = vmbus_br_idxinc(rindex, sizeof(uint64_t), br_dsize);
/* Update the read index _after_ the channel packet is fetched. */
rte_compiler_barrier();
vbr->rindex = rindex;
return 0;
}
int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr,
void *data, uint32_t *len)
{
struct vmbus_chanpkt_hdr pkt;
uint32_t dlen, bufferlen = *len;
int error;
error = vmbus_rxbr_peek(rxbr, &pkt, sizeof(pkt));
if (error)
return error;
if (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN))
/* XXX this channel is dead actually. */
return -EIO;
if (unlikely(pkt.hlen > pkt.tlen))
return -EIO;
/* Length are in quad words */
dlen = pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT;
*len = dlen;
/* If caller buffer is not large enough */
if (unlikely(dlen > bufferlen))
return -ENOBUFS;
/* Read data and skip packet header */
error = vmbus_rxbr_read(rxbr, data, dlen, 0);
if (error)
return error;
/* Return the number of bytes read */
return dlen + sizeof(uint64_t);
}

View File

@ -0,0 +1,158 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef _VMBUS_BUF_H_
#define _VMBUS_BUF_H_
#include <stdbool.h>
#include <stdint.h>
#define __packed __attribute__((__packed__))
#define unlikely(x) __builtin_expect(!!(x), 0)
#define ICMSGHDRFLAG_TRANSACTION 1
#define ICMSGHDRFLAG_REQUEST 2
#define ICMSGHDRFLAG_RESPONSE 4
#define IC_VERSION_NEGOTIATION_MAX_VER_COUNT 100
#define ICMSG_HDR (sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr))
#define ICMSG_NEGOTIATE_PKT_SIZE(icframe_vercnt, icmsg_vercnt) \
(ICMSG_HDR + sizeof(struct icmsg_negotiate) + \
(((icframe_vercnt) + (icmsg_vercnt)) * sizeof(struct ic_version)))
/*
* Channel packets
*/
/* Channel packet flags */
#define VMBUS_CHANPKT_TYPE_INBAND 0x0006
#define VMBUS_CHANPKT_TYPE_RXBUF 0x0007
#define VMBUS_CHANPKT_TYPE_GPA 0x0009
#define VMBUS_CHANPKT_TYPE_COMP 0x000b
#define VMBUS_CHANPKT_FLAG_NONE 0
#define VMBUS_CHANPKT_FLAG_RC 0x0001 /* report completion */
#define VMBUS_CHANPKT_SIZE_SHIFT 3
#define VMBUS_CHANPKT_SIZE_ALIGN BIT(VMBUS_CHANPKT_SIZE_SHIFT)
#define VMBUS_CHANPKT_HLEN_MIN \
(sizeof(struct vmbus_chanpkt_hdr) >> VMBUS_CHANPKT_SIZE_SHIFT)
/*
* Buffer ring
*/
struct vmbus_bufring {
volatile uint32_t windex;
volatile uint32_t rindex;
/*
* Interrupt mask {0,1}
*
* For TX bufring, host set this to 1, when it is processing
* the TX bufring, so that we can safely skip the TX event
* notification to host.
*
* For RX bufring, once this is set to 1 by us, host will not
* further dispatch interrupts to us, even if there are data
* pending on the RX bufring. This effectively disables the
* interrupt of the channel to which this RX bufring is attached.
*/
volatile uint32_t imask;
/*
* Win8 uses some of the reserved bits to implement
* interrupt driven flow management. On the send side
* we can request that the receiver interrupt the sender
* when the ring transitions from being full to being able
* to handle a message of size "pending_send_sz".
*
* Add necessary state for this enhancement.
*/
volatile uint32_t pending_send;
uint32_t reserved1[12];
union {
struct {
uint32_t feat_pending_send_sz:1;
};
uint32_t value;
} feature_bits;
/* Pad it to rte_mem_page_size() so that data starts on page boundary */
uint8_t reserved2[4028];
/*
* Ring data starts here + RingDataStartOffset
* !!! DO NOT place any fields below this !!!
*/
uint8_t data[];
} __packed;
struct vmbus_br {
struct vmbus_bufring *vbr;
uint32_t dsize;
uint32_t windex; /* next available location */
};
struct vmbus_chanpkt_hdr {
uint16_t type; /* VMBUS_CHANPKT_TYPE_ */
uint16_t hlen; /* header len, in 8 bytes */
uint16_t tlen; /* total len, in 8 bytes */
uint16_t flags; /* VMBUS_CHANPKT_FLAG_ */
uint64_t xactid;
} __packed;
struct vmbus_chanpkt {
struct vmbus_chanpkt_hdr hdr;
} __packed;
struct vmbuspipe_hdr {
unsigned int flags;
unsigned int msgsize;
} __packed;
struct ic_version {
unsigned short major;
unsigned short minor;
} __packed;
struct icmsg_negotiate {
unsigned short icframe_vercnt;
unsigned short icmsg_vercnt;
unsigned int reserved;
struct ic_version icversion_data[]; /* any size array */
} __packed;
struct icmsg_hdr {
struct ic_version icverframe;
unsigned short icmsgtype;
struct ic_version icvermsg;
unsigned short icmsgsize;
unsigned int status;
unsigned char ictransaction_id;
unsigned char icflags;
unsigned char reserved[2];
} __packed;
int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr, void *data, uint32_t *len);
int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data,
uint32_t dlen, uint32_t flags);
void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen);
void *vmbus_uio_map(int *fd, int size);
/* Amount of space available for write */
static inline uint32_t vmbus_br_availwrite(const struct vmbus_br *br, uint32_t windex)
{
uint32_t rindex = br->vbr->rindex;
if (windex >= rindex)
return br->dsize - (windex - rindex);
else
return rindex - windex;
}
static inline uint32_t vmbus_br_availread(const struct vmbus_br *br)
{
return br->dsize - vmbus_br_availwrite(br, br->vbr->windex);
}
#endif /* !_VMBUS_BUF_H_ */