On Sat, 13 Aug 2016 00:18:23 -0300 Thiago Jung Bauermann 
<bauer...@linux.vnet.ibm.com> wrote:

> 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.


> +                             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;
> +             }
> +     }
> +
> +     return 0;
> +}
> +


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to