Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * s390 code for kexec_file_load system call
0004  *
0005  * Copyright IBM Corp. 2018
0006  *
0007  * Author(s): Philipp Rudo <prudo@linux.vnet.ibm.com>
0008  */
0009 
0010 #define pr_fmt(fmt) "kexec: " fmt
0011 
0012 #include <linux/elf.h>
0013 #include <linux/errno.h>
0014 #include <linux/kexec.h>
0015 #include <linux/module_signature.h>
0016 #include <linux/verification.h>
0017 #include <linux/vmalloc.h>
0018 #include <asm/boot_data.h>
0019 #include <asm/ipl.h>
0020 #include <asm/setup.h>
0021 
0022 const struct kexec_file_ops * const kexec_file_loaders[] = {
0023     &s390_kexec_elf_ops,
0024     &s390_kexec_image_ops,
0025     NULL,
0026 };
0027 
0028 #ifdef CONFIG_KEXEC_SIG
0029 int s390_verify_sig(const char *kernel, unsigned long kernel_len)
0030 {
0031     const unsigned long marker_len = sizeof(MODULE_SIG_STRING) - 1;
0032     struct module_signature *ms;
0033     unsigned long sig_len;
0034     int ret;
0035 
0036     /* Skip signature verification when not secure IPLed. */
0037     if (!ipl_secure_flag)
0038         return 0;
0039 
0040     if (marker_len > kernel_len)
0041         return -EKEYREJECTED;
0042 
0043     if (memcmp(kernel + kernel_len - marker_len, MODULE_SIG_STRING,
0044            marker_len))
0045         return -EKEYREJECTED;
0046     kernel_len -= marker_len;
0047 
0048     ms = (void *)kernel + kernel_len - sizeof(*ms);
0049     kernel_len -= sizeof(*ms);
0050 
0051     sig_len = be32_to_cpu(ms->sig_len);
0052     if (sig_len >= kernel_len)
0053         return -EKEYREJECTED;
0054     kernel_len -= sig_len;
0055 
0056     if (ms->id_type != PKEY_ID_PKCS7)
0057         return -EKEYREJECTED;
0058 
0059     if (ms->algo != 0 ||
0060         ms->hash != 0 ||
0061         ms->signer_len != 0 ||
0062         ms->key_id_len != 0 ||
0063         ms->__pad[0] != 0 ||
0064         ms->__pad[1] != 0 ||
0065         ms->__pad[2] != 0) {
0066         return -EBADMSG;
0067     }
0068 
0069     ret = verify_pkcs7_signature(kernel, kernel_len,
0070                      kernel + kernel_len, sig_len,
0071                      VERIFY_USE_SECONDARY_KEYRING,
0072                      VERIFYING_MODULE_SIGNATURE,
0073                      NULL, NULL);
0074     if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING))
0075         ret = verify_pkcs7_signature(kernel, kernel_len,
0076                          kernel + kernel_len, sig_len,
0077                          VERIFY_USE_PLATFORM_KEYRING,
0078                          VERIFYING_MODULE_SIGNATURE,
0079                          NULL, NULL);
0080     return ret;
0081 }
0082 #endif /* CONFIG_KEXEC_SIG */
0083 
0084 static int kexec_file_update_purgatory(struct kimage *image,
0085                        struct s390_load_data *data)
0086 {
0087     u64 entry, type;
0088     int ret;
0089 
0090     if (image->type == KEXEC_TYPE_CRASH) {
0091         entry = STARTUP_KDUMP_OFFSET;
0092         type = KEXEC_TYPE_CRASH;
0093     } else {
0094         entry = STARTUP_NORMAL_OFFSET;
0095         type = KEXEC_TYPE_DEFAULT;
0096     }
0097 
0098     ret = kexec_purgatory_get_set_symbol(image, "kernel_entry", &entry,
0099                          sizeof(entry), false);
0100     if (ret)
0101         return ret;
0102 
0103     ret = kexec_purgatory_get_set_symbol(image, "kernel_type", &type,
0104                          sizeof(type), false);
0105     if (ret)
0106         return ret;
0107 
0108     if (image->type == KEXEC_TYPE_CRASH) {
0109         u64 crash_size;
0110 
0111         ret = kexec_purgatory_get_set_symbol(image, "crash_start",
0112                              &crashk_res.start,
0113                              sizeof(crashk_res.start),
0114                              false);
0115         if (ret)
0116             return ret;
0117 
0118         crash_size = crashk_res.end - crashk_res.start + 1;
0119         ret = kexec_purgatory_get_set_symbol(image, "crash_size",
0120                              &crash_size,
0121                              sizeof(crash_size),
0122                              false);
0123     }
0124     return ret;
0125 }
0126 
0127 static int kexec_file_add_purgatory(struct kimage *image,
0128                     struct s390_load_data *data)
0129 {
0130     struct kexec_buf buf;
0131     int ret;
0132 
0133     buf.image = image;
0134 
0135     data->memsz = ALIGN(data->memsz, PAGE_SIZE);
0136     buf.mem = data->memsz;
0137     if (image->type == KEXEC_TYPE_CRASH)
0138         buf.mem += crashk_res.start;
0139 
0140     ret = kexec_load_purgatory(image, &buf);
0141     if (ret)
0142         return ret;
0143     data->memsz += buf.memsz;
0144 
0145     return kexec_file_update_purgatory(image, data);
0146 }
0147 
0148 static int kexec_file_add_initrd(struct kimage *image,
0149                  struct s390_load_data *data)
0150 {
0151     struct kexec_buf buf;
0152     int ret;
0153 
0154     buf.image = image;
0155 
0156     buf.buffer = image->initrd_buf;
0157     buf.bufsz = image->initrd_buf_len;
0158 
0159     data->memsz = ALIGN(data->memsz, PAGE_SIZE);
0160     buf.mem = data->memsz;
0161     if (image->type == KEXEC_TYPE_CRASH)
0162         buf.mem += crashk_res.start;
0163     buf.memsz = buf.bufsz;
0164 
0165     data->parm->initrd_start = data->memsz;
0166     data->parm->initrd_size = buf.memsz;
0167     data->memsz += buf.memsz;
0168 
0169     ret = kexec_add_buffer(&buf);
0170     if (ret)
0171         return ret;
0172 
0173     return ipl_report_add_component(data->report, &buf, 0, 0);
0174 }
0175 
0176 static int kexec_file_add_ipl_report(struct kimage *image,
0177                      struct s390_load_data *data)
0178 {
0179     __u32 *lc_ipl_parmblock_ptr;
0180     unsigned int len, ncerts;
0181     struct kexec_buf buf;
0182     unsigned long addr;
0183     void *ptr, *end;
0184     int ret;
0185 
0186     buf.image = image;
0187 
0188     data->memsz = ALIGN(data->memsz, PAGE_SIZE);
0189     buf.mem = data->memsz;
0190     if (image->type == KEXEC_TYPE_CRASH)
0191         buf.mem += crashk_res.start;
0192 
0193     ptr = (void *)ipl_cert_list_addr;
0194     end = ptr + ipl_cert_list_size;
0195     ncerts = 0;
0196     while (ptr < end) {
0197         ncerts++;
0198         len = *(unsigned int *)ptr;
0199         ptr += sizeof(len);
0200         ptr += len;
0201     }
0202 
0203     addr = data->memsz + data->report->size;
0204     addr += ncerts * sizeof(struct ipl_rb_certificate_entry);
0205     ptr = (void *)ipl_cert_list_addr;
0206     while (ptr < end) {
0207         len = *(unsigned int *)ptr;
0208         ptr += sizeof(len);
0209         ipl_report_add_certificate(data->report, ptr, addr, len);
0210         addr += len;
0211         ptr += len;
0212     }
0213 
0214     ret = -ENOMEM;
0215     buf.buffer = ipl_report_finish(data->report);
0216     if (!buf.buffer)
0217         goto out;
0218     buf.bufsz = data->report->size;
0219     buf.memsz = buf.bufsz;
0220     image->arch.ipl_buf = buf.buffer;
0221 
0222     data->memsz += buf.memsz;
0223 
0224     lc_ipl_parmblock_ptr =
0225         data->kernel_buf + offsetof(struct lowcore, ipl_parmblock_ptr);
0226     *lc_ipl_parmblock_ptr = (__u32)buf.mem;
0227 
0228     ret = kexec_add_buffer(&buf);
0229 out:
0230     return ret;
0231 }
0232 
0233 void *kexec_file_add_components(struct kimage *image,
0234                 int (*add_kernel)(struct kimage *image,
0235                           struct s390_load_data *data))
0236 {
0237     unsigned long max_command_line_size = LEGACY_COMMAND_LINE_SIZE;
0238     struct s390_load_data data = {0};
0239     unsigned long minsize;
0240     int ret;
0241 
0242     data.report = ipl_report_init(&ipl_block);
0243     if (IS_ERR(data.report))
0244         return data.report;
0245 
0246     ret = add_kernel(image, &data);
0247     if (ret)
0248         goto out;
0249 
0250     ret = -EINVAL;
0251     minsize = PARMAREA + offsetof(struct parmarea, command_line);
0252     if (image->kernel_buf_len < minsize)
0253         goto out;
0254 
0255     if (data.parm->max_command_line_size)
0256         max_command_line_size = data.parm->max_command_line_size;
0257 
0258     if (minsize + max_command_line_size < minsize)
0259         goto out;
0260 
0261     if (image->kernel_buf_len < minsize + max_command_line_size)
0262         goto out;
0263 
0264     if (image->cmdline_buf_len >= max_command_line_size)
0265         goto out;
0266 
0267     memcpy(data.parm->command_line, image->cmdline_buf,
0268            image->cmdline_buf_len);
0269 
0270     if (image->type == KEXEC_TYPE_CRASH) {
0271         data.parm->oldmem_base = crashk_res.start;
0272         data.parm->oldmem_size = crashk_res.end - crashk_res.start + 1;
0273     }
0274 
0275     if (image->initrd_buf) {
0276         ret = kexec_file_add_initrd(image, &data);
0277         if (ret)
0278             goto out;
0279     }
0280 
0281     ret = kexec_file_add_purgatory(image, &data);
0282     if (ret)
0283         goto out;
0284 
0285     if (data.kernel_mem == 0) {
0286         unsigned long restart_psw =  0x0008000080000000UL;
0287         restart_psw += image->start;
0288         memcpy(data.kernel_buf, &restart_psw, sizeof(restart_psw));
0289         image->start = 0;
0290     }
0291 
0292     ret = kexec_file_add_ipl_report(image, &data);
0293 out:
0294     ipl_report_free(data.report);
0295     return ERR_PTR(ret);
0296 }
0297 
0298 int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
0299                      Elf_Shdr *section,
0300                      const Elf_Shdr *relsec,
0301                      const Elf_Shdr *symtab)
0302 {
0303     const char *strtab, *name, *shstrtab;
0304     const Elf_Shdr *sechdrs;
0305     Elf_Rela *relas;
0306     int i, r_type;
0307     int ret;
0308 
0309     /* String & section header string table */
0310     sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
0311     strtab = (char *)pi->ehdr + sechdrs[symtab->sh_link].sh_offset;
0312     shstrtab = (char *)pi->ehdr + sechdrs[pi->ehdr->e_shstrndx].sh_offset;
0313 
0314     relas = (void *)pi->ehdr + relsec->sh_offset;
0315 
0316     for (i = 0; i < relsec->sh_size / sizeof(*relas); i++) {
0317         const Elf_Sym *sym; /* symbol to relocate */
0318         unsigned long addr; /* final location after relocation */
0319         unsigned long val;  /* relocated symbol value */
0320         void *loc;      /* tmp location to modify */
0321 
0322         sym = (void *)pi->ehdr + symtab->sh_offset;
0323         sym += ELF64_R_SYM(relas[i].r_info);
0324 
0325         if (sym->st_name)
0326             name = strtab + sym->st_name;
0327         else
0328             name = shstrtab + sechdrs[sym->st_shndx].sh_name;
0329 
0330         if (sym->st_shndx == SHN_UNDEF) {
0331             pr_err("Undefined symbol: %s\n", name);
0332             return -ENOEXEC;
0333         }
0334 
0335         if (sym->st_shndx == SHN_COMMON) {
0336             pr_err("symbol '%s' in common section\n", name);
0337             return -ENOEXEC;
0338         }
0339 
0340         if (sym->st_shndx >= pi->ehdr->e_shnum &&
0341             sym->st_shndx != SHN_ABS) {
0342             pr_err("Invalid section %d for symbol %s\n",
0343                    sym->st_shndx, name);
0344             return -ENOEXEC;
0345         }
0346 
0347         loc = pi->purgatory_buf;
0348         loc += section->sh_offset;
0349         loc += relas[i].r_offset;
0350 
0351         val = sym->st_value;
0352         if (sym->st_shndx != SHN_ABS)
0353             val += pi->sechdrs[sym->st_shndx].sh_addr;
0354         val += relas[i].r_addend;
0355 
0356         addr = section->sh_addr + relas[i].r_offset;
0357 
0358         r_type = ELF64_R_TYPE(relas[i].r_info);
0359 
0360         if (r_type == R_390_PLT32DBL)
0361             r_type = R_390_PC32DBL;
0362 
0363         ret = arch_kexec_do_relocs(r_type, loc, val, addr);
0364         if (ret) {
0365             pr_err("Unknown rela relocation: %d\n", r_type);
0366             return -ENOEXEC;
0367         }
0368     }
0369     return 0;
0370 }
0371 
0372 int arch_kimage_file_post_load_cleanup(struct kimage *image)
0373 {
0374     vfree(image->arch.ipl_buf);
0375     image->arch.ipl_buf = NULL;
0376 
0377     return kexec_image_post_load_cleanup_default(image);
0378 }