Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2020 Arm Limited
0004  *
0005  * Based on arch/arm64/kernel/machine_kexec_file.c:
0006  *  Copyright (C) 2018 Linaro Limited
0007  *
0008  * And arch/powerpc/kexec/file_load.c:
0009  *  Copyright (C) 2016  IBM Corporation
0010  */
0011 
0012 #include <linux/ima.h>
0013 #include <linux/kernel.h>
0014 #include <linux/kexec.h>
0015 #include <linux/memblock.h>
0016 #include <linux/libfdt.h>
0017 #include <linux/of.h>
0018 #include <linux/of_fdt.h>
0019 #include <linux/random.h>
0020 #include <linux/slab.h>
0021 #include <linux/types.h>
0022 
0023 #define RNG_SEED_SIZE       128
0024 
0025 /*
0026  * Additional space needed for the FDT buffer so that we can add initrd,
0027  * bootargs, kaslr-seed, rng-seed, useable-memory-range and elfcorehdr.
0028  */
0029 #define FDT_EXTRA_SPACE 0x1000
0030 
0031 /**
0032  * fdt_find_and_del_mem_rsv - delete memory reservation with given address and size
0033  *
0034  * @fdt:    Flattened device tree for the current kernel.
0035  * @start:  Starting address of the reserved memory.
0036  * @size:   Size of the reserved memory.
0037  *
0038  * Return: 0 on success, or negative errno on error.
0039  */
0040 static int fdt_find_and_del_mem_rsv(void *fdt, unsigned long start, unsigned long size)
0041 {
0042     int i, ret, num_rsvs = fdt_num_mem_rsv(fdt);
0043 
0044     for (i = 0; i < num_rsvs; i++) {
0045         u64 rsv_start, rsv_size;
0046 
0047         ret = fdt_get_mem_rsv(fdt, i, &rsv_start, &rsv_size);
0048         if (ret) {
0049             pr_err("Malformed device tree.\n");
0050             return -EINVAL;
0051         }
0052 
0053         if (rsv_start == start && rsv_size == size) {
0054             ret = fdt_del_mem_rsv(fdt, i);
0055             if (ret) {
0056                 pr_err("Error deleting device tree reservation.\n");
0057                 return -EINVAL;
0058             }
0059 
0060             return 0;
0061         }
0062     }
0063 
0064     return -ENOENT;
0065 }
0066 
0067 /**
0068  * get_addr_size_cells - Get address and size of root node
0069  *
0070  * @addr_cells: Return address of the root node
0071  * @size_cells: Return size of the root node
0072  *
0073  * Return: 0 on success, or negative errno on error.
0074  */
0075 static int get_addr_size_cells(int *addr_cells, int *size_cells)
0076 {
0077     struct device_node *root;
0078 
0079     root = of_find_node_by_path("/");
0080     if (!root)
0081         return -EINVAL;
0082 
0083     *addr_cells = of_n_addr_cells(root);
0084     *size_cells = of_n_size_cells(root);
0085 
0086     of_node_put(root);
0087 
0088     return 0;
0089 }
0090 
0091 /**
0092  * do_get_kexec_buffer - Get address and size of device tree property
0093  *
0094  * @prop: Device tree property
0095  * @len: Size of @prop
0096  * @addr: Return address of the node
0097  * @size: Return size of the node
0098  *
0099  * Return: 0 on success, or negative errno on error.
0100  */
0101 static int do_get_kexec_buffer(const void *prop, int len, unsigned long *addr,
0102                    size_t *size)
0103 {
0104     int ret, addr_cells, size_cells;
0105 
0106     ret = get_addr_size_cells(&addr_cells, &size_cells);
0107     if (ret)
0108         return ret;
0109 
0110     if (len < 4 * (addr_cells + size_cells))
0111         return -ENOENT;
0112 
0113     *addr = of_read_number(prop, addr_cells);
0114     *size = of_read_number(prop + 4 * addr_cells, size_cells);
0115 
0116     return 0;
0117 }
0118 
0119 #ifdef CONFIG_HAVE_IMA_KEXEC
0120 /**
0121  * ima_get_kexec_buffer - get IMA buffer from the previous kernel
0122  * @addr:   On successful return, set to point to the buffer contents.
0123  * @size:   On successful return, set to the buffer size.
0124  *
0125  * Return: 0 on success, negative errno on error.
0126  */
0127 int __init ima_get_kexec_buffer(void **addr, size_t *size)
0128 {
0129     int ret, len;
0130     unsigned long tmp_addr;
0131     unsigned long start_pfn, end_pfn;
0132     size_t tmp_size;
0133     const void *prop;
0134 
0135     prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len);
0136     if (!prop)
0137         return -ENOENT;
0138 
0139     ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size);
0140     if (ret)
0141         return ret;
0142 
0143     /* Do some sanity on the returned size for the ima-kexec buffer */
0144     if (!tmp_size)
0145         return -ENOENT;
0146 
0147     /*
0148      * Calculate the PFNs for the buffer and ensure
0149      * they are with in addressable memory.
0150      */
0151     start_pfn = PHYS_PFN(tmp_addr);
0152     end_pfn = PHYS_PFN(tmp_addr + tmp_size - 1);
0153     if (!page_is_ram(start_pfn) || !page_is_ram(end_pfn)) {
0154         pr_warn("IMA buffer at 0x%lx, size = 0x%zx beyond memory\n",
0155             tmp_addr, tmp_size);
0156         return -EINVAL;
0157     }
0158 
0159     *addr = __va(tmp_addr);
0160     *size = tmp_size;
0161 
0162     return 0;
0163 }
0164 
0165 /**
0166  * ima_free_kexec_buffer - free memory used by the IMA buffer
0167  */
0168 int __init ima_free_kexec_buffer(void)
0169 {
0170     int ret;
0171     unsigned long addr;
0172     size_t size;
0173     struct property *prop;
0174 
0175     prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL);
0176     if (!prop)
0177         return -ENOENT;
0178 
0179     ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size);
0180     if (ret)
0181         return ret;
0182 
0183     ret = of_remove_property(of_chosen, prop);
0184     if (ret)
0185         return ret;
0186 
0187     return memblock_phys_free(addr, size);
0188 }
0189 #endif
0190 
0191 /**
0192  * remove_ima_buffer - remove the IMA buffer property and reservation from @fdt
0193  *
0194  * @fdt: Flattened Device Tree to update
0195  * @chosen_node: Offset to the chosen node in the device tree
0196  *
0197  * The IMA measurement buffer is of no use to a subsequent kernel, so we always
0198  * remove it from the device tree.
0199  */
0200 static void remove_ima_buffer(void *fdt, int chosen_node)
0201 {
0202     int ret, len;
0203     unsigned long addr;
0204     size_t size;
0205     const void *prop;
0206 
0207     if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
0208         return;
0209 
0210     prop = fdt_getprop(fdt, chosen_node, "linux,ima-kexec-buffer", &len);
0211     if (!prop)
0212         return;
0213 
0214     ret = do_get_kexec_buffer(prop, len, &addr, &size);
0215     fdt_delprop(fdt, chosen_node, "linux,ima-kexec-buffer");
0216     if (ret)
0217         return;
0218 
0219     ret = fdt_find_and_del_mem_rsv(fdt, addr, size);
0220     if (!ret)
0221         pr_debug("Removed old IMA buffer reservation.\n");
0222 }
0223 
0224 #ifdef CONFIG_IMA_KEXEC
0225 /**
0226  * setup_ima_buffer - add IMA buffer information to the fdt
0227  * @image:      kexec image being loaded.
0228  * @fdt:        Flattened device tree for the next kernel.
0229  * @chosen_node:    Offset to the chosen node.
0230  *
0231  * Return: 0 on success, or negative errno on error.
0232  */
0233 static int setup_ima_buffer(const struct kimage *image, void *fdt,
0234                 int chosen_node)
0235 {
0236     int ret;
0237 
0238     if (!image->ima_buffer_size)
0239         return 0;
0240 
0241     ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
0242                        "linux,ima-kexec-buffer",
0243                        image->ima_buffer_addr,
0244                        image->ima_buffer_size);
0245     if (ret < 0)
0246         return -EINVAL;
0247 
0248     ret = fdt_add_mem_rsv(fdt, image->ima_buffer_addr,
0249                   image->ima_buffer_size);
0250     if (ret)
0251         return -EINVAL;
0252 
0253     pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n",
0254          image->ima_buffer_addr, image->ima_buffer_size);
0255 
0256     return 0;
0257 }
0258 #else /* CONFIG_IMA_KEXEC */
0259 static inline int setup_ima_buffer(const struct kimage *image, void *fdt,
0260                    int chosen_node)
0261 {
0262     return 0;
0263 }
0264 #endif /* CONFIG_IMA_KEXEC */
0265 
0266 /*
0267  * of_kexec_alloc_and_setup_fdt - Alloc and setup a new Flattened Device Tree
0268  *
0269  * @image:      kexec image being loaded.
0270  * @initrd_load_addr:   Address where the next initrd will be loaded.
0271  * @initrd_len:     Size of the next initrd, or 0 if there will be none.
0272  * @cmdline:        Command line for the next kernel, or NULL if there will
0273  *          be none.
0274  * @extra_fdt_size: Additional size for the new FDT buffer.
0275  *
0276  * Return: fdt on success, or NULL errno on error.
0277  */
0278 void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
0279                    unsigned long initrd_load_addr,
0280                    unsigned long initrd_len,
0281                    const char *cmdline, size_t extra_fdt_size)
0282 {
0283     void *fdt;
0284     int ret, chosen_node;
0285     const void *prop;
0286     size_t fdt_size;
0287 
0288     fdt_size = fdt_totalsize(initial_boot_params) +
0289            (cmdline ? strlen(cmdline) : 0) +
0290            FDT_EXTRA_SPACE +
0291            extra_fdt_size;
0292     fdt = kvmalloc(fdt_size, GFP_KERNEL);
0293     if (!fdt)
0294         return NULL;
0295 
0296     ret = fdt_open_into(initial_boot_params, fdt, fdt_size);
0297     if (ret < 0) {
0298         pr_err("Error %d setting up the new device tree.\n", ret);
0299         goto out;
0300     }
0301 
0302     /* Remove memory reservation for the current device tree. */
0303     ret = fdt_find_and_del_mem_rsv(fdt, __pa(initial_boot_params),
0304                        fdt_totalsize(initial_boot_params));
0305     if (ret == -EINVAL) {
0306         pr_err("Error removing memory reservation.\n");
0307         goto out;
0308     }
0309 
0310     chosen_node = fdt_path_offset(fdt, "/chosen");
0311     if (chosen_node == -FDT_ERR_NOTFOUND)
0312         chosen_node = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
0313                           "chosen");
0314     if (chosen_node < 0) {
0315         ret = chosen_node;
0316         goto out;
0317     }
0318 
0319     ret = fdt_delprop(fdt, chosen_node, "linux,elfcorehdr");
0320     if (ret && ret != -FDT_ERR_NOTFOUND)
0321         goto out;
0322     ret = fdt_delprop(fdt, chosen_node, "linux,usable-memory-range");
0323     if (ret && ret != -FDT_ERR_NOTFOUND)
0324         goto out;
0325 
0326     /* Did we boot using an initrd? */
0327     prop = fdt_getprop(fdt, chosen_node, "linux,initrd-start", NULL);
0328     if (prop) {
0329         u64 tmp_start, tmp_end, tmp_size;
0330 
0331         tmp_start = fdt64_to_cpu(*((const fdt64_t *) prop));
0332 
0333         prop = fdt_getprop(fdt, chosen_node, "linux,initrd-end", NULL);
0334         if (!prop) {
0335             ret = -EINVAL;
0336             goto out;
0337         }
0338 
0339         tmp_end = fdt64_to_cpu(*((const fdt64_t *) prop));
0340 
0341         /*
0342          * kexec reserves exact initrd size, while firmware may
0343          * reserve a multiple of PAGE_SIZE, so check for both.
0344          */
0345         tmp_size = tmp_end - tmp_start;
0346         ret = fdt_find_and_del_mem_rsv(fdt, tmp_start, tmp_size);
0347         if (ret == -ENOENT)
0348             ret = fdt_find_and_del_mem_rsv(fdt, tmp_start,
0349                                round_up(tmp_size, PAGE_SIZE));
0350         if (ret == -EINVAL)
0351             goto out;
0352     }
0353 
0354     /* add initrd-* */
0355     if (initrd_load_addr) {
0356         ret = fdt_setprop_u64(fdt, chosen_node, "linux,initrd-start",
0357                       initrd_load_addr);
0358         if (ret)
0359             goto out;
0360 
0361         ret = fdt_setprop_u64(fdt, chosen_node, "linux,initrd-end",
0362                       initrd_load_addr + initrd_len);
0363         if (ret)
0364             goto out;
0365 
0366         ret = fdt_add_mem_rsv(fdt, initrd_load_addr, initrd_len);
0367         if (ret)
0368             goto out;
0369 
0370     } else {
0371         ret = fdt_delprop(fdt, chosen_node, "linux,initrd-start");
0372         if (ret && (ret != -FDT_ERR_NOTFOUND))
0373             goto out;
0374 
0375         ret = fdt_delprop(fdt, chosen_node, "linux,initrd-end");
0376         if (ret && (ret != -FDT_ERR_NOTFOUND))
0377             goto out;
0378     }
0379 
0380     if (image->type == KEXEC_TYPE_CRASH) {
0381         /* add linux,elfcorehdr */
0382         ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
0383                 "linux,elfcorehdr", image->elf_load_addr,
0384                 image->elf_headers_sz);
0385         if (ret)
0386             goto out;
0387 
0388         /*
0389          * Avoid elfcorehdr from being stomped on in kdump kernel by
0390          * setting up memory reserve map.
0391          */
0392         ret = fdt_add_mem_rsv(fdt, image->elf_load_addr,
0393                       image->elf_headers_sz);
0394         if (ret)
0395             goto out;
0396 
0397         /* add linux,usable-memory-range */
0398         ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
0399                 "linux,usable-memory-range", crashk_res.start,
0400                 crashk_res.end - crashk_res.start + 1);
0401         if (ret)
0402             goto out;
0403 
0404         if (crashk_low_res.end) {
0405             ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
0406                     "linux,usable-memory-range",
0407                     crashk_low_res.start,
0408                     crashk_low_res.end - crashk_low_res.start + 1);
0409             if (ret)
0410                 goto out;
0411         }
0412     }
0413 
0414     /* add bootargs */
0415     if (cmdline) {
0416         ret = fdt_setprop_string(fdt, chosen_node, "bootargs", cmdline);
0417         if (ret)
0418             goto out;
0419     } else {
0420         ret = fdt_delprop(fdt, chosen_node, "bootargs");
0421         if (ret && (ret != -FDT_ERR_NOTFOUND))
0422             goto out;
0423     }
0424 
0425     /* add kaslr-seed */
0426     ret = fdt_delprop(fdt, chosen_node, "kaslr-seed");
0427     if (ret == -FDT_ERR_NOTFOUND)
0428         ret = 0;
0429     else if (ret)
0430         goto out;
0431 
0432     if (rng_is_initialized()) {
0433         u64 seed = get_random_u64();
0434 
0435         ret = fdt_setprop_u64(fdt, chosen_node, "kaslr-seed", seed);
0436         if (ret)
0437             goto out;
0438     } else {
0439         pr_notice("RNG is not initialised: omitting \"%s\" property\n",
0440               "kaslr-seed");
0441     }
0442 
0443     /* add rng-seed */
0444     if (rng_is_initialized()) {
0445         void *rng_seed;
0446 
0447         ret = fdt_setprop_placeholder(fdt, chosen_node, "rng-seed",
0448                 RNG_SEED_SIZE, &rng_seed);
0449         if (ret)
0450             goto out;
0451         get_random_bytes(rng_seed, RNG_SEED_SIZE);
0452     } else {
0453         pr_notice("RNG is not initialised: omitting \"%s\" property\n",
0454               "rng-seed");
0455     }
0456 
0457     ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
0458     if (ret)
0459         goto out;
0460 
0461     remove_ima_buffer(fdt, chosen_node);
0462     ret = setup_ima_buffer(image, fdt, fdt_path_offset(fdt, "/chosen"));
0463 
0464 out:
0465     if (ret) {
0466         kvfree(fdt);
0467         fdt = NULL;
0468     }
0469 
0470     return fdt;
0471 }