[Skiboot] [PATCH v2 4/7] skiboot: Fetch the IMA dtb

Oliver O'Halloran oohall at gmail.com
Tue Nov 22 13:15:57 AEDT 2016


On Fri, Nov 18, 2016 at 1:10 PM, Hemant Kumar <hemant at linux.vnet.ibm.com> wrote:
> The sub-partition that we obtain from the IMA_CATALOG partition is a
> compressed device tree binary. We uncompress it using the libxz's
> functions. After uncompressing it, we link the device tree binary to the
> system's device tree. The kernel can now access the device tree and get
> the IMA PMUs and their events' information.
>
> Not all the IMA PMUs listed in the device tree may be available. This is
> indicated by ima availability vector (which is a part of the IMA control
> block structure). We need to check this vector and make sure to remove
> the IMA device nodes which are unavailable.
>
> Signed-off-by: Hemant Kumar <hemant at linux.vnet.ibm.com>
> ---
> Changelog:
> v1 -> v2:
>  - Added a new function disable_unavailable_units() to check the IMA
>    availability vector and remove the nest PMU nodes from the device
>    tree.
>
>  hw/ima.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 122 insertions(+)
>
> diff --git a/hw/ima.c b/hw/ima.c
> index 7f9184f..1fb07fd 100644
> --- a/hw/ima.c
> +++ b/hw/ima.c
> @@ -18,6 +18,66 @@
>  #include <xscom.h>
>  #include <ima.h>
>  #include <chip.h>
> +#include <libxz/xz.h>
> +#include <nest_ima.h>
> +
> +/*
> + * Decompresses the blob obtained from the IMA catalog sub-partition
> + * in "buf" of size "size", assigns the uncompressed device tree
> + * binary to "fdt" and returns.
> + * Returns 0 on success and -1 on error.
> + */
> +static int decompress_subpartition(char *buf, size_t size, void **fdt)
> +{
> +       struct xz_dec *s;
> +       struct xz_buf b;
> +       void *data;
> +       int ret = 0;
> +
> +       /* Initialize the xz library first */
> +       xz_crc32_init();
> +       s = xz_dec_init(XZ_SINGLE, 0);
> +       if (s == NULL)
> +       {
> +               prerror("IMA: initialization error for xz\n");
> +               return -1;
> +       }
> +
> +       /* Allocate memory for the uncompressed data */
> +       data = malloc(IMA_DTB_SIZE);
> +       if (!data) {
> +               prerror("IMA: memory allocation error\n");
> +               ret = -1;
> +               goto err;
> +       }
> +
> +       /*
> +        * Source address : buf
> +        * Source size : size
> +        * Destination address : data
> +        * Destination size : IMA_DTB_SIZE
> +        */
> +       b.in = buf;
> +       b.in_pos = 0;
> +       b.in_size = size;
> +       b.out = data;
> +       b.out_pos = 0;
> +       b.out_size = IMA_DTB_SIZE;
> +
> +       /* Start decompressing */
> +       ret = xz_dec_run(s, &b);
> +       if (ret != XZ_STREAM_END) {
> +               prerror("IMA: failed to decompress subpartition\n");
> +               free(data);
> +               ret = -1;
> +       }
> +       *fdt = data;
> +
> +err:
> +       /* Clean up memory */
> +       xz_dec_end(s);
> +       return ret;
> +}
>
>  static bool is_power8(void)
>  {
> @@ -32,8 +92,46 @@ static bool is_power8(void)
>  }
>
>  /*
> + * Remove the PMU device nodes from the system's dt, if they are not
> + * available in the hardware. The availability is described by the
> + * control block's ima_chip_avl_vector.
> + * Each bit represents a device unit. If the device is available, then
> + * the bit is set else its unset.
> + */
> +static int disable_unavailable_units(void)
> +{
> +       uint64_t cb_loc, avl_vec;
> +       struct ima_chip_cb *cb;
> +       struct proc_chip *chip;
> +       struct dt_node *target;
> +       int i;
> +
> +       chip = get_chip(this_cpu()->chip_id);
> +
> +       /* Fetch the IMA control block structure */
> +       cb_loc = chip->homer_base + CB_STRUCT_OFFSET;
> +       cb = (struct ima_chip_cb *)cb_loc;
> +
> +       avl_vec = cb->ima_chip_avl_vector;

Should we be using be64_to_cpu(), etc when reading from the control block?

> +       for (i = 0; i < 48; i++) {

48 shouldn't be hardcoded

> +               if (!(PPC_BITMASK(i, i) & avl_vec)) {
> +                       /* Check if the device node exists */
> +                       target = dt_find_by_name(dt_root, nest_pmus[i]);
> +                       if (!target)
> +                               continue;
> +                       /* Remove the device node */
> +                       dt_free(target);
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/*
>   * Fetch the IMA Catalog partition and find the appropriate sub-partition
>   * based on the platform's PVR.
> + * Decompress the sub-partition and link the ima device tree to the
> + * existing device tree.
>   */
>  void ima_init(void)
>  {
> @@ -41,6 +139,8 @@ void ima_init(void)
>         size_t size = IMA_DTB_SIZE;
>         uint32_t pvr = mfspr(SPR_PVR);
>         int ret;
> +       struct dt_node *dev;
> +       void *fdt = NULL;
>
>         /* Disable this for power 8 */
>         if (is_power8())
> @@ -64,6 +164,28 @@ void ima_init(void)
>                 goto err;
>         }
>
> +       /* Decompress the subpartition now */
> +       ret = decompress_subpartition(buf, size, &fdt);
> +       if (ret < 0) {
> +               goto err;
> +       }
> +
> +       /* Create a device tree entry for ima counters */
> +       dev = dt_new(dt_root, "ima-counters");
> +       if (!dev) {
> +               prerror("IMA: Unable to create device node\n");
> +               goto err;
> +       }
> +
> +       /* Attach the new fdt to the ima-counters node */
> +       ret = dt_expand_node(dev, fdt, 0);
> +       if (ret < 0)
> +               prerror("IMA: device tree couldn't be linked\n");
> +
> +       /* Check availability of the PMU units from the availability vector */
> +       ret = disable_unavailable_units();
> +       if (ret < 0)
> +               prerror("IMA: error in disabling unavailable units\n");

If this fails we would have an invalid device tree. Personally I'd
rework this to be something like:

dev = dt_new_root("ima-counters");
dt_expand_node(dev, fdt, 0);
disable_unavailable_units(dev);
dt_attach_root(dev, dt_root);

That way we can prune the expanded IMA without affecting the main DT
and safely exit if any part of the process fails.

>  err:
>         free(buf);
>  }
> --
> 2.7.4
>
> _______________________________________________
> Skiboot mailing list
> Skiboot at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/skiboot


More information about the Skiboot mailing list