[Skiboot] [PATCH v13 7/9] skiboot: Find the IMC DTB

Michael Neuling mikey at neuling.org
Mon Jun 19 21:03:49 AEST 2017


On Mon, 2017-06-19 at 12:35 +0530, Madhavan Srinivasan wrote:
> IMC (In Memory Collection) catalog is a repository of information
> about the Performance Monitoring Units (PMUs) and their events under
> the IMC infrastructure. The information include :
>  - The PMU names
>  - Event names
>  - Event description
>  - Event offsets
>  - Event scale
>  - Event unit
> 
> The catalog is provided as a flattened device tree (dtb). Processors
> with different PVR values may have different PMU or event names. Hence,
> for each processor, there can be multiple device tree binaries (dtbs)
> containing the IMC information. Each of the dtb is compressed and forms
> a sub-partition inside the PNOR partition "IMA_CATALOG". Here is a link
> to the commit adding this partition to PNOR :
> https://github.com/open-power/pnor/commit/c940142c6dc64dd176096dc648f433c88991
> 9e84
> 
> So, each compressed dtb forms a sub-partition inside the IMC pnor
> partition and can be accessed/loaded through a sub-partition id which
> is nothing but the PVR id. Based on the current processor's PVR, the
> appropriate sub-partion will be loaded.
> 
> Note however, that the catalog information is in the form of a dtb and
> the dtb is compressed too. So, the sub-partition loaded must be
> decompressed first before we can actually use it.
> 
> It is important to mention here that while a PNOR image built for one
> processor is specific to only that processor and isn't portable, a
> single system generation (Processor version) may have multiple revisions
> and these revisions may have some changes in their IMC PMUs and events,
> and hence, the need for multiple IMC DTBs.
> 
> The sub-partition that we obtain from the IMC pnor 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 IMC PMUs and their events' information.
> 
> Not all the IMC PMUs listed in the device tree may be available. This is
> indicated by imc availability vector (which is a part of the IMC control
> block structure). We need to check this vector and make sure to remove
> the IMC device nodes which are unavailable.
> 
> Signed-off-by: Hemant Kumar <hemant at linux.vnet.ibm.com>
> Signed-off-by: Anju T Sudhakar <anju at linux.vnet.ibm.com>
> Signed-off-by: Madhavan Srinivasan <maddy at linux.vnet.ibm.com>

Acked-by: Michael Neuling <mikey at neuling.org>


> ---
>  core/flash.c       |   1 +
>  core/init.c        |   7 +
>  hw/Makefile.inc    |   2 +-
>  hw/imc.c           | 429
> +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/chip.h     |  11 ++
>  include/imc.h      |   7 +
>  include/platform.h |   1 +
>  7 files changed, 457 insertions(+), 1 deletion(-)
>  create mode 100644 hw/imc.c
> 
> diff --git a/core/flash.c b/core/flash.c
> index 793401c94615..41ea760fdcbb 100644
> --- a/core/flash.c
> +++ b/core/flash.c
> @@ -421,6 +421,7 @@ static struct {
>  	{ RESOURCE_ID_KERNEL,	RESOURCE_SUBID_NONE,		"BOO
> TKERNEL" },
>  	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,		"ROOTFS"
> },
>  	{ RESOURCE_ID_CAPP,	RESOURCE_SUBID_SUPPORTED,	"CAPP" },
> +	{ RESOURCE_ID_IMA_CATALOG,  RESOURCE_SUBID_SUPPORTED,	"IMA_CAT
> ALOG" },
>  };
>  
>  
> diff --git a/core/init.c b/core/init.c
> index dce10fd6a20b..d9f359a5b033 100644
> --- a/core/init.c
> +++ b/core/init.c
> @@ -48,6 +48,7 @@
>  #include <libstb/stb.h>
>  #include <libstb/container.h>
>  #include <phys-map.h>
> +#include <imc.h>
>  
>  enum proc_gen proc_gen;
>  unsigned int pcie_max_link_speed;
> @@ -982,6 +983,9 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
>  	/* Read in NVRAM and set it up */
>  	nvram_init();
>  
> +	/* preload the IMC catalog dtb */
> +	imc_catalog_preload();
> +
>  	/* Set the console level */
>  	console_log_level();
>  
> @@ -1005,6 +1009,9 @@ void __noreturn __nomcount main_cpu_entry(const void
> *fdt)
>  	/* NX init */
>  	nx_init();
>  
> +	/* Init In-Memory Collection related stuff (load the IMC dtb into
> memory) */
> +	imc_init();
> +
>  	/* Probe IO hubs */
>  	probe_p7ioc();
>  
> diff --git a/hw/Makefile.inc b/hw/Makefile.inc
> index 97080aad17c3..13b085e82795 100644
> --- a/hw/Makefile.inc
> +++ b/hw/Makefile.inc
> @@ -1,7 +1,7 @@
>  # -*-Makefile-*-
>  SUBDIRS += hw
>  HW_OBJS  = xscom.o chiptod.o gx.o cec.o lpc.o lpc-uart.o psi.o
> -HW_OBJS += homer.o slw.o occ.o fsi-master.o centaur.o
> +HW_OBJS += homer.o slw.o occ.o fsi-master.o centaur.o imc.o
>  HW_OBJS += nx.o nx-rng.o nx-crypto.o nx-842.o
>  HW_OBJS += p7ioc.o p7ioc-inits.o p7ioc-phb.o
>  HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o prd.o
> diff --git a/hw/imc.c b/hw/imc.c
> new file mode 100644
> index 000000000000..696805ae23d2
> --- /dev/null
> +++ b/hw/imc.c
> @@ -0,0 +1,429 @@
> +/* Copyright 2016 IBM Corp.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> + * implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <skiboot.h>
> +#include <xscom.h>
> +#include <imc.h>
> +#include <chip.h>
> +#include <libxz/xz.h>
> +#include <device.h>
> +
> +/*
> + * Nest IMC PMU names along with their bit values as represented in the
> + * imc_chip_avl_vector(in struct imc_chip_cb, look at include/imc.h).
> + * nest_pmus[] is an array containing all the possible nest IMC PMU node
> names.
> + */
> +char const *nest_pmus[] = {
> +	"powerbus0",
> +	"mcs0",
> +	"mcs1",
> +	"mcs2",
> +	"mcs3",
> +	"mcs4",
> +	"mcs5",
> +	"mcs6",
> +	"mcs7",
> +	"mba0",
> +	"mba1",
> +	"mba2",
> +	"mba3",
> +	"mba4",
> +	"mba5",
> +	"mba6",
> +	"mba7",
> +	"cen0",
> +	"cen1",
> +	"cen2",
> +	"cen3",
> +	"cen4",
> +	"cen5",
> +	"cen6",
> +	"cen7",
> +	"xlink0",
> +	"xlink1",
> +	"xlink2",
> +	"mcd0",
> +	"mcd1",
> +	"phb0",
> +	"phb1",
> +	"phb2",
> +	"resvd",
> +	"nx",
> +	"capp0",
> +	"capp1",
> +	"vas",
> +	"int",
> +	"alink0",
> +	"alink1",
> +	"alink2",
> +	"nvlink0",
> +	"nvlink1",
> +	"nvlink2",
> +	"nvlink3",
> +	"nvlink4",
> +	"nvlink5",
> +	/* reserved bits : 48 - 64 */
> +};
> +
> +char *compress_buf;
> +size_t compress_buf_size;
> +const char **prop_to_fix(struct dt_node *node);
> +const char *props_to_fix[] = {"events", NULL};
> +
> +static bool is_nest_mem_initialized(struct imc_chip_cb *ptr)
> +{
> +	/*
> +	 * Non zero value in "Status" field indicate memory initialized.
> +	 */
> +	if (!ptr->imc_chip_run_status)
> +		return false;
> +
> +	return true;
> +}
> +
> +static struct imc_chip_cb *get_imc_cb(uint32_t chip_id)
> +{
> +	struct proc_chip *chip = get_chip(chip_id);
> +	struct imc_chip_cb *cb;
> +
> +	cb = (struct imc_chip_cb *)(chip->homer_base + P9_CB_STRUCT_OFFSET);
> +	if (!is_nest_mem_initialized(cb))
> +		return NULL;
> +
> +	return cb;
> +}
> +
> +/*
> + * Decompresses the blob obtained from the IMC pnor sub-partition
> + * in "src" of size "src_size", assigns the uncompressed device tree
> + * binary to "dst" and returns.
> + *
> + * Returns 0 on success and -1 on error.
> + *
> + * TODO: Ideally this should be part of generic subpartition load
> + * infrastructure. And decompression can be queued as another CPU job
> + */
> +static int decompress(void *dst, size_t dst_size, void *src, size_t src_size)
> +{
> +	struct xz_dec *s;
> +	struct xz_buf b;
> +	int ret = 0;
> +
> +	/* Initialize the xz library first */
> +	xz_crc32_init();
> +	s = xz_dec_init(XZ_SINGLE, 0);
> +	if (s == NULL) {
> +		prerror("IMC: initialization error for xz\n");
> +		return -1;
> +	}
> +
> +	/*
> +	 * Source address : src
> +	 * Source size : src_size
> +	 * Destination address : dst
> +	 * Destination size : dst_src
> +	 */
> +	b.in = src;
> +	b.in_pos = 0;
> +	b.in_size = src_size;
> +	b.out = dst;
> +	b.out_pos = 0;
> +	b.out_size = dst_size;
> +
> +	/* Start decompressing */
> +	ret = xz_dec_run(s, &b);
> +	if (ret != XZ_STREAM_END) {
> +		prerror("IMC: failed to decompress subpartition\n");
> +		ret = -1;
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	/* Clean up memory */
> +	xz_dec_end(s);
> +	return ret;
> +}
> +
> +/*
> + * Function return list of properties names for the fixup
> + */
> +const char **prop_to_fix(struct dt_node *node)
> +{
> +	if (dt_node_is_compatible(node, "ibm,imc-counters"))
> +		return props_to_fix;
> +
> +	return NULL;
> +}
> +
> +/* Helper to get the IMC device type for a device node */
> +static int get_imc_device_type(struct dt_node *node)
> +{
> +	const struct dt_property *type;
> +	u32 val=0;
> +
> +	if (!node)
> +		return -1;
> +
> +	type = dt_find_property(node, "type");
> +	if (!type)
> +		return -1;
> +
> +	val = dt_prop_get_u32(node, "type");
> +	switch (val){
> +	case IMC_COUNTER_CHIP:
> +		return IMC_COUNTER_CHIP;
> +	case IMC_COUNTER_CORE:
> +		return IMC_COUNTER_CORE;
> +	case IMC_COUNTER_THREAD:
> +		return IMC_COUNTER_THREAD;
> +	default:
> +		break;
> +	}
> +
> +	/* Unknown/Unsupported IMC device type */
> +	return -1;
> +}
> +
> +static bool is_nest_node(struct dt_node *node)
> +{
> +	if (get_imc_device_type(node) == IMC_COUNTER_CHIP)
> +		return true;
> +
> +	return false;
> +}
> +
> +static bool is_imc_device_type_supported(struct dt_node *node)
> +{
> +	u32 val = get_imc_device_type(node);
> +
> +	if ((val == IMC_COUNTER_CHIP) || (val == IMC_COUNTER_CORE) ||
> +						(val == IMC_COUNTER_THREAD))
> +		return true;
> +
> +	return false;
> +}
> +
> +/*
> + * Helper to check for the imc device type in the incoming device tree.
> + * Remove unsupported device node.
> + */
> +static void check_imc_device_type(struct dt_node *dev)
> +{
> +	struct dt_node *node;
> +
> +	dt_for_each_compatible(dev, node, "ibm,imc-counters") {
> +		if (!is_imc_device_type_supported(node)) {
> +			/*
> +			 * ah nice, found a device type which I didnt know.
> +			 * Remove it and also mark node as NULL, since
> dt_next
> +			 * will try to fetch info for "prev" which is removed
> +			 * by dt_free.
> +			 */
> +			dt_free(node);
> +			node = NULL;
> +		}
> +	}
> +
> +	return;
> +}
> +
> +/*
> + * Remove the PMU device nodes from the incoming new subtree, if they are not
> + * available in the hardware. The availability is described by the
> + * control block's imc_chip_avl_vector.
> + * Each bit represents a device unit. If the device is available, then
> + * the bit is set else its unset.
> + */
> +static void disable_unavailable_units(struct dt_node *dev)
> +{
> +	uint64_t avl_vec;
> +	struct imc_chip_cb *cb;
> +	struct dt_node *target;
> +	int i;
> +
> +	/* Fetch the IMC control block structure */
> +	cb = get_imc_cb(this_cpu()->chip_id);
> +	if (cb)
> +		avl_vec = be64_to_cpu(cb->imc_chip_avl_vector);
> +	else
> +		avl_vec = 0; /* Remove only nest imc device nodes */
> +
> +	for (i = 0; i < MAX_NEST_UNITS; i++) {
> +		if (!(PPC_BITMASK(i, i) & avl_vec)) {
> +			/* Check if the device node exists */
> +			target = dt_find_by_name(dev, nest_pmus[i]);
> +			if (!target)
> +				continue;
> +			/* Remove the device node */
> +			dt_free(target);
> +		}
> +	}
> +
> +	return;
> +}
> +
> +/*
> + * Function to queue the loading of imc catalog data
> + * from the IMC pnor partition.
> + */
> +void imc_catalog_preload(void)
> +{
> +	uint32_t pvr = (mfspr(SPR_PVR) & ~(0xf000));
> +	int ret = OPAL_SUCCESS;
> +	compress_buf_size = MAX_COMPRESSED_IMC_DTB_SIZE;
> +
> +	/* Enable only for power 9 */
> +	if (proc_gen != proc_gen_p9)
> +		return;
> +
> +	compress_buf = malloc(MAX_COMPRESSED_IMC_DTB_SIZE);
> +	if (!compress_buf) {
> +		prerror("IMC: Memory allocation for catalog failed\n");
> +		return;
> +	}
> +
> +	ret = start_preload_resource(RESOURCE_ID_IMA_CATALOG,
> +					pvr, compress_buf,
> &compress_buf_size);
> +	if (ret != OPAL_SUCCESS)
> +		free(compress_buf);
> +
> +	return;
> +}
> +
> +static void imc_dt_update_nest_node(struct dt_node *dev)
> +{
> +	struct proc_chip *chip;
> +	uint64_t *base_addr = NULL;
> +	uint32_t *chipids = NULL;
> +	int i=0, nr_chip = nr_chips();
> +	struct dt_node *node;
> +	const struct dt_property *type;
> +
> +	/* Add the base_addr and chip-id properties for the nest node */
> +	base_addr = malloc(sizeof(uint64_t) * nr_chip);
> +	chipids = malloc(sizeof(uint32_t) * nr_chip);
> +	for_each_chip(chip) {
> +		base_addr[i] = chip->homer_base;
> +		chipids[i] = chip->id;
> +		i++;
> +	}
> +
> +	dt_for_each_compatible(dev, node, "ibm,imc-counters") {
> +		type = dt_find_property(node, "type");
> +		if (type && is_nest_node(node)) {
> +			dt_add_property(node, "base-addr", base_addr, (i *
> sizeof(u64)));
> +			dt_add_property(node, "chip-id", chipids, (i *
> sizeof(u32)));
> +		}
> +	}
> +}
> +
> +/*
> + * Load the IMC pnor partition and find the appropriate sub-partition
> + * based on the platform's PVR.
> + * Decompress the sub-partition and link the imc device tree to the
> + * existing device tree.
> + */
> +void imc_init(void)
> +{
> +	void *decompress_buf;
> +	uint32_t pvr = (mfspr(SPR_PVR) & ~(0xf000));
> +	struct dt_node *dev;
> +	int ret;
> +
> +	/* Enable only for power 9 */
> +	if (proc_gen != proc_gen_p9)
> +		return;
> +
> +	ret = wait_for_resource_loaded(RESOURCE_ID_IMA_CATALOG, pvr);
> +	if (ret != OPAL_SUCCESS) {
> +		prerror("IMC Catalog load failed\n");
> +		return;
> +	}
> +
> +	/*
> +	 * Flow of the data from PNOR to main device tree:
> +	 *
> +	 * PNOR -> compressed local buffer (compress_buf)
> +	 * compressed local buffer -> decompressed local buf (decompress_buf)
> +	 * decompress local buffer -> main device tree
> +	 * free compressed local buffer
> +	 */
> +
> +	/*
> +	 * Memory for decompression.
> +	 */
> +	decompress_buf = malloc(MAX_DECOMPRESSED_IMC_DTB_SIZE);
> +	if (!decompress_buf) {
> +		prerror("IMC: No memory for decompress_buf \n");
> +		goto err;
> +	}
> +
> +	/*
> +	 * Decompress the compressed buffer
> +	 */
> +	ret = decompress(decompress_buf, MAX_DECOMPRESSED_IMC_DTB_SIZE,
> +				compress_buf, compress_buf_size);
> +	if (ret < 0)
> +		goto err;
> +
> +	/* Create a device tree entry for imc counters */
> +	dev = dt_new_root("imc-counters");
> +	if (!dev)
> +		goto err;
> +
> +	/*
> +	 * Attach the new decompress_buf to the imc-counters node.
> +	 * dt_expand_node() does sanity checks for fdt_header, piggyback
> +	 */
> +	ret = dt_expand_node(dev, decompress_buf, 0);
> +	if (ret < 0) {
> +		dt_free(dev);
> +		goto err;
> +	}
> +
> +	/* Check and remove unsupported imc device types */
> +	check_imc_device_type(dev);
> +
> +	/*
> +	 * Check and remove unsupported nest unit nodes by the microcode,
> +	 * from the incoming device tree.
> +	 */
> +	disable_unavailable_units(dev);
> +
> +	/* Fix the phandle in the incoming device tree */
> +	dt_adjust_subtree_phandle(dev, prop_to_fix);
> +
> +	/* Update the base_addr and chip-id for nest nodes */
> +	imc_dt_update_nest_node(dev);
> +
> +	/*
> +	 * If the dt_attach_root() fails, "imc-counters" node will not be
> +	 * seen in the device-tree and hence OS should not make any
> +	 * OPAL_IMC_* calls.
> +	 */
> +	if (!dt_attach_root(dt_root, dev)) {
> +		dt_free(dev);
> +		goto err;
> +	}
> +
> +	free(compress_buf);
> +	return;
> +err:
> +	prerror("IMC Devices not added\n");
> +	free(decompress_buf);
> +	free(compress_buf);
> +}
> diff --git a/include/chip.h b/include/chip.h
> index b957e455d5ec..30b9936d8733 100644
> --- a/include/chip.h
> +++ b/include/chip.h
> @@ -221,6 +221,17 @@ extern struct proc_chip *get_chip(uint32_t chip_id);
>  
>  extern void init_chips(void);
>  
> +/* helper to get number of chips in the system */
> +static inline int nr_chips(void)
> +{
> +	struct proc_chip *chip;
> +	int nr_chips = 0;
> +
> +	for_each_chip(chip)
> +		nr_chips++;
> +
> +	return nr_chips;
> +}
>  
>  #endif /* __CHIP_H */
>  
> diff --git a/include/imc.h b/include/imc.h
> index f749d373e38c..fcd220d1b316 100644
> --- a/include/imc.h
> +++ b/include/imc.h
> @@ -108,6 +108,11 @@ struct imc_chip_cb
>  #define MAX_DECOMPRESSED_IMC_DTB_SIZE		0x40000
>  #define MAX_COMPRESSED_IMC_DTB_SIZE		0x40000
>  
> +/* IMC device types */
> +#define IMC_COUNTER_CHIP		0x10
> +#define IMC_COUNTER_CORE		0x4
> +#define IMC_COUNTER_THREAD		0x1
> +
>  /*
>   * Nest IMC operations
>   */
> @@ -116,4 +121,6 @@ struct imc_chip_cb
>  
>  #define MAX_NEST_UNITS			48
>  
> +void imc_init(void);
> +void imc_catalog_preload(void);
>  #endif /* __IMC_H */
> diff --git a/include/platform.h b/include/platform.h
> index 9133204207d6..c42039cdabb1 100644
> --- a/include/platform.h
> +++ b/include/platform.h
> @@ -27,6 +27,7 @@ enum resource_id {
>  	RESOURCE_ID_KERNEL,
>  	RESOURCE_ID_INITRAMFS,
>  	RESOURCE_ID_CAPP,
> +	RESOURCE_ID_IMA_CATALOG,
>  };
>  #define RESOURCE_SUBID_NONE 0
>  #define RESOURCE_SUBID_SUPPORTED 1


More information about the Skiboot mailing list