| /* |
| * Copyright (c) 2024, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <arch_helpers.h> |
| #include <debug.h> |
| #include <drivers/arm/private_timer.h> |
| #include <events.h> |
| #include "fifo3d.h" |
| #include "nfifo.h" |
| #include <libfdt.h> |
| |
| #include <plat_topology.h> |
| #include <power_management.h> |
| #include <tftf_lib.h> |
| |
| extern char _binary___dtb_start[]; |
| extern void runtestfunction(int funcid); |
| |
| struct memmod tmod __aligned(65536) __section("smcfuzz"); |
| static int cntndarray; |
| static struct rand_smc_node *ndarray; |
| static struct memmod *mmod; |
| |
| /* |
| * switch to use either standard C malloc or custom SMC malloc |
| */ |
| |
| #define FIRST_NODE_DEVTREE_OFFSET (8) |
| |
| #ifdef SMC_FUZZ_TMALLOC |
| #define GENMALLOC(x) malloc((x)) |
| #define GENFREE(x) free((x)) |
| #else |
| #define GENMALLOC(x) smcmalloc((x), mmod) |
| #define GENFREE(x) smcfree((x), mmod) |
| #endif |
| |
| /* |
| * Device tree parameter struct |
| */ |
| |
| struct fdt_header_sf { |
| unsigned int magic; |
| unsigned int totalsize; |
| unsigned int off_dt_struct; |
| unsigned int off_dt_strings; |
| unsigned int off_mem_rsvmap; |
| unsigned int version; |
| unsigned int last_comp_version; |
| unsigned int boot_cpuid_phys; |
| unsigned int size_dt_strings; |
| unsigned int size_dt_struct; |
| }; |
| |
| /* |
| * Structure to read the fields of the device tree |
| */ |
| struct propval { |
| unsigned int len; |
| unsigned int nameoff; |
| }; |
| |
| /* |
| * Converting from big endian to little endian to read values |
| * of device tree |
| */ |
| unsigned int lendconv(unsigned int val) |
| { |
| unsigned int res; |
| |
| res = val << 24; |
| res |= ((val << 8) & 0xFF0000U); |
| res |= ((val >> 8) & 0xFF00U); |
| res |= ((val >> 24) & 0xFFU); |
| return res; |
| } |
| |
| /* |
| * Function to read strings from device tree |
| */ |
| void pullstringdt(void **dtb, |
| void *dtb_beg, |
| unsigned int offset, |
| char *cset) |
| { |
| int fistr; |
| int cntchr; |
| char rval; |
| |
| if (offset != 0U) { |
| *dtb = dtb_beg + offset; |
| } |
| fistr = 0; |
| |
| cntchr = 0; |
| while (fistr == 0) { |
| rval = *((char *)*dtb); |
| *dtb += sizeof(char); |
| cset[cntchr] = rval; |
| if (cset[cntchr] == 0) { |
| fistr = 1; |
| } |
| cntchr++; |
| } |
| |
| if ((cntchr % 4) != 0) { |
| for (unsigned int i = 0U; (int)i < (4 - (cntchr % 4)); i++) { |
| *dtb += sizeof(char); |
| } |
| } |
| } |
| |
| /* |
| * Structure for Node information extracted from device tree |
| */ |
| struct rand_smc_node { |
| int *biases; // Biases of the individual nodes |
| int *biasarray; // Array of biases across all nodes |
| char **snames; // String that is unique to the SMC call called in test |
| int *snameid; // ID that is unique to the SMC call called in test |
| struct rand_smc_node *treenodes; // Selection of nodes that are farther down in the tree |
| // that reference further rand_smc_node objects |
| int *norcall; // Specifies whether a particular node is a leaf node or tree node |
| int entries; // Number of nodes in object |
| int biasent; // Number that gives the total number of entries in biasarray |
| // based on all biases of the nodes |
| char **nname; // Array of node names |
| }; |
| |
| |
| /* |
| * Create bias tree from given device tree description |
| */ |
| |
| struct rand_smc_node *createsmctree(int *casz, |
| struct memmod *mmod) |
| { |
| void *dtb; |
| void *dtb_pn; |
| void *dtb_beg; |
| struct fdt_header fhd; |
| unsigned int rval; |
| struct propval pv; |
| char cset[MAX_NAME_CHARS]; |
| char nodename[MAX_NAME_CHARS]; |
| int dtdone; |
| struct fifo3d f3d; |
| int leafnode = 0; |
| unsigned int fnode = 0U; |
| unsigned int bias_count = 0U; |
| unsigned int bintnode = 0U; |
| unsigned int treenodetrack = 0U; |
| struct fdt_header *fhdptr; |
| struct rand_smc_node *ndarray = NULL; |
| int cntndarray; |
| struct rand_smc_node nrnode; |
| struct rand_smc_node *tndarray; |
| struct nfifo nf; |
| |
| nfifoinit(&nf, mmod); |
| |
| f3d.col = 0; |
| f3d.curr_col = 0; |
| |
| /* |
| * Read device tree header and check for valid type |
| */ |
| |
| fhdptr = (struct fdt_header *)_binary___dtb_start; |
| |
| fhd = *fhdptr; |
| cntndarray = 0; |
| nrnode.entries = 0; |
| |
| /* |
| * Create pointers to device tree data |
| */ |
| dtb = _binary___dtb_start; |
| dtb_pn = _binary___dtb_start; |
| |
| dtb_beg = dtb; |
| fhd = *((struct fdt_header *)dtb); |
| dtb += (fdt32_to_cpu(fhd.off_dt_struct) + FIRST_NODE_DEVTREE_OFFSET); |
| dtdone = 0; |
| |
| /* |
| * Reading device tree file |
| */ |
| while (dtdone == 0) { |
| rval = *((unsigned int *)dtb); |
| dtb += sizeof(unsigned int); |
| |
| /* |
| * Reading node name from device tree and pushing it into the raw data |
| * Table of possible values reading from device tree binary file: |
| * 1 New node found within current tree, possible leaf or tree variant |
| * 2 Node termination of current hiearchy. |
| * Could indicate end of tree or preparation for another branch |
| * 3 Leaf node indication where a bias with a function name should be |
| * found for the current node |
| * 9 End of device tree file and we end the read of the bias tree |
| */ |
| if (fdt32_to_cpu(rval) == 1) { |
| pullstringdt(&dtb, dtb_beg, 0U, cset); |
| push_3dfifo_col(&f3d, cset, mmod); |
| strlcpy(nodename, cset, MAX_NAME_CHARS); |
| |
| /* |
| * Error checking to make sure that bias is specified |
| */ |
| if (fnode == 0U) { |
| fnode = 1U; |
| } else { |
| if (!((fnode == 1U) && (bias_count == 1U))) { |
| printf("ERROR: Did not find bias or multiple bias "); |
| printf("designations before %s %u %u\n", |
| cset, fnode, bias_count); |
| } |
| bias_count = 0U; |
| } |
| } |
| |
| /* |
| * Reading node parameters of bias and function name |
| */ |
| if (fdt32_to_cpu(rval) == 3) { |
| pv = *((struct propval *)dtb); |
| dtb += sizeof(struct propval); |
| pullstringdt(&dtb_pn, dtb_beg, |
| (fdt32_to_cpu(fhd.off_dt_strings) + |
| fdt32_to_cpu(pv.nameoff)), cset); |
| if (strcmp(cset, "bias") == 0) { |
| rval = *((unsigned int *)dtb); |
| dtb += sizeof(unsigned int); |
| push_3dfifo_bias(&f3d, fdt32_to_cpu(rval)); |
| bias_count++; |
| if (bintnode == 1U) { |
| fnode = 0U; |
| bintnode = 0U; |
| bias_count = 0U; |
| } |
| } |
| if (strcmp(cset, "functionname") == 0) { |
| pullstringdt(&dtb, dtb_beg, 0, cset); |
| push_3dfifo_fname(&f3d, cset); |
| pushnme(cset, &nf, mmod); |
| push_3dfifo_fid(&f3d, searchnme(cset, &nf, mmod)); |
| leafnode = 1; |
| if (bias_count == 0U) { |
| bintnode = 1U; |
| fnode = 1U; |
| } else { |
| bias_count = 0U; |
| fnode = 0U; |
| } |
| } |
| } |
| |
| /* |
| * Node termination and evaluate whether the bias tree requires addition. |
| * The non tree nodes are added. |
| */ |
| if (fdt32_to_cpu(rval) == 2) { |
| if ((fnode > 0U) || (bias_count > 0U)) { |
| printf("ERROR: early node termination... "); |
| printf("no bias or functionname field for leaf node, near %s %u\n", |
| nodename, fnode); |
| } |
| f3d.col--; |
| if (leafnode == 1) { |
| leafnode = 0; |
| } else { |
| /* |
| * Create bias tree in memory from raw data |
| */ |
| tndarray = |
| GENMALLOC((cntndarray + 1) * |
| sizeof(struct rand_smc_node)); |
| unsigned int treenodetrackmal = 0; |
| for (unsigned int j = 0U; (int)j < cntndarray; j++) { |
| tndarray[j].biases = GENMALLOC(ndarray[j].entries * sizeof(int)); |
| tndarray[j].snames = GENMALLOC(ndarray[j].entries * sizeof(char *)); |
| tndarray[j].snameid = GENMALLOC(ndarray[j].entries * sizeof(int)); |
| tndarray[j].norcall = GENMALLOC(ndarray[j].entries * sizeof(int)); |
| tndarray[j].nname = GENMALLOC(ndarray[j].entries * sizeof(char *)); |
| tndarray[j].treenodes = GENMALLOC(ndarray[j].entries * sizeof(struct rand_smc_node)); |
| tndarray[j].entries = ndarray[j].entries; |
| for (unsigned int i = 0U; (int)i < ndarray[j].entries; i++) { |
| tndarray[j].snames[i] = GENMALLOC(1 * sizeof(char[MAX_NAME_CHARS])); |
| strlcpy(tndarray[j].snames[i], ndarray[j].snames[i], MAX_NAME_CHARS); |
| tndarray[j].snameid[i] = ndarray[j].snameid[i]; |
| tndarray[j].nname[i] = GENMALLOC(1 * sizeof(char[MAX_NAME_CHARS])); |
| strlcpy(tndarray[j].nname[i], ndarray[j].nname[i], MAX_NAME_CHARS); |
| tndarray[j].biases[i] = ndarray[j].biases[i]; |
| tndarray[j].norcall[i] = ndarray[j].norcall[i]; |
| if (tndarray[j].norcall[i] == 1) { |
| tndarray[j].treenodes[i] = tndarray[treenodetrackmal]; |
| treenodetrackmal++; |
| } |
| } |
| tndarray[j].biasent = ndarray[j].biasent; |
| tndarray[j].biasarray = GENMALLOC((tndarray[j].biasent) * sizeof(int)); |
| for (unsigned int i = 0U; (int)i < ndarray[j].biasent; i++) { |
| tndarray[j].biasarray[i] = ndarray[j].biasarray[i]; |
| } |
| } |
| tndarray[cntndarray].biases = GENMALLOC(f3d.row[f3d.col + 1] * sizeof(int)); |
| tndarray[cntndarray].snames = GENMALLOC(f3d.row[f3d.col + 1] * sizeof(char *)); |
| tndarray[cntndarray].snameid = GENMALLOC(f3d.row[f3d.col + 1] * sizeof(int)); |
| tndarray[cntndarray].norcall = GENMALLOC(f3d.row[f3d.col + 1] * sizeof(int)); |
| tndarray[cntndarray].nname = GENMALLOC(f3d.row[f3d.col + 1] * sizeof(char *)); |
| tndarray[cntndarray].treenodes = GENMALLOC(f3d.row[f3d.col + 1] * sizeof(struct rand_smc_node)); |
| tndarray[cntndarray].entries = f3d.row[f3d.col + 1]; |
| |
| /* |
| * Populate bias tree with former values in tree |
| */ |
| int cntbias = 0; |
| int bias_count = 0; |
| |
| for (unsigned int j = 0U; (int)j < f3d.row[f3d.col + 1]; j++) { |
| tndarray[cntndarray].snames[j] = GENMALLOC(1 * sizeof(char[MAX_NAME_CHARS])); |
| strlcpy(tndarray[cntndarray].snames[j], f3d.fnamefifo[f3d.col + 1][j], MAX_NAME_CHARS); |
| tndarray[cntndarray].snameid[j] = f3d.fidfifo[f3d.col + 1][j]; |
| tndarray[cntndarray].nname[j] = GENMALLOC(1 * sizeof(char[MAX_NAME_CHARS])); |
| strlcpy(tndarray[cntndarray].nname[j], f3d.nnfifo[f3d.col + 1][j], MAX_NAME_CHARS); |
| tndarray[cntndarray].biases[j] = f3d.biasfifo[f3d.col + 1][j]; |
| cntbias += tndarray[cntndarray].biases[j]; |
| if (strcmp(tndarray[cntndarray].snames[j], "none") != 0) { |
| strlcpy(tndarray[cntndarray].snames[j], f3d.fnamefifo[f3d.col + 1][j], MAX_NAME_CHARS); |
| tndarray[cntndarray].norcall[j] = 0; |
| tndarray[cntndarray].treenodes[j] = nrnode; |
| } else { |
| tndarray[cntndarray].norcall[j] = 1; |
| tndarray[cntndarray].treenodes[j] = tndarray[treenodetrack]; |
| treenodetrack++; |
| } |
| } |
| |
| tndarray[cntndarray].biasent = cntbias; |
| tndarray[cntndarray].biasarray = GENMALLOC((tndarray[cntndarray].biasent) * sizeof(int)); |
| for (unsigned int j = 0U; j < tndarray[cntndarray].entries; j++) { |
| for (unsigned int i = 0U; i < tndarray[cntndarray].biases[j]; i++) { |
| tndarray[cntndarray].biasarray[bias_count] = j; |
| bias_count++; |
| } |
| } |
| |
| /* |
| * Free memory of old bias tree |
| */ |
| if (cntndarray > 0) { |
| for (unsigned int j = 0U; (int)j < cntndarray; j++) { |
| for (unsigned int i = 0U; |
| (int)i < ndarray[j].entries; |
| i++) { |
| GENFREE(ndarray[j].snames[i]); |
| GENFREE(ndarray[j].nname[i]); |
| } |
| GENFREE(ndarray[j].biases); |
| GENFREE(ndarray[j].norcall); |
| GENFREE(ndarray[j].biasarray); |
| GENFREE(ndarray[j].snames); |
| GENFREE(ndarray[j].snameid); |
| GENFREE(ndarray[j].nname); |
| GENFREE(ndarray[j].treenodes); |
| } |
| GENFREE(ndarray); |
| } |
| |
| /* |
| * Move pointers to new bias tree to current tree |
| */ |
| ndarray = tndarray; |
| cntndarray++; |
| |
| /* |
| * Free raw data |
| */ |
| for (unsigned int j = 0U; (int)j < f3d.row[f3d.col + 1]; j++) { |
| GENFREE(f3d.nnfifo[f3d.col + 1][j]); |
| GENFREE(f3d.fnamefifo[f3d.col + 1][j]); |
| } |
| GENFREE(f3d.nnfifo[f3d.col + 1]); |
| GENFREE(f3d.fnamefifo[f3d.col + 1]); |
| GENFREE(f3d.biasfifo[f3d.col + 1]); |
| GENFREE(f3d.fidfifo[f3d.col + 1]); |
| f3d.curr_col -= 1; |
| } |
| } |
| |
| /* |
| * Ending device tree file and freeing raw data |
| */ |
| if (fdt32_to_cpu(rval) == 9) { |
| for (unsigned int i = 0U; (int)i < f3d.col; i++) { |
| for (unsigned int j = 0U; (int)j < f3d.row[i]; j++) { |
| GENFREE(f3d.nnfifo[i][j]); |
| GENFREE(f3d.fnamefifo[i][j]); |
| } |
| GENFREE(f3d.nnfifo[i]); |
| GENFREE(f3d.fnamefifo[i]); |
| GENFREE(f3d.biasfifo[i]); |
| GENFREE(f3d.fidfifo[i]); |
| } |
| GENFREE(f3d.nnfifo); |
| GENFREE(f3d.fnamefifo); |
| GENFREE(f3d.biasfifo); |
| GENFREE(f3d.fidfifo); |
| GENFREE(f3d.row); |
| dtdone = 1; |
| } |
| } |
| |
| *casz = cntndarray; |
| return ndarray; |
| } |
| |
| /* |
| * Function executes a single SMC fuzz test instance with a supplied seed. |
| */ |
| test_result_t init_smc_fuzzing(void) |
| { |
| /* |
| * Setting up malloc block parameters |
| */ |
| tmod.memptr = (void *)tmod.memory; |
| tmod.memptrend = (void *)tmod.memory; |
| tmod.maxmemblk = ((TOTALMEMORYSIZE / BLKSPACEDIV) / sizeof(struct memblk)); |
| tmod.nmemblk = 1; |
| tmod.memptr->address = 0U; |
| tmod.memptr->size = TOTALMEMORYSIZE - (TOTALMEMORYSIZE / BLKSPACEDIV); |
| tmod.memptr->valid = 1; |
| tmod.mallocdeladd[0] = 0U; |
| tmod.precblock[0] = (void *)tmod.memory; |
| tmod.trailblock[0] = NULL; |
| tmod.cntdeladd = 0U; |
| tmod.ptrmemblkqueue = 0U; |
| tmod.mallocdeladd_queue_cnt = 0U; |
| tmod.checkadd = 1U; |
| tmod.checknumentries = 0U; |
| tmod.memerror = 0U; |
| mmod = &tmod; |
| |
| /* |
| * Creating SMC bias tree |
| */ |
| ndarray = createsmctree(&cntndarray, &tmod); |
| |
| if (tmod.memerror != 0) { |
| return TEST_RESULT_FAIL; |
| } |
| |
| return TEST_RESULT_SUCCESS; |
| } |
| |
| test_result_t smc_fuzzing_instance(uint32_t seed) |
| { |
| struct rand_smc_node *tlnode; |
| /* |
| * Initialize pseudo random number generator with supplied seed. |
| */ |
| srand(seed); |
| |
| /* |
| * Code to traverse the bias tree and select function based on the biaes within |
| * |
| * The algorithm starts with the first node to pull up the biasarray. The |
| * array is specified as a series of values that reflect the bias of the nodes |
| * in question. So for instance if there are three nodes with a bias of 2,5,7 |
| * the biasarray would have the following constituency: |
| * |
| * 0,0,1,1,1,1,1,2,2,2,2,2,2,2. |
| * |
| * Mapping 0 as node 1, 1 as node 2, and 2 as node 3. |
| * The biasent variable contains the count of the size of the biasarray which |
| * provides the input for random selection. This is subsequently applied as an |
| * index to the biasarray. The selection pulls up the node and then is checked |
| * for whether it is a leaf or tree node using the norcall variable. |
| * If it is a leaf then the bias tree traversal ends with an SMC call. |
| * If it is a tree node then the process begins again with |
| * another loop to continue the process of selection until an eventual leaf |
| * node is found. |
| */ |
| for (unsigned int i = 0U; i < SMC_FUZZ_CALLS_PER_INSTANCE; i++) { |
| tlnode = &ndarray[cntndarray - 1]; |
| int nd = 0; |
| |
| while (nd == 0) { |
| int nch = rand()%tlnode->biasent; |
| int selent = tlnode->biasarray[nch]; |
| |
| if (tlnode->norcall[selent] == 0) { |
| runtestfunction(tlnode->snameid[selent]); |
| nd = 1; |
| } else { |
| tlnode = &tlnode->treenodes[selent]; |
| } |
| } |
| } |
| return TEST_RESULT_SUCCESS; |
| } |
| |
| test_result_t smc_fuzzing_deinit(void) |
| { |
| /* |
| * End of test SMC selection and freeing of nodes |
| */ |
| if (cntndarray > 0) { |
| for (unsigned int j = 0U; j < cntndarray; j++) { |
| for (unsigned int i = 0U; i < ndarray[j].entries; i++) { |
| GENFREE(ndarray[j].snames[i]); |
| GENFREE(ndarray[j].nname[i]); |
| } |
| GENFREE(ndarray[j].biases); |
| GENFREE(ndarray[j].norcall); |
| GENFREE(ndarray[j].biasarray); |
| GENFREE(ndarray[j].snames); |
| GENFREE(ndarray[j].snameid); |
| GENFREE(ndarray[j].nname); |
| GENFREE(ndarray[j].treenodes); |
| } |
| GENFREE(ndarray); |
| } |
| |
| return TEST_RESULT_SUCCESS; |
| } |
| |
| /* |
| * Top of SMC fuzzing module |
| */ |
| test_result_t smc_fuzzer_execute(void) |
| { |
| /* These SMC_FUZZ_x macros are supplied by the build system. */ |
| test_result_t results[SMC_FUZZ_INSTANCE_COUNT]; |
| uint32_t seeds[SMC_FUZZ_INSTANCE_COUNT] = {SMC_FUZZ_SEEDS}; |
| test_result_t result = TEST_RESULT_SUCCESS; |
| unsigned int i; |
| |
| /* Run each instance. */ |
| for (i = 0U; i < SMC_FUZZ_INSTANCE_COUNT; i++) { |
| printf("Starting SMC fuzz test with seed 0x%x\n", seeds[i]); |
| results[i] = smc_fuzzing_instance(seeds[i]); |
| } |
| |
| /* Report successes and failures. */ |
| printf("SMC Fuzz Test Results Summary\n"); |
| for (i = 0U; i < SMC_FUZZ_INSTANCE_COUNT; i++) { |
| /* Display instance number. */ |
| printf(" Instance #%d\n", i); |
| |
| /* Print test results. */ |
| printf(" Result: "); |
| if (results[i] == TEST_RESULT_SUCCESS) { |
| printf("SUCCESS\n"); |
| } else if (results[i] == TEST_RESULT_FAIL) { |
| printf("FAIL\n"); |
| /* If we got a failure, update the result value. */ |
| result = TEST_RESULT_FAIL; |
| } else if (results[i] == TEST_RESULT_SKIPPED) { |
| printf("SKIPPED\n"); |
| } |
| |
| /* Print seed used */ |
| printf(" Seed: 0x%x\n", seeds[i]); |
| } |
| |
| /* |
| * Print out the smc fuzzer parameters so this test can be replicated. |
| */ |
| printf("SMC fuzz build parameters to recreate this test:\n"); |
| printf(" SMC_FUZZ_INSTANCE_COUNT=%u\n", |
| SMC_FUZZ_INSTANCE_COUNT); |
| printf(" SMC_FUZZ_CALLS_PER_INSTANCE=%u\n", |
| SMC_FUZZ_CALLS_PER_INSTANCE); |
| printf(" SMC_FUZZ_SEEDS=0x%x", seeds[0]); |
| for (i = 1U; i < SMC_FUZZ_INSTANCE_COUNT; i++) { |
| printf(",0x%x", seeds[i]); |
| } |
| printf("\n"); |
| |
| return result; |
| } |
| |
| test_result_t smc_fuzzing_top(void) |
| { |
| test_result_t result = TEST_RESULT_SUCCESS; |
| init_smc_fuzzing(); |
| |
| #ifdef MULTI_CPU_SMC_FUZZER |
| u_register_t lead_mpid, target_mpid; |
| int cpu_node; |
| int32_t aff_info __unused; |
| int64_t ret; |
| |
| lead_mpid = read_mpidr_el1() & MPID_MASK; |
| for_each_cpu(cpu_node) { |
| target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK; |
| if (lead_mpid == target_mpid) { |
| /* Run on this CPU */ |
| if (smc_fuzzer_execute() != TEST_RESULT_SUCCESS) |
| return TEST_RESULT_FAIL; |
| } else { |
| /* Power on other CPU to run through fuzzing instructions */ |
| ret = tftf_cpu_on(target_mpid, |
| (uintptr_t) smc_fuzzer_execute, 0); |
| if (ret != PSCI_E_SUCCESS) { |
| ERROR("CPU ON failed for 0x%llx\n", |
| (unsigned long long) target_mpid); |
| return TEST_RESULT_FAIL; |
| } |
| |
| } |
| } |
| |
| smc_fuzzing_deinit(); |
| return result; |
| #else |
| result = smc_fuzzer_execute(); |
| smc_fuzzing_deinit(); |
| return result; |
| #endif |
| } |