aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/hv/hv_common.c12
-rw-r--r--drivers/hv/hyperv_vmbus.h2
-rw-r--r--drivers/hv/mshv_eventfd.c2
-rw-r--r--drivers/hv/mshv_regions.c93
-rw-r--r--drivers/hv/mshv_root_main.c17
-rw-r--r--include/hyperv/hvhdk.h47
6 files changed, 127 insertions, 46 deletions
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 0a3ab7efed46..f1c17fb60dc1 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -195,13 +195,15 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
/*
* Write dump contents to the page. No need to synchronize; panic should
- * be single-threaded.
+ * be single-threaded. Ignore failures from kmsg_dump_get_buffer() since
+ * panic notification should be done even if there is no message data.
+ * Don't assume bytes_written is set in case of failure, so initialize it.
*/
kmsg_dump_rewind(&iter);
- kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
+ bytes_written = 0;
+ (void)kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
&bytes_written);
- if (!bytes_written)
- return;
+
/*
* P3 to contain the physical address of the panic page & P4 to
* contain the size of the panic data in that page. Rest of the
@@ -210,7 +212,7 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
hv_set_msr(HV_MSR_CRASH_P0, 0);
hv_set_msr(HV_MSR_CRASH_P1, 0);
hv_set_msr(HV_MSR_CRASH_P2, 0);
- hv_set_msr(HV_MSR_CRASH_P3, virt_to_phys(hv_panic_page));
+ hv_set_msr(HV_MSR_CRASH_P3, bytes_written ? virt_to_phys(hv_panic_page) : 0);
hv_set_msr(HV_MSR_CRASH_P4, bytes_written);
/*
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index b2862e0a317a..cdbc5f5c3215 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -375,7 +375,7 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
return;
/*
- * The cmxchg() above does an implicit memory barrier to
+ * The cmpxchg() above does an implicit memory barrier to
* ensure the write to MessageType (ie set to
* HVMSG_NONE) happens before we read the
* MessagePending and EOMing. Otherwise, the EOMing
diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
index d93a18f09c76..0b75ff1edb73 100644
--- a/drivers/hv/mshv_eventfd.c
+++ b/drivers/hv/mshv_eventfd.c
@@ -388,7 +388,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
{
struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;
struct mshv_irqfd *irqfd, *tmp;
- unsigned int events;
+ __poll_t events;
int ret;
int idx;
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 30bacba6aec3..adba3564d9f1 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -20,6 +20,41 @@
#define MSHV_MAP_FAULT_IN_PAGES PTRS_PER_PMD
/**
+ * mshv_chunk_stride - Compute stride for mapping guest memory
+ * @page : The page to check for huge page backing
+ * @gfn : Guest frame number for the mapping
+ * @page_count: Total number of pages in the mapping
+ *
+ * Determines the appropriate stride (in pages) for mapping guest memory.
+ * Uses huge page stride if the backing page is huge and the guest mapping
+ * is properly aligned; otherwise falls back to single page stride.
+ *
+ * Return: Stride in pages, or -EINVAL if page order is unsupported.
+ */
+static int mshv_chunk_stride(struct page *page,
+ u64 gfn, u64 page_count)
+{
+ unsigned int page_order;
+
+ /*
+ * Use single page stride by default. For huge page stride, the
+ * page must be compound and point to the head of the compound
+ * page, and both gfn and page_count must be huge-page aligned.
+ */
+ if (!PageCompound(page) || !PageHead(page) ||
+ !IS_ALIGNED(gfn, PTRS_PER_PMD) ||
+ !IS_ALIGNED(page_count, PTRS_PER_PMD))
+ return 1;
+
+ page_order = folio_order(page_folio(page));
+ /* The hypervisor only supports 2M huge page */
+ if (page_order != PMD_ORDER)
+ return -EINVAL;
+
+ return 1 << page_order;
+}
+
+/**
* mshv_region_process_chunk - Processes a contiguous chunk of memory pages
* in a region.
* @region : Pointer to the memory region structure.
@@ -45,25 +80,23 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
int (*handler)(struct mshv_mem_region *region,
u32 flags,
u64 page_offset,
- u64 page_count))
+ u64 page_count,
+ bool huge_page))
{
- u64 count, stride;
- unsigned int page_order;
+ u64 gfn = region->start_gfn + page_offset;
+ u64 count;
struct page *page;
- int ret;
+ int stride, ret;
page = region->pages[page_offset];
if (!page)
return -EINVAL;
- page_order = folio_order(page_folio(page));
- /* The hypervisor only supports 4K and 2M page sizes */
- if (page_order && page_order != PMD_ORDER)
- return -EINVAL;
+ stride = mshv_chunk_stride(page, gfn, page_count);
+ if (stride < 0)
+ return stride;
- stride = 1 << page_order;
-
- /* Start at stride since the first page is validated */
+ /* Start at stride since the first stride is validated */
for (count = stride; count < page_count; count += stride) {
page = region->pages[page_offset + count];
@@ -71,12 +104,13 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
if (!page)
break;
- /* Break if page size changes */
- if (page_order != folio_order(page_folio(page)))
+ /* Break if stride size changes */
+ if (stride != mshv_chunk_stride(page, gfn + count,
+ page_count - count))
break;
}
- ret = handler(region, flags, page_offset, count);
+ ret = handler(region, flags, page_offset, count, stride > 1);
if (ret)
return ret;
@@ -108,7 +142,8 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
int (*handler)(struct mshv_mem_region *region,
u32 flags,
u64 page_offset,
- u64 page_count))
+ u64 page_count,
+ bool huge_page))
{
long ret;
@@ -162,11 +197,10 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
static int mshv_region_chunk_share(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
return hv_call_modify_spa_host_access(region->partition->pt_id,
@@ -188,11 +222,10 @@ int mshv_region_share(struct mshv_mem_region *region)
static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
return hv_call_modify_spa_host_access(region->partition->pt_id,
@@ -212,11 +245,10 @@ int mshv_region_unshare(struct mshv_mem_region *region)
static int mshv_region_chunk_remap(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_MAP_GPA_LARGE_PAGE;
return hv_call_map_gpa_pages(region->partition->pt_id,
@@ -295,11 +327,10 @@ release_pages:
static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_UNMAP_GPA_LARGE_PAGE;
return hv_call_unmap_gpa_pages(region->partition->pt_id,
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 1134a82c7881..681b58154d5e 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -611,7 +611,6 @@ mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
return NULL;
}
-#ifdef CONFIG_X86_64
static struct mshv_mem_region *
mshv_partition_region_by_gfn_get(struct mshv_partition *p, u64 gfn)
{
@@ -643,12 +642,17 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
{
struct mshv_partition *p = vp->vp_partition;
struct mshv_mem_region *region;
- struct hv_x64_memory_intercept_message *msg;
bool ret;
u64 gfn;
-
- msg = (struct hv_x64_memory_intercept_message *)
+#if defined(CONFIG_X86_64)
+ struct hv_x64_memory_intercept_message *msg =
+ (struct hv_x64_memory_intercept_message *)
+ vp->vp_intercept_msg_page->u.payload;
+#elif defined(CONFIG_ARM64)
+ struct hv_arm64_memory_intercept_message *msg =
+ (struct hv_arm64_memory_intercept_message *)
vp->vp_intercept_msg_page->u.payload;
+#endif
gfn = HVPFN_DOWN(msg->guest_physical_address);
@@ -666,9 +670,6 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
return ret;
}
-#else /* CONFIG_X86_64 */
-static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) { return false; }
-#endif /* CONFIG_X86_64 */
static bool mshv_vp_handle_intercept(struct mshv_vp *vp)
{
@@ -1280,7 +1281,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
long ret;
if (mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP) ||
- !access_ok((const void *)mem.userspace_addr, mem.size))
+ !access_ok((const void __user *)mem.userspace_addr, mem.size))
return -EINVAL;
mmap_read_lock(current->mm);
diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
index 469186df7826..08965970c17d 100644
--- a/include/hyperv/hvhdk.h
+++ b/include/hyperv/hvhdk.h
@@ -800,6 +800,53 @@ struct hv_x64_memory_intercept_message {
u8 instruction_bytes[16];
} __packed;
+#if IS_ENABLED(CONFIG_ARM64)
+union hv_arm64_vp_execution_state {
+ u16 as_uint16;
+ struct {
+ u16 cpl:2; /* Exception Level (EL) */
+ u16 debug_active:1;
+ u16 interruption_pending:1;
+ u16 vtl:4;
+ u16 virtualization_fault_active:1;
+ u16 reserved:7;
+ } __packed;
+};
+
+struct hv_arm64_intercept_message_header {
+ u32 vp_index;
+ u8 instruction_length;
+ u8 intercept_access_type;
+ union hv_arm64_vp_execution_state execution_state;
+ u64 pc;
+ u64 cpsr;
+} __packed;
+
+union hv_arm64_memory_access_info {
+ u8 as_uint8;
+ struct {
+ u8 gva_valid:1;
+ u8 gva_gpa_valid:1;
+ u8 hypercall_output_pending:1;
+ u8 reserved:5;
+ } __packed;
+};
+
+struct hv_arm64_memory_intercept_message {
+ struct hv_arm64_intercept_message_header header;
+ u32 cache_type; /* enum hv_cache_type */
+ u8 instruction_byte_count;
+ union hv_arm64_memory_access_info memory_access_info;
+ u16 reserved1;
+ u8 instruction_bytes[4];
+ u32 reserved2;
+ u64 guest_virtual_address;
+ u64 guest_physical_address;
+ u64 syndrome;
+} __packed;
+
+#endif /* CONFIG_ARM64 */
+
/*
* Dispatch state for the VP communicated by the hypervisor to the
* VP-dispatching thread in the root on return from HVCALL_DISPATCH_VP.