cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

firmware.c (5721B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Firmware loading and handling functions.
      4 */
      5
      6#include <linux/sched.h>
      7#include <linux/firmware.h>
      8#include <linux/module.h>
      9
     10#include "dev.h"
     11#include "decl.h"
     12
     13static void load_next_firmware_from_table(struct lbs_private *private);
     14
     15static void lbs_fw_loaded(struct lbs_private *priv, int ret,
     16	const struct firmware *helper, const struct firmware *mainfw)
     17{
     18	unsigned long flags;
     19
     20	lbs_deb_fw("firmware load complete, code %d\n", ret);
     21
     22	/* User must free helper/mainfw */
     23	priv->fw_callback(priv, ret, helper, mainfw);
     24
     25	spin_lock_irqsave(&priv->driver_lock, flags);
     26	priv->fw_callback = NULL;
     27	wake_up(&priv->fw_waitq);
     28	spin_unlock_irqrestore(&priv->driver_lock, flags);
     29}
     30
     31static void do_load_firmware(struct lbs_private *priv, const char *name,
     32	void (*cb)(const struct firmware *fw, void *context))
     33{
     34	int ret;
     35
     36	lbs_deb_fw("Requesting %s\n", name);
     37	ret = request_firmware_nowait(THIS_MODULE, true, name,
     38			priv->fw_device, GFP_KERNEL, priv, cb);
     39	if (ret) {
     40		lbs_deb_fw("request_firmware_nowait error %d\n", ret);
     41		lbs_fw_loaded(priv, ret, NULL, NULL);
     42	}
     43}
     44
     45static void main_firmware_cb(const struct firmware *firmware, void *context)
     46{
     47	struct lbs_private *priv = context;
     48
     49	if (!firmware) {
     50		/* Failed to find firmware: try next table entry */
     51		load_next_firmware_from_table(priv);
     52		return;
     53	}
     54
     55	/* Firmware found! */
     56	lbs_fw_loaded(priv, 0, priv->helper_fw, firmware);
     57	if (priv->helper_fw) {
     58		release_firmware (priv->helper_fw);
     59		priv->helper_fw = NULL;
     60	}
     61	release_firmware (firmware);
     62}
     63
     64static void helper_firmware_cb(const struct firmware *firmware, void *context)
     65{
     66	struct lbs_private *priv = context;
     67
     68	if (!firmware) {
     69		/* Failed to find firmware: try next table entry */
     70		load_next_firmware_from_table(priv);
     71		return;
     72	}
     73
     74	/* Firmware found! */
     75	if (priv->fw_iter->fwname) {
     76		priv->helper_fw = firmware;
     77		do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb);
     78	} else {
     79		/* No main firmware needed for this helper --> success! */
     80		lbs_fw_loaded(priv, 0, firmware, NULL);
     81	}
     82}
     83
     84static void load_next_firmware_from_table(struct lbs_private *priv)
     85{
     86	const struct lbs_fw_table *iter;
     87
     88	if (!priv->fw_iter)
     89		iter = priv->fw_table;
     90	else
     91		iter = ++priv->fw_iter;
     92
     93	if (priv->helper_fw) {
     94		release_firmware(priv->helper_fw);
     95		priv->helper_fw = NULL;
     96	}
     97
     98next:
     99	if (!iter->helper) {
    100		/* End of table hit. */
    101		lbs_fw_loaded(priv, -ENOENT, NULL, NULL);
    102		return;
    103	}
    104
    105	if (iter->model != priv->fw_model) {
    106		iter++;
    107		goto next;
    108	}
    109
    110	priv->fw_iter = iter;
    111	do_load_firmware(priv, iter->helper, helper_firmware_cb);
    112}
    113
    114void lbs_wait_for_firmware_load(struct lbs_private *priv)
    115{
    116	wait_event(priv->fw_waitq, priv->fw_callback == NULL);
    117}
    118
    119/**
    120 *  lbs_get_firmware_async - Retrieves firmware asynchronously. Can load
    121 *  either a helper firmware and a main firmware (2-stage), or just the helper.
    122 *
    123 *  @priv:      Pointer to lbs_private instance
    124 *  @device:   	A pointer to &device structure
    125 *  @card_model: Bus-specific card model ID used to filter firmware table
    126 *		elements
    127 *  @fw_table:	Table of firmware file names and device model numbers
    128 *		terminated by an entry with a NULL helper name
    129 *  @callback:	User callback to invoke when firmware load succeeds or fails.
    130 */
    131int lbs_get_firmware_async(struct lbs_private *priv, struct device *device,
    132			    u32 card_model, const struct lbs_fw_table *fw_table,
    133			    lbs_fw_cb callback)
    134{
    135	unsigned long flags;
    136
    137	spin_lock_irqsave(&priv->driver_lock, flags);
    138	if (priv->fw_callback) {
    139		lbs_deb_fw("firmware load already in progress\n");
    140		spin_unlock_irqrestore(&priv->driver_lock, flags);
    141		return -EBUSY;
    142	}
    143
    144	priv->fw_device = device;
    145	priv->fw_callback = callback;
    146	priv->fw_table = fw_table;
    147	priv->fw_iter = NULL;
    148	priv->fw_model = card_model;
    149	spin_unlock_irqrestore(&priv->driver_lock, flags);
    150
    151	lbs_deb_fw("Starting async firmware load\n");
    152	load_next_firmware_from_table(priv);
    153	return 0;
    154}
    155EXPORT_SYMBOL_GPL(lbs_get_firmware_async);
    156
    157/**
    158 *  lbs_get_firmware - Retrieves two-stage firmware
    159 *
    160 *  @dev:     	A pointer to &device structure
    161 *  @card_model: Bus-specific card model ID used to filter firmware table
    162 *		elements
    163 *  @fw_table:	Table of firmware file names and device model numbers
    164 *		terminated by an entry with a NULL helper name
    165 *  @helper:	On success, the helper firmware; caller must free
    166 *  @mainfw:	On success, the main firmware; caller must free
    167 *
    168 * Deprecated: use lbs_get_firmware_async() instead.
    169 *
    170 *  returns:		0 on success, non-zero on failure
    171 */
    172int lbs_get_firmware(struct device *dev, u32 card_model,
    173			const struct lbs_fw_table *fw_table,
    174			const struct firmware **helper,
    175			const struct firmware **mainfw)
    176{
    177	const struct lbs_fw_table *iter;
    178	int ret;
    179
    180	BUG_ON(helper == NULL);
    181	BUG_ON(mainfw == NULL);
    182
    183	/* Search for firmware to use from the table. */
    184	iter = fw_table;
    185	while (iter && iter->helper) {
    186		if (iter->model != card_model)
    187			goto next;
    188
    189		if (*helper == NULL) {
    190			ret = request_firmware(helper, iter->helper, dev);
    191			if (ret)
    192				goto next;
    193
    194			/* If the device has one-stage firmware (ie cf8305) and
    195			 * we've got it then we don't need to bother with the
    196			 * main firmware.
    197			 */
    198			if (iter->fwname == NULL)
    199				return 0;
    200		}
    201
    202		if (*mainfw == NULL) {
    203			ret = request_firmware(mainfw, iter->fwname, dev);
    204			if (ret) {
    205				/* Clear the helper to ensure we don't have
    206				 * mismatched firmware pairs.
    207				 */
    208				release_firmware(*helper);
    209				*helper = NULL;
    210			}
    211		}
    212
    213		if (*helper && *mainfw)
    214			return 0;
    215
    216  next:
    217		iter++;
    218	}
    219
    220	/* Failed */
    221	release_firmware(*helper);
    222	*helper = NULL;
    223	release_firmware(*mainfw);
    224	*mainfw = NULL;
    225
    226	return -ENOENT;
    227}
    228EXPORT_SYMBOL_GPL(lbs_get_firmware);