diff options
Diffstat (limited to 'hdata/paca.c')
-rw-r--r-- | hdata/paca.c | 322 |
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(); + } +} |