diff mbox series

[05/10] kexec: implement functions to map and unmap segment to kimage

Message ID 20230703215709.1195644-6-tusharsu@linux.microsoft.com (mailing list archive)
State New, archived
Headers show
Series ima: measure events between kexec load and execute | expand

Commit Message

Tushar Sugandhi July 3, 2023, 9:57 p.m. UTC
Currently, there's no mechanism to map and unmap segments to the kimage
structure.  This functionality is needed when dealing with memory segments
in the context of a kexec operation.

The patch adds two new functions: kimage_map_segment() and
kimage_unmap_segment().

Implement kimage_map_segment() which takes a kimage pointer, an address,
and a size.  Ensures that the entire segment is being mapped by comparing
the given address and size to each segment in the kimage's segment array.
Collect the source pages that correspond to the given address range,
allocate an array of pointers to these pages, and map them to a contiguous
range of virtual addresses.  If the mapping operation is successful, the
function returns the start of this range.  Otherwise, it frees the page
pointer array and returns NULL.

Implement kimage_unmap_segment() that takes a pointer to a segment buffer
and unmaps it using vunmap().

Finally, move for_each_kimage_entry() macro to kexec.h.

Note: Use kimage_map_segment() and kimage_unmap_segment() carefully to
avoid memory leaks and ensure that all mapped segments are properly
unmapped when they're no longer needed.

Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com>
---
 include/linux/kexec.h | 13 ++++++++
 kernel/kexec_core.c   | 72 ++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 80 insertions(+), 5 deletions(-)

Comments

Stefan Berger July 7, 2023, 12:28 p.m. UTC | #1
On 7/3/23 17:57, Tushar Sugandhi wrote:
> Currently, there's no mechanism to map and unmap segments to the kimage
> structure.  This functionality is needed when dealing with memory segments
> in the context of a kexec operation.
> 
> The patch adds two new functions: kimage_map_segment() and
> kimage_unmap_segment().
> 
> Implement kimage_map_segment() which takes a kimage pointer, an address,
> and a size.  Ensures that the entire segment is being mapped by comparing
> the given address and size to each segment in the kimage's segment array.
> Collect the source pages that correspond to the given address range,
> allocate an array of pointers to these pages, and map them to a contiguous
> range of virtual addresses.  If the mapping operation is successful, the
> function returns the start of this range.  Otherwise, it frees the page
> pointer array and returns NULL.
> 
> Implement kimage_unmap_segment() that takes a pointer to a segment buffer
> and unmaps it using vunmap().
> 
> Finally, move for_each_kimage_entry() macro to kexec.h.
> 
> Note: Use kimage_map_segment() and kimage_unmap_segment() carefully to
> avoid memory leaks and ensure that all mapped segments are properly
> unmapped when they're no longer needed.
> 
> Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com>

> +
> +	i = 0;
> +	for_each_kimage_entry(image, ptr, entry) {
> +		if (entry & IND_DESTINATION)
> +			dest_page_addr = entry & PAGE_MASK;
> +		else if (entry & IND_SOURCE) {
> +			if (dest_page_addr >= addr && dest_page_addr < eaddr) {
> +				src_page_addr = entry & PAGE_MASK;
> +				src_pages[i++] = phys_to_page(src_page_addr);

Since phys_to_page is not defined on many/most architectures I change it for ppc64 and have successfully used the following:

+                               src_pages[i++] = virt_to_page(__va(src_page_addr))


After several kexecs the following check still works:

# evmctl ima_measurement --ignore-violations /sys/kernel/security/ima/binary_runtime_measurements
Matched per TPM bank calculated digest(s).


    Stefan
Tushar Sugandhi July 11, 2023, 6:41 p.m. UTC | #2
Adding Eric to cc.

On 7/7/23 05:28, Stefan Berger wrote:
>
>
> On 7/3/23 17:57, Tushar Sugandhi wrote:
>> Currently, there's no mechanism to map and unmap segments to the kimage
>> structure.  This functionality is needed when dealing with memory 
>> segments
>> in the context of a kexec operation.
>>
>> The patch adds two new functions: kimage_map_segment() and
>> kimage_unmap_segment().
>>
>> Implement kimage_map_segment() which takes a kimage pointer, an address,
>> and a size.  Ensures that the entire segment is being mapped by 
>> comparing
>> the given address and size to each segment in the kimage's segment 
>> array.
>> Collect the source pages that correspond to the given address range,
>> allocate an array of pointers to these pages, and map them to a 
>> contiguous
>> range of virtual addresses.  If the mapping operation is successful, the
>> function returns the start of this range.  Otherwise, it frees the page
>> pointer array and returns NULL.
>>
>> Implement kimage_unmap_segment() that takes a pointer to a segment 
>> buffer
>> and unmaps it using vunmap().
>>
>> Finally, move for_each_kimage_entry() macro to kexec.h.
>>
>> Note: Use kimage_map_segment() and kimage_unmap_segment() carefully to
>> avoid memory leaks and ensure that all mapped segments are properly
>> unmapped when they're no longer needed.
>>
>> Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com>
>
>> +
>> +    i = 0;
>> +    for_each_kimage_entry(image, ptr, entry) {
>> +        if (entry & IND_DESTINATION)
>> +            dest_page_addr = entry & PAGE_MASK;
>> +        else if (entry & IND_SOURCE) {
>> +            if (dest_page_addr >= addr && dest_page_addr < eaddr) {
>> +                src_page_addr = entry & PAGE_MASK;
>> +                src_pages[i++] = phys_to_page(src_page_addr);
>
> Since phys_to_page is not defined on many/most architectures I change 
> it for ppc64 and have successfully used the following:
>
> +                               src_pages[i++] = 
> virt_to_page(__va(src_page_addr))
>
>
> After several kexecs the following check still works:
>
> # evmctl ima_measurement --ignore-violations 
> /sys/kernel/security/ima/binary_runtime_measurements
> Matched per TPM bank calculated digest(s).
>
>
>    Stefan
Thank you so much Stefan for reviewing this series, and catching this
issue.  Are you suggesting I should use virt_to_page on all architectures
unconditionally, or use it only when phys_to_page is not available?

~Tushar
Stefan Berger July 11, 2023, 7:19 p.m. UTC | #3
On 7/11/23 14:41, Tushar Sugandhi wrote:
> Adding Eric to cc.
> 
> On 7/7/23 05:28, Stefan Berger wrote:
>>
>>
>> On 7/3/23 17:57, Tushar Sugandhi wrote:
>>> Currently, there's no mechanism to map and unmap segments to the kimage
>>> structure.  This functionality is needed when dealing with memory segments
>>> in the context of a kexec operation.
>>>
>>> The patch adds two new functions: kimage_map_segment() and
>>> kimage_unmap_segment().
>>>
>>> Implement kimage_map_segment() which takes a kimage pointer, an address,
>>> and a size.  Ensures that the entire segment is being mapped by comparing
>>> the given address and size to each segment in the kimage's segment array.
>>> Collect the source pages that correspond to the given address range,
>>> allocate an array of pointers to these pages, and map them to a contiguous
>>> range of virtual addresses.  If the mapping operation is successful, the
>>> function returns the start of this range.  Otherwise, it frees the page
>>> pointer array and returns NULL.
>>>
>>> Implement kimage_unmap_segment() that takes a pointer to a segment buffer
>>> and unmaps it using vunmap().
>>>
>>> Finally, move for_each_kimage_entry() macro to kexec.h.
>>>
>>> Note: Use kimage_map_segment() and kimage_unmap_segment() carefully to
>>> avoid memory leaks and ensure that all mapped segments are properly
>>> unmapped when they're no longer needed.
>>>
>>> Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com>
>>
>>> +
>>> +    i = 0;
>>> +    for_each_kimage_entry(image, ptr, entry) {
>>> +        if (entry & IND_DESTINATION)
>>> +            dest_page_addr = entry & PAGE_MASK;
>>> +        else if (entry & IND_SOURCE) {
>>> +            if (dest_page_addr >= addr && dest_page_addr < eaddr) {
>>> +                src_page_addr = entry & PAGE_MASK;
>>> +                src_pages[i++] = phys_to_page(src_page_addr);
>>
>> Since phys_to_page is not defined on many/most architectures I change it for ppc64 and have successfully used the following:
>>
>> +                               src_pages[i++] = virt_to_page(__va(src_page_addr))
>>
>>
>> After several kexecs the following check still works:
>>
>> # evmctl ima_measurement --ignore-violations /sys/kernel/security/ima/binary_runtime_measurements
>> Matched per TPM bank calculated digest(s).
>>
>>
>>    Stefan
> Thank you so much Stefan for reviewing this series, and catching this
> issue.  Are you suggesting I should use virt_to_page on all architectures
> unconditionally, or use it only when phys_to_page is not available?

I would try to used it on all architectures.

    Stefan

> 
> ~Tushar
> 
> _______________________________________________
> kexec mailing list
> kexec@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
Tushar Sugandhi July 12, 2023, 7:51 p.m. UTC | #4
On 7/11/23 12:19, Stefan Berger wrote:
>
>
> On 7/11/23 14:41, Tushar Sugandhi wrote:
>> Adding Eric to cc.
>>
>> On 7/7/23 05:28, Stefan Berger wrote:
>>>
>>>
>>> On 7/3/23 17:57, Tushar Sugandhi wrote:
>>>> Currently, there's no mechanism to map and unmap segments to the 
>>>> kimage
>>>> structure.  This functionality is needed when dealing with memory 
>>>> segments
>>>> in the context of a kexec operation.
>>>>
>>>> The patch adds two new functions: kimage_map_segment() and
>>>> kimage_unmap_segment().
>>>>
>>>> Implement kimage_map_segment() which takes a kimage pointer, an 
>>>> address,
>>>> and a size.  Ensures that the entire segment is being mapped by 
>>>> comparing
>>>> the given address and size to each segment in the kimage's segment 
>>>> array.
>>>> Collect the source pages that correspond to the given address range,
>>>> allocate an array of pointers to these pages, and map them to a 
>>>> contiguous
>>>> range of virtual addresses.  If the mapping operation is 
>>>> successful, the
>>>> function returns the start of this range.  Otherwise, it frees the 
>>>> page
>>>> pointer array and returns NULL.
>>>>
>>>> Implement kimage_unmap_segment() that takes a pointer to a segment 
>>>> buffer
>>>> and unmaps it using vunmap().
>>>>
>>>> Finally, move for_each_kimage_entry() macro to kexec.h.
>>>>
>>>> Note: Use kimage_map_segment() and kimage_unmap_segment() carefully to
>>>> avoid memory leaks and ensure that all mapped segments are properly
>>>> unmapped when they're no longer needed.
>>>>
>>>> Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com>
>>>
>>>> +
>>>> +    i = 0;
>>>> +    for_each_kimage_entry(image, ptr, entry) {
>>>> +        if (entry & IND_DESTINATION)
>>>> +            dest_page_addr = entry & PAGE_MASK;
>>>> +        else if (entry & IND_SOURCE) {
>>>> +            if (dest_page_addr >= addr && dest_page_addr < eaddr) {
>>>> +                src_page_addr = entry & PAGE_MASK;
>>>> +                src_pages[i++] = phys_to_page(src_page_addr);
>>>
>>> Since phys_to_page is not defined on many/most architectures I 
>>> change it for ppc64 and have successfully used the following:
>>>
>>> +                               src_pages[i++] = 
>>> virt_to_page(__va(src_page_addr))
>>>
>>>
>>> After several kexecs the following check still works:
>>>
>>> # evmctl ima_measurement --ignore-violations 
>>> /sys/kernel/security/ima/binary_runtime_measurements
>>> Matched per TPM bank calculated digest(s).
>>>
>>>
>>>    Stefan
>> Thank you so much Stefan for reviewing this series, and catching this
>> issue.  Are you suggesting I should use virt_to_page on all 
>> architectures
>> unconditionally, or use it only when phys_to_page is not available?
>
> I would try to used it on all architectures.
>
>    Stefan
>
Ok.  Thanks.
>>
>> ~Tushar
>>
>> _______________________________________________
>> kexec mailing list
>> kexec@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/kexec
diff mbox series

Patch

diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 22b5cd24f581..e00b8101b53b 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -490,6 +490,15 @@  static inline int arch_kexec_post_alloc_pages(void *vaddr, unsigned int pages, g
 static inline void arch_kexec_pre_free_pages(void *vaddr, unsigned int pages) { }
 #endif
 
+#define for_each_kimage_entry(image, ptr, entry) \
+	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
+		ptr = (entry & IND_INDIRECTION) ? \
+			boot_phys_to_virt((entry & PAGE_MASK)) : ptr + 1)
+
+extern void *kimage_map_segment(struct kimage *image,
+				unsigned long addr, unsigned long size);
+extern void kimage_unmap_segment(void *buffer);
+
 #else /* !CONFIG_KEXEC_CORE */
 struct pt_regs;
 struct task_struct;
@@ -497,6 +506,10 @@  static inline void __crash_kexec(struct pt_regs *regs) { }
 static inline void crash_kexec(struct pt_regs *regs) { }
 static inline int kexec_should_crash(struct task_struct *p) { return 0; }
 static inline int kexec_crash_loaded(void) { return 0; }
+static inline void *kimage_map_segment(struct kimage *image,
+				       unsigned long addr, unsigned long size)
+{ return NULL; }
+static inline void kimage_unmap_segment(void *buffer) { }
 #define kexec_in_progress false
 #endif /* CONFIG_KEXEC_CORE */
 
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 3d578c6fefee..424e303fce25 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -594,11 +594,6 @@  void kimage_terminate(struct kimage *image)
 	*image->entry = IND_DONE;
 }
 
-#define for_each_kimage_entry(image, ptr, entry) \
-	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
-		ptr = (entry & IND_INDIRECTION) ? \
-			boot_phys_to_virt((entry & PAGE_MASK)) : ptr + 1)
-
 static void kimage_free_entry(kimage_entry_t entry)
 {
 	struct page *page;
@@ -921,6 +916,73 @@  int kimage_load_segment(struct kimage *image,
 	return result;
 }
 
+void *kimage_map_segment(struct kimage *image,
+			 unsigned long addr, unsigned long size)
+{
+	unsigned long eaddr = addr + size;
+	unsigned long src_page_addr, dest_page_addr;
+	struct page **src_pages;
+	int i, npages;
+	kimage_entry_t *ptr, entry;
+	void *vaddr = NULL;
+
+	/*
+	 * Make sure that we are mapping a whole segment.
+	 */
+	for (i = 0; i < image->nr_segments; i++) {
+		if (addr == image->segment[i].mem &&
+		    size == image->segment[i].memsz) {
+			break;
+		}
+	}
+
+	if (i == image->nr_segments) {
+		pr_err("%s: No segment matching [%lx, %lx)\n", __func__,
+		       addr, eaddr);
+		return NULL;
+	}
+
+	/*
+	 * Collect the source pages and map them in a contiguous VA range.
+	 */
+	npages = PFN_UP(eaddr) - PFN_DOWN(addr);
+	src_pages = kmalloc(sizeof(*src_pages) * npages, GFP_KERNEL);
+	if (!src_pages) {
+		pr_err("%s: Could not allocate ima pages array.\n", __func__);
+		return NULL;
+	}
+
+	i = 0;
+	for_each_kimage_entry(image, ptr, entry) {
+		if (entry & IND_DESTINATION)
+			dest_page_addr = entry & PAGE_MASK;
+		else if (entry & IND_SOURCE) {
+			if (dest_page_addr >= addr && dest_page_addr < eaddr) {
+				src_page_addr = entry & PAGE_MASK;
+				src_pages[i++] = phys_to_page(src_page_addr);
+				if (i == npages)
+					break;
+				dest_page_addr += PAGE_SIZE;
+			}
+		}
+	}
+
+	/* Sanity check. */
+	WARN_ON(i < npages);
+
+	vaddr = vmap(src_pages, npages, VM_MAP, PAGE_KERNEL);
+	if (!vaddr) {
+		pr_err("%s: Could not map imap buffer.\n", __func__);
+		kfree(src_pages);
+	}
+	return vaddr;
+}
+
+void kimage_unmap_segment(void *segment_buffer)
+{
+	vunmap(segment_buffer);
+}
+
 struct kexec_load_limit {
 	/* Mutex protects the limit count. */
 	struct mutex mutex;