linux/arch/powerpc/kernel/ima_kexec.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 IBM Corporation
   4 *
   5 * Authors:
   6 * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
   7 */
   8
   9#include <linux/slab.h>
  10#include <linux/kexec.h>
  11#include <linux/of.h>
  12#include <linux/memblock.h>
  13#include <linux/libfdt.h>
  14
  15static int get_addr_size_cells(int *addr_cells, int *size_cells)
  16{
  17        struct device_node *root;
  18
  19        root = of_find_node_by_path("/");
  20        if (!root)
  21                return -EINVAL;
  22
  23        *addr_cells = of_n_addr_cells(root);
  24        *size_cells = of_n_size_cells(root);
  25
  26        of_node_put(root);
  27
  28        return 0;
  29}
  30
  31static int do_get_kexec_buffer(const void *prop, int len, unsigned long *addr,
  32                               size_t *size)
  33{
  34        int ret, addr_cells, size_cells;
  35
  36        ret = get_addr_size_cells(&addr_cells, &size_cells);
  37        if (ret)
  38                return ret;
  39
  40        if (len < 4 * (addr_cells + size_cells))
  41                return -ENOENT;
  42
  43        *addr = of_read_number(prop, addr_cells);
  44        *size = of_read_number(prop + 4 * addr_cells, size_cells);
  45
  46        return 0;
  47}
  48
  49/**
  50 * ima_get_kexec_buffer - get IMA buffer from the previous kernel
  51 * @addr:       On successful return, set to point to the buffer contents.
  52 * @size:       On successful return, set to the buffer size.
  53 *
  54 * Return: 0 on success, negative errno on error.
  55 */
  56int ima_get_kexec_buffer(void **addr, size_t *size)
  57{
  58        int ret, len;
  59        unsigned long tmp_addr;
  60        size_t tmp_size;
  61        const void *prop;
  62
  63        prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len);
  64        if (!prop)
  65                return -ENOENT;
  66
  67        ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size);
  68        if (ret)
  69                return ret;
  70
  71        *addr = __va(tmp_addr);
  72        *size = tmp_size;
  73
  74        return 0;
  75}
  76
  77/**
  78 * ima_free_kexec_buffer - free memory used by the IMA buffer
  79 */
  80int ima_free_kexec_buffer(void)
  81{
  82        int ret;
  83        unsigned long addr;
  84        size_t size;
  85        struct property *prop;
  86
  87        prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL);
  88        if (!prop)
  89                return -ENOENT;
  90
  91        ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size);
  92        if (ret)
  93                return ret;
  94
  95        ret = of_remove_property(of_chosen, prop);
  96        if (ret)
  97                return ret;
  98
  99        return memblock_free(addr, size);
 100
 101}
 102
 103/**
 104 * remove_ima_buffer - remove the IMA buffer property and reservation from @fdt
 105 *
 106 * The IMA measurement buffer is of no use to a subsequent kernel, so we always
 107 * remove it from the device tree.
 108 */
 109void remove_ima_buffer(void *fdt, int chosen_node)
 110{
 111        int ret, len;
 112        unsigned long addr;
 113        size_t size;
 114        const void *prop;
 115
 116        prop = fdt_getprop(fdt, chosen_node, "linux,ima-kexec-buffer", &len);
 117        if (!prop)
 118                return;
 119
 120        ret = do_get_kexec_buffer(prop, len, &addr, &size);
 121        fdt_delprop(fdt, chosen_node, "linux,ima-kexec-buffer");
 122        if (ret)
 123                return;
 124
 125        ret = delete_fdt_mem_rsv(fdt, addr, size);
 126        if (!ret)
 127                pr_debug("Removed old IMA buffer reservation.\n");
 128}
 129
 130#ifdef CONFIG_IMA_KEXEC
 131/**
 132 * arch_ima_add_kexec_buffer - do arch-specific steps to add the IMA buffer
 133 *
 134 * Architectures should use this function to pass on the IMA buffer
 135 * information to the next kernel.
 136 *
 137 * Return: 0 on success, negative errno on error.
 138 */
 139int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr,
 140                              size_t size)
 141{
 142        image->arch.ima_buffer_addr = load_addr;
 143        image->arch.ima_buffer_size = size;
 144
 145        return 0;
 146}
 147
 148static int write_number(void *p, u64 value, int cells)
 149{
 150        if (cells == 1) {
 151                u32 tmp;
 152
 153                if (value > U32_MAX)
 154                        return -EINVAL;
 155
 156                tmp = cpu_to_be32(value);
 157                memcpy(p, &tmp, sizeof(tmp));
 158        } else if (cells == 2) {
 159                u64 tmp;
 160
 161                tmp = cpu_to_be64(value);
 162                memcpy(p, &tmp, sizeof(tmp));
 163        } else
 164                return -EINVAL;
 165
 166        return 0;
 167}
 168
 169/**
 170 * setup_ima_buffer - add IMA buffer information to the fdt
 171 * @image:              kexec image being loaded.
 172 * @fdt:                Flattened device tree for the next kernel.
 173 * @chosen_node:        Offset to the chosen node.
 174 *
 175 * Return: 0 on success, or negative errno on error.
 176 */
 177int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node)
 178{
 179        int ret, addr_cells, size_cells, entry_size;
 180        u8 value[16];
 181
 182        remove_ima_buffer(fdt, chosen_node);
 183        if (!image->arch.ima_buffer_size)
 184                return 0;
 185
 186        ret = get_addr_size_cells(&addr_cells, &size_cells);
 187        if (ret)
 188                return ret;
 189
 190        entry_size = 4 * (addr_cells + size_cells);
 191
 192        if (entry_size > sizeof(value))
 193                return -EINVAL;
 194
 195        ret = write_number(value, image->arch.ima_buffer_addr, addr_cells);
 196        if (ret)
 197                return ret;
 198
 199        ret = write_number(value + 4 * addr_cells, image->arch.ima_buffer_size,
 200                           size_cells);
 201        if (ret)
 202                return ret;
 203
 204        ret = fdt_setprop(fdt, chosen_node, "linux,ima-kexec-buffer", value,
 205                          entry_size);
 206        if (ret < 0)
 207                return -EINVAL;
 208
 209        ret = fdt_add_mem_rsv(fdt, image->arch.ima_buffer_addr,
 210                              image->arch.ima_buffer_size);
 211        if (ret)
 212                return -EINVAL;
 213
 214        pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n",
 215                 image->arch.ima_buffer_addr, image->arch.ima_buffer_size);
 216
 217        return 0;
 218}
 219#endif /* CONFIG_IMA_KEXEC */
 220