Index: head/sys/dev/bhnd/bcma/bcma_erom.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_erom.c (revision 327830) +++ head/sys/dev/bhnd/bcma/bcma_erom.c (revision 327831) @@ -1,1432 +1,1432 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2015-2017 Landon Fuller * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Landon Fuller * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "bcma_eromreg.h" #include "bcma_eromvar.h" /* * BCMA Enumeration ROM (EROM) Table * * Provides auto-discovery of BCMA cores on Broadcom's HND SoC. * * The EROM core address can be found at BCMA_CC_EROM_ADDR within the * ChipCommon registers. The table itself is comprised of 32-bit * type-tagged entries, organized into an array of variable-length * core descriptor records. * * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF) * marker. */ static const char *bcma_erom_entry_type_name (uint8_t entry); static int bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry); static int bcma_erom_skip32(struct bcma_erom *erom); static int bcma_erom_skip_core(struct bcma_erom *erom); static int bcma_erom_skip_mport(struct bcma_erom *erom); static int bcma_erom_skip_sport_region(struct bcma_erom *erom); static int bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype); static int bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type, bhnd_port_type *port_type); static int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry); static bus_size_t bcma_erom_tell(struct bcma_erom *erom); static void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset); static void bcma_erom_reset(struct bcma_erom *erom); static int bcma_erom_seek_matching_core(struct bcma_erom *sc, const struct bhnd_core_match *desc, struct bhnd_core_info *core); static int bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core); static int bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport); static int bcma_erom_parse_sport_region(struct bcma_erom *erom, struct bcma_erom_sport_region *region); static void bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx, int core_unit, struct bhnd_core_info *info); /** * BCMA EROM per-instance state. */ struct bcma_erom { struct bhnd_erom obj; device_t dev; /**< parent device, or NULL if none. */ struct bhnd_erom_io *eio; /**< bus I/O callbacks */ bhnd_size_t offset; /**< current read offset */ }; #define EROM_LOG(erom, fmt, ...) do { \ printf("%s erom[0x%llx]: " fmt, __FUNCTION__, \ (unsigned long long)(erom->offset), ##__VA_ARGS__); \ } while(0) /** Return the type name for an EROM entry */ static const char * bcma_erom_entry_type_name (uint8_t entry) { switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: return "core"; case BCMA_EROM_ENTRY_TYPE_MPORT: return "mport"; case BCMA_EROM_ENTRY_TYPE_REGION: return "region"; default: return "unknown"; } } /* BCMA implementation of BHND_EROM_INIT() */ static int bcma_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid, struct bhnd_erom_io *eio) { struct bcma_erom *sc; bhnd_addr_t table_addr; int error; sc = (struct bcma_erom *)erom; sc->eio = eio; sc->offset = 0; /* Determine erom table address */ if (BHND_ADDR_MAX - BCMA_EROM_TABLE_START < cid->enum_addr) return (ENXIO); /* would overflow */ table_addr = cid->enum_addr + BCMA_EROM_TABLE_START; /* Try to map the erom table */ error = bhnd_erom_io_map(sc->eio, table_addr, BCMA_EROM_TABLE_SIZE); if (error) return (error); return (0); } /* BCMA implementation of BHND_EROM_PROBE() */ static int bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { uint32_t idreg, eromptr; /* Hints aren't supported; all BCMA devices have a ChipCommon * core */ if (hint != NULL) return (EINVAL); /* Confirm CHIPC_EROMPTR availability */ idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4); if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS))) return (ENXIO); /* Fetch EROM address */ eromptr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4); /* Parse chip identifier */ *cid = bhnd_parse_chipid(idreg, eromptr); /* Verify chip type */ switch (cid->chip_type) { case BHND_CHIPTYPE_BCMA: return (BUS_PROBE_DEFAULT); case BHND_CHIPTYPE_BCMA_ALT: case BHND_CHIPTYPE_UBUS: return (BUS_PROBE_GENERIC); default: return (ENXIO); } } static void bcma_erom_fini(bhnd_erom_t *erom) { struct bcma_erom *sc = (struct bcma_erom *)erom; bhnd_erom_io_fini(sc->eio); } static int bcma_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct bcma_erom *sc = (struct bcma_erom *)erom; /* Search for the first matching core */ return (bcma_erom_seek_matching_core(sc, desc, core)); } static int bcma_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type port_type, u_int port_num, u_int region_num, struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size) { struct bcma_erom *sc; struct bcma_erom_core ec; uint32_t entry; uint8_t region_port, region_type; bool found; int error; sc = (struct bcma_erom *)erom; /* Seek to the first matching core and provide the core info * to the caller */ if ((error = bcma_erom_seek_matching_core(sc, desc, core))) return (error); if ((error = bcma_erom_parse_core(sc, &ec))) return (error); /* Skip master ports */ for (u_long i = 0; i < ec.num_mport; i++) { if ((error = bcma_erom_skip_mport(sc))) return (error); } /* Seek to the region block for the given port type */ found = false; while (1) { bhnd_port_type p_type; uint8_t r_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Expected region type? */ r_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); error = bcma_erom_region_to_port_type(sc, r_type, &p_type); if (error) return (error); if (p_type == port_type) { found = true; break; } /* Skip to next entry */ if ((error = bcma_erom_skip_sport_region(sc))) return (error); } if (!found) return (ENOENT); /* Found the appropriate port type block; now find the region records * for the given port number */ found = false; for (u_int i = 0; i <= port_num; i++) { bhnd_port_type p_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Fetch the type/port of the first region entry */ region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); /* Have we found the region entries for the desired port? */ if (i == port_num) { error = bcma_erom_region_to_port_type(sc, region_type, &p_type); if (error) return (error); if (p_type == port_type) found = true; break; } /* Otherwise, seek to next block of region records */ while (1) { uint8_t next_type, next_port; if ((error = bcma_erom_skip_sport_region(sc))) return (error); if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); if (next_type != region_type || next_port != region_port) break; } } if (!found) return (ENOENT); /* Finally, search for the requested region number */ for (u_int i = 0; i <= region_num; i++) { struct bcma_erom_sport_region region; uint8_t next_port, next_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Check for the end of the region block */ next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); if (next_type != region_type || next_port != region_port) break; /* Parse the region */ if ((error = bcma_erom_parse_sport_region(sc, ®ion))) return (error); /* Is this our target region_num? */ if (i == region_num) { /* Found */ *addr = region.base_addr; *size = region.size; return (0); } } /* Not found */ return (ENOENT); }; static int bcma_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { struct bcma_erom *sc; struct bhnd_core_info *buffer; bus_size_t initial_offset; u_int count; int error; sc = (struct bcma_erom *)erom; buffer = NULL; initial_offset = bcma_erom_tell(sc); /* Determine the core count */ bcma_erom_reset(sc); for (count = 0, error = 0; !error; count++) { struct bcma_erom_core core; /* Seek to the first readable core entry */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error == ENOENT) break; else if (error) goto cleanup; /* Read past the core descriptor */ if ((error = bcma_erom_parse_core(sc, &core))) goto cleanup; } /* Allocate our output buffer */ - buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND, + buffer = mallocarray(count, sizeof(struct bhnd_core_info), M_BHND, M_NOWAIT); if (buffer == NULL) { error = ENOMEM; goto cleanup; } /* Parse all core descriptors */ bcma_erom_reset(sc); for (u_int i = 0; i < count; i++) { struct bcma_erom_core core; int unit; /* Parse the core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) goto cleanup; error = bcma_erom_parse_core(sc, &core); if (error) goto cleanup; /* Determine the unit number */ unit = 0; for (u_int j = 0; j < i; j++) { if (buffer[i].vendor == buffer[j].vendor && buffer[i].device == buffer[j].device) unit++; } /* Convert to a bhnd info record */ bcma_erom_to_core_info(&core, i, unit, &buffer[i]); } cleanup: if (!error) { *cores = buffer; *num_cores = count; } else { if (buffer != NULL) free(buffer, M_BHND); } /* Restore the initial position */ bcma_erom_seek(sc, initial_offset); return (error); } static void bcma_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { free(cores, M_BHND); } /** * Return the current read position. */ static bus_size_t bcma_erom_tell(struct bcma_erom *erom) { return (erom->offset); } /** * Seek to an absolute read position. */ static void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset) { erom->offset = offset; } /** * Read a 32-bit entry value from the EROM table without advancing the * read position. * * @param erom EROM read state. * @param entry Will contain the read result on success. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry) { if (erom->offset >= (BCMA_EROM_TABLE_SIZE - sizeof(uint32_t))) { EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n"); return (EINVAL); } *entry = bhnd_erom_io_read(erom->eio, erom->offset, 4); return (0); } /** * Read a 32-bit entry value from the EROM table. * * @param erom EROM read state. * @param entry Will contain the read result on success. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry) { int error; if ((error = bcma_erom_peek32(erom, entry)) == 0) erom->offset += 4; return (error); } /** * Read and discard 32-bit entry value from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip32(struct bcma_erom *erom) { uint32_t entry; return bcma_erom_read32(erom, &entry); } /** * Read and discard a core descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_core(struct bcma_erom *erom) { struct bcma_erom_core core; return (bcma_erom_parse_core(erom, &core)); } /** * Read and discard a master port descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_mport(struct bcma_erom *erom) { struct bcma_erom_mport mp; return (bcma_erom_parse_mport(erom, &mp)); } /** * Read and discard a port region descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_sport_region(struct bcma_erom *erom) { struct bcma_erom_sport_region r; return (bcma_erom_parse_sport_region(erom, &r)); } /** * Seek to the next entry matching the given EROM entry type. * * @param erom EROM read state. * @param etype One of BCMA_EROM_ENTRY_TYPE_CORE, * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype) { uint32_t entry; int error; /* Iterate until we hit an entry matching the requested type. */ while (!(error = bcma_erom_peek32(erom, &entry))) { /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) return (ENOENT); /* Invalid entry */ if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) return (EINVAL); /* Entry type matches? */ if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype) return (0); /* Skip non-matching entry types. */ switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: if ((error = bcma_erom_skip_core(erom))) return (error); break; case BCMA_EROM_ENTRY_TYPE_MPORT: if ((error = bcma_erom_skip_mport(erom))) return (error); break; case BCMA_EROM_ENTRY_TYPE_REGION: if ((error = bcma_erom_skip_sport_region(erom))) return (error); break; default: /* Unknown entry type! */ return (EINVAL); } } return (error); } /** * Return the read position to the start of the EROM table. * * @param erom EROM read state. */ static void bcma_erom_reset(struct bcma_erom *erom) { erom->offset = 0; } /** * Seek to the first core entry matching @p desc. * * @param erom EROM read state. * @param desc The core match descriptor. * @param[out] core On success, the matching core info. If the core info * is not desired, a NULL pointer may be provided. * @retval 0 success * @retval ENOENT The end of the EROM table was reached before @p index was * found. * @retval non-zero Reading or parsing failed. */ static int bcma_erom_seek_matching_core(struct bcma_erom *sc, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct bhnd_core_match imatch; bus_size_t core_offset, next_offset; int error; /* Seek to table start. */ bcma_erom_reset(sc); /* We can't determine a core's unit number during the initial scan. */ imatch = *desc; imatch.m.match.core_unit = 0; /* Locate the first matching core */ for (u_int i = 0; i < UINT_MAX; i++) { struct bcma_erom_core ec; struct bhnd_core_info ci; /* Seek to the next core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); /* Save the core offset */ core_offset = bcma_erom_tell(sc); /* Parse the core */ if ((error = bcma_erom_parse_core(sc, &ec))) return (error); bcma_erom_to_core_info(&ec, i, 0, &ci); /* Check for initial match */ if (!bhnd_core_matches(&ci, &imatch)) continue; /* Re-scan preceding cores to determine the unit number. */ next_offset = bcma_erom_tell(sc); bcma_erom_reset(sc); for (u_int j = 0; j < i; j++) { /* Parse the core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); if ((error = bcma_erom_parse_core(sc, &ec))) return (error); /* Bump the unit number? */ if (ec.vendor == ci.vendor && ec.device == ci.device) ci.unit++; } /* Check for full match against now-valid unit number */ if (!bhnd_core_matches(&ci, desc)) { /* Reposition to allow reading the next core */ bcma_erom_seek(sc, next_offset); continue; } /* Found; seek to the core's initial offset and provide * the core info to the caller */ bcma_erom_seek(sc, core_offset); if (core != NULL) *core = ci; return (0); } /* Not found, or a parse error occured */ return (error); } /** * Read the next core descriptor from the EROM table. * * @param erom EROM read state. * @param[out] core On success, will be populated with the parsed core * descriptor data. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero Reading or parsing the core descriptor failed. */ static int bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core) { uint32_t entry; int error; /* Parse CoreDescA */ if ((error = bcma_erom_read32(erom, &entry))) return (error); /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) return (ENOENT); if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n", entry, bcma_erom_entry_type_name(entry)); return (EINVAL); } core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER); core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID); /* Parse CoreDescB */ if ((error = bcma_erom_read32(erom, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { return (EINVAL); } core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV); core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP); core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP); core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP); core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP); return (0); } /** * Read the next master port descriptor from the EROM table. * * @param erom EROM read state. * @param[out] mport On success, will be populated with the parsed * descriptor data. * @retval 0 success * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport) { uint32_t entry; int error; /* Parse the master port descriptor */ if ((error = bcma_erom_read32(erom, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, MPORT)) return (EINVAL); mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID); mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM); return (0); } /** * Read the next slave port region descriptor from the EROM table. * * @param erom EROM read state. * @param[out] mport On success, will be populated with the parsed * descriptor data. * @retval 0 success * @retval ENOENT The end of the region descriptor table was reached. * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_parse_sport_region(struct bcma_erom *erom, struct bcma_erom_sport_region *region) { uint32_t entry; uint8_t size_type; int error; /* Peek at the region descriptor */ if (bcma_erom_peek32(erom, &entry)) return (EINVAL); /* A non-region entry signals the end of the region table */ if (!BCMA_EROM_ENTRY_IS(entry, REGION)) { return (ENOENT); } else { bcma_erom_skip32(erom); } region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE); region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE); /* If region address is 64-bit, fetch the high bits. */ if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->base_addr |= ((bhnd_addr_t) entry << 32); } /* Parse the region size; it's either encoded as the binary logarithm * of the number of 4K pages (i.e. log2 n), or its encoded as a * 32-bit/64-bit literal value directly following the current entry. */ if (size_type == BCMA_EROM_REGION_SIZE_OTHER) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL); if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->size |= ((bhnd_size_t) entry << 32); } } else { region->size = BCMA_EROM_REGION_SIZE_BASE << size_type; } /* Verify that addr+size does not overflow. */ if (region->size != 0 && BHND_ADDR_MAX - (region->size - 1) < region->base_addr) { EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n", bcma_erom_entry_type_name(region->region_type), region->region_port, (unsigned long long) region->base_addr, (unsigned long long) region->size); return (EINVAL); } return (0); } /** * Convert a bcma_erom_core record to its bhnd_core_info representation. * * @param core EROM core record to convert. * @param core_idx The core index of @p core. * @param core_unit The core unit of @p core. * @param[out] info The populated bhnd_core_info representation. */ static void bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx, int core_unit, struct bhnd_core_info *info) { info->vendor = core->vendor; info->device = core->device; info->hwrev = core->rev; info->core_idx = core_idx; info->unit = core_unit; } /** * Map an EROM region type to its corresponding port type. * * @param region_type Region type value. * @param[out] port_type On success, the corresponding port type. */ static int bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type, bhnd_port_type *port_type) { switch (region_type) { case BCMA_EROM_REGION_TYPE_DEVICE: *port_type = BHND_PORT_DEVICE; return (0); case BCMA_EROM_REGION_TYPE_BRIDGE: *port_type = BHND_PORT_BRIDGE; return (0); case BCMA_EROM_REGION_TYPE_MWRAP: case BCMA_EROM_REGION_TYPE_SWRAP: *port_type = BHND_PORT_AGENT; return (0); default: EROM_LOG(erom, "unsupported region type %hhx\n", region_type); return (EINVAL); } } /** * Register all MMIO region descriptors for the given slave port. * * @param erom EROM read state. * @param corecfg Core info to be populated with the scanned port regions. * @param port_num Port index for which regions will be parsed. * @param region_type The region type to be parsed. * @param[out] offset The offset at which to perform parsing. On success, this * will be updated to point to the next EROM table entry. */ static int bcma_erom_corecfg_fill_port_regions(struct bcma_erom *erom, struct bcma_corecfg *corecfg, bcma_pid_t port_num, uint8_t region_type) { struct bcma_sport *sport; struct bcma_sport_list *sports; bus_size_t entry_offset; int error; bhnd_port_type port_type; error = 0; /* Determine the port type for this region type. */ error = bcma_erom_region_to_port_type(erom, region_type, &port_type); if (error) return (error); /* Fetch the list to be populated */ sports = bcma_corecfg_get_port_list(corecfg, port_type); /* Allocate a new port descriptor */ sport = bcma_alloc_sport(port_num, port_type); if (sport == NULL) return (ENOMEM); /* Read all address regions defined for this port */ for (bcma_rmid_t region_num = 0;; region_num++) { struct bcma_map *map; struct bcma_erom_sport_region spr; /* No valid port definition should come anywhere near * BCMA_RMID_MAX. */ if (region_num == BCMA_RMID_MAX) { EROM_LOG(erom, "core%u %s%u: region count reached " "upper limit of %u\n", corecfg->core_info.core_idx, bhnd_port_type_name(port_type), port_num, BCMA_RMID_MAX); error = EINVAL; goto cleanup; } /* Parse the next region entry. */ entry_offset = bcma_erom_tell(erom); error = bcma_erom_parse_sport_region(erom, &spr); if (error && error != ENOENT) { EROM_LOG(erom, "core%u %s%u.%u: invalid slave port " "address region\n", corecfg->core_info.core_idx, bhnd_port_type_name(port_type), port_num, region_num); goto cleanup; } /* ENOENT signals no further region entries */ if (error == ENOENT) { /* No further entries */ error = 0; break; } /* A region or type mismatch also signals no further region * entries */ if (spr.region_port != port_num || spr.region_type != region_type) { /* We don't want to consume this entry */ bcma_erom_seek(erom, entry_offset); error = 0; goto cleanup; } /* * Create the map entry. */ map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT); if (map == NULL) { error = ENOMEM; goto cleanup; } map->m_region_num = region_num; map->m_base = spr.base_addr; map->m_size = spr.size; map->m_rid = -1; /* Add the region map to the port */ STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link); sport->sp_num_maps++; } cleanup: /* Append the new port descriptor on success, or deallocate the * partially parsed descriptor on failure. */ if (error == 0) { STAILQ_INSERT_TAIL(sports, sport, sp_link); } else if (sport != NULL) { bcma_free_sport(sport); } return error; } /** * Parse the next core entry from the EROM table and produce a bcma_corecfg * to be owned by the caller. * * @param erom A bcma EROM instance. * @param[out] result On success, the core's device info. The caller inherits * ownership of this allocation. * * @return If successful, returns 0. If the end of the EROM table is hit, * ENOENT will be returned. On error, returns a non-zero error value. */ int bcma_erom_next_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result) { struct bcma_corecfg *cfg; struct bcma_erom_core core; uint8_t first_region_type; bus_size_t initial_offset; u_int core_index; int core_unit; int error; cfg = NULL; initial_offset = bcma_erom_tell(erom); /* Parse the next core entry */ if ((error = bcma_erom_parse_core(erom, &core))) return (error); /* Determine the core's index and unit numbers */ bcma_erom_reset(erom); core_unit = 0; core_index = 0; for (; bcma_erom_tell(erom) != initial_offset; core_index++) { struct bcma_erom_core prev_core; /* Parse next core */ error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); if ((error = bcma_erom_parse_core(erom, &prev_core))) return (error); /* Is earlier unit? */ if (core.vendor == prev_core.vendor && core.device == prev_core.device) { core_unit++; } /* Seek to next core */ error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); } /* We already parsed the core descriptor */ if ((error = bcma_erom_skip_core(erom))) return (error); /* Allocate our corecfg */ cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor, core.device, core.rev); if (cfg == NULL) return (ENOMEM); /* These are 5-bit values in the EROM table, and should never be able * to overflow BCMA_PID_MAX. */ KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count")); KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count")); KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX, ("unsupported wport count")); if (bootverbose) { EROM_LOG(erom, "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n", core_index, bhnd_vendor_name(core.vendor), bhnd_find_core_name(core.vendor, core.device), core.device, core.rev, core_unit); } cfg->num_master_ports = core.num_mport; cfg->num_dev_ports = 0; /* determined below */ cfg->num_bridge_ports = 0; /* determined blow */ cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap; /* Parse Master Port Descriptors */ for (uint8_t i = 0; i < core.num_mport; i++) { struct bcma_mport *mport; struct bcma_erom_mport mpd; /* Parse the master port descriptor */ error = bcma_erom_parse_mport(erom, &mpd); if (error) goto failed; /* Initialize a new bus mport structure */ mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT); if (mport == NULL) { error = ENOMEM; goto failed; } mport->mp_vid = mpd.port_vid; mport->mp_num = mpd.port_num; /* Update dinfo */ STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link); } /* * Determine whether this is a bridge device; if so, we can * expect the first sequence of address region descriptors to * be of EROM_REGION_TYPE_BRIDGE instead of * BCMA_EROM_REGION_TYPE_DEVICE. * * It's unclear whether this is the correct mechanism by which we * should detect/handle bridge devices, but this approach matches * that of (some of) Broadcom's published drivers. */ if (core.num_dport > 0) { uint32_t entry; if ((error = bcma_erom_peek32(erom, &entry))) goto failed; if (BCMA_EROM_ENTRY_IS(entry, REGION) && BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE) { first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE; cfg->num_dev_ports = 0; cfg->num_bridge_ports = core.num_dport; } else { first_region_type = BCMA_EROM_REGION_TYPE_DEVICE; cfg->num_dev_ports = core.num_dport; cfg->num_bridge_ports = 0; } } /* Device/bridge port descriptors */ for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) { error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, first_region_type); if (error) goto failed; } /* Wrapper (aka device management) descriptors (for master ports). */ for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) { error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, BCMA_EROM_REGION_TYPE_MWRAP); if (error) goto failed; } /* Wrapper (aka device management) descriptors (for slave ports). */ for (uint8_t i = 0; i < core.num_swrap; i++) { /* Slave wrapper ports are not numbered distinctly from master * wrapper ports. */ /* * Broadcom DDR1/DDR2 Memory Controller * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) -> * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2) * * ARM BP135 AMBA3 AXI to APB Bridge * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) -> * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2) * * core.num_mwrap * ===> * (core.num_mwrap > 0) ? * core.num_mwrap : * ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) */ uint8_t sp_num; sp_num = (core.num_mwrap > 0) ? core.num_mwrap : ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i; error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, BCMA_EROM_REGION_TYPE_SWRAP); if (error) goto failed; } /* * Seek to the next core entry (if any), skipping any dangling/invalid * region entries. * * On the BCM4706, the EROM entry for the memory controller core * (0x4bf/0x52E) contains a dangling/unused slave wrapper port region * descriptor. */ if ((error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE))) { if (error != ENOENT) goto failed; } *result = cfg; return (0); failed: if (cfg != NULL) bcma_free_corecfg(cfg); return error; } static int bcma_erom_dump(bhnd_erom_t *erom) { struct bcma_erom *sc; uint32_t entry; int error; sc = (struct bcma_erom *)erom; bcma_erom_reset(sc); while (!(error = bcma_erom_read32(sc, &entry))) { /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) { EROM_LOG(sc, "EOF\n"); return (0); } /* Invalid entry */ if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) { EROM_LOG(sc, "invalid EROM entry %#x\n", entry); return (EINVAL); } switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: { /* CoreDescA */ EROM_LOG(sc, "coreA (0x%x)\n", entry); EROM_LOG(sc, "\tdesigner:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER)); EROM_LOG(sc, "\tid:\t\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREA_ID)); EROM_LOG(sc, "\tclass:\t\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREA_CLASS)); /* CoreDescB */ if ((error = bcma_erom_read32(sc, &entry))) { EROM_LOG(sc, "error reading CoreDescB: %d\n", error); return (error); } if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { EROM_LOG(sc, "invalid core descriptor; found " "unexpected entry %#x (type=%s)\n", entry, bcma_erom_entry_type_name(entry)); return (EINVAL); } EROM_LOG(sc, "coreB (0x%x)\n", entry); EROM_LOG(sc, "\trev:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREB_REV)); EROM_LOG(sc, "\tnummp:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP)); EROM_LOG(sc, "\tnumdp:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP)); EROM_LOG(sc, "\tnumwmp:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP)); EROM_LOG(sc, "\tnumwsp:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP)); break; } case BCMA_EROM_ENTRY_TYPE_MPORT: EROM_LOG(sc, "\tmport 0x%x\n", entry); EROM_LOG(sc, "\t\tport:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, MPORT_NUM)); EROM_LOG(sc, "\t\tid:\t\t0x%x\n", BCMA_EROM_GET_ATTR(entry, MPORT_ID)); break; case BCMA_EROM_ENTRY_TYPE_REGION: { bool addr64; uint8_t size_type; addr64 = (BCMA_EROM_GET_ATTR(entry, REGION_64BIT) != 0); size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE); EROM_LOG(sc, "\tregion 0x%x:\n", entry); EROM_LOG(sc, "\t\t%s:\t0x%x\n", addr64 ? "baselo" : "base", BCMA_EROM_GET_ATTR(entry, REGION_BASE)); EROM_LOG(sc, "\t\tport:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, REGION_PORT)); EROM_LOG(sc, "\t\ttype:\t0x%x\n", BCMA_EROM_GET_ATTR(entry, REGION_TYPE)); EROM_LOG(sc, "\t\tsztype:\t0x%hhx\n", size_type); /* Read the base address high bits */ if (addr64) { if ((error = bcma_erom_read32(sc, &entry))) { EROM_LOG(sc, "error reading region " "base address high bits %d\n", error); return (error); } EROM_LOG(sc, "\t\tbasehi:\t0x%x\n", entry); } /* Read extended size descriptor */ if (size_type == BCMA_EROM_REGION_SIZE_OTHER) { bool size64; if ((error = bcma_erom_read32(sc, &entry))) { EROM_LOG(sc, "error reading region " "size descriptor %d\n", error); return (error); } if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) size64 = true; else size64 = false; EROM_LOG(sc, "\t\t%s:\t0x%x\n", size64 ? "sizelo" : "size", BCMA_EROM_GET_ATTR(entry, RSIZE_VAL)); if (size64) { error = bcma_erom_read32(sc, &entry); if (error) { EROM_LOG(sc, "error reading " "region size high bits: " "%d\n", error); return (error); } EROM_LOG(sc, "\t\tsizehi:\t0x%x\n", entry); } } break; } default: EROM_LOG(sc, "unknown EROM entry 0x%x (type=%s)\n", entry, bcma_erom_entry_type_name(entry)); return (EINVAL); } } if (error == ENOENT) EROM_LOG(sc, "BCMA EROM table missing terminating EOF\n"); else if (error) EROM_LOG(sc, "EROM read failed: %d\n", error); return (error); } static kobj_method_t bcma_erom_methods[] = { KOBJMETHOD(bhnd_erom_probe, bcma_erom_probe), KOBJMETHOD(bhnd_erom_init, bcma_erom_init), KOBJMETHOD(bhnd_erom_fini, bcma_erom_fini), KOBJMETHOD(bhnd_erom_get_core_table, bcma_erom_get_core_table), KOBJMETHOD(bhnd_erom_free_core_table, bcma_erom_free_core_table), KOBJMETHOD(bhnd_erom_lookup_core, bcma_erom_lookup_core), KOBJMETHOD(bhnd_erom_lookup_core_addr, bcma_erom_lookup_core_addr), KOBJMETHOD(bhnd_erom_dump, bcma_erom_dump), KOBJMETHOD_END }; BHND_EROM_DEFINE_CLASS(bcma_erom, bcma_erom_parser, bcma_erom_methods, sizeof(struct bcma_erom)); Index: head/sys/dev/bhnd/nvram/bhnd_nvram_private.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_private.h (revision 327830) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_private.h (revision 327831) @@ -1,459 +1,459 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ #define _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ /* * Private BHND NVRAM definitions. */ #include #ifdef _KERNEL #include #include #else #include #include #include #include #endif #include "bhnd_nvram.h" #include "bhnd_nvram_value.h" /* * bhnd_nvram_crc8() lookup table. */ extern const uint8_t bhnd_nvram_crc8_tab[]; /* Forward declarations */ struct bhnd_nvram_vardefn; #ifdef _KERNEL MALLOC_DECLARE(M_BHND_NVRAM); #define bhnd_nv_isupper(c) isupper(c) #define bhnd_nv_islower(c) islower(c) #define bhnd_nv_isalpha(c) isalpha(c) #define bhnd_nv_isprint(c) isprint(c) #define bhnd_nv_isspace(c) isspace(c) #define bhnd_nv_isdigit(c) isdigit(c) #define bhnd_nv_isxdigit(c) isxdigit(c) #define bhnd_nv_toupper(c) toupper(c) #define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_NOWAIT) -#define bhnd_nv_calloc(n, size) malloc((n) * (size), M_BHND_NVRAM, \ +#define bhnd_nv_calloc(n, size) mallocarray((n), (size), M_BHND_NVRAM, \ M_NOWAIT | M_ZERO) #define bhnd_nv_reallocf(buf, size) reallocf((buf), (size), M_BHND_NVRAM, \ M_NOWAIT) #define bhnd_nv_free(buf) free((buf), M_BHND_NVRAM) #define bhnd_nv_asprintf(buf, fmt, ...) asprintf((buf), M_BHND_NVRAM, \ fmt, ## __VA_ARGS__) /* We need our own strdup() implementation to pass required M_NOWAIT */ static inline char * bhnd_nv_strdup(const char *str) { char *dest; size_t len; len = strlen(str); dest = malloc(len + 1, M_BHND_NVRAM, M_NOWAIT); memcpy(dest, str, len); dest[len] = '\0'; return (dest); } /* We need our own strndup() implementation to pass required M_NOWAIT */ static inline char * bhnd_nv_strndup(const char *str, size_t len) { char *dest; len = strnlen(str, len); dest = malloc(len + 1, M_BHND_NVRAM, M_NOWAIT); memcpy(dest, str, len); dest[len] = '\0'; return (dest); } #ifdef INVARIANTS #define BHND_NV_INVARIANTS #endif #define BHND_NV_ASSERT(expr, ...) KASSERT(expr, __VA_ARGS__) #define BHND_NV_VERBOSE (bootverbose) #define BHND_NV_PANIC(...) panic(__VA_ARGS__) #define BHND_NV_LOG(fmt, ...) \ printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) #define bhnd_nv_ummax(a, b) ummax((a), (b)) #define bhnd_nv_ummin(a, b) ummin((a), (b)) #else /* !_KERNEL */ #include #include #include #include /* ASCII-specific ctype variants that work consistently regardless * of current locale */ #define bhnd_nv_isupper(c) ((c) >= 'A' && (c) <= 'Z') #define bhnd_nv_islower(c) ((c) >= 'a' && (c) <= 'z') #define bhnd_nv_isalpha(c) (bhnd_nv_isupper(c) || bhnd_nv_islower(c)) #define bhnd_nv_isprint(c) ((c) >= ' ' && (c) <= '~') #define bhnd_nv_isspace(c) ((c) == ' ' || ((c) >= '\t' && (c) <= '\r')) #define bhnd_nv_isdigit(c) isdigit(c) #define bhnd_nv_isxdigit(c) isxdigit(c) #define bhnd_nv_toupper(c) ((c) - \ (('a' - 'A') * ((c) >= 'a' && (c) <= 'z'))) #define bhnd_nv_malloc(size) malloc((size)) #define bhnd_nv_calloc(n, size) calloc((n), (size)) #define bhnd_nv_reallocf(buf, size) reallocf((buf), (size)) #define bhnd_nv_free(buf) free((buf)) #define bhnd_nv_strdup(str) strdup(str) #define bhnd_nv_strndup(str, len) strndup(str, len) #define bhnd_nv_asprintf(buf, fmt, ...) asprintf((buf), fmt, ## __VA_ARGS__) #ifndef NDEBUG #define BHND_NV_INVARIANTS #endif #ifdef BHND_NV_INVARIANTS #define BHND_NV_ASSERT(expr, msg) do { \ if (!(expr)) { \ fprintf(stderr, "Assertion failed: %s, function %s, " \ "file %s, line %u\n", __STRING(expr), __FUNCTION__, \ __FILE__, __LINE__); \ BHND_NV_PANIC msg; \ } \ } while(0) #else /* !BHND_NV_INVARIANTS */ #define BHND_NV_ASSERT(expr, msg) #endif /* BHND_NV_INVARIANTS */ #define BHND_NV_VERBOSE (0) #define BHND_NV_PANIC(fmt, ...) do { \ fprintf(stderr, "panic: " fmt "\n", ##__VA_ARGS__); \ abort(); \ } while(0) #define BHND_NV_LOG(fmt, ...) \ fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) static inline uintmax_t bhnd_nv_ummax(uintmax_t a, uintmax_t b) { return (a > b ? a : b); } static inline uintmax_t bhnd_nv_ummin(uintmax_t a, uintmax_t b) { return (a < b ? a : b); } #endif /* _KERNEL */ #ifdef BHND_NV_VERBOSE #define BHND_NV_DEBUG(...) BHND_NV_LOG(__VA_ARGS__) #else /* !BHND_NV_VERBOSE */ #define BHND_NV_DEBUG(...) #endif /* BHND_NV_VERBOSE */ /* Limit a size_t value to a suitable range for use as a printf string field * width */ #define BHND_NV_PRINT_WIDTH(_len) \ ((_len) > (INT_MAX) ? (INT_MAX) : (int)(_len)) int bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype); int bhnd_nvram_value_check_aligned(const void *inp, size_t ilen, bhnd_nvram_type itype); int bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype, size_t *nelem); size_t bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype, size_t nelem); int bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, char *outp, size_t *olen, ...); int bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap); const void *bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype, const void *prev, size_t *olen); const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname); const struct bhnd_nvram_vardefn *bhnd_nvram_get_vardefn(size_t id); size_t bhnd_nvram_get_vardefn_id( const struct bhnd_nvram_vardefn *defn); int bhnd_nvram_parse_int(const char *s, size_t maxlen, u_int base, size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype); int bhnd_nvram_parse_env(const char *env, size_t env_len, char delim, const char **name, size_t *name_len, const char **value, size_t *value_len); size_t bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim); size_t bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim); const char *bhnd_nvram_trim_path_name(const char *name); bool bhnd_nvram_validate_name(const char *name); /** * Calculate CRC-8 over @p buf using the Broadcom SPROM/NVRAM CRC-8 * polynomial. * * @param buf input buffer * @param size buffer size * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL */ static inline uint8_t bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc) { const uint8_t *p = (const uint8_t *)buf; while (size--) crc = bhnd_nvram_crc8_tab[(crc ^ *p++)]; return (crc); } #define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */ #define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */ /** NVRAM variable flags */ enum { BHND_NVRAM_VF_MFGINT = 1<<0, /**< mfg-internal variable; should not be externally visible */ BHND_NVRAM_VF_IGNALL1 = 1<<1 /**< hide variable if its value has all bits set. */ }; /** * SPROM layout flags */ enum { /** * SPROM layout does not have magic identification value. * * This applies to SPROM revisions 1-3, where the actual * layout must be determined by looking for a matching sromrev * at the expected offset, and then verifying the CRC to ensure * that the match was not a false positive. */ SPROM_LAYOUT_MAGIC_NONE = (1<<0), }; /** NVRAM variable definition */ struct bhnd_nvram_vardefn { const char *name; /**< variable name */ const char *desc; /**< human readable description, or NULL */ const char *help; /**< human readable help text, or NULL */ bhnd_nvram_type type; /**< variable type */ uint8_t nelem; /**< element count, or 1 if not an array-typed variable */ const bhnd_nvram_val_fmt *fmt; /**< value format */ uint32_t flags; /**< flags (BHND_NVRAM_VF_*) */ }; /* * NVRAM variable definitions generated from nvram_map. */ extern const struct bhnd_nvram_vardefn bhnd_nvram_vardefns[]; extern const size_t bhnd_nvram_num_vardefns; /** * SPROM layout descriptor. */ typedef struct bhnd_sprom_layout { size_t size; /**< SPROM image size, in bytes */ uint8_t rev; /**< SPROM revision */ uint8_t flags; /**< layout flags (SPROM_LAYOUT_*) */ size_t srev_offset; /**< offset to SROM revision */ size_t magic_offset; /**< offset to magic value */ uint16_t magic_value; /**< expected magic value */ size_t crc_offset; /**< offset to crc8 value */ const uint8_t *bindings; /**< SPROM binding opcode table */ size_t bindings_size; /**< SPROM binding opcode table size */ uint16_t num_vars; /**< total number of variables defined for this layout by the binding table */ } bhnd_sprom_layout; /* * SPROM layout descriptions generated from nvram_map. */ extern const struct bhnd_sprom_layout bhnd_sprom_layouts[]; extern const size_t bhnd_sprom_num_layouts; /* * SPROM binding opcodes. * * Most opcodes are provided with two variants: * * - Standard: The opcode's data directly follows the opcode. The data type * (SPROM_OPCODE_DATA_*) is encoded in the opcode immediate (IMM). * - Immediate: The opcode's data is encoded directly in the opcode immediate * (IMM). */ #define SPROM_OPC_MASK 0xF0 /**< operation mask */ #define SPROM_IMM_MASK 0x0F /**< immediate value mask */ #define SPROM_IMM_MAX SPROM_IMM_MASK #define SPROM_OP_DATA_U8 0x00 /**< data is u8 */ #define SPROM_OP_DATA_U8_SCALED 0x01 /**< data is u8; multiply by type width */ #define SPROM_OP_DATA_U16 0x02 /**< data is u16-le */ #define SPROM_OP_DATA_U32 0x03 /**< data is u32-le */ #define SPROM_OP_DATA_I8 0x04 /**< data is i8 */ #define SPROM_OPCODE_EXT 0x00 /**< extended opcodes defined in IMM */ #define SPROM_OPCODE_EOF 0x00 /**< marks end of opcode stream */ #define SPROM_OPCODE_NELEM 0x01 /**< variable array element count follows as U8 */ #define SPROM_OPCODE_VAR_END 0x02 /**< marks end of variable definition */ #define SPROM_OPCODE_TYPE 0x03 /**< input type follows as U8 (see BHND_NVRAM_TYPE_*) */ #define SPROM_OPCODE_VAR_IMM 0x10 /**< variable ID (imm) */ #define SPROM_OPCODE_VAR_REL_IMM 0x20 /**< relative variable ID (last ID + imm) */ #define SPROM_OPCODE_VAR 0x30 /**< variable ID */ #define SPROM_OPCODE_REV_IMM 0x40 /**< revision range (imm) */ #define SPROM_OPCODE_REV_RANGE 0x50 /**< revision range (8-bit range)*/ #define SPROM_OP_REV_RANGE_MAX 0x0F /**< maximum representable SROM revision */ #define SPROM_OP_REV_START_MASK 0xF0 #define SPROM_OP_REV_START_SHIFT 4 #define SPROM_OP_REV_END_MASK 0x0F #define SPROM_OP_REV_END_SHIFT 0 #define SPROM_OPCODE_MASK_IMM 0x60 /**< value mask (imm) */ #define SPROM_OPCODE_MASK 0x70 /**< value mask */ #define SPROM_OPCODE_SHIFT_IMM 0x80 /**< value shift (unsigned imm, multipled by 2) */ #define SPROM_OPCODE_SHIFT 0x90 /**< value shift */ #define SPROM_OPCODE_OFFSET_REL_IMM 0xA0 /**< relative input offset (last offset + (imm * type width)) */ #define SPROM_OPCODE_OFFSET 0xB0 /**< input offset */ #define SPROM_OPCODE_TYPE_IMM 0xC0 /**< input type (imm, see BHND_NVRAM_TYPE_*) */ #define SPROM_OPCODE_DO_BIND 0xD0 /**< bind current value, advance input/output offsets as per IMM */ #define SPROM_OP_BIND_SKIP_IN_MASK 0x03 /**< the number of input elements to advance after the bind */ #define SPROM_OP_BIND_SKIP_IN_SHIFT 0 #define SPROM_OP_BIND_SKIP_IN_SIGN (1<<2) /**< SKIP_IN sign bit */ #define SPROM_OP_BIND_SKIP_OUT_MASK 0x08 /**< the number of output elements to advance after the bind */ #define SPROM_OP_BIND_SKIP_OUT_SHIFT 3 #define SPROM_OPCODE_DO_BINDN_IMM 0xE0 /**< bind IMM times, advancing input/output offsets by one element each time */ #define SPROM_OPCODE_DO_BINDN 0xF0 /**< bind N times, advancing input/output offsets as per SPROM_OP_BIND_SKIP_IN/SPROM_OP_BIND_SKIP_OUT IMM values. The U8 element count follows. */ /** Evaluates to true if opcode is an extended opcode */ #define SPROM_OPCODE_IS_EXT(_opcode) \ (((_opcode) & SPROM_OPC_MASK) == SPROM_OPCODE_EXT) /** Return the opcode constant for a simple or extended opcode */ #define SPROM_OPCODE_OP(_opcode) \ (SPROM_OPCODE_IS_EXT(_opcode) ? (_opcode) : ((_opcode) & SPROM_OPC_MASK)) /** Return the opcode immediate for a simple opcode, or zero if this is * an extended opcode */ #define SPROM_OPCODE_IMM(_opcode) \ (SPROM_OPCODE_IS_EXT(_opcode) ? 0 : ((_opcode) & SPROM_IMM_MASK)) /** Evaluates to true if the given opcode produces an implicit * SPROM_OPCODE_VAR_END instruction for any open variable */ #define SPROM_OP_IS_IMPLICIT_VAR_END(_opcode) \ (((_opcode) == SPROM_OPCODE_VAR_IMM) || \ ((_opcode) == SPROM_OPCODE_VAR_REL_IMM) || \ ((_opcode) == SPROM_OPCODE_VAR) || \ ((_opcode) == SPROM_OPCODE_REV_IMM) || \ ((_opcode) == SPROM_OPCODE_REV_RANGE)) /** Evaluates to true if the given opcode is either an explicit * SPROM_OPCODE_VAR_END instruction, or is an opcode that produces an * implicit terminatation of any open variable */ #define SPROM_OP_IS_VAR_END(_opcode) \ (((_opcode) == SPROM_OPCODE_VAR_END) || \ SPROM_OP_IS_IMPLICIT_VAR_END(_opcode)) /** maximum representable immediate value */ #define SPROM_OP_IMM_MAX SPROM_IMM_MASK /** maximum representable SROM revision */ #define SPROM_OP_REV_MAX MAX(SPROM_OP_REV_RANGE_MAX, SPROM_IMM_MAX) #endif /* _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ */ Index: head/sys/dev/bhnd/siba/siba_erom.c =================================================================== --- head/sys/dev/bhnd/siba/siba_erom.c (revision 327830) +++ head/sys/dev/bhnd/siba/siba_erom.c (revision 327831) @@ -1,553 +1,553 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Landon Fuller * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "sibareg.h" #include "sibavar.h" struct siba_erom; struct siba_erom_io; static int siba_eio_init(struct siba_erom_io *io, struct bhnd_erom_io *eio, u_int ncores); static uint32_t siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset); static struct siba_core_id siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit); static int siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr, struct bhnd_chipid *cid); /** * SIBA EROM generic I/O context */ struct siba_erom_io { struct bhnd_erom_io *eio; /**< erom I/O callbacks */ bhnd_addr_t base_addr; /**< address of first core */ u_int ncores; /**< core count */ }; /** * SIBA EROM per-instance state. */ struct siba_erom { struct bhnd_erom obj; struct siba_erom_io io; /**< i/o context */ }; #define EROM_LOG(io, fmt, ...) do { \ printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \ } while(0) /* SIBA implementation of BHND_EROM_PROBE() */ static int siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { struct siba_erom_io io; uint32_t idreg; int error; /* Initialize I/O context, assuming at least the first core is mapped */ if ((error = siba_eio_init(&io, eio, 1))) return (error); /* Try using the provided hint. */ if (hint != NULL) { struct siba_core_id sid; /* Validate bus type */ if (hint->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); /* * Verify the first core's IDHIGH/IDLOW identification. * * The core must be a Broadcom core, but must *not* be * a chipcommon core; those shouldn't be hinted. * * The first core on EXTIF-equipped devices varies, but on the * BCM4710, it's a SDRAM core (0x803). */ sid = siba_eio_read_core_id(&io, 0, 0); if (sid.core_info.vendor != BHND_MFGID_BCM) return (ENXIO); if (sid.core_info.device == BHND_COREID_CC) return (EINVAL); *cid = *hint; } else { /* Validate bus type */ idreg = siba_eio_read_4(&io, 0, CHIPC_ID); if (CHIPC_GET_BITS(idreg, CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA) return (ENXIO); /* Identify the chipset */ if ((error = siba_eio_read_chipid(&io, SIBA_ENUM_ADDR, cid))) return (error); /* Verify the chip type */ if (cid->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); } /* * gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES * without triggering build failure due to -Wtype-limits * * if (cid.ncores > SIBA_MAX_CORES) * return (EINVAL) */ _Static_assert((2^sizeof(cid->ncores)) <= SIBA_MAX_CORES, "ncores could result in over-read of backing resource"); return (0); } /* SIBA implementation of BHND_EROM_INIT() */ static int siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid, struct bhnd_erom_io *eio) { struct siba_erom *sc; int error; sc = (struct siba_erom *)erom; /* Attempt to map the full core enumeration space */ error = bhnd_erom_io_map(eio, cid->enum_addr, cid->ncores * SIBA_CORE_SIZE); if (error) { printf("%s: failed to map %u cores: %d\n", __FUNCTION__, cid->ncores, error); return (error); } /* Initialize I/O context */ return (siba_eio_init(&sc->io, eio, cid->ncores)); } /* SIBA implementation of BHND_EROM_FINI() */ static void siba_erom_fini(bhnd_erom_t *erom) { struct siba_erom *sc = (struct siba_erom *)erom; bhnd_erom_io_fini(sc->io.eio); } /* Initialize siba_erom resource I/O context */ static int siba_eio_init(struct siba_erom_io *io, struct bhnd_erom_io *eio, u_int ncores) { io->eio = eio; io->ncores = ncores; return (0); } /** * Read a 32-bit value from @p offset relative to the base address of * the given @p core_idx. * * @param io EROM I/O context. * @param core_idx Core index. * @param offset Core register offset. */ static uint32_t siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset) { /* Sanity check core index and offset */ if (core_idx >= io->ncores) panic("core index %u out of range (ncores=%u)", core_idx, io->ncores); if (offset > SIBA_CORE_SIZE - sizeof(uint32_t)) panic("invalid core offset %#jx", (uintmax_t)offset); /* Perform read */ return (bhnd_erom_io_read(io->eio, SIBA_CORE_OFFSET(core_idx) + offset, 4)); } /** * Read and parse identification registers for the given @p core_index. * * @param io EROM I/O context. * @param core_idx The core index. * @param unit The caller-specified unit number to be included in the return * value. */ static struct siba_core_id siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit) { uint32_t idhigh, idlow; idhigh = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); idlow = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW)); return (siba_parse_core_id(idhigh, idlow, core_idx, unit)); } /** * Read and parse the chip identification register from the ChipCommon core. * * @param io EROM I/O context. * @param enum_addr The physical address mapped by @p io. * @param cid On success, the parsed chip identifier. */ static int siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr, struct bhnd_chipid *cid) { struct siba_core_id ccid; uint32_t idreg; /* Identify the chipcommon core */ ccid = siba_eio_read_core_id(io, 0, 0); if (ccid.core_info.vendor != BHND_MFGID_BCM || ccid.core_info.device != BHND_COREID_CC) { if (bootverbose) { EROM_LOG(io, "first core not chipcommon " "(vendor=%#hx, core=%#hx)\n", ccid.core_info.vendor, ccid.core_info.device); } return (ENXIO); } /* Identify the chipset */ idreg = siba_eio_read_4(io, 0, CHIPC_ID); *cid = bhnd_parse_chipid(idreg, enum_addr); /* Fix up the core count in-place */ return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev, &cid->ncores)); } static int siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct siba_erom *sc; struct bhnd_core_match imatch; sc = (struct siba_erom *)erom; /* We can't determine a core's unit number during the initial scan. */ imatch = *desc; imatch.m.match.core_unit = 0; /* Locate the first matching core */ for (u_int i = 0; i < sc->io.ncores; i++) { struct siba_core_id sid; struct bhnd_core_info ci; /* Read the core info */ sid = siba_eio_read_core_id(&sc->io, i, 0); ci = sid.core_info; /* Check for initial match */ if (!bhnd_core_matches(&ci, &imatch)) continue; /* Re-scan preceding cores to determine the unit number. */ for (u_int j = 0; j < i; j++) { sid = siba_eio_read_core_id(&sc->io, j, 0); /* Bump the unit number? */ if (sid.core_info.vendor == ci.vendor && sid.core_info.device == ci.device) ci.unit++; } /* Check for full match against now-valid unit number */ if (!bhnd_core_matches(&ci, desc)) continue; /* Matching core found */ *core = ci; return (0); } /* Not found */ return (ENOENT); } static int siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *info, bhnd_addr_t *addr, bhnd_size_t *size) { struct siba_erom *sc; struct bhnd_core_info core; struct siba_core_id sid; uint32_t am, am_addr, am_size; u_int am_offset; u_int addrspace, cfg; int error; sc = (struct siba_erom *)erom; /* Locate the requested core */ if ((error = siba_erom_lookup_core(erom, desc, &core))) return (error); /* Fetch full siba core ident */ sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit); /* Is port valid? */ if (!siba_is_port_valid(&sid, type, port)) return (ENOENT); /* Is region valid? */ if (region >= siba_port_region_count(&sid, type, port)) return (ENOENT); /* Is this a siba configuration region? If so, this is mapped to an * offset within the device0.0 port */ error = siba_cfg_index(&sid, type, port, region, &cfg); if (!error) { bhnd_addr_t region_addr; bhnd_addr_t region_size; bhnd_size_t cfg_offset, cfg_size; cfg_offset = SIBA_CFG_OFFSET(cfg); cfg_size = SIBA_CFG_SIZE; /* Fetch the device0.0 addr/size */ error = siba_erom_lookup_core_addr(erom, desc, BHND_PORT_DEVICE, 0, 0, NULL, ®ion_addr, ®ion_size); if (error) return (error); /* Verify that our offset fits within the region */ if (region_size < cfg_size) { printf("%s%u.%u offset %ju exceeds %s0.0 size %ju\n", bhnd_port_type_name(type), port, region, cfg_offset, bhnd_port_type_name(BHND_PORT_DEVICE), region_size); return (ENXIO); } if (BHND_ADDR_MAX - region_addr < cfg_offset) { printf("%s%u.%u offset %ju would overflow %s0.0 addr " "%ju\n", bhnd_port_type_name(type), port, region, cfg_offset, bhnd_port_type_name(BHND_PORT_DEVICE), region_addr); return (ENXIO); } if (info != NULL) *info = core; *addr = region_addr + cfg_offset; *size = cfg_size; return (0); } /* * Otherwise, must be a device port. * * Map the bhnd device port to a siba addrspace index. Unlike siba(4) * bus drivers, we do not exclude the siba(4) configuration blocks from * the first device port. */ error = siba_addrspace_index(&sid, type, port, region, &addrspace); if (error) return (error); /* Determine the register offset */ am_offset = siba_admatch_offset(addrspace); if (am_offset == 0) { printf("addrspace %u is unsupported", addrspace); return (ENODEV); } /* Read and parse the address match register */ am = siba_eio_read_4(&sc->io, core.core_idx, am_offset); if ((error = siba_parse_admatch(am, &am_addr, &am_size))) { printf("failed to decode address match register value 0x%x\n", am); return (error); } if (info != NULL) *info = core; *addr = am_addr; *size = am_size; return (0); } /* BHND_EROM_GET_CORE_TABLE() */ static int siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { struct siba_erom *sc; struct bhnd_core_info *out; sc = (struct siba_erom *)erom; /* Allocate our core array */ - out = malloc(sizeof(*out) * sc->io.ncores, M_BHND, M_NOWAIT); + out = mallocarray(sc->io.ncores, sizeof(*out), M_BHND, M_NOWAIT); if (out == NULL) return (ENOMEM); *cores = out; *num_cores = sc->io.ncores; /* Enumerate all cores. */ for (u_int i = 0; i < sc->io.ncores; i++) { struct siba_core_id sid; /* Read the core info */ sid = siba_eio_read_core_id(&sc->io, i, 0); out[i] = sid.core_info; /* Determine unit number */ for (u_int j = 0; j < i; j++) { if (out[j].vendor == out[i].vendor && out[j].device == out[i].device) out[i].unit++; } } return (0); } /* BHND_EROM_FREE_CORE_TABLE() */ static void siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { free(cores, M_BHND); } /* BHND_EROM_DUMP() */ static int siba_erom_dump(bhnd_erom_t *erom) { struct siba_erom *sc; int error; sc = (struct siba_erom *)erom; /* Enumerate all cores. */ for (u_int i = 0; i < sc->io.ncores; i++) { uint32_t idhigh, idlow; uint32_t nraddr; idhigh = siba_eio_read_4(&sc->io, i, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); idlow = siba_eio_read_4(&sc->io, i, SB0_REG_ABS(SIBA_CFG0_IDLOW)); printf("siba core %u:\n", i); printf("\tvendor:\t0x%04x\n", SIBA_REG_GET(idhigh, IDH_VENDOR)); printf("\tdevice:\t0x%04x\n", SIBA_REG_GET(idhigh, IDH_DEVICE)); printf("\trev:\t0x%04x\n", SIBA_IDH_CORE_REV(idhigh)); printf("\tsbrev:\t0x%02x\n", SIBA_REG_GET(idlow, IDL_SBREV)); /* Enumerate the address match registers */ nraddr = SIBA_REG_GET(idlow, IDL_NRADDR); printf("\tnraddr\t0x%04x\n", nraddr); for (size_t addrspace = 0; addrspace < nraddr; addrspace++) { uint32_t am, am_addr, am_size; u_int am_offset; /* Determine the register offset */ am_offset = siba_admatch_offset(addrspace); if (am_offset == 0) { printf("addrspace %zu unsupported", addrspace); break; } /* Read and parse the address match register */ am = siba_eio_read_4(&sc->io, i, am_offset); error = siba_parse_admatch(am, &am_addr, &am_size); if (error) { printf("failed to decode address match " "register value 0x%x\n", am); continue; } printf("\taddrspace %zu\n", addrspace); printf("\t\taddr: 0x%08x\n", am_addr); printf("\t\tsize: 0x%08x\n", am_size); } } return (0); } static kobj_method_t siba_erom_methods[] = { KOBJMETHOD(bhnd_erom_probe, siba_erom_probe), KOBJMETHOD(bhnd_erom_init, siba_erom_init), KOBJMETHOD(bhnd_erom_fini, siba_erom_fini), KOBJMETHOD(bhnd_erom_get_core_table, siba_erom_get_core_table), KOBJMETHOD(bhnd_erom_free_core_table, siba_erom_free_core_table), KOBJMETHOD(bhnd_erom_lookup_core, siba_erom_lookup_core), KOBJMETHOD(bhnd_erom_lookup_core_addr, siba_erom_lookup_core_addr), KOBJMETHOD(bhnd_erom_dump, siba_erom_dump), KOBJMETHOD_END }; BHND_EROM_DEFINE_CLASS(siba_erom, siba_erom_parser, siba_erom_methods, sizeof(struct siba_erom));