Commit b711538a authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'hyperv-fixes-signed-20231009' of...

Merge tag 'hyperv-fixes-signed-20231009' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull hyperv fixes from Wei Liu:

 - fixes for Hyper-V VTL code (Saurabh Sengar and Olaf Hering)

 - fix hv_kvp_daemon to support keyfile based connection profile
   (Shradha Gupta)

* tag 'hyperv-fixes-signed-20231009' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux:
  hv/hv_kvp_daemon:Support for keyfile based connection profile
  hyperv: reduce size of ms_hyperv_info
  x86/hyperv: Add common print prefix "Hyper-V" in hv_init
  x86/hyperv: Remove hv_vtl_early_init initcall
  x86/hyperv: Restrict get_vtl to only VTL platforms
parents 832b5d0b 42999c90
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -7,6 +7,8 @@
 * Author : K. Y. Srinivasan <kys@microsoft.com>
 */

#define pr_fmt(fmt)  "Hyper-V: " fmt

#include <linux/efi.h>
#include <linux/types.h>
#include <linux/bitfield.h>
@@ -191,7 +193,7 @@ void set_hv_tscchange_cb(void (*cb)(void))
	struct hv_tsc_emulation_control emu_ctrl = {.enabled = 1};

	if (!hv_reenlightenment_available()) {
		pr_warn("Hyper-V: reenlightenment support is unavailable\n");
		pr_warn("reenlightenment support is unavailable\n");
		return;
	}

@@ -394,6 +396,7 @@ static void __init hv_get_partition_id(void)
	local_irq_restore(flags);
}

#if IS_ENABLED(CONFIG_HYPERV_VTL_MODE)
static u8 __init get_vtl(void)
{
	u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_REGISTERS;
@@ -416,13 +419,16 @@ static u8 __init get_vtl(void)
	if (hv_result_success(ret)) {
		ret = output->as64.low & HV_X64_VTL_MASK;
	} else {
		pr_err("Failed to get VTL(%lld) and set VTL to zero by default.\n", ret);
		ret = 0;
		pr_err("Failed to get VTL(error: %lld) exiting...\n", ret);
		BUG();
	}

	local_irq_restore(flags);
	return ret;
}
#else
static inline u8 get_vtl(void) { return 0; }
#endif

/*
 * This function is to be invoked early in the boot sequence after the
@@ -564,7 +570,7 @@ void __init hyperv_init(void)
	if (cpu_feature_enabled(X86_FEATURE_IBT) &&
	    *(u32 *)hv_hypercall_pg != gen_endbr()) {
		setup_clear_cpu_cap(X86_FEATURE_IBT);
		pr_warn("Hyper-V: Disabling IBT because of Hyper-V bug\n");
		pr_warn("Disabling IBT because of Hyper-V bug\n");
	}
#endif

@@ -604,9 +610,11 @@ void __init hyperv_init(void)
	hv_query_ext_cap(0);

	/* Find the VTL */
	if (!ms_hyperv.paravisor_present && hv_isolation_type_snp())
	ms_hyperv.vtl = get_vtl();

	if (ms_hyperv.vtl > 0) /* non default VTL */
		hv_vtl_early_init();

	return;

clean_guest_os_id:
+1 −2
Original line number Diff line number Diff line
@@ -215,7 +215,7 @@ static int hv_vtl_wakeup_secondary_cpu(int apicid, unsigned long start_eip)
	return hv_vtl_bringup_vcpu(vp_id, start_eip);
}

static int __init hv_vtl_early_init(void)
int __init hv_vtl_early_init(void)
{
	/*
	 * `boot_cpu_has` returns the runtime feature support,
@@ -230,4 +230,3 @@ static int __init hv_vtl_early_init(void)

	return 0;
}
early_initcall(hv_vtl_early_init);
+2 −0
Original line number Diff line number Diff line
@@ -340,8 +340,10 @@ static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; }

#ifdef CONFIG_HYPERV_VTL_MODE
void __init hv_vtl_init_platform(void);
int __init hv_vtl_early_init(void);
#else
static inline void __init hv_vtl_init_platform(void) {}
static inline int __init hv_vtl_early_init(void) { return 0; }
#endif

#include <asm-generic/mshyperv.h>
+1 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct ms_hyperv_info {
	u32 nested_features;
	u32 max_vp_index;
	u32 max_lp_index;
	u8 vtl;
	union {
		u32 isolation_config_a;
		struct {
@@ -54,7 +55,6 @@ struct ms_hyperv_info {
		};
	};
	u64 shared_gpa_boundary;
	u8 vtl;
};
extern struct ms_hyperv_info ms_hyperv;
extern bool hv_nested;
+202 −31
Original line number Diff line number Diff line
@@ -1171,12 +1171,79 @@ static int process_ip_string(FILE *f, char *ip_string, int type)
	return 0;
}

/*
 * Only IPv4 subnet strings needs to be converted to plen
 * For IPv6 the subnet is already privided in plen format
 */
static int kvp_subnet_to_plen(char *subnet_addr_str)
{
	int plen = 0;
	struct in_addr subnet_addr4;

	/*
	 * Convert subnet address to binary representation
	 */
	if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) {
		uint32_t subnet_mask = ntohl(subnet_addr4.s_addr);

		while (subnet_mask & 0x80000000) {
			plen++;
			subnet_mask <<= 1;
		}
	} else {
		return -1;
	}

	return plen;
}

static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
				int is_ipv6)
{
	char addr[INET6_ADDRSTRLEN];
	char subnet_addr[INET6_ADDRSTRLEN];
	int error, i = 0;
	int ip_offset = 0, subnet_offset = 0;
	int plen;

	memset(addr, 0, sizeof(addr));
	memset(subnet_addr, 0, sizeof(subnet_addr));

	while (parse_ip_val_buffer(ip_string, &ip_offset, addr,
				   (MAX_IP_ADDR_SIZE * 2)) &&
				   parse_ip_val_buffer(subnet,
						       &subnet_offset,
						       subnet_addr,
						       (MAX_IP_ADDR_SIZE *
							2))) {
		if (!is_ipv6)
			plen = kvp_subnet_to_plen((char *)subnet_addr);
		else
			plen = atoi(subnet_addr);

		if (plen < 0)
			return plen;

		error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr,
				plen);
		if (error < 0)
			return error;

		memset(addr, 0, sizeof(addr));
		memset(subnet_addr, 0, sizeof(subnet_addr));
	}

	return 0;
}

static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
{
	int error = 0;
	char if_file[PATH_MAX];
	FILE *file;
	char if_filename[PATH_MAX];
	char nm_filename[PATH_MAX];
	FILE *ifcfg_file, *nmfile;
	char cmd[PATH_MAX];
	int is_ipv6 = 0;
	char *mac_addr;
	int str_len;

@@ -1197,7 +1264,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
	 * in a given distro to configure the interface and so are free
	 * ignore information that may not be relevant.
	 *
	 * Here is the format of the ip configuration file:
	 * Here is the ifcfg format of the ip configuration file:
	 *
	 * HWADDR=macaddr
	 * DEVICE=interface name
@@ -1220,6 +1287,32 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
	 * 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;dns2
	 *
	 * [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
@@ -1227,17 +1320,32 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
	 * call.
	 */

	snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
	/*
	 * We are populating both ifcfg and nmconnection files
	 */
	snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC,
		 "/ifcfg-", if_name);

	file = fopen(if_file, "w");
	ifcfg_file = fopen(if_filename, "w");

	if (file == NULL) {
	if (!ifcfg_file) {
		syslog(LOG_ERR, "Failed to open config file; error: %d %s",
		       errno, strerror(errno));
		return HV_E_FAIL;
	}

	snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC,
		 "/", if_name, ".nmconnection");

	nmfile = fopen(nm_filename, "w");

	if (!nmfile) {
		syslog(LOG_ERR, "Failed to open config file; error: %d %s",
		       errno, strerror(errno));
		fclose(ifcfg_file);
		return HV_E_FAIL;
	}

	/*
	 * First write out the MAC address.
	 */
@@ -1248,14 +1356,31 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
		goto setval_error;
	}

	error = kvp_write_file(file, "HWADDR", "", mac_addr);
	free(mac_addr);
	error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr);
	if (error < 0)
		goto setmac_error;

	error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name);
	if (error < 0)
		goto setmac_error;

	error = fprintf(nmfile, "\n[connection]\n");
	if (error < 0)
		goto setmac_error;

	error = kvp_write_file(nmfile, "interface-name", "", if_name);
	if (error)
		goto setval_error;
		goto setmac_error;

	error = kvp_write_file(file, "DEVICE", "", if_name);
	error = fprintf(nmfile, "\n[ethernet]\n");
	if (error < 0)
		goto setmac_error;

	error = kvp_write_file(nmfile, "mac-address", "", mac_addr);
	if (error)
		goto setval_error;
		goto setmac_error;

	free(mac_addr);

	/*
	 * The dhcp_enabled flag is only for IPv4. In the case the host only
@@ -1263,47 +1388,91 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
	 * proceed to parse and pass the IPv6 information to the
	 * disto-specific script hv_set_ifconfig.
	 */

	/*
	 * First populate the ifcfg file format
	 */
	if (new_val->dhcp_enabled) {
		error = kvp_write_file(file, "BOOTPROTO", "", "dhcp");
		error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp");
		if (error)
			goto setval_error;

	} else {
		error = kvp_write_file(file, "BOOTPROTO", "", "none");
		error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none");
		if (error)
			goto setval_error;
	}

	/*
	 * Write the configuration for ipaddress, netmask, gateway and
	 * name servers.
	 */

	error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
	error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr,
				  IPADDR);
	if (error)
		goto setval_error;

	error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
	error = process_ip_string(ifcfg_file, (char *)new_val->sub_net,
				  NETMASK);
	if (error)
		goto setval_error;

	error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
	error = process_ip_string(ifcfg_file, (char *)new_val->gate_way,
				  GATEWAY);
	if (error)
		goto setval_error;

	error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
	error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS);
	if (error)
		goto setval_error;

	fclose(file);
	if (new_val->addr_family == ADDR_FAMILY_IPV6) {
		error = fprintf(nmfile, "\n[ipv6]\n");
		if (error < 0)
			goto setval_error;
		is_ipv6 = 1;
	} else {
		error = fprintf(nmfile, "\n[ipv4]\n");
		if (error < 0)
			goto setval_error;
	}

	/*
	 * Now we populate the keyfile format
	 */

	if (new_val->dhcp_enabled) {
		error = kvp_write_file(nmfile, "method", "", "auto");
		if (error < 0)
			goto setval_error;
	} else {
		error = kvp_write_file(nmfile, "method", "", "manual");
		if (error < 0)
			goto setval_error;
	}

	/*
	 * Write the configuration for ipaddress, netmask, gateway and
	 * name services
	 */
	error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,
				     (char *)new_val->sub_net, is_ipv6);
	if (error < 0)
		goto setval_error;

	error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way);
	if (error < 0)
		goto setval_error;

	error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr);
	if (error < 0)
		goto setval_error;

	fclose(nmfile);
	fclose(ifcfg_file);

	/*
	 * Now that we have populated the configuration file,
	 * invoke the external script to do its magic.
	 */

	str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s",
			   "hv_set_ifconfig", if_file);
	str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s",
			   "hv_set_ifconfig", if_filename, nm_filename);
	/*
	 * This is a little overcautious, but it's necessary to suppress some
	 * false warnings from gcc 8.0.1.
@@ -1320,10 +1489,12 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
		return HV_E_FAIL;
	}
	return 0;

setmac_error:
	free(mac_addr);
setval_error:
	syslog(LOG_ERR, "Failed to write config file");
	fclose(file);
	fclose(ifcfg_file);
	fclose(nmfile);
	return error;
}

Loading