diff options
author | Philipp Rudo <prudo@linux.vnet.ibm.com> | 2018-04-13 18:36:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-13 20:10:28 -0400 |
commit | 930457057abe4e6d57433dea75e97e0e39fd0ab6 (patch) | |
tree | d453da1ff18b01034a8120988abdee14813b7064 /kernel/kexec_file.c | |
parent | 8aec395b8478310521031157ef5d44ef19c2c581 (diff) |
kernel/kexec_file.c: split up __kexec_load_puragory
When inspecting __kexec_load_purgatory you find that it has two tasks
1) setting up the kexec_buffer for the new kernel and,
2) setting up pi->sechdrs for the final load address.
The two tasks are independent of each other. To improve readability
split up __kexec_load_purgatory into two functions, one for each task,
and call them directly from kexec_load_purgatory.
Link: http://lkml.kernel.org/r/20180321112751.22196-7-prudo@linux.vnet.ibm.com
Signed-off-by: Philipp Rudo <prudo@linux.vnet.ibm.com>
Acked-by: Dave Young <dyoung@redhat.com>
Cc: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kexec_file.c')
-rw-r--r-- | kernel/kexec_file.c | 200 |
1 files changed, 103 insertions, 97 deletions
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 5c70f7f2bae3..878b97bd3067 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c | |||
@@ -710,39 +710,97 @@ out: | |||
710 | } | 710 | } |
711 | 711 | ||
712 | #ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY | 712 | #ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY |
713 | /* Actually load purgatory. Lot of code taken from kexec-tools */ | 713 | /* |
714 | static int __kexec_load_purgatory(struct kimage *image, unsigned long min, | 714 | * kexec_purgatory_setup_kbuf - prepare buffer to load purgatory. |
715 | unsigned long max, int top_down) | 715 | * @pi: Purgatory to be loaded. |
716 | * @kbuf: Buffer to setup. | ||
717 | * | ||
718 | * Allocates the memory needed for the buffer. Caller is responsible to free | ||
719 | * the memory after use. | ||
720 | * | ||
721 | * Return: 0 on success, negative errno on error. | ||
722 | */ | ||
723 | static int kexec_purgatory_setup_kbuf(struct purgatory_info *pi, | ||
724 | struct kexec_buf *kbuf) | ||
716 | { | 725 | { |
717 | struct purgatory_info *pi = &image->purgatory_info; | 726 | const Elf_Shdr *sechdrs; |
718 | unsigned long align, bss_align, bss_sz, bss_pad; | 727 | unsigned long bss_align; |
719 | unsigned long entry, load_addr, curr_load_addr, bss_addr, offset; | 728 | unsigned long bss_sz; |
720 | unsigned char *buf_addr, *src; | 729 | unsigned long align; |
721 | int i, ret = 0, entry_sidx = -1; | 730 | int i, ret; |
722 | const Elf_Shdr *sechdrs_c; | ||
723 | Elf_Shdr *sechdrs = NULL; | ||
724 | struct kexec_buf kbuf = { .image = image, .bufsz = 0, .buf_align = 1, | ||
725 | .buf_min = min, .buf_max = max, | ||
726 | .top_down = top_down }; | ||
727 | 731 | ||
728 | /* | 732 | sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff; |
729 | * sechdrs_c points to section headers in purgatory and are read | 733 | bss_align = 1; |
730 | * only. No modifications allowed. | 734 | bss_sz = 0; |
731 | */ | 735 | |
732 | sechdrs_c = (void *)pi->ehdr + pi->ehdr->e_shoff; | 736 | for (i = 0; i < pi->ehdr->e_shnum; i++) { |
737 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
738 | continue; | ||
739 | |||
740 | align = sechdrs[i].sh_addralign; | ||
741 | if (sechdrs[i].sh_type != SHT_NOBITS) { | ||
742 | if (kbuf->buf_align < align) | ||
743 | kbuf->buf_align = align; | ||
744 | kbuf->bufsz = ALIGN(kbuf->bufsz, align); | ||
745 | kbuf->bufsz += sechdrs[i].sh_size; | ||
746 | } else { | ||
747 | if (bss_align < align) | ||
748 | bss_align = align; | ||
749 | bss_sz = ALIGN(bss_sz, align); | ||
750 | bss_sz += sechdrs[i].sh_size; | ||
751 | } | ||
752 | } | ||
753 | kbuf->bufsz = ALIGN(kbuf->bufsz, bss_align); | ||
754 | kbuf->memsz = kbuf->bufsz + bss_sz; | ||
755 | if (kbuf->buf_align < bss_align) | ||
756 | kbuf->buf_align = bss_align; | ||
757 | |||
758 | kbuf->buffer = vzalloc(kbuf->bufsz); | ||
759 | if (!kbuf->buffer) | ||
760 | return -ENOMEM; | ||
761 | pi->purgatory_buf = kbuf->buffer; | ||
762 | |||
763 | ret = kexec_add_buffer(kbuf); | ||
764 | if (ret) | ||
765 | goto out; | ||
766 | pi->purgatory_load_addr = kbuf->mem; | ||
767 | |||
768 | return 0; | ||
769 | out: | ||
770 | vfree(pi->purgatory_buf); | ||
771 | pi->purgatory_buf = NULL; | ||
772 | return ret; | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * kexec_purgatory_setup_sechdrs - prepares the pi->sechdrs buffer. | ||
777 | * @pi: Purgatory to be loaded. | ||
778 | * @kbuf: Buffer prepared to store purgatory. | ||
779 | * | ||
780 | * Allocates the memory needed for the buffer. Caller is responsible to free | ||
781 | * the memory after use. | ||
782 | * | ||
783 | * Return: 0 on success, negative errno on error. | ||
784 | */ | ||
785 | static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi, | ||
786 | struct kexec_buf *kbuf) | ||
787 | { | ||
788 | unsigned long curr_load_addr; | ||
789 | unsigned long load_addr; | ||
790 | unsigned long bss_addr; | ||
791 | unsigned long offset; | ||
792 | unsigned char *buf_addr; | ||
793 | unsigned char *src; | ||
794 | Elf_Shdr *sechdrs; | ||
795 | int entry_sidx = -1; | ||
796 | int i; | ||
733 | 797 | ||
734 | /* | ||
735 | * We can not modify sechdrs_c[] and its fields. It is read only. | ||
736 | * Copy it over to a local copy where one can store some temporary | ||
737 | * data and free it at the end. We need to modify ->sh_addr and | ||
738 | * ->sh_offset fields to keep track of permanent and temporary | ||
739 | * locations of sections. | ||
740 | */ | ||
741 | sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr)); | 798 | sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr)); |
742 | if (!sechdrs) | 799 | if (!sechdrs) |
743 | return -ENOMEM; | 800 | return -ENOMEM; |
744 | 801 | memcpy(sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff, | |
745 | memcpy(sechdrs, sechdrs_c, pi->ehdr->e_shnum * sizeof(Elf_Shdr)); | 802 | pi->ehdr->e_shnum * sizeof(Elf_Shdr)); |
803 | pi->sechdrs = sechdrs; | ||
746 | 804 | ||
747 | /* | 805 | /* |
748 | * We seem to have multiple copies of sections. First copy is which | 806 | * We seem to have multiple copies of sections. First copy is which |
@@ -770,7 +828,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min, | |||
770 | * Identify entry point section and make entry relative to section | 828 | * Identify entry point section and make entry relative to section |
771 | * start. | 829 | * start. |
772 | */ | 830 | */ |
773 | entry = pi->ehdr->e_entry; | 831 | kbuf->image->start = pi->ehdr->e_entry; |
774 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | 832 | for (i = 0; i < pi->ehdr->e_shnum; i++) { |
775 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | 833 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) |
776 | continue; | 834 | continue; |
@@ -783,63 +841,19 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min, | |||
783 | ((sechdrs[i].sh_addr + sechdrs[i].sh_size) > | 841 | ((sechdrs[i].sh_addr + sechdrs[i].sh_size) > |
784 | pi->ehdr->e_entry)) { | 842 | pi->ehdr->e_entry)) { |
785 | entry_sidx = i; | 843 | entry_sidx = i; |
786 | entry -= sechdrs[i].sh_addr; | 844 | kbuf->image->start -= sechdrs[i].sh_addr; |
787 | break; | 845 | break; |
788 | } | 846 | } |
789 | } | 847 | } |
790 | 848 | ||
791 | /* Determine how much memory is needed to load relocatable object. */ | ||
792 | bss_align = 1; | ||
793 | bss_sz = 0; | ||
794 | |||
795 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | ||
796 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
797 | continue; | ||
798 | |||
799 | align = sechdrs[i].sh_addralign; | ||
800 | if (sechdrs[i].sh_type != SHT_NOBITS) { | ||
801 | if (kbuf.buf_align < align) | ||
802 | kbuf.buf_align = align; | ||
803 | kbuf.bufsz = ALIGN(kbuf.bufsz, align); | ||
804 | kbuf.bufsz += sechdrs[i].sh_size; | ||
805 | } else { | ||
806 | /* bss section */ | ||
807 | if (bss_align < align) | ||
808 | bss_align = align; | ||
809 | bss_sz = ALIGN(bss_sz, align); | ||
810 | bss_sz += sechdrs[i].sh_size; | ||
811 | } | ||
812 | } | ||
813 | |||
814 | /* Determine the bss padding required to align bss properly */ | ||
815 | bss_pad = 0; | ||
816 | if (kbuf.bufsz & (bss_align - 1)) | ||
817 | bss_pad = bss_align - (kbuf.bufsz & (bss_align - 1)); | ||
818 | |||
819 | kbuf.memsz = kbuf.bufsz + bss_pad + bss_sz; | ||
820 | |||
821 | /* Allocate buffer for purgatory */ | ||
822 | kbuf.buffer = vzalloc(kbuf.bufsz); | ||
823 | if (!kbuf.buffer) { | ||
824 | ret = -ENOMEM; | ||
825 | goto out; | ||
826 | } | ||
827 | |||
828 | if (kbuf.buf_align < bss_align) | ||
829 | kbuf.buf_align = bss_align; | ||
830 | |||
831 | /* Add buffer to segment list */ | ||
832 | ret = kexec_add_buffer(&kbuf); | ||
833 | if (ret) | ||
834 | goto out; | ||
835 | pi->purgatory_load_addr = kbuf.mem; | ||
836 | |||
837 | /* Load SHF_ALLOC sections */ | 849 | /* Load SHF_ALLOC sections */ |
838 | buf_addr = kbuf.buffer; | 850 | buf_addr = kbuf->buffer; |
839 | load_addr = curr_load_addr = pi->purgatory_load_addr; | 851 | load_addr = curr_load_addr = kbuf->mem; |
840 | bss_addr = load_addr + kbuf.bufsz + bss_pad; | 852 | bss_addr = load_addr + kbuf->bufsz; |
841 | 853 | ||
842 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | 854 | for (i = 0; i < pi->ehdr->e_shnum; i++) { |
855 | unsigned long align; | ||
856 | |||
843 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | 857 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) |
844 | continue; | 858 | continue; |
845 | 859 | ||
@@ -871,24 +885,9 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min, | |||
871 | 885 | ||
872 | /* Update entry point based on load address of text section */ | 886 | /* Update entry point based on load address of text section */ |
873 | if (entry_sidx >= 0) | 887 | if (entry_sidx >= 0) |
874 | entry += sechdrs[entry_sidx].sh_addr; | 888 | kbuf->image->start += sechdrs[entry_sidx].sh_addr; |
875 | |||
876 | /* Make kernel jump to purgatory after shutdown */ | ||
877 | image->start = entry; | ||
878 | |||
879 | /* Used later to get/set symbol values */ | ||
880 | pi->sechdrs = sechdrs; | ||
881 | 889 | ||
882 | /* | 890 | return 0; |
883 | * Used later to identify which section is purgatory and skip it | ||
884 | * from checksumming. | ||
885 | */ | ||
886 | pi->purgatory_buf = kbuf.buffer; | ||
887 | return ret; | ||
888 | out: | ||
889 | vfree(sechdrs); | ||
890 | vfree(kbuf.buffer); | ||
891 | return ret; | ||
892 | } | 891 | } |
893 | 892 | ||
894 | static int kexec_apply_relocations(struct kimage *image) | 893 | static int kexec_apply_relocations(struct kimage *image) |
@@ -958,16 +957,23 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min, | |||
958 | { | 957 | { |
959 | struct purgatory_info *pi = &image->purgatory_info; | 958 | struct purgatory_info *pi = &image->purgatory_info; |
960 | int ret; | 959 | int ret; |
960 | struct kexec_buf kbuf = { .image = image, .bufsz = 0, .buf_align = 1, | ||
961 | .buf_min = min, .buf_max = max, | ||
962 | .top_down = top_down }; | ||
961 | 963 | ||
962 | if (kexec_purgatory_size <= 0) | 964 | if (kexec_purgatory_size <= 0) |
963 | return -EINVAL; | 965 | return -EINVAL; |
964 | 966 | ||
965 | pi->ehdr = (const Elf_Ehdr *)kexec_purgatory; | 967 | pi->ehdr = (const Elf_Ehdr *)kexec_purgatory; |
966 | 968 | ||
967 | ret = __kexec_load_purgatory(image, min, max, top_down); | 969 | ret = kexec_purgatory_setup_kbuf(pi, &kbuf); |
968 | if (ret) | 970 | if (ret) |
969 | return ret; | 971 | return ret; |
970 | 972 | ||
973 | ret = kexec_purgatory_setup_sechdrs(pi, &kbuf); | ||
974 | if (ret) | ||
975 | goto out_free_kbuf; | ||
976 | |||
971 | ret = kexec_apply_relocations(image); | 977 | ret = kexec_apply_relocations(image); |
972 | if (ret) | 978 | if (ret) |
973 | goto out; | 979 | goto out; |
@@ -977,7 +983,7 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min, | |||
977 | out: | 983 | out: |
978 | vfree(pi->sechdrs); | 984 | vfree(pi->sechdrs); |
979 | pi->sechdrs = NULL; | 985 | pi->sechdrs = NULL; |
980 | 986 | out_free_kbuf: | |
981 | vfree(pi->purgatory_buf); | 987 | vfree(pi->purgatory_buf); |
982 | pi->purgatory_buf = NULL; | 988 | pi->purgatory_buf = NULL; |
983 | return ret; | 989 | return ret; |