// SPDX-License-Identifier: GPL-2.0/* * pptt.c - parsing of Processor Properties Topology Table (PPTT) * * Copyright (C) 2018, ARM * * This file implements parsing of the Processor Properties Topology Table * which is optionally used to describe the processor and cache topology. * Due to the relative pointers used throughout the table, this doesn't * leverage the existing subtable parsing in the kernel. * * The PPTT structure is an inverted tree, with each node potentially * holding one or two inverted tree data structures describing * the caches available at that level. Each cache structure optionally * contains properties describing the cache at a given level which can be * used to override hardware probed values. */#define pr_fmt(fmt) "ACPI PPTT: " fmt #include <linux/acpi.h>#include <linux/cacheinfo.h>#include <acpi/processor.h> static struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr, u32 pptt_ref){ struct acpi_subtable_header *entry; /* there isn't a subtable at reference 0 */ if (pptt_ref < sizeof(struct acpi_subtable_header)) return NULL; if (pptt_ref + sizeof(struct acpi_subtable_header) > table_hdr->length) return NULL; entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, pptt_ref); if (entry->length == 0) return NULL; if (pptt_ref + entry->length > table_hdr->length) return NULL; return entry;} static struct acpi_pptt_processor *fetch_pptt_node(struct acpi_table_header *table_hdr, u32 pptt_ref){ return (struct acpi_pptt_processor *)fetch_pptt_subtable(table_hdr, pptt_ref);} static struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_hdr, u32 pptt_ref){ return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref);} static struct acpi_pptt_cache_v1 *upgrade_pptt_cache(struct acpi_pptt_cache *cache){ if (cache->header.length < sizeof(struct acpi_pptt_cache_v1)) return NULL; /* No use for v1 if the only additional field is invalid */ if (!(cache->flags & ACPI_PPTT_CACHE_ID_VALID)) return NULL; return (struct acpi_pptt_cache_v1 *)cache;} static struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *node, int resource){ u32 *ref; if (resource >= node->number_of_priv_resources) return NULL; ref = ACPI_ADD_PTR(u32, node, sizeof(struct acpi_pptt_processor)); ref += resource; return fetch_pptt_subtable(table_hdr, *ref);} static inline bool acpi_pptt_match_type(int table_type, int type){ return ((table_type & ACPI_PPTT_MASK_CACHE_TYPE) == type || table_type & ACPI_PPTT_CACHE_TYPE_UNIFIED & type);} /** * acpi_pptt_walk_cache() - Attempt to find the requested acpi_pptt_cache * @table_hdr: Pointer to the head of the PPTT table * @local_level: passed res reflects this cache level * @split_levels: Number of split cache levels (data/instruction). * @res: cache resource in the PPTT we want to walk * @found: returns a pointer to the requested level if found * @level: the requested cache level * @type: the requested cache type * * Attempt to find a given cache level, while counting the max number * of cache levels for the cache node. * * Given a pptt resource, verify that it is a cache node, then walk * down each level of caches, counting how many levels are found * as well as checking the cache type (icache, dcache, unified). If a * level & type match, then we set found, and continue the search. * Once the entire cache branch has been walked return its max * depth. * * Return: The cache structure and the level we terminated with. */static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr, unsigned int local_level, unsigned int *split_levels, struct acpi_subtable_header *res, struct acpi_pptt_cache **found, unsigned int level, int type){ struct acpi_pptt_cache *cache; if (res->type != ACPI_PPTT_TYPE_CACHE) return 0; cache = (struct acpi_pptt_cache *) res; while (cache) { local_level++; if (!(cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)) { cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache); continue; } if (split_levels && (acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_DATA) || acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_INSTR))) *split_levels = local_level; if (local_level == level && acpi_pptt_match_type(cache->attributes, type)) { if (*found != NULL && cache != *found) pr_warn("Found duplicate cache level/type unable to determine uniqueness\n"); pr_debug("Found cache @ level %u\n", level); *found = cache; /* * continue looking at this node's resource list * to verify that we don't find a duplicate * cache node. */ } cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache); } return local_level;} static struct acpi_pptt_cache *acpi_find_cache_level(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu_node, unsigned int *starting_level, unsigned int *split_levels, unsigned int level, int type){ struct acpi_subtable_header *res; unsigned int number_of_levels = *starting_level; int resource = 0; struct acpi_pptt_cache *ret = NULL; unsigned int local_level; /* walk down from processor node */ while ((res = acpi_get_pptt_resource(table_hdr, cpu_node, resource))) { resource++; local_level = acpi_pptt_walk_cache(table_hdr, *starting_level, split_levels, res, &ret, level, type); /* * we are looking for the max depth. Since its potentially * possible for a given node to have resources with differing * depths verify that the depth we have found is the largest. */ if (number_of_levels < local_level) number_of_levels = local_level; } if (number_of_levels > *starting_level) *starting_level = number_of_levels; return ret;} /** * acpi_count_levels() - Given a PPTT table, and a CPU node, count the * total number of levels and split cache levels (data/instruction). * @table_hdr: Pointer to the head of the PPTT table * @cpu_node: processor node we wish to count caches for * @split_levels: Number of split cache levels (data/instruction) if * success. Can by NULL. * * Return: number of levels. * Given a processor node containing a processing unit, walk into it and count * how many levels exist solely for it, and then walk up each level until we hit * the root node (ignore the package level because it may be possible to have * caches that exist across packages). Count the number of cache levels and * split cache levels (data/instruction) that exist at each level on the way * up. */static int acpi_count_levels(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu_node, unsigned int *split_levels){ int current_level = 0; do { acpi_find_cache_level(table_hdr, cpu_node, ¤t_level, split_levels, 0, 0); cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent); } while (cpu_node); return current_level;} /** * acpi_pptt_leaf_node() - Given a processor node, determine if its a leaf * @table_hdr: Pointer to the head of the PPTT table * @node: passed node is checked to see if its a leaf * * Determine if the *node parameter is a leaf node by iterating the * PPTT table, looking for nodes which reference it. * * Return: 0 if we find a node referencing the passed node (or table error), * or 1 if we don't. */static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *node){ struct acpi_subtable_header *entry; unsigned long table_end; u32 node_entry; struct acpi_pptt_processor *cpu_node; u32 proc_sz; if (table_hdr->revision > 1) return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE); table_end = (unsigned long)table_hdr + table_hdr->length; node_entry = ACPI_PTR_DIFF(node, table_hdr); entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, sizeof(struct acpi_table_pptt)); proc_sz = sizeof(struct acpi_pptt_processor); /* ignore subtable types that are smaller than a processor node */ while ((unsigned long)entry + proc_sz <= table_end) { cpu_node = (struct acpi_pptt_processor *)entry; if (entry->type == ACPI_PPTT_TYPE_PROCESSOR && cpu_node->parent == node_entry) return 0; if (entry->length == 0) return 0; entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry, entry->length); } return 1;} /** * acpi_find_processor_node() - Given a PPTT table find the requested processor * @table_hdr: Pointer to the head of the PPTT table * @acpi_cpu_id: CPU we are searching for * * Find the subtable entry describing the provided processor. * This is done by iterating the PPTT table looking for processor nodes * which have an acpi_processor_id that matches the acpi_cpu_id parameter * passed into the function. If we find a node that matches this criteria * we verify that its a leaf node in the topology rather than depending * on the valid flag, which doesn't need to be set for leaf nodes. * * Return: NULL, or the processors acpi_pptt_processor* */static struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_header *table_hdr, u32 acpi_cpu_id){ struct acpi_subtable_header *entry; unsigned long table_end; struct acpi_pptt_processor *cpu_node; u32 proc_sz; table_end = (unsigned long)table_hdr + table_hdr->length; entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, sizeof(struct acpi_table_pptt)); proc_sz = sizeof(struct acpi_pptt_processor); /* find the processor structure associated with this cpuid */ while ((unsigned long)entry + proc_sz <= table_end) { cpu_node = (struct acpi_pptt_processor *)entry; if (entry->length == 0) { pr_warn("Invalid zero length subtable\n"); break; } /* entry->length may not equal proc_sz, revalidate the processor structure length */ if (entry->type == ACPI_PPTT_TYPE_PROCESSOR && acpi_cpu_id == cpu_node->acpi_processor_id && (unsigned long)entry + entry->length <= table_end && entry->length == proc_sz + cpu_node->number_of_priv_resources * sizeof(u32) && acpi_pptt_leaf_node(table_hdr, cpu_node)) { return (struct acpi_pptt_processor *)entry; } entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry, entry->length); } return NULL;} static u8 acpi_cache_type(enum cache_type type){ switch (type) { case CACHE_TYPE_DATA: pr_debug("Looking for data cache\n"); return ACPI_PPTT_CACHE_TYPE_DATA; case CACHE_TYPE_INST: pr_debug("Looking for instruction cache\n"); return ACPI_PPTT_CACHE_TYPE_INSTR; default: case CACHE_TYPE_UNIFIED: pr_debug("Looking for unified cache\n"); /* * It is important that ACPI_PPTT_CACHE_TYPE_UNIFIED * contains the bit pattern that will match both * ACPI unified bit patterns because we use it later * to match both cases. */ return ACPI_PPTT_CACHE_TYPE_UNIFIED; }} static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *table_hdr, u32 acpi_cpu_id, enum cache_type type, unsigned int level, struct acpi_pptt_processor **node){ unsigned int total_levels = 0; struct acpi_pptt_cache *found = NULL; struct acpi_pptt_processor *cpu_node; u8 acpi_type = acpi_cache_type(type); pr_debug("Looking for CPU %d's level %u cache type %d\n", acpi_cpu_id, level, acpi_type); cpu_node = acpi_find_processor_node(table_hdr, acpi_cpu_id); while (cpu_node && !found) { found = acpi_find_cache_level(table_hdr, cpu_node, &total_levels, NULL, level, acpi_type); *node = cpu_node; cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent); } return found;} /** * update_cache_properties() - Update cacheinfo for the given processor * @this_leaf: Kernel cache info structure being updated * @found_cache: The PPTT node describing this cache instance * @cpu_node: A unique reference to describe this cache instance * * The ACPI spec implies that the fields in the cache structures are used to * extend and correct the information probed from the hardware. Lets only * set fields that we determine are VALID. * * Return: nothing. Side effect of updating the global cacheinfo */static void update_cache_properties(struct cacheinfo *this_leaf, struct acpi_pptt_cache *found_cache, struct acpi_pptt_processor *cpu_node){ struct acpi_pptt_cache_v1 *found_cache_v1; this_leaf->fw_token = cpu_node; if (found_cache->flags & ACPI_PPTT_SIZE_PROPERTY_VALID) this_leaf->size = found_cache->size; if (found_cache->flags & ACPI_PPTT_LINE_SIZE_VALID) this_leaf->coherency_line_size = found_cache->line_size; if (found_cache->flags & ACPI_PPTT_NUMBER_OF_SETS_VALID) this_leaf->number_of_sets = found_cache->number_of_sets; if (found_cache->flags & ACPI_PPTT_ASSOCIATIVITY_VALID) this_leaf->ways_of_associativity = found_cache->associativity; if (found_cache->flags & ACPI_PPTT_WRITE_POLICY_VALID) { switch (found_cache->attributes & ACPI_PPTT_MASK_WRITE_POLICY) { case ACPI_PPTT_CACHE_POLICY_WT: this_leaf->attributes = CACHE_WRITE_THROUGH; break; case ACPI_PPTT_CACHE_POLICY_WB: this_leaf->attributes = CACHE_WRITE_BACK; break; } } if (found_cache->flags & ACPI_PPTT_ALLOCATION_TYPE_VALID) { switch (found_cache->attributes & ACPI_PPTT_MASK_ALLOCATION_TYPE) { case ACPI_PPTT_CACHE_READ_ALLOCATE: this_leaf->attributes |= CACHE_READ_ALLOCATE; break; case ACPI_PPTT_CACHE_WRITE_ALLOCATE: this_leaf->attributes |= CACHE_WRITE_ALLOCATE; break; case ACPI_PPTT_CACHE_RW_ALLOCATE: case ACPI_PPTT_CACHE_RW_ALLOCATE_ALT: this_leaf->attributes |= CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE; break; } } /* * If cache type is NOCACHE, then the cache hasn't been specified * via other mechanisms. Update the type if a cache type has been * provided. * * Note, we assume such caches are unified based on conventional system * design and known examples. Significant work is required elsewhere to * fully support data/instruction only type caches which are only * specified in PPTT. */ if (this_leaf->type == CACHE_TYPE_NOCACHE && found_cache->flags & ACPI_PPTT_CACHE_TYPE_VALID) this_leaf->type = CACHE_TYPE_UNIFIED; found_cache_v1 = upgrade_pptt_cache(found_cache); if (found_cache_v1) { this_leaf->id = found_cache_v1->cache_id; this_leaf->attributes |= CACHE_ID; }} static void cache_setup_acpi_cpu(struct acpi_table_header *table, unsigned int cpu){ struct acpi_pptt_cache *found_cache; struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); u32 acpi_cpu_id; struct cacheinfo *this_leaf; unsigned int index = 0; struct acpi_pptt_processor *cpu_node = NULL; if (acpi_get_cpu_uid(cpu, &acpi_cpu_id) != 0) return; while (index < get_cpu_cacheinfo(cpu)->num_leaves) { this_leaf = this_cpu_ci->info_list + index; found_cache = acpi_find_cache_node(table, acpi_cpu_id, this_leaf->type, this_leaf->level, &cpu_node); pr_debug("found = %p %p\n", found_cache, cpu_node); if (found_cache) update_cache_properties(this_leaf, found_cache, ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table))); index++; }} static bool flag_identical(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu){ struct acpi_pptt_processor *next; /* heterogeneous machines must use PPTT revision > 1 */ if (table_hdr->revision < 2) return false; /* Locate the last node in the tree with IDENTICAL set */ if (cpu->flags & ACPI_PPTT_ACPI_IDENTICAL) { next = fetch_pptt_node(table_hdr, cpu->parent); if (!(next && next->flags & ACPI_PPTT_ACPI_IDENTICAL)) return true; } return false;} /* Passing level values greater than this will result in search termination */#define PPTT_ABORT_PACKAGE 0xFF static struct acpi_pptt_processor *acpi_find_processor_tag(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu, int level, int flag){ struct acpi_pptt_processor *prev_node; while (cpu && level) { /* special case the identical flag to find last identical */ if (flag == ACPI_PPTT_ACPI_IDENTICAL) { if (flag_identical(table_hdr, cpu)) break; } else if (cpu->flags & flag) break;Lines 1–500 of 1070. Load more
// SPDX-License-Identifier: GPL-2.0 /* * pptt.c - parsing of Processor Properties Topology Table (PPTT) * * Copyright (C) 2018, ARM * * This file implements parsing of the Processor Properties Topology Table * which is optionally used to describe the processor and cache topology. * Due to the relative pointers used throughout the table, this doesn't * leverage the existing subtable parsing in the kernel. * * The PPTT structure is an inverted tree, with each node potentially * holding one or two inverted tree data structures describing * the caches available at that level. Each cache structure optionally * contains properties describing the cache at a given level which can be * used to override hardware probed values. */ #define pr_fmt(fmt) "ACPI PPTT: " fmt #include <linux/acpi.h> #include <linux/cacheinfo.h> #include <acpi/processor.h> static struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr, u32 pptt_ref) { struct acpi_subtable_header *entry; /* there isn't a subtable at reference 0 */ if (pptt_ref < sizeof(struct acpi_subtable_header)) return NULL; if (pptt_ref + sizeof(struct acpi_subtable_header) > table_hdr->length) return NULL; entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, pptt_ref); if (entry->length == 0) return NULL; if (pptt_ref + entry->length > table_hdr->length) return NULL; return entry; } static struct acpi_pptt_processor *fetch_pptt_node(struct acpi_table_header *table_hdr, u32 pptt_ref) { return (struct acpi_pptt_processor *)fetch_pptt_subtable(table_hdr, pptt_ref); } static struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_hdr, u32 pptt_ref) { return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref); } static struct acpi_pptt_cache_v1 *upgrade_pptt_cache(struct acpi_pptt_cache *cache) { if (cache->header.length < sizeof(struct acpi_pptt_cache_v1)) return NULL; /* No use for v1 if the only additional field is invalid */ if (!(cache->flags & ACPI_PPTT_CACHE_ID_VALID)) return NULL; return (struct acpi_pptt_cache_v1 *)cache; } static struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *node, int resource) { u32 *ref; if (resource >= node->number_of_priv_resources) return NULL; ref = ACPI_ADD_PTR(u32, node, sizeof(struct acpi_pptt_processor)); ref += resource; return fetch_pptt_subtable(table_hdr, *ref); } static inline bool acpi_pptt_match_type(int table_type, int type) { return ((table_type & ACPI_PPTT_MASK_CACHE_TYPE) == type || table_type & ACPI_PPTT_CACHE_TYPE_UNIFIED & type); } /** * acpi_pptt_walk_cache() - Attempt to find the requested acpi_pptt_cache * @table_hdr: Pointer to the head of the PPTT table * @local_level: passed res reflects this cache level * @split_levels: Number of split cache levels (data/instruction). * @res: cache resource in the PPTT we want to walk * @found: returns a pointer to the requested level if found * @level: the requested cache level * @type: the requested cache type * * Attempt to find a given cache level, while counting the max number * of cache levels for the cache node. * * Given a pptt resource, verify that it is a cache node, then walk * down each level of caches, counting how many levels are found * as well as checking the cache type (icache, dcache, unified). If a * level & type match, then we set found, and continue the search. * Once the entire cache branch has been walked return its max * depth. * * Return: The cache structure and the level we terminated with. */ static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr, unsigned int local_level, unsigned int *split_levels, struct acpi_subtable_header *res, struct acpi_pptt_cache **found, unsigned int level, int type) { struct acpi_pptt_cache *cache; if (res->type != ACPI_PPTT_TYPE_CACHE) return 0; cache = (struct acpi_pptt_cache *) res; while (cache) { local_level++; if (!(cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)) { cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache); continue; } if (split_levels && (acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_DATA) || acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_INSTR))) *split_levels = local_level; if (local_level == level && acpi_pptt_match_type(cache->attributes, type)) { if (*found != NULL && cache != *found) pr_warn("Found duplicate cache level/type unable to determine uniqueness\n"); pr_debug("Found cache @ level %u\n", level); *found = cache; /* * continue looking at this node's resource list * to verify that we don't find a duplicate * cache node. */ } cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache); } return local_level; } static struct acpi_pptt_cache * acpi_find_cache_level(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu_node, unsigned int *starting_level, unsigned int *split_levels, unsigned int level, int type) { struct acpi_subtable_header *res; unsigned int number_of_levels = *starting_level; int resource = 0; struct acpi_pptt_cache *ret = NULL; unsigned int local_level; /* walk down from processor node */ while ((res = acpi_get_pptt_resource(table_hdr, cpu_node, resource))) { resource++; local_level = acpi_pptt_walk_cache(table_hdr, *starting_level, split_levels, res, &ret, level, type); /* * we are looking for the max depth. Since its potentially * possible for a given node to have resources with differing * depths verify that the depth we have found is the largest. */ if (number_of_levels < local_level) number_of_levels = local_level; } if (number_of_levels > *starting_level) *starting_level = number_of_levels; return ret; } /** * acpi_count_levels() - Given a PPTT table, and a CPU node, count the * total number of levels and split cache levels (data/instruction). * @table_hdr: Pointer to the head of the PPTT table * @cpu_node: processor node we wish to count caches for * @split_levels: Number of split cache levels (data/instruction) if * success. Can by NULL. * * Return: number of levels. * Given a processor node containing a processing unit, walk into it and count * how many levels exist solely for it, and then walk up each level until we hit * the root node (ignore the package level because it may be possible to have * caches that exist across packages). Count the number of cache levels and * split cache levels (data/instruction) that exist at each level on the way * up. */ static int acpi_count_levels(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu_node, unsigned int *split_levels) { int current_level = 0; do { acpi_find_cache_level(table_hdr, cpu_node, ¤t_level, split_levels, 0, 0); cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent); } while (cpu_node); return current_level; } /** * acpi_pptt_leaf_node() - Given a processor node, determine if its a leaf * @table_hdr: Pointer to the head of the PPTT table * @node: passed node is checked to see if its a leaf * * Determine if the *node parameter is a leaf node by iterating the * PPTT table, looking for nodes which reference it. * * Return: 0 if we find a node referencing the passed node (or table error), * or 1 if we don't. */ static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *node) { struct acpi_subtable_header *entry; unsigned long table_end; u32 node_entry; struct acpi_pptt_processor *cpu_node; u32 proc_sz; if (table_hdr->revision > 1) return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE); table_end = (unsigned long)table_hdr + table_hdr->length; node_entry = ACPI_PTR_DIFF(node, table_hdr); entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, sizeof(struct acpi_table_pptt)); proc_sz = sizeof(struct acpi_pptt_processor); /* ignore subtable types that are smaller than a processor node */ while ((unsigned long)entry + proc_sz <= table_end) { cpu_node = (struct acpi_pptt_processor *)entry; if (entry->type == ACPI_PPTT_TYPE_PROCESSOR && cpu_node->parent == node_entry) return 0; if (entry->length == 0) return 0; entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry, entry->length); } return 1; } /** * acpi_find_processor_node() - Given a PPTT table find the requested processor * @table_hdr: Pointer to the head of the PPTT table * @acpi_cpu_id: CPU we are searching for * * Find the subtable entry describing the provided processor. * This is done by iterating the PPTT table looking for processor nodes * which have an acpi_processor_id that matches the acpi_cpu_id parameter * passed into the function. If we find a node that matches this criteria * we verify that its a leaf node in the topology rather than depending * on the valid flag, which doesn't need to be set for leaf nodes. * * Return: NULL, or the processors acpi_pptt_processor* */ static struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_header *table_hdr, u32 acpi_cpu_id) { struct acpi_subtable_header *entry; unsigned long table_end; struct acpi_pptt_processor *cpu_node; u32 proc_sz; table_end = (unsigned long)table_hdr + table_hdr->length; entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, sizeof(struct acpi_table_pptt)); proc_sz = sizeof(struct acpi_pptt_processor); /* find the processor structure associated with this cpuid */ while ((unsigned long)entry + proc_sz <= table_end) { cpu_node = (struct acpi_pptt_processor *)entry; if (entry->length == 0) { pr_warn("Invalid zero length subtable\n"); break; } /* entry->length may not equal proc_sz, revalidate the processor structure length */ if (entry->type == ACPI_PPTT_TYPE_PROCESSOR && acpi_cpu_id == cpu_node->acpi_processor_id && (unsigned long)entry + entry->length <= table_end && entry->length == proc_sz + cpu_node->number_of_priv_resources * sizeof(u32) && acpi_pptt_leaf_node(table_hdr, cpu_node)) { return (struct acpi_pptt_processor *)entry; } entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry, entry->length); } return NULL; } static u8 acpi_cache_type(enum cache_type type) { switch (type) { case CACHE_TYPE_DATA: pr_debug("Looking for data cache\n"); return ACPI_PPTT_CACHE_TYPE_DATA; case CACHE_TYPE_INST: pr_debug("Looking for instruction cache\n"); return ACPI_PPTT_CACHE_TYPE_INSTR; default: case CACHE_TYPE_UNIFIED: pr_debug("Looking for unified cache\n"); /* * It is important that ACPI_PPTT_CACHE_TYPE_UNIFIED * contains the bit pattern that will match both * ACPI unified bit patterns because we use it later * to match both cases. */ return ACPI_PPTT_CACHE_TYPE_UNIFIED; } } static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *table_hdr, u32 acpi_cpu_id, enum cache_type type, unsigned int level, struct acpi_pptt_processor **node) { unsigned int total_levels = 0; struct acpi_pptt_cache *found = NULL; struct acpi_pptt_processor *cpu_node; u8 acpi_type = acpi_cache_type(type); pr_debug("Looking for CPU %d's level %u cache type %d\n", acpi_cpu_id, level, acpi_type); cpu_node = acpi_find_processor_node(table_hdr, acpi_cpu_id); while (cpu_node && !found) { found = acpi_find_cache_level(table_hdr, cpu_node, &total_levels, NULL, level, acpi_type); *node = cpu_node; cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent); } return found; } /** * update_cache_properties() - Update cacheinfo for the given processor * @this_leaf: Kernel cache info structure being updated * @found_cache: The PPTT node describing this cache instance * @cpu_node: A unique reference to describe this cache instance * * The ACPI spec implies that the fields in the cache structures are used to * extend and correct the information probed from the hardware. Lets only * set fields that we determine are VALID. * * Return: nothing. Side effect of updating the global cacheinfo */ static void update_cache_properties(struct cacheinfo *this_leaf, struct acpi_pptt_cache *found_cache, struct acpi_pptt_processor *cpu_node) { struct acpi_pptt_cache_v1 *found_cache_v1; this_leaf->fw_token = cpu_node; if (found_cache->flags & ACPI_PPTT_SIZE_PROPERTY_VALID) this_leaf->size = found_cache->size; if (found_cache->flags & ACPI_PPTT_LINE_SIZE_VALID) this_leaf->coherency_line_size = found_cache->line_size; if (found_cache->flags & ACPI_PPTT_NUMBER_OF_SETS_VALID) this_leaf->number_of_sets = found_cache->number_of_sets; if (found_cache->flags & ACPI_PPTT_ASSOCIATIVITY_VALID) this_leaf->ways_of_associativity = found_cache->associativity; if (found_cache->flags & ACPI_PPTT_WRITE_POLICY_VALID) { switch (found_cache->attributes & ACPI_PPTT_MASK_WRITE_POLICY) { case ACPI_PPTT_CACHE_POLICY_WT: this_leaf->attributes = CACHE_WRITE_THROUGH; break; case ACPI_PPTT_CACHE_POLICY_WB: this_leaf->attributes = CACHE_WRITE_BACK; break; } } if (found_cache->flags & ACPI_PPTT_ALLOCATION_TYPE_VALID) { switch (found_cache->attributes & ACPI_PPTT_MASK_ALLOCATION_TYPE) { case ACPI_PPTT_CACHE_READ_ALLOCATE: this_leaf->attributes |= CACHE_READ_ALLOCATE; break; case ACPI_PPTT_CACHE_WRITE_ALLOCATE: this_leaf->attributes |= CACHE_WRITE_ALLOCATE; break; case ACPI_PPTT_CACHE_RW_ALLOCATE: case ACPI_PPTT_CACHE_RW_ALLOCATE_ALT: this_leaf->attributes |= CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE; break; } } /* * If cache type is NOCACHE, then the cache hasn't been specified * via other mechanisms. Update the type if a cache type has been * provided. * * Note, we assume such caches are unified based on conventional system * design and known examples. Significant work is required elsewhere to * fully support data/instruction only type caches which are only * specified in PPTT. */ if (this_leaf->type == CACHE_TYPE_NOCACHE && found_cache->flags & ACPI_PPTT_CACHE_TYPE_VALID) this_leaf->type = CACHE_TYPE_UNIFIED; found_cache_v1 = upgrade_pptt_cache(found_cache); if (found_cache_v1) { this_leaf->id = found_cache_v1->cache_id; this_leaf->attributes |= CACHE_ID; } } static void cache_setup_acpi_cpu(struct acpi_table_header *table, unsigned int cpu) { struct acpi_pptt_cache *found_cache; struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); u32 acpi_cpu_id; struct cacheinfo *this_leaf; unsigned int index = 0; struct acpi_pptt_processor *cpu_node = NULL; if (acpi_get_cpu_uid(cpu, &acpi_cpu_id) != 0) return; while (index < get_cpu_cacheinfo(cpu)->num_leaves) { this_leaf = this_cpu_ci->info_list + index; found_cache = acpi_find_cache_node(table, acpi_cpu_id, this_leaf->type, this_leaf->level, &cpu_node); pr_debug("found = %p %p\n", found_cache, cpu_node); if (found_cache) update_cache_properties(this_leaf, found_cache, ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table))); index++; } } static bool flag_identical(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu) { struct acpi_pptt_processor *next; /* heterogeneous machines must use PPTT revision > 1 */ if (table_hdr->revision < 2) return false; /* Locate the last node in the tree with IDENTICAL set */ if (cpu->flags & ACPI_PPTT_ACPI_IDENTICAL) { next = fetch_pptt_node(table_hdr, cpu->parent); if (!(next && next->flags & ACPI_PPTT_ACPI_IDENTICAL)) return true; } return false; } /* Passing level values greater than this will result in search termination */ #define PPTT_ABORT_PACKAGE 0xFF static struct acpi_pptt_processor *acpi_find_processor_tag(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu, int level, int flag) { struct acpi_pptt_processor *prev_node; while (cpu && level) { /* special case the identical flag to find last identical */ if (flag == ACPI_PPTT_ACPI_IDENTICAL) { if (flag_identical(table_hdr, cpu)) break; } else if (cpu->flags & flag) break;