mbox series

[Resend,RFC,V2,00/12] x86/Hyper-V: Add Hyper-V Isolation VM support

Message ID 20210414144945.3460554-1-ltykernel@gmail.com
Headers show
Series x86/Hyper-V: Add Hyper-V Isolation VM support | expand

Message

Tianyu Lan April 14, 2021, 2:49 p.m. UTC
From: Tianyu Lan <Tianyu.Lan@microsoft.com>

"Resend all patches because someone in CC list didn't receive all
patchset. Sorry for nosy."

Hyper-V provides two kinds of Isolation VMs. VBS(Virtualization-based
security) and AMD SEV-SNP unenlightened Isolation VMs. This patchset
is to add support for these Isolation VM support in Linux.

The memory of these vms are encrypted and host can't access guest
memory directly. Hyper-V provides new host visibility hvcall and
the guest needs to call new hvcall to mark memory visible to host
before sharing memory with host. For security, all network/storage
stack memory should not be shared with host and so there is bounce
buffer requests.

Vmbus channel ring buffer already plays bounce buffer role because
all data from/to host needs to copy from/to between the ring buffer
and IO stack memory. So mark vmbus channel ring buffer visible.

There are two exceptions - packets sent by vmbus_sendpacket_
pagebuffer() and vmbus_sendpacket_mpb_desc(). These packets
contains IO stack memory address and host will access these memory.
So add allocation bounce buffer support in vmbus for these packets.

For SNP isolation VM, guest needs to access the shared memory via
extra address space which is specified by Hyper-V CPUID HYPERV_CPUID_
ISOLATION_CONFIG. The access physical address of the shared memory
should be bounce buffer memory GPA plus with shared_gpa_boundary
reported by CPUID.

Tianyu Lan (12):
  x86/HV: Initialize GHCB page in Isolation VM
  x86/HV: Initialize shared memory boundary in Isolation VM
  x86/Hyper-V: Add new hvcall guest address host visibility support
  HV: Add Write/Read MSR registers via ghcb
  HV: Add ghcb hvcall support for SNP VM
  HV/Vmbus: Add SNP support for VMbus channel initiate message
  HV/Vmbus: Initialize VMbus ring buffer for Isolation VM
  UIO/Hyper-V: Not load UIO HV driver in the isolation VM.
  swiotlb: Add bounce buffer remap address setting function
  HV/IOMMU: Add Hyper-V dma ops support
  HV/Netvsc: Add Isolation VM support for netvsc driver
  HV/Storvsc: Add Isolation VM support for storvsc driver

 arch/x86/hyperv/Makefile           |   2 +-
 arch/x86/hyperv/hv_init.c          |  70 +++++--
 arch/x86/hyperv/ivm.c              | 289 +++++++++++++++++++++++++++++
 arch/x86/include/asm/hyperv-tlfs.h |  22 +++
 arch/x86/include/asm/mshyperv.h    |  90 +++++++--
 arch/x86/kernel/cpu/mshyperv.c     |   5 +
 arch/x86/kernel/pci-swiotlb.c      |   3 +-
 drivers/hv/channel.c               |  44 ++++-
 drivers/hv/connection.c            |  68 ++++++-
 drivers/hv/hv.c                    |  73 ++++++--
 drivers/hv/hyperv_vmbus.h          |   3 +
 drivers/hv/ring_buffer.c           |  83 ++++++---
 drivers/hv/vmbus_drv.c             |   3 +
 drivers/iommu/hyperv-iommu.c       | 127 +++++++++++++
 drivers/net/hyperv/hyperv_net.h    |  11 ++
 drivers/net/hyperv/netvsc.c        | 137 +++++++++++++-
 drivers/net/hyperv/rndis_filter.c  |   3 +
 drivers/scsi/storvsc_drv.c         |  67 ++++++-
 drivers/uio/uio_hv_generic.c       |   5 +
 include/asm-generic/hyperv-tlfs.h  |   1 +
 include/asm-generic/mshyperv.h     |  18 +-
 include/linux/hyperv.h             |  12 +-
 include/linux/swiotlb.h            |   5 +
 kernel/dma/swiotlb.c               |  13 +-
 mm/ioremap.c                       |   1 +
 mm/vmalloc.c                       |   1 +
 26 files changed, 1068 insertions(+), 88 deletions(-)
 create mode 100644 arch/x86/hyperv/ivm.c

Comments

Christoph Hellwig April 14, 2021, 3:40 p.m. UTC | #1
> +/*
> + * hv_set_mem_host_visibility - Set host visibility for specified memory.
> + */

I don't think this comment really clarifies anything over the function
name.  What is 'host visibility'

> +int hv_set_mem_host_visibility(void *kbuffer, u32 size, u32 visibility)

Should size be a size_t?
Should visibility be an enum of some kind?

> +int hv_mark_gpa_visibility(u16 count, const u64 pfn[], u32 visibility)

Not sure what this does either.

> +	local_irq_save(flags);
> +	input_pcpu = (struct hv_input_modify_sparse_gpa_page_host_visibility **)

Is there a chance we could find a shorter but still descriptive
name for this variable?  Why do we need the cast?

> +#define VMBUS_PAGE_VISIBLE_READ_ONLY HV_MAP_GPA_READABLE
> +#define VMBUS_PAGE_VISIBLE_READ_WRITE (HV_MAP_GPA_READABLE|HV_MAP_GPA_WRITABLE)

pointlessly overlong line.
Tianyu Lan April 15, 2021, 8:13 a.m. UTC | #2
Hi Christoph:
	Thanks for your review.

On 4/14/2021 11:40 PM, Christoph Hellwig wrote:
>> +/*

>> + * hv_set_mem_host_visibility - Set host visibility for specified memory.

>> + */

> 

> I don't think this comment really clarifies anything over the function

> name.  What is 'host visibility'


OK. Will update the comment.

> 

>> +int hv_set_mem_host_visibility(void *kbuffer, u32 size, u32 visibility)

> 

> Should size be a size_t?

> Should visibility be an enum of some kind?

> 


Will update.

>> +int hv_mark_gpa_visibility(u16 count, const u64 pfn[], u32 visibility)

> 

> Not sure what this does either.


Will add a comment.

> 

>> +	local_irq_save(flags);

>> +	input_pcpu = (struct hv_input_modify_sparse_gpa_page_host_visibility **)

> 

> Is there a chance we could find a shorter but still descriptive

> name for this variable?  Why do we need the cast?


Sure. The cast is to avoid build error due to "incompatible-pointer-types"
> 

>> +#define VMBUS_PAGE_VISIBLE_READ_ONLY HV_MAP_GPA_READABLE

>> +#define VMBUS_PAGE_VISIBLE_READ_WRITE (HV_MAP_GPA_READABLE|HV_MAP_GPA_WRITABLE)

> 

> pointlessly overlong line.

>
Tianyu Lan April 15, 2021, 8:39 a.m. UTC | #3
On 4/14/2021 11:50 PM, Christoph Hellwig wrote:
>> +struct dma_range {
>> +	dma_addr_t dma;
>> +	u32 mapping_size;
>> +};
> 
> That's a rather generic name that is bound to create a conflict sooner
> or later.

Good point. Will update.

> 
>>   #include "hyperv_net.h"
>>   #include "netvsc_trace.h"
>> +#include "../../hv/hyperv_vmbus.h"
> 
> Please move public interfaces out of the private header rather than doing
> this.

OK. Will update.

> 
>> +	if (hv_isolation_type_snp()) {
>> +		area = get_vm_area(buf_size, VM_IOREMAP);
> 
> Err, no.  get_vm_area is private a for a reason.
> 
>> +		if (!area)
>> +			goto cleanup;
>> +
>> +		vaddr = (unsigned long)area->addr;
>> +		for (i = 0; i < buf_size / HV_HYP_PAGE_SIZE; i++) {
>> +			extra_phys = (virt_to_hvpfn(net_device->recv_buf + i * HV_HYP_PAGE_SIZE)
>> +				<< HV_HYP_PAGE_SHIFT) + ms_hyperv.shared_gpa_boundary;
>> +			ret |= ioremap_page_range(vaddr + i * HV_HYP_PAGE_SIZE,
>> +					   vaddr + (i + 1) * HV_HYP_PAGE_SIZE,
>> +					   extra_phys, PAGE_KERNEL_IO);
>> +		}
>> +
>> +		if (ret)
>> +			goto cleanup;
> 
> And this is not something a driver should ever do.  I think you are badly
> reimplementing functionality that should be in the dma coherent allocator
> here.
>
OK. I will try hiding these in the Hyper-V dma ops callback. Thanks.
Konrad Rzeszutek Wilk April 15, 2021, 8:24 p.m. UTC | #4
On Wed, Apr 14, 2021 at 10:49:40AM -0400, Tianyu Lan wrote:
> From: Tianyu Lan <Tianyu.Lan@microsoft.com>

> 

> VMbus ring buffer are shared with host and it's need to

> be accessed via extra address space of Isolation VM with

> SNP support. This patch is to map the ring buffer

> address in extra address space via ioremap(). HV host


Why do you need to use ioremap()? Why not just use vmap?


> visibility hvcall smears data in the ring buffer and

> so reset the ring buffer memory to zero after calling

> visibility hvcall.


So you are exposing these two:
 EXPORT_SYMBOL_GPL(get_vm_area);
 EXPORT_SYMBOL_GPL(ioremap_page_range);

But if you used vmap wouldn't you get the same thing for free?

> 

> Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>

> ---

>  drivers/hv/channel.c      | 10 +++++

>  drivers/hv/hyperv_vmbus.h |  2 +

>  drivers/hv/ring_buffer.c  | 83 +++++++++++++++++++++++++++++----------

>  mm/ioremap.c              |  1 +

>  mm/vmalloc.c              |  1 +

>  5 files changed, 76 insertions(+), 21 deletions(-)

> 

> diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c

> index 407b74d72f3f..4a9fb7ad4c72 100644

> --- a/drivers/hv/channel.c

> +++ b/drivers/hv/channel.c

> @@ -634,6 +634,16 @@ static int __vmbus_open(struct vmbus_channel *newchannel,

>  	if (err)

>  		goto error_clean_ring;

>  

> +	err = hv_ringbuffer_post_init(&newchannel->outbound,

> +				      page, send_pages);

> +	if (err)

> +		goto error_free_gpadl;

> +

> +	err = hv_ringbuffer_post_init(&newchannel->inbound,

> +				      &page[send_pages], recv_pages);

> +	if (err)

> +		goto error_free_gpadl;

> +

>  	/* Create and init the channel open message */

>  	open_info = kzalloc(sizeof(*open_info) +

>  			   sizeof(struct vmbus_channel_open_channel),

> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h

> index 0778add21a9c..d78a04ad5490 100644

> --- a/drivers/hv/hyperv_vmbus.h

> +++ b/drivers/hv/hyperv_vmbus.h

> @@ -172,6 +172,8 @@ extern int hv_synic_cleanup(unsigned int cpu);

>  /* Interface */

>  

>  void hv_ringbuffer_pre_init(struct vmbus_channel *channel);

> +int hv_ringbuffer_post_init(struct hv_ring_buffer_info *ring_info,

> +		struct page *pages, u32 page_cnt);

>  

>  int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,

>  		       struct page *pages, u32 pagecnt);

> diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c

> index 35833d4d1a1d..c8b0f7b45158 100644

> --- a/drivers/hv/ring_buffer.c

> +++ b/drivers/hv/ring_buffer.c

> @@ -17,6 +17,8 @@

>  #include <linux/vmalloc.h>

>  #include <linux/slab.h>

>  #include <linux/prefetch.h>

> +#include <linux/io.h>

> +#include <asm/mshyperv.h>

>  

>  #include "hyperv_vmbus.h"

>  

> @@ -188,6 +190,44 @@ void hv_ringbuffer_pre_init(struct vmbus_channel *channel)

>  	mutex_init(&channel->outbound.ring_buffer_mutex);

>  }

>  

> +int hv_ringbuffer_post_init(struct hv_ring_buffer_info *ring_info,

> +		       struct page *pages, u32 page_cnt)

> +{

> +	struct vm_struct *area;

> +	u64 physic_addr = page_to_pfn(pages) << PAGE_SHIFT;

> +	unsigned long vaddr;

> +	int err = 0;

> +

> +	if (!hv_isolation_type_snp())

> +		return 0;

> +

> +	physic_addr += ms_hyperv.shared_gpa_boundary;

> +	area = get_vm_area((2 * page_cnt - 1) * PAGE_SIZE, VM_IOREMAP);

> +	if (!area || !area->addr)

> +		return -EFAULT;

> +

> +	vaddr = (unsigned long)area->addr;

> +	err = ioremap_page_range(vaddr, vaddr + page_cnt * PAGE_SIZE,

> +			   physic_addr, PAGE_KERNEL_IO);

> +	err |= ioremap_page_range(vaddr + page_cnt * PAGE_SIZE,

> +				  vaddr + (2 * page_cnt - 1) * PAGE_SIZE,

> +				  physic_addr + PAGE_SIZE, PAGE_KERNEL_IO);

> +	if (err) {

> +		vunmap((void *)vaddr);

> +		return -EFAULT;

> +	}

> +

> +	/* Clean memory after setting host visibility. */

> +	memset((void *)vaddr, 0x00, page_cnt * PAGE_SIZE);

> +

> +	ring_info->ring_buffer = (struct hv_ring_buffer *)vaddr;

> +	ring_info->ring_buffer->read_index = 0;

> +	ring_info->ring_buffer->write_index = 0;

> +	ring_info->ring_buffer->feature_bits.value = 1;

> +

> +	return 0;

> +}

> +

>  /* Initialize the ring buffer. */

>  int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,

>  		       struct page *pages, u32 page_cnt)

> @@ -197,33 +237,34 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,

>  

>  	BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE));

>  

> -	/*

> -	 * First page holds struct hv_ring_buffer, do wraparound mapping for

> -	 * the rest.

> -	 */

> -	pages_wraparound = kcalloc(page_cnt * 2 - 1, sizeof(struct page *),

> -				   GFP_KERNEL);

> -	if (!pages_wraparound)

> -		return -ENOMEM;

> -

> -	pages_wraparound[0] = pages;

> -	for (i = 0; i < 2 * (page_cnt - 1); i++)

> -		pages_wraparound[i + 1] = &pages[i % (page_cnt - 1) + 1];

> +	if (!hv_isolation_type_snp()) {

> +		/*

> +		 * First page holds struct hv_ring_buffer, do wraparound mapping for

> +		 * the rest.

> +		 */

> +		pages_wraparound = kcalloc(page_cnt * 2 - 1, sizeof(struct page *),

> +					   GFP_KERNEL);

> +		if (!pages_wraparound)

> +			return -ENOMEM;

>  

> -	ring_info->ring_buffer = (struct hv_ring_buffer *)

> -		vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP, PAGE_KERNEL);

> +		pages_wraparound[0] = pages;

> +		for (i = 0; i < 2 * (page_cnt - 1); i++)

> +			pages_wraparound[i + 1] = &pages[i % (page_cnt - 1) + 1];

>  

> -	kfree(pages_wraparound);

> +		ring_info->ring_buffer = (struct hv_ring_buffer *)

> +			vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP, PAGE_KERNEL);

>  

> +		kfree(pages_wraparound);

>  

> -	if (!ring_info->ring_buffer)

> -		return -ENOMEM;

> +		if (!ring_info->ring_buffer)

> +			return -ENOMEM;

>  

> -	ring_info->ring_buffer->read_index =

> -		ring_info->ring_buffer->write_index = 0;

> +		ring_info->ring_buffer->read_index =

> +			ring_info->ring_buffer->write_index = 0;

>  

> -	/* Set the feature bit for enabling flow control. */

> -	ring_info->ring_buffer->feature_bits.value = 1;

> +		/* Set the feature bit for enabling flow control. */

> +		ring_info->ring_buffer->feature_bits.value = 1;

> +	}

>  

>  	ring_info->ring_size = page_cnt << PAGE_SHIFT;

>  	ring_info->ring_size_div10_reciprocal =

> diff --git a/mm/ioremap.c b/mm/ioremap.c

> index 5fa1ab41d152..d63c4ba067f9 100644

> --- a/mm/ioremap.c

> +++ b/mm/ioremap.c

> @@ -248,6 +248,7 @@ int ioremap_page_range(unsigned long addr,

>  

>  	return err;

>  }

> +EXPORT_SYMBOL_GPL(ioremap_page_range);

>  

>  #ifdef CONFIG_GENERIC_IOREMAP

>  void __iomem *ioremap_prot(phys_addr_t addr, size_t size, unsigned long prot)

> diff --git a/mm/vmalloc.c b/mm/vmalloc.c

> index e6f352bf0498..19724a8ebcb7 100644

> --- a/mm/vmalloc.c

> +++ b/mm/vmalloc.c

> @@ -2131,6 +2131,7 @@ struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)

>  				  NUMA_NO_NODE, GFP_KERNEL,

>  				  __builtin_return_address(0));

>  }

> +EXPORT_SYMBOL_GPL(get_vm_area);

>  

>  struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,

>  				const void *caller)

> -- 

> 2.25.1

>
Konrad Rzeszutek Wilk April 15, 2021, 8:28 p.m. UTC | #5
On Wed, Apr 14, 2021 at 10:49:42AM -0400, Tianyu Lan wrote:
> From: Tianyu Lan <Tianyu.Lan@microsoft.com>

> 

> For Hyper-V isolation VM with AMD SEV SNP, the bounce buffer(shared memory)

> needs to be accessed via extra address space(e.g address above bit39).

> Hyper-V code may remap extra address space outside of swiotlb. swiotlb_bounce()


May? Isn't this a MUST in this case?

> needs to use remap virtual address to copy data from/to bounce buffer. Add

> new interface swiotlb_set_bounce_remap() to do that.


I am bit lost - why can't you use the swiotlb_init and pass in your
DMA pool that is already above bit 39?

> 

> Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>

> ---

>  include/linux/swiotlb.h |  5 +++++

>  kernel/dma/swiotlb.c    | 13 ++++++++++++-

>  2 files changed, 17 insertions(+), 1 deletion(-)

> 

> diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h

> index d9c9fc9ca5d2..3ccd08116683 100644

> --- a/include/linux/swiotlb.h

> +++ b/include/linux/swiotlb.h

> @@ -82,8 +82,13 @@ unsigned int swiotlb_max_segment(void);

>  size_t swiotlb_max_mapping_size(struct device *dev);

>  bool is_swiotlb_active(void);

>  void __init swiotlb_adjust_size(unsigned long new_size);

> +void swiotlb_set_bounce_remap(unsigned char *vaddr);

>  #else

>  #define swiotlb_force SWIOTLB_NO_FORCE

> +static inline void swiotlb_set_bounce_remap(unsigned char *vaddr)

> +{

> +}

> +

>  static inline bool is_swiotlb_buffer(phys_addr_t paddr)

>  {

>  	return false;

> diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c

> index 7c42df6e6100..5fd2db6aa149 100644

> --- a/kernel/dma/swiotlb.c

> +++ b/kernel/dma/swiotlb.c

> @@ -94,6 +94,7 @@ static unsigned int io_tlb_index;

>   * not be bounced (unless SWIOTLB_FORCE is set).

>   */

>  static unsigned int max_segment;

> +static unsigned char *swiotlb_bounce_remap_addr;

>  

>  /*

>   * We need to save away the original address corresponding to a mapped entry

> @@ -421,6 +422,11 @@ void __init swiotlb_exit(void)

>  	swiotlb_cleanup();

>  }

>  

> +void swiotlb_set_bounce_remap(unsigned char *vaddr)

> +{

> +	swiotlb_bounce_remap_addr = vaddr;

> +}

> +

>  /*

>   * Bounce: copy the swiotlb buffer from or back to the original dma location

>   */

> @@ -428,7 +434,12 @@ static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr,

>  			   size_t size, enum dma_data_direction dir)

>  {

>  	unsigned long pfn = PFN_DOWN(orig_addr);

> -	unsigned char *vaddr = phys_to_virt(tlb_addr);

> +	unsigned char *vaddr;

> +

> +	if (swiotlb_bounce_remap_addr)

> +		vaddr = swiotlb_bounce_remap_addr + tlb_addr - io_tlb_start;

> +	else

> +		vaddr = phys_to_virt(tlb_addr);

>  

>  	if (PageHighMem(pfn_to_page(pfn))) {

>  		/* The buffer does not have a mapping.  Map it in and copy */

> -- 

> 2.25.1

>
Christoph Hellwig April 19, 2021, 6:36 a.m. UTC | #6
On Thu, Apr 15, 2021 at 04:24:15PM -0400, Konrad Rzeszutek Wilk wrote:
> So you are exposing these two:

>  EXPORT_SYMBOL_GPL(get_vm_area);

>  EXPORT_SYMBOL_GPL(ioremap_page_range);

> 

> But if you used vmap wouldn't you get the same thing for free?


Yes, this needs to go into some vmap version, preferably reusing the
existing code in kernel/dma/remap.c.

Exporting get_vm_area is a complete dealbreaker and not going to happen.
We worked hard on not exposing it to modules.