1*48a1cce4SGatien Chevallier // SPDX-License-Identifier: BSD-2-Clause 2*48a1cce4SGatien Chevallier /* 3*48a1cce4SGatien Chevallier * Copyright (C) 2024, STMicroelectronics 4*48a1cce4SGatien Chevallier */ 5*48a1cce4SGatien Chevallier 6*48a1cce4SGatien Chevallier #include <assert.h> 7*48a1cce4SGatien Chevallier #include <config.h> 8*48a1cce4SGatien Chevallier #include <drivers/firewall.h> 9*48a1cce4SGatien Chevallier #include <kernel/dt_driver.h> 10*48a1cce4SGatien Chevallier #include <kernel/panic.h> 11*48a1cce4SGatien Chevallier #include <libfdt.h> 12*48a1cce4SGatien Chevallier #include <malloc.h> 13*48a1cce4SGatien Chevallier #include <trace.h> 14*48a1cce4SGatien Chevallier 15*48a1cce4SGatien Chevallier /* The firewall framework requires device tree support */ 16*48a1cce4SGatien Chevallier static_assert(IS_ENABLED(CFG_DT)); 17*48a1cce4SGatien Chevallier 18*48a1cce4SGatien Chevallier static TEE_Result firewall_get(struct dt_pargs *parg, void *data, 19*48a1cce4SGatien Chevallier struct firewall_query **out_fw) 20*48a1cce4SGatien Chevallier { 21*48a1cce4SGatien Chevallier struct firewall_query *fw = NULL; 22*48a1cce4SGatien Chevallier unsigned int i = 0; 23*48a1cce4SGatien Chevallier 24*48a1cce4SGatien Chevallier assert(parg->args_count >= 0); 25*48a1cce4SGatien Chevallier 26*48a1cce4SGatien Chevallier fw = calloc(1, sizeof(*fw)); 27*48a1cce4SGatien Chevallier if (!fw) 28*48a1cce4SGatien Chevallier return TEE_ERROR_OUT_OF_MEMORY; 29*48a1cce4SGatien Chevallier 30*48a1cce4SGatien Chevallier fw->ctrl = (struct firewall_controller *)data; 31*48a1cce4SGatien Chevallier fw->arg_count = parg->args_count; 32*48a1cce4SGatien Chevallier 33*48a1cce4SGatien Chevallier if (fw->arg_count) { 34*48a1cce4SGatien Chevallier fw->args = calloc(fw->arg_count, sizeof(*fw->args)); 35*48a1cce4SGatien Chevallier if (!fw->args) { 36*48a1cce4SGatien Chevallier free(fw); 37*48a1cce4SGatien Chevallier return TEE_ERROR_OUT_OF_MEMORY; 38*48a1cce4SGatien Chevallier } 39*48a1cce4SGatien Chevallier } 40*48a1cce4SGatien Chevallier 41*48a1cce4SGatien Chevallier for (i = 0; i < (unsigned int)parg->args_count; i++) 42*48a1cce4SGatien Chevallier fw->args[i] = parg->args[i]; 43*48a1cce4SGatien Chevallier 44*48a1cce4SGatien Chevallier *out_fw = fw; 45*48a1cce4SGatien Chevallier 46*48a1cce4SGatien Chevallier return TEE_SUCCESS; 47*48a1cce4SGatien Chevallier } 48*48a1cce4SGatien Chevallier 49*48a1cce4SGatien Chevallier /* Firewall device API */ 50*48a1cce4SGatien Chevallier 51*48a1cce4SGatien Chevallier void firewall_put(struct firewall_query *fw) 52*48a1cce4SGatien Chevallier { 53*48a1cce4SGatien Chevallier if (fw) { 54*48a1cce4SGatien Chevallier free(fw->args); 55*48a1cce4SGatien Chevallier free(fw); 56*48a1cce4SGatien Chevallier } 57*48a1cce4SGatien Chevallier } 58*48a1cce4SGatien Chevallier 59*48a1cce4SGatien Chevallier TEE_Result firewall_dt_get_by_index(const void *fdt, int node, uint32_t index, 60*48a1cce4SGatien Chevallier struct firewall_query **out_fw) 61*48a1cce4SGatien Chevallier { 62*48a1cce4SGatien Chevallier return dt_driver_device_from_node_idx_prop("access-controllers", fdt, 63*48a1cce4SGatien Chevallier node, index, 64*48a1cce4SGatien Chevallier DT_DRIVER_FIREWALL, 65*48a1cce4SGatien Chevallier out_fw); 66*48a1cce4SGatien Chevallier } 67*48a1cce4SGatien Chevallier 68*48a1cce4SGatien Chevallier TEE_Result firewall_dt_get_by_name(const void *fdt, int node, const char *name, 69*48a1cce4SGatien Chevallier struct firewall_query **out_fw) 70*48a1cce4SGatien Chevallier { 71*48a1cce4SGatien Chevallier int index = 0; 72*48a1cce4SGatien Chevallier 73*48a1cce4SGatien Chevallier index = fdt_stringlist_search(fdt, node, "access-controllers-names", 74*48a1cce4SGatien Chevallier name); 75*48a1cce4SGatien Chevallier if (index == -FDT_ERR_NOTFOUND) 76*48a1cce4SGatien Chevallier return TEE_ERROR_ITEM_NOT_FOUND; 77*48a1cce4SGatien Chevallier else if (index < 0) 78*48a1cce4SGatien Chevallier return TEE_ERROR_GENERIC; 79*48a1cce4SGatien Chevallier 80*48a1cce4SGatien Chevallier return firewall_dt_get_by_index(fdt, node, index, out_fw); 81*48a1cce4SGatien Chevallier } 82*48a1cce4SGatien Chevallier 83*48a1cce4SGatien Chevallier TEE_Result firewall_set_configuration(struct firewall_query *fw) 84*48a1cce4SGatien Chevallier { 85*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 86*48a1cce4SGatien Chevallier 87*48a1cce4SGatien Chevallier if (!fw->ctrl->ops->set_conf) 88*48a1cce4SGatien Chevallier return TEE_ERROR_NOT_SUPPORTED; 89*48a1cce4SGatien Chevallier 90*48a1cce4SGatien Chevallier return fw->ctrl->ops->set_conf(fw); 91*48a1cce4SGatien Chevallier } 92*48a1cce4SGatien Chevallier 93*48a1cce4SGatien Chevallier TEE_Result firewall_check_access(struct firewall_query *fw) 94*48a1cce4SGatien Chevallier { 95*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 96*48a1cce4SGatien Chevallier 97*48a1cce4SGatien Chevallier if (!fw->ctrl->ops->check_access) 98*48a1cce4SGatien Chevallier return TEE_ERROR_NOT_SUPPORTED; 99*48a1cce4SGatien Chevallier 100*48a1cce4SGatien Chevallier return fw->ctrl->ops->check_access(fw); 101*48a1cce4SGatien Chevallier } 102*48a1cce4SGatien Chevallier 103*48a1cce4SGatien Chevallier TEE_Result firewall_acquire_access(struct firewall_query *fw) 104*48a1cce4SGatien Chevallier { 105*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 106*48a1cce4SGatien Chevallier 107*48a1cce4SGatien Chevallier if (!fw->ctrl->ops->acquire_access) 108*48a1cce4SGatien Chevallier return TEE_ERROR_NOT_SUPPORTED; 109*48a1cce4SGatien Chevallier 110*48a1cce4SGatien Chevallier return fw->ctrl->ops->acquire_access(fw); 111*48a1cce4SGatien Chevallier } 112*48a1cce4SGatien Chevallier 113*48a1cce4SGatien Chevallier TEE_Result firewall_check_memory_access(struct firewall_query *fw, 114*48a1cce4SGatien Chevallier paddr_t paddr, size_t size, bool read, 115*48a1cce4SGatien Chevallier bool write) 116*48a1cce4SGatien Chevallier { 117*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 118*48a1cce4SGatien Chevallier 119*48a1cce4SGatien Chevallier if (!fw->ctrl->ops->check_memory_access) 120*48a1cce4SGatien Chevallier return TEE_ERROR_NOT_SUPPORTED; 121*48a1cce4SGatien Chevallier 122*48a1cce4SGatien Chevallier return fw->ctrl->ops->check_memory_access(fw, paddr, size, read, write); 123*48a1cce4SGatien Chevallier } 124*48a1cce4SGatien Chevallier 125*48a1cce4SGatien Chevallier TEE_Result firewall_acquire_memory_access(struct firewall_query *fw, 126*48a1cce4SGatien Chevallier paddr_t paddr, size_t size, bool read, 127*48a1cce4SGatien Chevallier bool write) 128*48a1cce4SGatien Chevallier { 129*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 130*48a1cce4SGatien Chevallier 131*48a1cce4SGatien Chevallier if (!fw->ctrl->ops->acquire_memory_access) 132*48a1cce4SGatien Chevallier return TEE_ERROR_NOT_SUPPORTED; 133*48a1cce4SGatien Chevallier 134*48a1cce4SGatien Chevallier return fw->ctrl->ops->acquire_memory_access(fw, paddr, size, read, 135*48a1cce4SGatien Chevallier write); 136*48a1cce4SGatien Chevallier } 137*48a1cce4SGatien Chevallier 138*48a1cce4SGatien Chevallier void firewall_release_access(struct firewall_query *fw) 139*48a1cce4SGatien Chevallier { 140*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 141*48a1cce4SGatien Chevallier 142*48a1cce4SGatien Chevallier if (fw->ctrl->ops->release_access) 143*48a1cce4SGatien Chevallier fw->ctrl->ops->release_access(fw); 144*48a1cce4SGatien Chevallier } 145*48a1cce4SGatien Chevallier 146*48a1cce4SGatien Chevallier void firewall_release_memory_access(struct firewall_query *fw, paddr_t paddr, 147*48a1cce4SGatien Chevallier size_t size, bool read, bool write) 148*48a1cce4SGatien Chevallier { 149*48a1cce4SGatien Chevallier assert(fw && fw->ctrl && fw->ctrl->ops); 150*48a1cce4SGatien Chevallier 151*48a1cce4SGatien Chevallier if (fw->ctrl->ops->release_memory_access) 152*48a1cce4SGatien Chevallier fw->ctrl->ops->release_memory_access(fw, paddr, size, read, 153*48a1cce4SGatien Chevallier write); 154*48a1cce4SGatien Chevallier } 155*48a1cce4SGatien Chevallier 156*48a1cce4SGatien Chevallier /* Firewall controller API */ 157*48a1cce4SGatien Chevallier 158*48a1cce4SGatien Chevallier TEE_Result firewall_dt_controller_register(const void *fdt, int node, 159*48a1cce4SGatien Chevallier struct firewall_controller *ctrl) 160*48a1cce4SGatien Chevallier { 161*48a1cce4SGatien Chevallier assert(ctrl); 162*48a1cce4SGatien Chevallier 163*48a1cce4SGatien Chevallier DMSG("Registering %s firewall controller", ctrl->name); 164*48a1cce4SGatien Chevallier 165*48a1cce4SGatien Chevallier return dt_driver_register_provider(fdt, node, 166*48a1cce4SGatien Chevallier (get_of_device_func)firewall_get, 167*48a1cce4SGatien Chevallier ctrl, DT_DRIVER_FIREWALL); 168*48a1cce4SGatien Chevallier } 169*48a1cce4SGatien Chevallier 170*48a1cce4SGatien Chevallier TEE_Result firewall_dt_probe_bus(const void *fdt, int node, 171*48a1cce4SGatien Chevallier struct firewall_controller *ctrl) 172*48a1cce4SGatien Chevallier { 173*48a1cce4SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 174*48a1cce4SGatien Chevallier struct firewall_query *fw = NULL; 175*48a1cce4SGatien Chevallier int subnode = 0; 176*48a1cce4SGatien Chevallier 177*48a1cce4SGatien Chevallier DMSG("Populating %s firewall bus", ctrl->name); 178*48a1cce4SGatien Chevallier 179*48a1cce4SGatien Chevallier fdt_for_each_subnode(subnode, fdt, node) { 180*48a1cce4SGatien Chevallier unsigned int i = 0; 181*48a1cce4SGatien Chevallier 182*48a1cce4SGatien Chevallier if (fdt_get_status(fdt, subnode) == DT_STATUS_DISABLED) 183*48a1cce4SGatien Chevallier continue; 184*48a1cce4SGatien Chevallier 185*48a1cce4SGatien Chevallier DMSG("Acquiring firewall access for %s when probing bus", 186*48a1cce4SGatien Chevallier fdt_get_name(fdt, subnode, NULL)); 187*48a1cce4SGatien Chevallier 188*48a1cce4SGatien Chevallier do { 189*48a1cce4SGatien Chevallier /* 190*48a1cce4SGatien Chevallier * The access-controllers property is mandatory for 191*48a1cce4SGatien Chevallier * firewall bus devices 192*48a1cce4SGatien Chevallier */ 193*48a1cce4SGatien Chevallier res = firewall_dt_get_by_index(fdt, subnode, i, &fw); 194*48a1cce4SGatien Chevallier if (res == TEE_ERROR_ITEM_NOT_FOUND) { 195*48a1cce4SGatien Chevallier /* Stop when nothing more to parse */ 196*48a1cce4SGatien Chevallier break; 197*48a1cce4SGatien Chevallier } else if (res) { 198*48a1cce4SGatien Chevallier EMSG("%s: Error on node %s: %#"PRIx32, 199*48a1cce4SGatien Chevallier ctrl->name, 200*48a1cce4SGatien Chevallier fdt_get_name(fdt, subnode, NULL), res); 201*48a1cce4SGatien Chevallier panic(); 202*48a1cce4SGatien Chevallier } 203*48a1cce4SGatien Chevallier 204*48a1cce4SGatien Chevallier res = firewall_acquire_access(fw); 205*48a1cce4SGatien Chevallier if (res) { 206*48a1cce4SGatien Chevallier EMSG("%s: %s not accessible: %#"PRIx32, 207*48a1cce4SGatien Chevallier ctrl->name, 208*48a1cce4SGatien Chevallier fdt_get_name(fdt, subnode, NULL), res); 209*48a1cce4SGatien Chevallier panic(); 210*48a1cce4SGatien Chevallier } 211*48a1cce4SGatien Chevallier 212*48a1cce4SGatien Chevallier firewall_put(fw); 213*48a1cce4SGatien Chevallier i++; 214*48a1cce4SGatien Chevallier } while (true); 215*48a1cce4SGatien Chevallier 216*48a1cce4SGatien Chevallier res = dt_driver_maybe_add_probe_node(fdt, subnode); 217*48a1cce4SGatien Chevallier if (res) { 218*48a1cce4SGatien Chevallier EMSG("Failed on node %s with %#"PRIx32, 219*48a1cce4SGatien Chevallier fdt_get_name(fdt, subnode, NULL), res); 220*48a1cce4SGatien Chevallier panic(); 221*48a1cce4SGatien Chevallier } 222*48a1cce4SGatien Chevallier } 223*48a1cce4SGatien Chevallier 224*48a1cce4SGatien Chevallier return TEE_SUCCESS; 225*48a1cce4SGatien Chevallier } 226