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

nfp_nffw.c (6697B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
      2/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
      3
      4/*
      5 * nfp_nffw.c
      6 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
      7 *          Jason McMullan <jason.mcmullan@netronome.com>
      8 *          Francois H. Theron <francois.theron@netronome.com>
      9 */
     10
     11#include <linux/kernel.h>
     12#include <linux/slab.h>
     13
     14#include "nfp.h"
     15#include "nfp_cpp.h"
     16#include "nfp_nffw.h"
     17#include "nfp6000/nfp6000.h"
     18
     19/* Init-CSR owner IDs for firmware map to firmware IDs which start at 4.
     20 * Lower IDs are reserved for target and loader IDs.
     21 */
     22#define NFFW_FWID_EXT   3 /* For active MEs that we didn't load. */
     23#define NFFW_FWID_BASE  4
     24
     25#define NFFW_FWID_ALL   255
     26
     27/*
     28 * NFFW_INFO_VERSION history:
     29 * 0: This was never actually used (before versioning), but it refers to
     30 *    the previous struct which had FWINFO_CNT = MEINFO_CNT = 120 that later
     31 *    changed to 200.
     32 * 1: First versioned struct, with
     33 *     FWINFO_CNT = 120
     34 *     MEINFO_CNT = 120
     35 * 2:  FWINFO_CNT = 200
     36 *     MEINFO_CNT = 200
     37 */
     38#define NFFW_INFO_VERSION_CURRENT 2
     39
     40/* Enough for all current chip families */
     41#define NFFW_MEINFO_CNT_V1 120
     42#define NFFW_FWINFO_CNT_V1 120
     43#define NFFW_MEINFO_CNT_V2 200
     44#define NFFW_FWINFO_CNT_V2 200
     45
     46/* Work in 32-bit words to make cross-platform endianness easier to handle */
     47
     48/** nfp.nffw meinfo **/
     49struct nffw_meinfo {
     50	__le32 ctxmask__fwid__meid;
     51};
     52
     53struct nffw_fwinfo {
     54	__le32 loaded__mu_da__mip_off_hi;
     55	__le32 mip_cppid; /* 0 means no MIP */
     56	__le32 mip_offset_lo;
     57};
     58
     59struct nfp_nffw_info_v1 {
     60	struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V1];
     61	struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V1];
     62};
     63
     64struct nfp_nffw_info_v2 {
     65	struct nffw_meinfo meinfo[NFFW_MEINFO_CNT_V2];
     66	struct nffw_fwinfo fwinfo[NFFW_FWINFO_CNT_V2];
     67};
     68
     69/** Resource: nfp.nffw main **/
     70struct nfp_nffw_info_data {
     71	__le32 flags[2];
     72	union {
     73		struct nfp_nffw_info_v1 v1;
     74		struct nfp_nffw_info_v2 v2;
     75	} info;
     76};
     77
     78struct nfp_nffw_info {
     79	struct nfp_cpp *cpp;
     80	struct nfp_resource *res;
     81
     82	struct nfp_nffw_info_data fwinf;
     83};
     84
     85/* flg_info_version = flags[0]<27:16>
     86 * This is a small version counter intended only to detect if the current
     87 * implementation can read the current struct. Struct changes should be very
     88 * rare and as such a 12-bit counter should cover large spans of time. By the
     89 * time it wraps around, we don't expect to have 4096 versions of this struct
     90 * to be in use at the same time.
     91 */
     92static u32 nffw_res_info_version_get(const struct nfp_nffw_info_data *res)
     93{
     94	return (le32_to_cpu(res->flags[0]) >> 16) & 0xfff;
     95}
     96
     97/* flg_init = flags[0]<0> */
     98static u32 nffw_res_flg_init_get(const struct nfp_nffw_info_data *res)
     99{
    100	return (le32_to_cpu(res->flags[0]) >> 0) & 1;
    101}
    102
    103/* loaded = loaded__mu_da__mip_off_hi<31:31> */
    104static u32 nffw_fwinfo_loaded_get(const struct nffw_fwinfo *fi)
    105{
    106	return (le32_to_cpu(fi->loaded__mu_da__mip_off_hi) >> 31) & 1;
    107}
    108
    109/* mip_cppid = mip_cppid */
    110static u32 nffw_fwinfo_mip_cppid_get(const struct nffw_fwinfo *fi)
    111{
    112	return le32_to_cpu(fi->mip_cppid);
    113}
    114
    115/* loaded = loaded__mu_da__mip_off_hi<8:8> */
    116static u32 nffw_fwinfo_mip_mu_da_get(const struct nffw_fwinfo *fi)
    117{
    118	return (le32_to_cpu(fi->loaded__mu_da__mip_off_hi) >> 8) & 1;
    119}
    120
    121/* mip_offset = (loaded__mu_da__mip_off_hi<7:0> << 8) | mip_offset_lo */
    122static u64 nffw_fwinfo_mip_offset_get(const struct nffw_fwinfo *fi)
    123{
    124	u64 mip_off_hi = le32_to_cpu(fi->loaded__mu_da__mip_off_hi);
    125
    126	return (mip_off_hi & 0xFF) << 32 | le32_to_cpu(fi->mip_offset_lo);
    127}
    128
    129static unsigned int
    130nffw_res_fwinfos(struct nfp_nffw_info_data *fwinf, struct nffw_fwinfo **arr)
    131{
    132	/* For the this code, version 0 is most likely to be
    133	 * version 1 in this case. Since the kernel driver
    134	 * does not take responsibility for initialising the
    135	 * nfp.nffw resource, any previous code (CA firmware or
    136	 * userspace) that left the version 0 and did set
    137	 * the init flag is going to be version 1.
    138	 */
    139	switch (nffw_res_info_version_get(fwinf)) {
    140	case 0:
    141	case 1:
    142		*arr = &fwinf->info.v1.fwinfo[0];
    143		return NFFW_FWINFO_CNT_V1;
    144	case 2:
    145		*arr = &fwinf->info.v2.fwinfo[0];
    146		return NFFW_FWINFO_CNT_V2;
    147	default:
    148		*arr = NULL;
    149		return 0;
    150	}
    151}
    152
    153/**
    154 * nfp_nffw_info_open() - Acquire the lock on the NFFW table
    155 * @cpp:	NFP CPP handle
    156 *
    157 * Return: pointer to nfp_nffw_info object or ERR_PTR()
    158 */
    159struct nfp_nffw_info *nfp_nffw_info_open(struct nfp_cpp *cpp)
    160{
    161	struct nfp_nffw_info_data *fwinf;
    162	struct nfp_nffw_info *state;
    163	u32 info_ver;
    164	int err;
    165
    166	state = kzalloc(sizeof(*state), GFP_KERNEL);
    167	if (!state)
    168		return ERR_PTR(-ENOMEM);
    169
    170	state->res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_NFFW);
    171	if (IS_ERR(state->res))
    172		goto err_free;
    173
    174	fwinf = &state->fwinf;
    175
    176	if (sizeof(*fwinf) > nfp_resource_size(state->res))
    177		goto err_release;
    178
    179	err = nfp_cpp_read(cpp, nfp_resource_cpp_id(state->res),
    180			   nfp_resource_address(state->res),
    181			   fwinf, sizeof(*fwinf));
    182	if (err < (int)sizeof(*fwinf))
    183		goto err_release;
    184
    185	if (!nffw_res_flg_init_get(fwinf))
    186		goto err_release;
    187
    188	info_ver = nffw_res_info_version_get(fwinf);
    189	if (info_ver > NFFW_INFO_VERSION_CURRENT)
    190		goto err_release;
    191
    192	state->cpp = cpp;
    193	return state;
    194
    195err_release:
    196	nfp_resource_release(state->res);
    197err_free:
    198	kfree(state);
    199	return ERR_PTR(-EIO);
    200}
    201
    202/**
    203 * nfp_nffw_info_close() - Release the lock on the NFFW table and free state
    204 * @state:	NFP FW info state
    205 */
    206void nfp_nffw_info_close(struct nfp_nffw_info *state)
    207{
    208	nfp_resource_release(state->res);
    209	kfree(state);
    210}
    211
    212/**
    213 * nfp_nffw_info_fwid_first() - Return the first firmware ID in the NFFW
    214 * @state:	NFP FW info state
    215 *
    216 * Return: First NFFW firmware info, NULL on failure
    217 */
    218static struct nffw_fwinfo *nfp_nffw_info_fwid_first(struct nfp_nffw_info *state)
    219{
    220	struct nffw_fwinfo *fwinfo;
    221	unsigned int cnt, i;
    222
    223	cnt = nffw_res_fwinfos(&state->fwinf, &fwinfo);
    224	if (!cnt)
    225		return NULL;
    226
    227	for (i = 0; i < cnt; i++)
    228		if (nffw_fwinfo_loaded_get(&fwinfo[i]))
    229			return &fwinfo[i];
    230
    231	return NULL;
    232}
    233
    234/**
    235 * nfp_nffw_info_mip_first() - Retrieve the location of the first FW's MIP
    236 * @state:	NFP FW info state
    237 * @cpp_id:	Pointer to the CPP ID of the MIP
    238 * @off:	Pointer to the CPP Address of the MIP
    239 *
    240 * Return: 0, or -ERRNO
    241 */
    242int nfp_nffw_info_mip_first(struct nfp_nffw_info *state, u32 *cpp_id, u64 *off)
    243{
    244	struct nffw_fwinfo *fwinfo;
    245
    246	fwinfo = nfp_nffw_info_fwid_first(state);
    247	if (!fwinfo)
    248		return -EINVAL;
    249
    250	*cpp_id = nffw_fwinfo_mip_cppid_get(fwinfo);
    251	*off = nffw_fwinfo_mip_offset_get(fwinfo);
    252
    253	if (nffw_fwinfo_mip_mu_da_get(fwinfo)) {
    254		int locality_off = nfp_cpp_mu_locality_lsb(state->cpp);
    255
    256		*off &= ~(NFP_MU_ADDR_ACCESS_TYPE_MASK << locality_off);
    257		*off |= NFP_MU_ADDR_ACCESS_TYPE_DIRECT << locality_off;
    258	}
    259
    260	return 0;
    261}