summaryrefslogtreecommitdiffstats
path: root/hdata/paca.c
diff options
context:
space:
mode:
Diffstat (limited to 'hdata/paca.c')
-rw-r--r--hdata/paca.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/hdata/paca.c b/hdata/paca.c
new file mode 100644
index 00000000..d4360e70
--- /dev/null
+++ b/hdata/paca.c
@@ -0,0 +1,322 @@
+/* Copyright 2013-2014 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 "spira.h"
+#include <cpu.h>
+#include <fsp.h>
+#include <opal.h>
+#include <ccan/str/str.h>
+#include <device.h>
+#include <types.h>
+
+#include "hdata.h"
+
+#define PACA_MAX_THREADS 4
+
+static unsigned int paca_index(const struct HDIF_common_hdr *paca)
+{
+ void *start = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
+ return ((void *)paca - start)
+ / be32_to_cpu(spira.ntuples.paca.alloc_len);
+}
+
+static struct dt_node *add_cpu_node(struct dt_node *cpus,
+ const struct HDIF_common_hdr *paca,
+ const struct sppaca_cpu_id *id,
+ bool okay)
+{
+ const struct sppaca_cpu_timebase *timebase;
+ const struct sppaca_cpu_cache *cache;
+ const struct sppaca_cpu_attr *attr;
+ struct dt_node *cpu;
+ u32 no, size, ve_flags, l2_phandle, chip_id;
+
+ /* We use the process_interrupt_line as the res id */
+ no = be32_to_cpu(id->process_interrupt_line);
+
+ ve_flags = be32_to_cpu(id->verify_exists_flags);
+ printf("CPU[%i]: PIR=%i RES=%i %s %s(%u threads)\n",
+ paca_index(paca), be32_to_cpu(id->pir), no,
+ ve_flags & CPU_ID_PACA_RESERVED
+ ? "**RESERVED**" : cpu_state(ve_flags),
+ ve_flags & CPU_ID_SECONDARY_THREAD
+ ? "[secondary] " :
+ (be32_to_cpu(id->pir) == boot_cpu->pir ? "[boot] " : ""),
+ ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK)
+ >> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1);
+
+ timebase = HDIF_get_idata(paca, SPPACA_IDATA_TIMEBASE, &size);
+ if (!timebase || size < sizeof(*timebase)) {
+ prerror("CPU[%i]: bad timebase size %u @ %p\n",
+ paca_index(paca), size, timebase);
+ return NULL;
+ }
+
+ cache = HDIF_get_idata(paca, SPPACA_IDATA_CACHE_SIZE, &size);
+ if (!cache || size < sizeof(*cache)) {
+ prerror("CPU[%i]: bad cache size %u @ %p\n",
+ paca_index(paca), size, cache);
+ return NULL;
+ }
+
+ cpu = add_core_common(cpus, cache, timebase, no, okay);
+
+ /* Core attributes */
+ attr = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ATTR, &size);
+ if (attr)
+ add_core_attr(cpu, be32_to_cpu(attr->attr));
+
+ /* Add cache info */
+ l2_phandle = add_core_cache_info(cpus, cache, no, okay);
+ dt_add_property_cells(cpu, "l2-cache", l2_phandle);
+
+ /* We append the secondary cpus in __cpu_parse */
+ dt_add_property_cells(cpu, "ibm,ppc-interrupt-server#s", no);
+
+ dt_add_property_cells(cpu, DT_PRIVATE "hw_proc_id",
+ be32_to_cpu(id->hardware_proc_id));
+ dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(id->pir));
+
+ chip_id = pcid_to_chip_id(be32_to_cpu(id->processor_chip_id));
+ dt_add_property_cells(cpu, "ibm,chip-id", chip_id);
+
+ return cpu;
+}
+
+static struct dt_node *find_cpu_by_hardware_proc_id(struct dt_node *root,
+ u32 hw_proc_id)
+{
+ struct dt_node *i;
+
+ dt_for_each_node(root, i) {
+ const struct dt_property *prop;
+
+ if (!dt_has_node_property(i, "device_type", "cpu"))
+ continue;
+
+ prop = dt_find_property(i, DT_PRIVATE "hw_proc_id");
+ if (be32_to_cpu(*(u32 *)prop->prop) == hw_proc_id)
+ return i;
+ }
+ return NULL;
+}
+
+/* Note that numbers are small. */
+static void add_be32_sorted(__be32 arr[], __be32 new, unsigned num)
+{
+ unsigned int i;
+
+ /* Walk until we find where we belong (insertion sort). */
+ for (i = 0; i < num; i++) {
+ if (be32_to_cpu(new) < be32_to_cpu(arr[i])) {
+ __be32 tmp = arr[i];
+ arr[i] = new;
+ new = tmp;
+ }
+ }
+ arr[i] = new;
+}
+
+static void add_icps(void)
+{
+ struct dt_node *cpu;
+ unsigned int i;
+ u64 reg[PACA_MAX_THREADS * 2];
+ struct dt_node *icp;
+
+ dt_for_each_node(dt_root, cpu) {
+ u32 irange[2], size, pir;
+ const struct dt_property *intsrv;
+ const struct HDIF_common_hdr *paca;
+ u64 ibase;
+ unsigned int num_threads;
+ bool found = false;
+
+ if (!dt_has_node_property(cpu, "device_type", "cpu"))
+ continue;
+
+ intsrv = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
+ pir = dt_prop_get_u32(cpu, "ibm,pir");
+
+ /* Get ibase address */
+ paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
+ for_each_paca(paca) {
+ const struct sppaca_cpu_id *id;
+ id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
+
+ if (pir != be32_to_cpu(id->pir))
+ continue;
+ ibase = cleanup_addr(be64_to_cpu(id->ibase));
+ found = true;
+ break;
+ }
+ if (!found)
+ return;
+
+ num_threads = intsrv->len / sizeof(u32);
+ assert(num_threads <= PACA_MAX_THREADS);
+
+ icp = dt_new_addr(dt_root, "interrupt-controller", ibase);
+ if (!icp)
+ continue;
+
+ dt_add_property_strings(icp, "compatible",
+ "IBM,ppc-xicp",
+ "IBM,power7-xicp");
+
+ irange[0] = dt_property_get_cell(intsrv, 0); /* Index */
+ irange[1] = num_threads; /* num servers */
+ dt_add_property(icp, "ibm,interrupt-server-ranges",
+ irange, sizeof(irange));
+ dt_add_property(icp, "interrupt-controller", NULL, 0);
+ dt_add_property_cells(icp, "#address-cells", 0);
+ dt_add_property_cells(icp, "#interrupt-cells", 1);
+ dt_add_property_string(icp, "device_type",
+ "PowerPC-External-Interrupt-Presentation");
+ for (i = 0; i < num_threads*2; i += 2) {
+ reg[i] = ibase;
+ /* One page is enough for a handful of regs. */
+ reg[i+1] = 4096;
+ ibase += reg[i+1];
+ }
+ dt_add_property(icp, "reg", reg, sizeof(reg));
+ }
+}
+
+static bool __paca_parse(void)
+{
+ const struct HDIF_common_hdr *paca;
+ struct dt_node *cpus;
+
+ paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
+ if (!paca) {
+ prerror("Invalid PACA (PCIA = %p)\n",
+ ntuple_addr(&spira.ntuples.pcia));
+ return false;
+ }
+
+ if (be32_to_cpu(spira.ntuples.paca.act_len) < sizeof(*paca)) {
+ prerror("PACA: invalid size %u\n",
+ be32_to_cpu(spira.ntuples.paca.act_len));
+ return false;
+ }
+
+ cpus = dt_new(dt_root, "cpus");
+ dt_add_property_cells(cpus, "#address-cells", 1);
+ dt_add_property_cells(cpus, "#size-cells", 0);
+
+ for_each_paca(paca) {
+ const struct sppaca_cpu_id *id;
+ u32 size, ve_flags;
+ bool okay;
+
+ id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
+
+ /* The ID structure on Blade314 is only 0x54 long. We can
+ * cope with it as we don't use all the additional fields.
+ * The minimum size we support is 0x40
+ */
+ if (!id || size < SPIRA_CPU_ID_MIN_SIZE) {
+ prerror("CPU[%i]: bad id size %u @ %p\n",
+ paca_index(paca), size, id);
+ return false;
+ }
+
+ ve_flags = be32_to_cpu(id->verify_exists_flags);
+ switch ((ve_flags&CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) {
+ case CPU_ID_VERIFY_USABLE_NO_FAILURES:
+ case CPU_ID_VERIFY_USABLE_FAILURES:
+ okay = true;
+ break;
+ default:
+ okay = false;
+ }
+
+ printf("CPU[%i]: PIR=%i RES=%i %s\n",
+ paca_index(paca), be32_to_cpu(id->pir),
+ be32_to_cpu(id->process_interrupt_line),
+ okay ? "OK" : "UNAVAILABLE");
+
+ /* Secondary threads don't get their own node. */
+ if (ve_flags & CPU_ID_SECONDARY_THREAD)
+ continue;
+
+ if (!add_cpu_node(cpus, paca, id, okay))
+ return false;
+ }
+
+ /* Now account for secondaries. */
+ for_each_paca(paca) {
+ const struct dt_property *prop;
+ const struct sppaca_cpu_id *id;
+ u32 size, state, num, ve_flags;
+ struct dt_node *cpu;
+ __be32 *new_prop;
+
+ id = HDIF_get_idata(paca, 2, &size);
+ ve_flags = be32_to_cpu(id->verify_exists_flags);
+ state = (ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT;
+ switch (state) {
+ case CPU_ID_VERIFY_USABLE_NO_FAILURES:
+ case CPU_ID_VERIFY_USABLE_FAILURES:
+ break;
+ default:
+ continue;
+ }
+
+ /* Only interested in secondary threads. */
+ if (!(ve_flags & CPU_ID_SECONDARY_THREAD))
+ continue;
+
+ cpu = find_cpu_by_hardware_proc_id(cpus,
+ be32_to_cpu(id->hardware_proc_id));
+ if (!cpu) {
+ prerror("CPU[%i]: could not find primary hwid %i\n",
+ paca_index(paca),
+ be32_to_cpu(id->hardware_proc_id));
+ return false;
+ }
+
+ /* Add the cpu #. */
+ prop = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
+ num = prop->len / sizeof(u32);
+ new_prop = malloc((num + 1) * sizeof(u32));
+ if (!new_prop) {
+ prerror("Property allocation length %zu failed\n",
+ (num + 1) * sizeof(u32));
+ return false;
+ }
+ memcpy(new_prop, prop->prop, prop->len);
+ add_be32_sorted(new_prop, id->process_interrupt_line, num);
+ dt_del_property(cpu, (struct dt_property *)prop);
+ dt_add_property(cpu, "ibm,ppc-interrupt-server#s",
+ new_prop, (num + 1) * sizeof(__be32));
+ free(new_prop);
+ }
+
+ add_icps();
+
+ return true;
+}
+
+void paca_parse(void)
+{
+ if (!__paca_parse()) {
+ prerror("CPU: Initial CPU parsing failed\n");
+ abort();
+ }
+}
OpenPOWER on IntegriCloud