Hello,
This patch series implements a mechanism which allows the kernel to pass
on a buffer to the kernel that will be kexec'd. This buffer is passed
as a segment which is added to the kimage when it is being prepared
by kexec_file_load.
How the second kernel is informed of this buffer is architecture-specific.
On powerpc, this is done via the device tree, by checking
the properties /chosen/linux,kexec-handover-buffer-start and
/chosen/linux,kexec-handover-buffer-end, which is analogous to how the
kernel finds the initrd.
This is needed because the Integrity Measurement Architecture subsystem
needs to preserve its measurement list accross the kexec reboot. The
following patch series for the IMA subsystem uses this feature for that
purpose:
https://lists.infradead.org/pipermail/kexec/2016-August/016745.html
This is so that IMA can implement trusted boot support on the OpenPower
platform, because on such systems an intermediary Linux instance running
as part of the firmware is used to boot the target operating system via
kexec. Using this mechanism, IMA on this intermediary instance can
hand over to the target OS the measurements of the components that were
used to boot it.
Because there could be additional measurement events between the
kexec_file_load call and the actual reboot, IMA needs a way to update the
buffer with those additional events before rebooting. One can minimize
the interval between the kexec_file_load and the reboot syscalls, but as
small as it can be, there is always the possibility that the measurement
list will be out of date at the time of reboot.
To address this issue, this patch series also introduces
kexec_update_segment, which allows a reboot notifier to change the
contents of the image segment during the reboot process.
Patch 5 makes kimage_load_normal_segment and kexec_update_segment share
code. It's not much code that they can share though, so I'm not sure if
the result is actually better.
The last patch is not intended to be merged, it just demonstrates how
this feature can be used.
This series applies on top of v5 of the "kexec_file_load implementation
for PowerPC" patch series (which applies on top of v4.8-rc1):
https://lists.infradead.org/pipermail/kexec/2016-August/016843.html
Changes for v2:
- Rebased on v5 of kexec_file_load implementation for PowerPC patch series.
- Patch "kexec_file: Add buffer hand-over support for the next kernel"
- Changed kexec_add_handover_buffer to receive a struct kexec_buf, as
suggested by Dave Young.
- Patch "powerpc: kexec_file: Add buffer hand-over support for the next kernel"
- Moved setup_handover_buffer from kexec_elf_64.c to machine_kexec_64.c.
- Call setup_handover_buffer from setup_new_fdt instead of elf64_load.
- Changed kexec_get_handover_buffer to read from the expanded device tree
instead of the flattened device tree.
- Patch "kexec_file: Add mechanism to update kexec segments.":
- Removed unnecessary "#include <linux/highmem.h>" in kexec_file.c.
- Round up memsz argument to PAGE_SIZE.
- Check if kexec_image is NULL in kexec_update_segment.
- Patch "IMA: Demonstration code for kexec buffer passing."
- Avoid registering reboot notifier again if kexec_file_load is called
more than once.
Thiago Jung Bauermann (6):
kexec_file: Add buffer hand-over support for the next kernel
powerpc: kexec_file: Add buffer hand-over support for the next kernel
kexec_file: Allow skipping checksum calculation for some segments.
kexec_file: Add mechanism to update kexec segments.
kexec: Share logic to copy segment page contents.
IMA: Demonstration code for kexec buffer passing.
arch/powerpc/include/asm/kexec.h | 12 +-
arch/powerpc/kernel/kexec_elf_64.c | 8 +-
arch/powerpc/kernel/machine_kexec_64.c | 114 ++++++++++++++++-
arch/x86/kernel/crash.c | 4 +-
arch/x86/kernel/kexec-bzimage64.c | 6 +-
include/linux/ima.h | 11 ++
include/linux/kexec.h | 37 +++++-
kernel/kexec_core.c | 216 ++++++++++++++++++++++++++-------
kernel/kexec_file.c | 91 ++++++++++++--
security/integrity/ima/ima.h | 5 +
security/integrity/ima/ima_init.c | 26 ++++
security/integrity/ima/ima_template.c | 85 +++++++++++++
12 files changed, 546 insertions(+), 69 deletions(-)
--
1.9.1
The buffer hand-over mechanism allows the currently running kernel to pass
data to kernel that will be kexec'd via a kexec segment. The second kernel
can check whether the previous kernel sent data and retrieve it.
This is the architecture-independent part of the feature.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
include/linux/kexec.h | 29 ++++++++++++++++++++++
kernel/kexec_file.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+)
The buffer hand-over mechanism allows the currently running kernel to pass
data to kernel that will be kexec'd via a kexec segment. The second kernel
can check whether the previous kernel sent data and retrieve it.
This is the architecture-specific part.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
arch/powerpc/include/asm/kexec.h | 12 +++-
arch/powerpc/kernel/kexec_elf_64.c | 2 +-
arch/powerpc/kernel/machine_kexec_64.c | 114 +++++++++++++++++++++++++++++++--
3 files changed, 120 insertions(+), 8 deletions(-)
@@ -490,6 +490,60 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)returnimage->fops->cleanup(image->image_loader_data);}+boolkexec_can_hand_over_buffer(void)+{+returntrue;+}++intarch_kexec_add_handover_buffer(structkimage*image,+unsignedlongload_addr,unsignedlongsize)+{+image->arch.handover_buffer_addr=load_addr;+image->arch.handover_buffer_size=size;++return0;+}++intkexec_get_handover_buffer(void**addr,unsignedlong*size)+{+intret;+u64start_addr,end_addr;++ret=of_property_read_u64(of_chosen,+"linux,kexec-handover-buffer-start",+&start_addr);+if(ret==-EINVAL)+return-ENOENT;+elseif(ret)+return-EINVAL;++ret=of_property_read_u64(of_chosen,"linux,kexec-handover-buffer-end",+&end_addr);+if(ret==-EINVAL)+return-ENOENT;+elseif(ret)+return-EINVAL;++*addr=__va(start_addr);+/* -end is the first address after the buffer. */+*size=end_addr-start_addr;++return0;+}++intkexec_free_handover_buffer(void)+{+intret;+void*addr;+unsignedlongsize;++ret=kexec_get_handover_buffer(&addr,&size);+if(ret)+returnret;++returnmemblock_free((phys_addr_t)addr,size);+}+/***arch_kexec_walk_mem()-callfunc(data)foreachunreservedmemoryblock*@kbuf:Contextinfoforthesearch.Alsopassedto@func.
@@ -687,9 +741,52 @@ int setup_purgatory(struct kimage *image, const void *slave_code,return0;}-/*-*setup_new_fdt()-modify/chosenandmemoryreservationforthenextkernel-*@fdt:+/**+*setup_handover_buffer()-addpropertiesandreservationforthehandoverbuffer+*@image:kexecimagebeingloaded.+*@fdt:Flatteneddevicetreeforthenextkernel.+*@chosen_node:Offsettothechosennode.+*+*Return:0onsuccess,negativeerrnoonerror.+*/+staticintsetup_handover_buffer(conststructkimage*image,void*fdt,+intchosen_node)+{+intret;++if(image->arch.handover_buffer_addr==0)+return0;++ret=fdt_setprop_u64(fdt,chosen_node,+"linux,kexec-handover-buffer-start",+image->arch.handover_buffer_addr);+if(ret<0)+return-EINVAL;++/* -end is the first address after the buffer. */+ret=fdt_setprop_u64(fdt,chosen_node,+"linux,kexec-handover-buffer-end",+image->arch.handover_buffer_addr++image->arch.handover_buffer_size);+if(ret<0)+return-EINVAL;++ret=fdt_add_mem_rsv(fdt,image->arch.handover_buffer_addr,+image->arch.handover_buffer_size);+if(ret)+return-EINVAL;++pr_debug("kexec handover buffer at 0x%llx, size = 0x%lx\n",+image->arch.handover_buffer_addr,+image->arch.handover_buffer_size);++return0;+}++/**+*setup_new_fdt()-modify/chosenandmemoryreservationsforthenextkernel+*@image:kexecimagebeingloaded.+*@fdt:Flatteneddevicetreeforthenextkernel.*@initrd_load_addr:Addresswherethenextinitrdwillbeloaded.*@initrd_len:Sizeofthenextinitrd,or0iftherewillbenone.*@cmdline:Commandlineforthenextkernel,orNULLiftherewill
@@ -847,6 +945,12 @@ int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,}}+ret=setup_handover_buffer(image,fdt,chosen_node);+if(ret){+pr_err("Error setting up the new device tree.\n");+returnret;+}+ret=fdt_setprop(fdt,chosen_node,"linux,booted-from-kexec",NULL,0);if(ret){pr_err("Error setting up the new device tree.\n");
Adds checksum argument to kexec_add_buffer specifying whether the given
segment should be part of the checksum calculation.
The next patch will add a way to update segments after a kimage is loaded.
Segments that will be updated in this way should not be checksummed,
otherwise they will cause the purgatory checksum verification to fail
when the machine is rebooted.
As a bonus, we don't need to special-case the purgatory segment anymore
to avoid checksumming it.
Adjust call sites for the new argument.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
arch/powerpc/kernel/kexec_elf_64.c | 6 +++---
arch/x86/kernel/crash.c | 4 ++--
arch/x86/kernel/kexec-bzimage64.c | 6 +++---
include/linux/kexec.h | 10 +++++++---
kernel/kexec_file.c | 23 ++++++++++++-----------
5 files changed, 27 insertions(+), 22 deletions(-)
@@ -100,6 +100,9 @@ struct kexec_segment {size_tbufsz;unsignedlongmem;size_tmemsz;++/* Whether this segment is part of the checksum calculation. */+booldo_checksum;};#ifdef CONFIG_COMPAT
@@ -161,6 +161,7 @@ int __weak arch_kexec_add_handover_buffer(struct kimage *image,/***kexec_add_handover_buffer-addbuffertobeusedbythenextkernel*@kbuf:Buffercontentsandmemoryparameters.+*@checksum:Shouldthesegmentchecksumbeverifiedbythepurgatory?**Thisfunctionassumesthatkexec_mutexisheld.*Onsuccessfulreturn,@kbuf->memwillhavethephysicaladdressof
@@ -168,14 +169,14 @@ int __weak arch_kexec_add_handover_buffer(struct kimage *image,**Return:0onsuccess,negativeerrnoonerror.*/-intkexec_add_handover_buffer(structkexec_buf*kbuf)+intkexec_add_handover_buffer(structkexec_buf*kbuf,boolchecksum){intret;if(!kexec_can_hand_over_buffer())return-ENOTSUPP;-ret=kexec_add_buffer(kbuf);+ret=kexec_add_buffer(kbuf,checksum);if(ret)returnret;
@@ -611,6 +612,7 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf)/***kexec_add_buffer-placeabufferinakexecsegment*@kbuf:Buffercontentsandmemoryparameters.+*@checksum:Shouldthesegmentchecksumbeverifiedbythepurgatory?**Thisfunctionassumesthatkexec_mutexisheld.*Onsuccessfulreturn,@kbuf->memwillhavethephysicaladdressof
@@ -618,7 +620,7 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf)**Return:0onsuccess,negativeerrnoonerror.*/-intkexec_add_buffer(structkexec_buf*kbuf)+intkexec_add_buffer(structkexec_buf*kbuf,boolchecksum){structkexec_segment*ksegment;
@@ -658,6 +660,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)ksegment->bufsz=kbuf->bufsz;ksegment->mem=kbuf->mem;ksegment->memsz=kbuf->memsz;+ksegment->do_checksum=checksum;kbuf->image->nr_segments++;return0;}
@@ -672,7 +675,6 @@ static int kexec_calculate_store_digests(struct kimage *image)char*digest;void*zero_buf;structkexec_sha_region*sha_regions;-structpurgatory_info*pi=&image->purgatory_info;zero_buf=__va(page_to_pfn(ZERO_PAGE(0))<<PAGE_SHIFT);zero_buf_sz=PAGE_SIZE;
@@ -712,11 +714,7 @@ static int kexec_calculate_store_digests(struct kimage *image)structkexec_segment*ksegment;ksegment=&image->segment[i];-/*-*Skippurgatoryasitwillbemodifiedonceweputdigest-*infoinpurgatory.-*/-if(ksegment->kbuf==pi->purgatory_buf)+if(!ksegment->do_checksum)continue;ret=crypto_shash_update(desc,ksegment->kbuf,
@@ -893,8 +891,11 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,if(kbuf.buf_align<bss_align)kbuf.buf_align=bss_align;-/* Add buffer to segment list */-ret=kexec_add_buffer(&kbuf);+/*+*Addbuffertosegmentlist.Don'tchecksumthesegmentas+*itwillbemodifiedonceweputdigestinfoinpurgatory.+*/+ret=kexec_add_buffer(&kbuf,false);if(ret)gotoout;pi->purgatory_load_addr=kbuf.mem;
kexec_update_segment allows a given segment in kexec_image to have
its contents updated. This is useful if the current kernel wants to
send information to the next kernel that is up-to-date at the time of
reboot.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
include/linux/kexec.h | 2 ++
kernel/kexec_core.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 101 insertions(+)
@@ -721,6 +721,105 @@ static struct page *kimage_alloc_page(struct kimage *image,returnpage;}+/**+*kexec_update_segment-updatethecontentsofakimagesegment+*@buffer:Newcontentsofthesegment.+*@bufsz:@buffersize.+*@load_addr:Segment'sphysicaladdressinthenextkernel.+*@memsz:Segmentsize.+*+*Thisfunctionassumeskexec_mutexisheld.+*+*Return:0onsuccess,negativeerrnoonerror.+*/+intkexec_update_segment(constchar*buffer,unsignedlongbufsz,+unsignedlongload_addr,unsignedlongmemsz)+{+inti;+unsignedlongentry;+unsignedlong*ptr=NULL;+void*dest=NULL;++if(kexec_image==NULL){+pr_err("Can't update segment: no kexec image loaded.\n");+return-EINVAL;+}++/*+*kexec_add_bufferroundsupsegmentsizestoPAGE_SIZE,so+*wehavetodoithereaswell.+*/+memsz=ALIGN(memsz,PAGE_SIZE);++for(i=0;i<kexec_image->nr_segments;i++)+/* We only support updating whole segments. */+if(load_addr==kexec_image->segment[i].mem&&+memsz==kexec_image->segment[i].memsz){+if(kexec_image->segment[i].do_checksum){+pr_err("Trying to update non-modifiable segment.\n");+return-EINVAL;+}++break;+}+if(i==kexec_image->nr_segments){+pr_err("Couldn't find segment to update: 0x%lx, size 0x%lx\n",+load_addr,memsz);+return-EINVAL;+}++for(entry=kexec_image->head;!(entry&IND_DONE)&&memsz;+entry=*ptr++){+void*addr=(void*)(entry&PAGE_MASK);++switch(entry&IND_FLAGS){+caseIND_DESTINATION:+dest=addr;+break;+caseIND_INDIRECTION:+ptr=__va(addr);+break;+caseIND_SOURCE:+/* Shouldn't happen, but verify just to be safe. */+if(dest==NULL){+pr_err("Invalid kexec entries list.");+return-EINVAL;+}++if(dest==(void*)load_addr){+structpage*page;+char*ptr;+size_tuchunk,mchunk;++page=kmap_to_page(addr);++ptr=kmap(page);+ptr+=load_addr&~PAGE_MASK;+mchunk=min_t(size_t,memsz,+PAGE_SIZE-(load_addr&~PAGE_MASK));+uchunk=min(bufsz,mchunk);+memcpy(ptr,buffer,uchunk);++kunmap(page);++bufsz-=uchunk;+load_addr+=mchunk;+buffer+=mchunk;+memsz-=mchunk;+}+dest+=PAGE_SIZE;+}++/* Shouldn't happen, but verify just to be safe. */+if(ptr==NULL){+pr_err("Invalid kexec entries list.");+return-EINVAL;+}+}++return0;+}+staticintkimage_load_normal_segment(structkimage*image,structkexec_segment*segment){
@@ -721,6 +721,65 @@ static struct page *kimage_alloc_page(struct kimage *image,returnpage;}+structkimage_update_buffer_state{+/* Destination memory address currently being copied to. */+unsignedlongmaddr;++/* Bytes in buffer still left to copy. */+size_tubytes;++/* Bytes in memory still left to copy. */+size_tmbytes;++/* If true, copy from kbuf. */+boolfrom_kernel;++/* Clear pages before copying? */+boolclear_pages;++/* Buffer position to continue copying from. */+constunsignedchar*kbuf;+constunsignedchar__user*buf;+};++staticintkimage_update_page(structpage*page,+structkimage_update_buffer_state*state)+{+char*ptr;+intresult=0;+size_tuchunk,mchunk;++ptr=kmap(page);++/* Start with a clear page */+if(state->clear_pages)+clear_page(ptr);++ptr+=state->maddr&~PAGE_MASK;+mchunk=min_t(size_t,state->mbytes,+PAGE_SIZE-(state->maddr&~PAGE_MASK));+uchunk=min(state->ubytes,mchunk);++if(state->from_kernel)+memcpy(ptr,state->kbuf,uchunk);+else+result=copy_from_user(ptr,state->buf,uchunk);++kunmap(page);+if(result)+return-EFAULT;++state->ubytes-=uchunk;+state->maddr+=mchunk;+if(state->from_kernel)+state->kbuf+=mchunk;+else+state->buf+=mchunk;+state->mbytes-=mchunk;++return0;+}+/***kexec_update_segment-updatethecontentsofakimagesegment*@buffer:Newcontentsofthesegment.
@@ -739,6 +798,7 @@ int kexec_update_segment(const char *buffer, unsigned long bufsz,unsignedlongentry;unsignedlong*ptr=NULL;void*dest=NULL;+structkimage_update_buffer_statestate;if(kexec_image==NULL){pr_err("Can't update segment: no kexec image loaded.\n");
@@ -768,8 +828,15 @@ int kexec_update_segment(const char *buffer, unsigned long bufsz,return-EINVAL;}-for(entry=kexec_image->head;!(entry&IND_DONE)&&memsz;-entry=*ptr++){+state.maddr=load_addr;+state.ubytes=bufsz;+state.mbytes=memsz;+state.kbuf=buffer;+state.from_kernel=true;+state.clear_pages=false;++for(entry=kexec_image->head;!(entry&IND_DONE)&&+state.mbytes;entry=*ptr++){void*addr=(void*)(entry&PAGE_MASK);switch(entry&IND_FLAGS){
@@ -786,26 +853,13 @@ int kexec_update_segment(const char *buffer, unsigned long bufsz,return-EINVAL;}-if(dest==(void*)load_addr){-structpage*page;-char*ptr;-size_tuchunk,mchunk;--page=kmap_to_page(addr);--ptr=kmap(page);-ptr+=load_addr&~PAGE_MASK;-mchunk=min_t(size_t,memsz,-PAGE_SIZE-(load_addr&~PAGE_MASK));-uchunk=min(bufsz,mchunk);-memcpy(ptr,buffer,uchunk);--kunmap(page);+if(dest==(void*)state.maddr){+intret;-bufsz-=uchunk;-load_addr+=mchunk;-buffer+=mchunk;-memsz-=mchunk;+ret=kimage_update_page(kmap_to_page(addr),+&state);+if(ret)+returnret;}dest+=PAGE_SIZE;}
@@ -823,31 +877,30 @@ int kexec_update_segment(const char *buffer, unsigned long bufsz,staticintkimage_load_normal_segment(structkimage*image,structkexec_segment*segment){-unsignedlongmaddr;-size_tubytes,mbytes;-intresult;-unsignedchar__user*buf=NULL;-unsignedchar*kbuf=NULL;--result=0;-if(image->file_mode)-kbuf=segment->kbuf;-else-buf=segment->buf;-ubytes=segment->bufsz;-mbytes=segment->memsz;-maddr=segment->mem;+intresult=0;+structkimage_update_buffer_statestate;++/* For file based kexec, source pages are in kernel memory */+if(image->file_mode){+state.kbuf=segment->kbuf;+state.from_kernel=true;+}else{+state.buf=segment->buf;+state.from_kernel=false;+}+state.ubytes=segment->bufsz;+state.mbytes=segment->memsz;+state.maddr=segment->mem;+state.clear_pages=true;-result=kimage_set_destination(image,maddr);+result=kimage_set_destination(image,state.maddr);if(result<0)gotoout;-while(mbytes){+while(state.mbytes){structpage*page;-char*ptr;-size_tuchunk,mchunk;-page=kimage_alloc_page(image,GFP_HIGHUSER,maddr);+page=kimage_alloc_page(image,GFP_HIGHUSER,state.maddr);if(!page){result=-ENOMEM;gotoout;
@@ -857,31 +910,9 @@ static int kimage_load_normal_segment(struct kimage *image,if(result<0)gotoout;-ptr=kmap(page);-/* Start with a clear page */-clear_page(ptr);-ptr+=maddr&~PAGE_MASK;-mchunk=min_t(size_t,mbytes,-PAGE_SIZE-(maddr&~PAGE_MASK));-uchunk=min(ubytes,mchunk);--/* For file based kexec, source pages are in kernel memory */-if(image->file_mode)-memcpy(ptr,kbuf,uchunk);-else-result=copy_from_user(ptr,buf,uchunk);-kunmap(page);-if(result){-result=-EFAULT;+result=kimage_update_page(page,&state);+if(result)gotoout;-}-ubytes-=uchunk;-maddr+=mchunk;-if(image->file_mode)-kbuf+=mchunk;-else-buf+=mchunk;-mbytes-=mchunk;}out:returnresult;
This patch is not intended to be committed.
It shows how kernel code can use the kexec buffer passing mechanism
to pass information to the next kernel.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
include/linux/ima.h | 11 +++++
kernel/kexec_file.c | 4 ++
security/integrity/ima/ima.h | 5 +++
security/integrity/ima/ima_init.c | 26 +++++++++++
security/integrity/ima/ima_template.c | 85 +++++++++++++++++++++++++++++++++++
5 files changed, 131 insertions(+)
@@ -321,6 +322,9 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,}}+/* IMA needs to pass the measurement list to the next kernel. */+ima_add_kexec_buffer(image);+/* Call arch image load handlers */ldata=arch_kexec_kernel_image_load(image);
@@ -102,6 +102,11 @@ struct ima_queue_entry {};externstructlist_headima_measurements;/* list of all measurements */+#ifdef CONFIG_KEXEC_FILE+externvoid*kexec_buffer;+externsize_tkexec_buffer_size;+#endif+/* Internal IMA function definitions */intima_init(void);intima_fs_init(void);
@@ -182,6 +184,89 @@ static int template_desc_init_fields(const char *template_fmt,return0;}+#ifdef CONFIG_KEXEC_FILE+void*kexec_buffer=NULL;+size_tkexec_buffer_size=0;++/* Physical address of the measurement buffer in the next kernel. */+unsignedlongkexec_buffer_load_addr=0;++/*+*Calledduringreboot.IMAcanaddhereneweventsthatweregeneratedafter+*thekexecimagewasloaded.+*/+staticintima_update_kexec_buffer(structnotifier_block*self,+unsignedlongaction,void*data)+{+intret;++if(!kexec_in_progress)+returnNOTIFY_OK;++/*+*Addcontentdeepinthebuffertoshowthatwecanupdate+*allofit.+*/+strcpy(kexec_buffer+4*PAGE_SIZE+10,+"Updated kexec buffer contents.");++ret=kexec_update_segment(kexec_buffer,kexec_buffer_size,+kexec_buffer_load_addr,kexec_buffer_size);+if(ret)+pr_err("Error updating kexec buffer: %d\n",ret);++returnNOTIFY_OK;+}++structnotifier_blockupdate_buffer_nb={+.notifier_call=ima_update_kexec_buffer,+};++/*+*Calledduringkexec_file_loadsothatIMAcanaddasegmenttothekexec+*imagewiththemeasurementeventlogforthenextkernel.+*/+voidima_add_kexec_buffer(structkimage*image)+{+structkexec_bufkbuf={.image=image,.buf_align=PAGE_SIZE,+.buf_min=0,.buf_max=ULONG_MAX,+.top_down=true};+boolfirst_kexec_load=kexec_buffer_load_addr==0;+intret;++if(!kexec_can_hand_over_buffer())+return;++if(!first_kexec_load)+kfree(kexec_buffer);++/* Create a relatively big buffer, for testing. */+kexec_buffer_size=kbuf.bufsz=kbuf.memsz=5*PAGE_SIZE;+kexec_buffer=kbuf.buffer=kzalloc(kexec_buffer_size,GFP_KERNEL);+if(!kexec_buffer){+pr_err("Not enough memory for the kexec measurement buffer.\n");+return;+}++/* Add some content for demonstration purposes. */+strcpy(kexec_buffer,"Buffer contents at kexec load time.");++/* Ask not to checksum the segment, we may have to update it later. */+ret=kexec_add_handover_buffer(&kbuf,false);+if(ret){+pr_err("Error passing over kexec measurement buffer.\n");+return;+}+kexec_buffer_load_addr=kbuf.mem;++if(first_kexec_load)+register_reboot_notifier(&update_buffer_nb);++pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",+kexec_buffer_load_addr);+}+#endif /* CONFIG_KEXEC_FILE */+structima_template_desc*ima_template_desc_current(void){if(!ima_template)
From: Andrew Morton <akpm@linux-foundation.org> Date: 2016-08-15 22:28:03
On Sat, 13 Aug 2016 00:18:23 -0300 Thiago Jung Bauermann [off-list ref] wrote:
quoted hunk
kexec_update_segment allows a given segment in kexec_image to have
its contents updated. This is useful if the current kernel wants to
send information to the next kernel that is up-to-date at the time of
reboot.
...
@@ -721,6 +721,105 @@ static struct page *kimage_alloc_page(struct kimage *image, return page; }+/**+ * kexec_update_segment - update the contents of a kimage segment+ * @buffer: New contents of the segment.+ * @bufsz: @buffer size.+ * @load_addr: Segment's physical address in the next kernel.+ * @memsz: Segment size.+ *+ * This function assumes kexec_mutex is held.+ *+ * Return: 0 on success, negative errno on error.+ */+int kexec_update_segment(const char *buffer, unsigned long bufsz,+ unsigned long load_addr, unsigned long memsz)+{+ int i;+ unsigned long entry;+ unsigned long *ptr = NULL;+ void *dest = NULL;++ if (kexec_image == NULL) {+ pr_err("Can't update segment: no kexec image loaded.\n");+ return -EINVAL;+ }++ /*+ * kexec_add_buffer rounds up segment sizes to PAGE_SIZE, so+ * we have to do it here as well.+ */+ memsz = ALIGN(memsz, PAGE_SIZE);++ for (i = 0; i < kexec_image->nr_segments; i++)+ /* We only support updating whole segments. */+ if (load_addr == kexec_image->segment[i].mem &&+ memsz == kexec_image->segment[i].memsz) {+ if (kexec_image->segment[i].do_checksum) {+ pr_err("Trying to update non-modifiable segment.\n");+ return -EINVAL;+ }++ break;+ }+ if (i == kexec_image->nr_segments) {+ pr_err("Couldn't find segment to update: 0x%lx, size 0x%lx\n",+ load_addr, memsz);+ return -EINVAL;+ }++ for (entry = kexec_image->head; !(entry & IND_DONE) && memsz;+ entry = *ptr++) {+ void *addr = (void *) (entry & PAGE_MASK);++ switch (entry & IND_FLAGS) {+ case IND_DESTINATION:+ dest = addr;+ break;+ case IND_INDIRECTION:+ ptr = __va(addr);+ break;+ case IND_SOURCE:+ /* Shouldn't happen, but verify just to be safe. */+ if (dest == NULL) {+ pr_err("Invalid kexec entries list.");+ return -EINVAL;+ }++ if (dest == (void *) load_addr) {+ struct page *page;+ char *ptr;+ size_t uchunk, mchunk;++ page = kmap_to_page(addr);++ ptr = kmap(page);
kmap_atomic() could be used here, and it is appreciably faster.
Hello Andrew,
Thank you for your review!
Am Montag, 15 August 2016, 15:27:56 schrieb Andrew Morton:
On Sat, 13 Aug 2016 00:18:23 -0300 Thiago Jung Bauermann [off-list ref] wrote:
quoted
+/**
+ * kexec_update_segment - update the contents of a kimage segment
+ * @buffer: New contents of the segment.
+ * @bufsz: @buffer size.
+ * @load_addr: Segment's physical address in the next kernel.
+ * @memsz: Segment size.
+ *
+ * This function assumes kexec_mutex is held.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+int kexec_update_segment(const char *buffer, unsigned long bufsz,
+ unsigned long load_addr, unsigned long memsz)
+{
+ int i;
+ unsigned long entry;
+ unsigned long *ptr = NULL;
+ void *dest = NULL;
+
+ if (kexec_image == NULL) {
+ pr_err("Can't update segment: no kexec image loaded.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * kexec_add_buffer rounds up segment sizes to PAGE_SIZE, so
+ * we have to do it here as well.
+ */
+ memsz = ALIGN(memsz, PAGE_SIZE);
+
+ for (i = 0; i < kexec_image->nr_segments; i++)
+ /* We only support updating whole segments. */
+ if (load_addr == kexec_image->segment[i].mem &&
+ memsz == kexec_image->segment[i].memsz) {
+ if (kexec_image->segment[i].do_checksum) {
+ pr_err("Trying to update non-modifiable segment.\n");
+ return -EINVAL;
+ }
+
+ break;
+ }
+ if (i == kexec_image->nr_segments) {
+ pr_err("Couldn't find segment to update: 0x%lx, size 0x%lx\n",
+ load_addr, memsz);
+ return -EINVAL;
+ }
+
+ for (entry = kexec_image->head; !(entry & IND_DONE) && memsz;
+ entry = *ptr++) {
+ void *addr = (void *) (entry & PAGE_MASK);
+
+ switch (entry & IND_FLAGS) {
+ case IND_DESTINATION:
+ dest = addr;
+ break;
+ case IND_INDIRECTION:
+ ptr = __va(addr);
+ break;
+ case IND_SOURCE:
+ /* Shouldn't happen, but verify just to be safe. */
+ if (dest == NULL) {
+ pr_err("Invalid kexec entries list.");
+ return -EINVAL;
+ }
+
+ if (dest == (void *) load_addr) {
+ struct page *page;
+ char *ptr;
+ size_t uchunk, mchunk;
+
+ page = kmap_to_page(addr);
+
+ ptr = kmap(page);
kmap_atomic() could be used here, and it is appreciably faster.
Good idea. The patch below implements your suggestion.
This has a consequence for patch 5/6 in this series, because it makes
this code be used in the path of the kexec_file_load and
kexec_load syscalls.
In the latter case, there's a call to copy_from_user and thus kmap_atomic
can't be used. I can change the patch to use kmap_atomic if
state->from_kernel is true and kmap otherwise, but perhaps this is one more
hint that patch 5/6 is not a very good idea after all.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
Subject: [PATCH v2 4/6] kexec_file: Add mechanism to update kexec segments.
kexec_update_segment allows a given segment in kexec_image to have
its contents updated. This is useful if the current kernel wants to
send information to the next kernel that is up-to-date at the time of
reboot.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
include/linux/kexec.h | 2 ++
kernel/kexec_core.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 101 insertions(+)
@@ -721,6 +721,105 @@ static struct page *kimage_alloc_page(struct kimage *image,returnpage;}+/**+*kexec_update_segment-updatethecontentsofakimagesegment+*@buffer:Newcontentsofthesegment.+*@bufsz:@buffersize.+*@load_addr:Segment'sphysicaladdressinthenextkernel.+*@memsz:Segmentsize.+*+*Thisfunctionassumeskexec_mutexisheld.+*+*Return:0onsuccess,negativeerrnoonerror.+*/+intkexec_update_segment(constchar*buffer,unsignedlongbufsz,+unsignedlongload_addr,unsignedlongmemsz)+{+inti;+unsignedlongentry;+unsignedlong*ptr=NULL;+void*dest=NULL;++if(kexec_image==NULL){+pr_err("Can't update segment: no kexec image loaded.\n");+return-EINVAL;+}++/*+*kexec_add_bufferroundsupsegmentsizestoPAGE_SIZE,so+*wehavetodoithereaswell.+*/+memsz=ALIGN(memsz,PAGE_SIZE);++for(i=0;i<kexec_image->nr_segments;i++)+/* We only support updating whole segments. */+if(load_addr==kexec_image->segment[i].mem&&+memsz==kexec_image->segment[i].memsz){+if(kexec_image->segment[i].do_checksum){+pr_err("Trying to update non-modifiable segment.\n");+return-EINVAL;+}++break;+}+if(i==kexec_image->nr_segments){+pr_err("Couldn't find segment to update: 0x%lx, size 0x%lx\n",+load_addr,memsz);+return-EINVAL;+}++for(entry=kexec_image->head;!(entry&IND_DONE)&&memsz;+entry=*ptr++){+void*addr=(void*)(entry&PAGE_MASK);++switch(entry&IND_FLAGS){+caseIND_DESTINATION:+dest=addr;+break;+caseIND_INDIRECTION:+ptr=__va(addr);+break;+caseIND_SOURCE:+/* Shouldn't happen, but verify just to be safe. */+if(dest==NULL){+pr_err("Invalid kexec entries list.");+return-EINVAL;+}++if(dest==(void*)load_addr){+structpage*page;+char*ptr;+size_tuchunk,mchunk;++page=kmap_to_page(addr);++ptr=kmap_atomic(page);+ptr+=load_addr&~PAGE_MASK;+mchunk=min_t(size_t,memsz,+PAGE_SIZE-(load_addr&~PAGE_MASK));+uchunk=min(bufsz,mchunk);+memcpy(ptr,buffer,uchunk);++kunmap_atomic(ptr);++bufsz-=uchunk;+load_addr+=mchunk;+buffer+=mchunk;+memsz-=mchunk;+}+dest+=PAGE_SIZE;+}++/* Shouldn't happen, but verify just to be safe. */+if(ptr==NULL){+pr_err("Invalid kexec entries list.");+return-EINVAL;+}+}++return0;+}+staticintkimage_load_normal_segment(structkimage*image,structkexec_segment*segment){
From: Dave Young <hidden> Date: 2016-08-17 02:52:44
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
Hello,
This patch series implements a mechanism which allows the kernel to pass
on a buffer to the kernel that will be kexec'd. This buffer is passed
as a segment which is added to the kimage when it is being prepared
by kexec_file_load.
How the second kernel is informed of this buffer is architecture-specific.
On powerpc, this is done via the device tree, by checking
the properties /chosen/linux,kexec-handover-buffer-start and
/chosen/linux,kexec-handover-buffer-end, which is analogous to how the
kernel finds the initrd.
This is needed because the Integrity Measurement Architecture subsystem
needs to preserve its measurement list accross the kexec reboot. The
following patch series for the IMA subsystem uses this feature for that
purpose:
https://lists.infradead.org/pipermail/kexec/2016-August/016745.html
This is so that IMA can implement trusted boot support on the OpenPower
platform, because on such systems an intermediary Linux instance running
as part of the firmware is used to boot the target operating system via
kexec. Using this mechanism, IMA on this intermediary instance can
hand over to the target OS the measurements of the components that were
used to boot it.
Because there could be additional measurement events between the
kexec_file_load call and the actual reboot, IMA needs a way to update the
buffer with those additional events before rebooting. One can minimize
the interval between the kexec_file_load and the reboot syscalls, but as
small as it can be, there is always the possibility that the measurement
list will be out of date at the time of reboot.
To address this issue, this patch series also introduces
kexec_update_segment, which allows a reboot notifier to change the
contents of the image segment during the reboot process.
Patch 5 makes kimage_load_normal_segment and kexec_update_segment share
code. It's not much code that they can share though, so I'm not sure if
the result is actually better.
The last patch is not intended to be merged, it just demonstrates how
this feature can be used.
This series applies on top of v5 of the "kexec_file_load implementation
for PowerPC" patch series (which applies on top of v4.8-rc1):
https://lists.infradead.org/pipermail/kexec/2016-August/016843.html
I'm trying to review your patches, but seems I can not apply them
cleanly to mainline kernel or v4.8-rc1
Apply the kexec_file_load series failed as below on v4.8-rc1:
Applying: kexec_file: Allow arch-specific memory walking for
kexec_add_buffer
error: patch failed: include/linux/kexec.h:149
error: include/linux/kexec.h: patch does not apply
Patch failed at 0001 kexec_file: Allow arch-specific memory walking for
kexec_add_buffer
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
What is the order of your patch series of the three patchset?
[PATCH v2 0/2] extend kexec_file_load system call
[PATCH v5 00/13] kexec_file_load implementation for PowerPC
[PATCH v2 0/6] kexec_file: Add buffer hand-over for the next kernel
Do they depend on other patches?
Thanks
Dave
I'm trying to review your patches, but seems I can not apply them
cleanly to mainline kernel or v4.8-rc1
Strange, I just did a test using the patches I received via the kexec
mailing list, and git am applied them cleanly on v4.8-rc1.
Apply the kexec_file_load series failed as below on v4.8-rc1:
Applying: kexec_file: Allow arch-specific memory walking for
kexec_add_buffer
error: patch failed: include/linux/kexec.h:149
error: include/linux/kexec.h: patch does not apply
Patch failed at 0001 kexec_file: Allow arch-specific memory walking for
kexec_add_buffer
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
What is the order of your patch series of the three patchset?
[PATCH v2 0/2] extend kexec_file_load system call
[PATCH v5 00/13] kexec_file_load implementation for PowerPC
[PATCH v2 0/6] kexec_file: Add buffer hand-over for the next kernel
Yes, that is correct.
Do they depend on other patches?
No, they apply directly on v4.8-rc1.
I just published a branch with the patches, if you want you can use that
instead. The branch is called kexec-patches and is at the repo
git@github.com:bauermann/linux
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
From: Dave Young <hidden> Date: 2016-08-18 09:04:02
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
Adds checksum argument to kexec_add_buffer specifying whether the given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a new
field?
Like kbuf.no_checksum, default value is 0 that means checksum is needed
if it is 1 then no need a checksum.
quoted hunk
The next patch will add a way to update segments after a kimage is loaded.
Segments that will be updated in this way should not be checksummed,
otherwise they will cause the purgatory checksum verification to fail
when the machine is rebooted.
As a bonus, we don't need to special-case the purgatory segment anymore
to avoid checksumming it.
Adjust call sites for the new argument.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
arch/powerpc/kernel/kexec_elf_64.c | 6 +++---
arch/x86/kernel/crash.c | 4 ++--
arch/x86/kernel/kexec-bzimage64.c | 6 +++---
include/linux/kexec.h | 10 +++++++---
kernel/kexec_file.c | 23 ++++++++++++-----------
5 files changed, 27 insertions(+), 22 deletions(-)
@@ -100,6 +100,9 @@ struct kexec_segment {size_tbufsz;unsignedlongmem;size_tmemsz;++/* Whether this segment is part of the checksum calculation. */+booldo_checksum;};#ifdef CONFIG_COMPAT
@@ -161,6 +161,7 @@ int __weak arch_kexec_add_handover_buffer(struct kimage *image,/***kexec_add_handover_buffer-addbuffertobeusedbythenextkernel*@kbuf:Buffercontentsandmemoryparameters.+*@checksum:Shouldthesegmentchecksumbeverifiedbythepurgatory?**Thisfunctionassumesthatkexec_mutexisheld.*Onsuccessfulreturn,@kbuf->memwillhavethephysicaladdressof
@@ -168,14 +169,14 @@ int __weak arch_kexec_add_handover_buffer(struct kimage *image,**Return:0onsuccess,negativeerrnoonerror.*/-intkexec_add_handover_buffer(structkexec_buf*kbuf)+intkexec_add_handover_buffer(structkexec_buf*kbuf,boolchecksum){intret;if(!kexec_can_hand_over_buffer())return-ENOTSUPP;-ret=kexec_add_buffer(kbuf);+ret=kexec_add_buffer(kbuf,checksum);if(ret)returnret;
@@ -611,6 +612,7 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf)/***kexec_add_buffer-placeabufferinakexecsegment*@kbuf:Buffercontentsandmemoryparameters.+*@checksum:Shouldthesegmentchecksumbeverifiedbythepurgatory?**Thisfunctionassumesthatkexec_mutexisheld.*Onsuccessfulreturn,@kbuf->memwillhavethephysicaladdressof
@@ -618,7 +620,7 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf)**Return:0onsuccess,negativeerrnoonerror.*/-intkexec_add_buffer(structkexec_buf*kbuf)+intkexec_add_buffer(structkexec_buf*kbuf,boolchecksum){structkexec_segment*ksegment;
@@ -658,6 +660,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)ksegment->bufsz=kbuf->bufsz;ksegment->mem=kbuf->mem;ksegment->memsz=kbuf->memsz;+ksegment->do_checksum=checksum;kbuf->image->nr_segments++;return0;}
@@ -672,7 +675,6 @@ static int kexec_calculate_store_digests(struct kimage *image)char*digest;void*zero_buf;structkexec_sha_region*sha_regions;-structpurgatory_info*pi=&image->purgatory_info;zero_buf=__va(page_to_pfn(ZERO_PAGE(0))<<PAGE_SHIFT);zero_buf_sz=PAGE_SIZE;
@@ -712,11 +714,7 @@ static int kexec_calculate_store_digests(struct kimage *image)structkexec_segment*ksegment;ksegment=&image->segment[i];-/*-*Skippurgatoryasitwillbemodifiedonceweputdigest-*infoinpurgatory.-*/-if(ksegment->kbuf==pi->purgatory_buf)+if(!ksegment->do_checksum)continue;ret=crypto_shash_update(desc,ksegment->kbuf,
@@ -893,8 +891,11 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,if(kbuf.buf_align<bss_align)kbuf.buf_align=bss_align;-/* Add buffer to segment list */-ret=kexec_add_buffer(&kbuf);+/*+*Addbuffertosegmentlist.Don'tchecksumthesegmentas+*itwillbemodifiedonceweputdigestinfoinpurgatory.+*/+ret=kexec_add_buffer(&kbuf,false);if(ret)gotoout;pi->purgatory_load_addr=kbuf.mem;
--
1.9.1
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
Hello Dave,
Thanks for your review!
[ Trimming down Cc: list a little to try to clear the "too many recipients"
mailing list restriction. ]
Am Donnerstag, 18 August 2016, 17:03:30 schrieb Dave Young:
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted
Adds checksum argument to kexec_add_buffer specifying whether the given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a new
field?
I was on the fence about adding it as a new argument to kexec_add_buffer or
as a new field to struct kexec_buf. Both alternatives make sense to me. I
implemented your suggestion in the patch below, what do you think?
Like kbuf.no_checksum, default value is 0 that means checksum is needed
if it is 1 then no need a checksum.
It's an interesting idea and I implemented it that way, though in practice
all current users of struct kexec_buf put it on the stack so the field needs
to be initialized explicitly.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
Subject: [PATCH v2 3/6] kexec_file: Allow skipping checksum calculation for
some segments.
Add skip_checksum member to struct kexec_buf to specify whether the
corresponding segment should be part of the checksum calculation.
The next patch will add a way to update segments after a kimage is loaded.
Segments that will be updated in this way should not be checksummed,
otherwise they will cause the purgatory checksum verification to fail
when the machine is rebooted.
As a bonus, we don't need to special-case the purgatory segment anymore
to avoid checksumming it.
Adjust places using struct kexec_buf to set skip_checksum.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
arch/powerpc/kernel/kexec_elf_64.c | 5 +++--
arch/x86/kernel/crash.c | 3 ++-
arch/x86/kernel/kexec-bzimage64.c | 2 +-
include/linux/kexec.h | 23 ++++++++++++++---------
kernel/kexec_file.c | 15 +++++++--------
5 files changed, 27 insertions(+), 21 deletions(-)
@@ -100,6 +100,9 @@ struct kexec_segment {size_tbufsz;unsignedlongmem;size_tmemsz;++/* Whether this segment is ignored in the checksum calculation. */+boolskip_checksum;};#ifdef CONFIG_COMPAT
@@ -658,6 +658,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)ksegment->bufsz=kbuf->bufsz;ksegment->mem=kbuf->mem;ksegment->memsz=kbuf->memsz;+ksegment->skip_checksum=kbuf->skip_checksum;kbuf->image->nr_segments++;return0;}
@@ -672,7 +673,6 @@ static int kexec_calculate_store_digests(struct kimage *image)char*digest;void*zero_buf;structkexec_sha_region*sha_regions;-structpurgatory_info*pi=&image->purgatory_info;zero_buf=__va(page_to_pfn(ZERO_PAGE(0))<<PAGE_SHIFT);zero_buf_sz=PAGE_SIZE;
@@ -712,11 +712,7 @@ static int kexec_calculate_store_digests(struct kimage *image)structkexec_segment*ksegment;ksegment=&image->segment[i];-/*-*Skippurgatoryasitwillbemodifiedonceweputdigest-*infoinpurgatory.-*/-if(ksegment->kbuf==pi->purgatory_buf)+if(ksegment->skip_checksum)continue;ret=crypto_shash_update(desc,ksegment->kbuf,
@@ -788,7 +784,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,Elf_Shdr*sechdrs=NULL;structkexec_bufkbuf={.image=image,.bufsz=0,.buf_align=1,.buf_min=min,.buf_max=max,-.top_down=top_down};+.top_down=top_down,.skip_checksum=true};/**sechdrs_cpointstosectionheadersinpurgatoryandareread
@@ -893,7 +889,10 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,if(kbuf.buf_align<bss_align)kbuf.buf_align=bss_align;-/* Add buffer to segment list */+/*+*Addbuffertosegmentlist.Don'tchecksumthesegmentas+*itwillbemodifiedonceweputdigestinfoinpurgatory.+*/ret=kexec_add_buffer(&kbuf);if(ret)gotoout;
From: Dave Young <hidden> Date: 2016-08-22 03:18:06
On 08/18/16 at 06:09pm, Thiago Jung Bauermann wrote:
Hello Dave,
Thanks for your review!
[ Trimming down Cc: list a little to try to clear the "too many recipients"
mailing list restriction. ]
I also got "too many recipients".. Thanks for the trimming.
quoted hunk
Am Donnerstag, 18 August 2016, 17:03:30 schrieb Dave Young:
quoted
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted
Adds checksum argument to kexec_add_buffer specifying whether the given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a new
field?
I was on the fence about adding it as a new argument to kexec_add_buffer or
as a new field to struct kexec_buf. Both alternatives make sense to me. I
implemented your suggestion in the patch below, what do you think?
quoted
Like kbuf.no_checksum, default value is 0 that means checksum is needed
if it is 1 then no need a checksum.
It's an interesting idea and I implemented it that way, though in practice
all current users of struct kexec_buf put it on the stack so the field needs
to be initialized explicitly.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
Subject: [PATCH v2 3/6] kexec_file: Allow skipping checksum calculation for
some segments.
Add skip_checksum member to struct kexec_buf to specify whether the
corresponding segment should be part of the checksum calculation.
The next patch will add a way to update segments after a kimage is loaded.
Segments that will be updated in this way should not be checksummed,
otherwise they will cause the purgatory checksum verification to fail
when the machine is rebooted.
As a bonus, we don't need to special-case the purgatory segment anymore
to avoid checksumming it.
Adjust places using struct kexec_buf to set skip_checksum.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
arch/powerpc/kernel/kexec_elf_64.c | 5 +++--
arch/x86/kernel/crash.c | 3 ++-
arch/x86/kernel/kexec-bzimage64.c | 2 +-
include/linux/kexec.h | 23 ++++++++++++++---------
kernel/kexec_file.c | 15 +++++++--------
5 files changed, 27 insertions(+), 21 deletions(-)
@@ -100,6 +100,9 @@ struct kexec_segment {size_tbufsz;unsignedlongmem;size_tmemsz;++/* Whether this segment is ignored in the checksum calculation. */+boolskip_checksum;};#ifdef CONFIG_COMPAT
@@ -658,6 +658,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)ksegment->bufsz=kbuf->bufsz;ksegment->mem=kbuf->mem;ksegment->memsz=kbuf->memsz;+ksegment->skip_checksum=kbuf->skip_checksum;kbuf->image->nr_segments++;return0;}
@@ -672,7 +673,6 @@ static int kexec_calculate_store_digests(struct kimage *image)char*digest;void*zero_buf;structkexec_sha_region*sha_regions;-structpurgatory_info*pi=&image->purgatory_info;zero_buf=__va(page_to_pfn(ZERO_PAGE(0))<<PAGE_SHIFT);zero_buf_sz=PAGE_SIZE;
@@ -712,11 +712,7 @@ static int kexec_calculate_store_digests(struct kimage *image)structkexec_segment*ksegment;ksegment=&image->segment[i];-/*-*Skippurgatoryasitwillbemodifiedonceweputdigest-*infoinpurgatory.-*/-if(ksegment->kbuf==pi->purgatory_buf)+if(ksegment->skip_checksum)continue;ret=crypto_shash_update(desc,ksegment->kbuf,
@@ -788,7 +784,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,Elf_Shdr*sechdrs=NULL;structkexec_bufkbuf={.image=image,.bufsz=0,.buf_align=1,.buf_min=min,.buf_max=max,-.top_down=top_down};+.top_down=top_down,.skip_checksum=true};/**sechdrs_cpointstosectionheadersinpurgatoryandareread
@@ -893,7 +889,10 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,if(kbuf.buf_align<bss_align)kbuf.buf_align=bss_align;-/* Add buffer to segment list */+/*+*Addbuffertosegmentlist.Don'tchecksumthesegmentas+*itwillbemodifiedonceweputdigestinfoinpurgatory.+*/ret=kexec_add_buffer(&kbuf);if(ret)gotoout;
--
1.9.1
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
From: Dave Young <hidden> Date: 2016-08-22 03:21:56
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted hunk
The buffer hand-over mechanism allows the currently running kernel to pass
data to kernel that will be kexec'd via a kexec segment. The second kernel
can check whether the previous kernel sent data and retrieve it.
This is the architecture-specific part.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
arch/powerpc/include/asm/kexec.h | 12 +++-
arch/powerpc/kernel/kexec_elf_64.c | 2 +-
arch/powerpc/kernel/machine_kexec_64.c | 114 +++++++++++++++++++++++++++++++--
3 files changed, 120 insertions(+), 8 deletions(-)
@@ -490,6 +490,60 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)returnimage->fops->cleanup(image->image_loader_data);}+boolkexec_can_hand_over_buffer(void)+{+returntrue;+}++intarch_kexec_add_handover_buffer(structkimage*image,+unsignedlongload_addr,unsignedlongsize)+{+image->arch.handover_buffer_addr=load_addr;+image->arch.handover_buffer_size=size;++return0;+}++intkexec_get_handover_buffer(void**addr,unsignedlong*size)+{+intret;+u64start_addr,end_addr;++ret=of_property_read_u64(of_chosen,+"linux,kexec-handover-buffer-start",+&start_addr);+if(ret==-EINVAL)+return-ENOENT;+elseif(ret)+return-EINVAL;++ret=of_property_read_u64(of_chosen,"linux,kexec-handover-buffer-end",+&end_addr);+if(ret==-EINVAL)+return-ENOENT;+elseif(ret)+return-EINVAL;++*addr=__va(start_addr);+/* -end is the first address after the buffer. */+*size=end_addr-start_addr;++return0;+}
This depends on dtb, so if IMA want to extend it to arches like x86 in
the future you will have to think about other way to pass it.
How about think about a general way now?
quoted hunk
+
+int kexec_free_handover_buffer(void)
+{
+ int ret;
+ void *addr;
+ unsigned long size;
+
+ ret = kexec_get_handover_buffer(&addr, &size);
+ if (ret)
+ return ret;
+
+ return memblock_free((phys_addr_t) addr, size);
+}
+
/**
* arch_kexec_walk_mem() - call func(data) for each unreserved memory block
* @kbuf: Context info for the search. Also passed to @func.
@@ -687,9 +741,52 @@ int setup_purgatory(struct kimage *image, const void *slave_code, return 0; }-/*- * setup_new_fdt() - modify /chosen and memory reservation for the next kernel- * @fdt:+/**+ * setup_handover_buffer() - add properties and reservation for the handover buffer+ * @image: kexec image being loaded.+ * @fdt: Flattened device tree for the next kernel.+ * @chosen_node: Offset to the chosen node.+ *+ * Return: 0 on success, negative errno on error.+ */+static int setup_handover_buffer(const struct kimage *image, void *fdt,+ int chosen_node)+{+ int ret;++ if (image->arch.handover_buffer_addr == 0)+ return 0;++ ret = fdt_setprop_u64(fdt, chosen_node,+ "linux,kexec-handover-buffer-start",+ image->arch.handover_buffer_addr);+ if (ret < 0)+ return -EINVAL;++ /* -end is the first address after the buffer. */+ ret = fdt_setprop_u64(fdt, chosen_node,+ "linux,kexec-handover-buffer-end",+ image->arch.handover_buffer_addr ++ image->arch.handover_buffer_size);+ if (ret < 0)+ return -EINVAL;++ ret = fdt_add_mem_rsv(fdt, image->arch.handover_buffer_addr,+ image->arch.handover_buffer_size);+ if (ret)+ return -EINVAL;++ pr_debug("kexec handover buffer at 0x%llx, size = 0x%lx\n",+ image->arch.handover_buffer_addr,+ image->arch.handover_buffer_size);++ return 0;+}++/**+ * setup_new_fdt() - modify /chosen and memory reservations for the next kernel+ * @image: kexec image being loaded.+ * @fdt: Flattened device tree for the next kernel. * @initrd_load_addr: Address where the next initrd will be loaded. * @initrd_len: Size of the next initrd, or 0 if there will be none. * @cmdline: Command line for the next kernel, or NULL if there will
@@ -697,8 +794,9 @@ int setup_purgatory(struct kimage *image, const void *slave_code, * * Return: 0 on success, or negative errno on error. */-int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,- unsigned long initrd_len, const char *cmdline)+int setup_new_fdt(const struct kimage *image, void *fdt,+ unsigned long initrd_load_addr, unsigned long initrd_len,+ const char *cmdline) { uint64_t oldfdt_addr; int i, ret, chosen_node;
@@ -847,6 +945,12 @@ int setup_new_fdt(void *fdt, unsigned long initrd_load_addr, } }+ ret = setup_handover_buffer(image, fdt, chosen_node);+ if (ret) {+ pr_err("Error setting up the new device tree.\n");+ return ret;+ }+ ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0); if (ret) { pr_err("Error setting up the new device tree.\n");
--
1.9.1
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
Am Montag, 22 August 2016, 11:17:45 schrieb Dave Young:
On 08/18/16 at 06:09pm, Thiago Jung Bauermann wrote:
quoted
Hello Dave,
Thanks for your review!
[ Trimming down Cc: list a little to try to clear the "too many
recipients">
mailing list restriction. ]
I also got "too many recipients".. Thanks for the trimming.
Didn't work though. What is the maximum number of recipients?
quoted
Am Donnerstag, 18 August 2016, 17:03:30 schrieb Dave Young:
quoted
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted
Adds checksum argument to kexec_add_buffer specifying whether the
given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a new
field?
I was on the fence about adding it as a new argument to kexec_add_buffer
or as a new field to struct kexec_buf. Both alternatives make sense to
me. I implemented your suggestion in the patch below, what do you
think?>
quoted
Like kbuf.no_checksum, default value is 0 that means checksum is
needed
if it is 1 then no need a checksum.
It's an interesting idea and I implemented it that way, though in
practice all current users of struct kexec_buf put it on the stack so
the field needs to be initialized explicitly.
No need to set it as false because it will be initialized to 0 by
default?
As far as I know, variables on the stack are not initialized. Only global
and static variables are.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
From: Dave Young <hidden> Date: 2016-08-22 03:37:00
On 08/22/16 at 12:25am, Thiago Jung Bauermann wrote:
Am Montag, 22 August 2016, 11:17:45 schrieb Dave Young:
quoted
On 08/18/16 at 06:09pm, Thiago Jung Bauermann wrote:
quoted
Hello Dave,
Thanks for your review!
[ Trimming down Cc: list a little to try to clear the "too many
recipients">
mailing list restriction. ]
I also got "too many recipients".. Thanks for the trimming.
Didn't work though. What is the maximum number of recipients?
I have no idea as well..
quoted
quoted
Am Donnerstag, 18 August 2016, 17:03:30 schrieb Dave Young:
quoted
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted
Adds checksum argument to kexec_add_buffer specifying whether the
given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a new
field?
I was on the fence about adding it as a new argument to kexec_add_buffer
or as a new field to struct kexec_buf. Both alternatives make sense to
me. I implemented your suggestion in the patch below, what do you
think?>
quoted
Like kbuf.no_checksum, default value is 0 that means checksum is
needed
if it is 1 then no need a checksum.
It's an interesting idea and I implemented it that way, though in
practice all current users of struct kexec_buf put it on the stack so
the field needs to be initialized explicitly.
No need to set it as false because it will be initialized to 0 by
default?
As far as I know, variables on the stack are not initialized. Only global
and static variables are.
But designated initializers will do it.
Thanks
Dave
+{
+ image->arch.handover_buffer_addr = load_addr;
+ image->arch.handover_buffer_size = size;
+
+ return 0;
+}
+
+int kexec_get_handover_buffer(void **addr, unsigned long *size)
+{
+ int ret;
+ u64 start_addr, end_addr;
+
+ ret = of_property_read_u64(of_chosen,
+ "linux,kexec-handover-buffer-start",
+ &start_addr);
+ if (ret == -EINVAL)
+ return -ENOENT;
+ else if (ret)
+ return -EINVAL;
+
+ ret = of_property_read_u64(of_chosen,
"linux,kexec-handover-buffer-end", +
&end_addr);
quoted
+ if (ret == -EINVAL)
+ return -ENOENT;
+ else if (ret)
+ return -EINVAL;
+
+ *addr = __va(start_addr);
+ /* -end is the first address after the buffer. */
+ *size = end_addr - start_addr;
+
+ return 0;
+}
This depends on dtb, so if IMA want to extend it to arches like x86 in
the future you will have to think about other way to pass it.
How about think about a general way now?
The only general way I can think of is by adding a kernel command line
parameter which the first kernel would pass to the second kernel, but IMHO
that is ugly, because such parameter wouldn't be useful to a user, and it
would also be something that, from the perspective of the user, would
magically appear in the kernel command line of the second kernel...
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
Am Montag, 22 August 2016, 11:36:43 schrieb Dave Young:
On 08/22/16 at 12:25am, Thiago Jung Bauermann wrote:
quoted
Am Montag, 22 August 2016, 11:17:45 schrieb Dave Young:
quoted
On 08/18/16 at 06:09pm, Thiago Jung Bauermann wrote:
quoted
Hello Dave,
Thanks for your review!
[ Trimming down Cc: list a little to try to clear the "too many
recipients">
mailing list restriction. ]
I also got "too many recipients".. Thanks for the trimming.
Didn't work though. What is the maximum number of recipients?
I have no idea as well..
quoted
quoted
quoted
Am Donnerstag, 18 August 2016, 17:03:30 schrieb Dave Young:
quoted
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted
Adds checksum argument to kexec_add_buffer specifying whether
the
given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a
new
field?
I was on the fence about adding it as a new argument to
kexec_add_buffer
or as a new field to struct kexec_buf. Both alternatives make sense
to
me. I implemented your suggestion in the patch below, what do you
think?>
quoted
Like kbuf.no_checksum, default value is 0 that means checksum is
needed
if it is 1 then no need a checksum.
It's an interesting idea and I implemented it that way, though in
practice all current users of struct kexec_buf put it on the stack
so
the field needs to be initialized explicitly.
No need to set it as false because it will be initialized to 0 by
default?
As far as I know, variables on the stack are not initialized. Only
global
and static variables are.
But designated initializers will do it.
Ah, you are right! I'll provide an updated patch then. Thanks for your
suggestion.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
+{
+ image->arch.handover_buffer_addr = load_addr;
+ image->arch.handover_buffer_size = size;
+
+ return 0;
+}
+
+int kexec_get_handover_buffer(void **addr, unsigned long *size)
+{
+ int ret;
+ u64 start_addr, end_addr;
+
+ ret = of_property_read_u64(of_chosen,
+ "linux,kexec-handover-buffer-start",
+ &start_addr);
+ if (ret == -EINVAL)
+ return -ENOENT;
+ else if (ret)
+ return -EINVAL;
+
+ ret = of_property_read_u64(of_chosen,
"linux,kexec-handover-buffer-end", +
&end_addr);
quoted
quoted
+ if (ret == -EINVAL)
+ return -ENOENT;
+ else if (ret)
+ return -EINVAL;
+
+ *addr = __va(start_addr);
+ /* -end is the first address after the buffer. */
+ *size = end_addr - start_addr;
+
+ return 0;
+}
This depends on dtb, so if IMA want to extend it to arches like x86 in
the future you will have to think about other way to pass it.
How about think about a general way now?
The only general way I can think of is by adding a kernel command line
parameter which the first kernel would pass to the second kernel, but IMHO
that is ugly, because such parameter wouldn't be useful to a user, and it
would also be something that, from the perspective of the user, would
magically appear in the kernel command line of the second kernel...
Sorry I just brought up the question, actually I have no idea either.
Maybe we have to do this with arch specific ways..
Thanks
Dave
Am Montag, 22 August 2016, 11:36:43 schrieb Dave Young:
On 08/22/16 at 12:25am, Thiago Jung Bauermann wrote:
quoted
Am Montag, 22 August 2016, 11:17:45 schrieb Dave Young:
quoted
On 08/18/16 at 06:09pm, Thiago Jung Bauermann wrote:
quoted
Am Donnerstag, 18 August 2016, 17:03:30 schrieb Dave Young:
quoted
On 08/13/16 at 12:18am, Thiago Jung Bauermann wrote:
quoted
Adds checksum argument to kexec_add_buffer specifying whether
the
given
segment should be part of the checksum calculation.
Since it is used with add buffer, could it be added to kbuf as a
new
field?
I was on the fence about adding it as a new argument to
kexec_add_buffer
or as a new field to struct kexec_buf. Both alternatives make sense
to
me. I implemented your suggestion in the patch below, what do you
think?>
quoted
Like kbuf.no_checksum, default value is 0 that means checksum is
needed
if it is 1 then no need a checksum.
It's an interesting idea and I implemented it that way, though in
practice all current users of struct kexec_buf put it on the stack
so
the field needs to be initialized explicitly.
No need to set it as false because it will be initialized to 0 by
default?
As far as I know, variables on the stack are not initialized. Only
global
and static variables are.
But designated initializers will do it.
Here is the new version, relying on the default value for
kexec_buf.skip_checksum. A nice side effect is that I don't have to
cc the x86 maintainers anymore.
What do you think?
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
Subject: [PATCH v2 3/6] kexec_file: Allow skipping checksum calculation for
some segments.
Add skip_checksum member to struct kexec_buf to specify whether the
corresponding segment should be part of the checksum calculation.
The next patch will add a way to update segments after a kimage is loaded.
Segments that will be updated in this way should not be checksummed,
otherwise they will cause the purgatory checksum verification to fail
when the machine is rebooted.
As a bonus, we don't need to special-case the purgatory segment anymore
to avoid checksumming it.
Places currently using struct kexec_buf will get false as the default
value for skip_checksum since they all use designated initializers.
Therefore, there will not be any behavior change with this patch and
their buffers will continue being checksummed.
Signed-off-by: Thiago Jung Bauermann <redacted>
---
include/linux/kexec.h | 23 ++++++++++++++---------
kernel/kexec_file.c | 15 +++++++--------
2 files changed, 21 insertions(+), 17 deletions(-)
@@ -100,6 +100,9 @@ struct kexec_segment {size_tbufsz;unsignedlongmem;size_tmemsz;++/* Whether this segment is ignored in the checksum calculation. */+boolskip_checksum;};#ifdef CONFIG_COMPAT
@@ -658,6 +658,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)ksegment->bufsz=kbuf->bufsz;ksegment->mem=kbuf->mem;ksegment->memsz=kbuf->memsz;+ksegment->skip_checksum=kbuf->skip_checksum;kbuf->image->nr_segments++;return0;}
@@ -672,7 +673,6 @@ static int kexec_calculate_store_digests(struct kimage *image)char*digest;void*zero_buf;structkexec_sha_region*sha_regions;-structpurgatory_info*pi=&image->purgatory_info;zero_buf=__va(page_to_pfn(ZERO_PAGE(0))<<PAGE_SHIFT);zero_buf_sz=PAGE_SIZE;
@@ -712,11 +712,7 @@ static int kexec_calculate_store_digests(struct kimage *image)structkexec_segment*ksegment;ksegment=&image->segment[i];-/*-*Skippurgatoryasitwillbemodifiedonceweputdigest-*infoinpurgatory.-*/-if(ksegment->kbuf==pi->purgatory_buf)+if(ksegment->skip_checksum)continue;ret=crypto_shash_update(desc,ksegment->kbuf,
@@ -788,7 +784,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,Elf_Shdr*sechdrs=NULL;structkexec_bufkbuf={.image=image,.bufsz=0,.buf_align=1,.buf_min=min,.buf_max=max,-.top_down=top_down};+.top_down=top_down,.skip_checksum=true};/**sechdrs_cpointstosectionheadersinpurgatoryandareread
@@ -893,7 +889,10 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,if(kbuf.buf_align<bss_align)kbuf.buf_align=bss_align;-/* Add buffer to segment list */+/*+*Addbuffertosegmentlist.Don'tchecksumthesegmentas+*itwillbemodifiedonceweputdigestinfoinpurgatory.+*/ret=kexec_add_buffer(&kbuf);if(ret)gotoout;
+ &start_addr);
+ if (ret == -EINVAL)
+ return -ENOENT;
+ else if (ret)
+ return -EINVAL;
+
+ ret = of_property_read_u64(of_chosen,
"linux,kexec-handover-buffer-end", +
&end_addr);
quoted
quoted
+ if (ret == -EINVAL)
+ return -ENOENT;
+ else if (ret)
+ return -EINVAL;
+
+ *addr = __va(start_addr);
+ /* -end is the first address after the buffer. */
+ *size = end_addr - start_addr;
+
+ return 0;
+}
This depends on dtb, so if IMA want to extend it to arches like x86 in
the future you will have to think about other way to pass it.
How about think about a general way now?
The only general way I can think of is by adding a kernel command line
parameter which the first kernel would pass to the second kernel, but
IMHO that is ugly, because such parameter wouldn't be useful to a user,
and it would also be something that, from the perspective of the user,
would magically appear in the kernel command line of the second
kernel...
Sorry I just brought up the question, actually I have no idea either.
Maybe we have to do this with arch specific ways..
Actually, I don't think it's possible to avoid arch-specific code because
the first kernel has to put the buffer memory region in a reserved memory
map, and that is arch-specific.
On powerpc, this is done by adding it to the device tree memory reservation
map. On x86, I believe this would be done added to the e820 map.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center