// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2023, STMicroelectronics - All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stm32_firewall.h" /* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */ #define STM32_FIREWALL_MAX_ARGS (STM32_FIREWALL_MAX_EXTRA_ARGS + 1) static LIST_HEAD(firewall_controller_list); static DEFINE_MUTEX(firewall_controller_list_lock); /* Firewall device API */ int stm32_firewall_get_firewall(struct device_node *np, struct stm32_firewall *firewall, unsigned int nb_firewall) { struct stm32_firewall_controller *ctrl; struct of_phandle_iterator it; unsigned int i, j = 0; int err; if (!firewall || !nb_firewall) return -EINVAL; /* Parse property with phandle parsed out */ of_for_each_phandle(&it, err, np, "access-controllers", "#access-controller-cells", 0) { struct of_phandle_args provider_args; struct device_node *provider = it.node; const char *fw_entry; bool match = false; if (err) { pr_err("Unable to get access-controllers property for node %s\n, err: %d", np->full_name, err); of_node_put(provider); return err; } if (j >= nb_firewall) { pr_err("Too many firewall controllers"); of_node_put(provider); return -EINVAL; } provider_args.args_count = of_phandle_iterator_args(&it, provider_args.args, STM32_FIREWALL_MAX_ARGS); /* Check if the parsed phandle corresponds to a registered firewall controller */ mutex_lock(&firewall_controller_list_lock); list_for_each_entry(ctrl, &firewall_controller_list, entry) { if (ctrl->dev->of_node->phandle == it.phandle) { match = true; firewall[j].firewall_ctrl = ctrl; break; } } mutex_unlock(&firewall_controller_list_lock); if (!match) { firewall[j].firewall_ctrl = NULL; pr_err("No firewall controller registered for %s\n", np->full_name); of_node_put(provider); return -ENODEV; } err = of_property_read_string_index(np, "access-controller-names", j, &fw_entry); if (err == 0) firewall[j].entry = fw_entry; /* Handle the case when there are no arguments given along with the phandle */ if (provider_args.args_count < 0 || provider_args.args_count > STM32_FIREWALL_MAX_ARGS) { of_node_put(provider); return -EINVAL; } else if (provider_args.args_count == 0) { firewall[j].extra_args_size = 0; firewall[j].firewall_id = U32_MAX; j++; continue; } /* The firewall ID is always the first argument */ firewall[j].firewall_id = provider_args.args[0]; /* Extra args start at the second argument */ for (i = 0; i < provider_args.args_count - 1; i++) firewall[j].extra_args[i] = provider_args.args[i + 1]; /* Remove the firewall ID arg that is not an extra argument */ firewall[j].extra_args_size = provider_args.args_count - 1; j++; } return 0; } EXPORT_SYMBOL_GPL(stm32_firewall_get_firewall); int stm32_firewall_grant_access(struct stm32_firewall *firewall) { struct stm32_firewall_controller *firewall_controller; if (!firewall || firewall->firewall_id == U32_MAX) return -EINVAL; firewall_controller = firewall->firewall_ctrl; if (!firewall_controller) return -ENODEV; return firewall_controller->grant_access(firewall_controller, firewall->firewall_id); } EXPORT_SYMBOL_GPL(stm32_firewall_grant_access); int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id) { struct stm32_firewall_controller *firewall_controller; if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) return -EINVAL; firewall_controller = firewall->firewall_ctrl; if (!firewall_controller) return -ENODEV; return firewall_controller->grant_access(firewall_controller, subsystem_id); } EXPORT_SYMBOL_GPL(stm32_firewall_grant_access_by_id); void stm32_firewall_release_access(struct stm32_firewall *firewall) { struct stm32_firewall_controller *firewall_controller; if (!firewall || firewall->firewall_id == U32_MAX) { pr_debug("Incorrect arguments when releasing a firewall access\n"); return; } firewall_controller = firewall->firewall_ctrl; if (!firewall_controller) { pr_debug("No firewall controller to release\n"); return; } firewall_controller->release_access(firewall_controller, firewall->firewall_id); } EXPORT_SYMBOL_GPL(stm32_firewall_release_access); void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id) { struct stm32_firewall_controller *firewall_controller; if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) { pr_debug("Incorrect arguments when releasing a firewall access"); return; } firewall_controller = firewall->firewall_ctrl; if (!firewall_controller) { pr_debug("No firewall controller to release"); return; } firewall_controller->release_access(firewall_controller, subsystem_id); } EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id); /* Firewall controller API */ int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller) { struct stm32_firewall_controller *ctrl; if (!firewall_controller) return -ENODEV; pr_info("Registering %s firewall controller\n", firewall_controller->name); mutex_lock(&firewall_controller_list_lock); list_for_each_entry(ctrl, &firewall_controller_list, entry) { if (ctrl == firewall_controller) { pr_debug("%s firewall controller already registered\n", firewall_controller->name); mutex_unlock(&firewall_controller_list_lock); return 0; } } list_add_tail(&firewall_controller->entry, &firewall_controller_list); mutex_unlock(&firewall_controller_list_lock); return 0; } EXPORT_SYMBOL_GPL(stm32_firewall_controller_register); void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller) { struct stm32_firewall_controller *ctrl; bool controller_removed = false; if (!firewall_controller) { pr_debug("Null reference while unregistering firewall controller\n"); return; } mutex_lock(&firewall_controller_list_lock); list_for_each_entry(ctrl, &firewall_controller_list, entry) { if (ctrl == firewall_controller) { controller_removed = true; list_del_init(&ctrl->entry); break; } } mutex_unlock(&firewall_controller_list_lock); if (!controller_removed) pr_debug("There was no firewall controller named %s to unregister\n", firewall_controller->name); } EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister); int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller) { struct stm32_firewall *firewalls; struct device_node *child; struct device *parent; unsigned int i; int len; int err; parent = firewall_controller->dev; dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev)); for_each_available_child_of_node(dev_of_node(parent), child) { /* The access-controllers property is mandatory for firewall bus devices */ len = of_count_phandle_with_args(child, "access-controllers", "#access-controller-cells"); if (len <= 0) { of_node_put(child); return -EINVAL; } firewalls = kcalloc(len, sizeof(*firewalls), GFP_KERNEL); if (!firewalls) { of_node_put(child); return -ENOMEM; } err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len); if (err) { kfree(firewalls); of_node_put(child); return err; } for (i = 0; i < len; i++) { if (firewall_controller->grant_access(firewall_controller, firewalls[i].firewall_id)) { /* * Peripheral access not allowed or not defined. * Mark the node as populated so platform bus won't probe it */ of_detach_node(child); dev_err(parent, "%s: Device driver will not be probed\n", child->full_name); } } kfree(firewalls); } return 0; } EXPORT_SYMBOL_GPL(stm32_firewall_populate_bus);