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

nvm.c (6326B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * NVM helpers
      4 *
      5 * Copyright (C) 2020, Intel Corporation
      6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
      7 */
      8
      9#include <linux/idr.h>
     10#include <linux/slab.h>
     11#include <linux/vmalloc.h>
     12
     13#include "tb.h"
     14
     15static DEFINE_IDA(nvm_ida);
     16
     17/**
     18 * tb_nvm_alloc() - Allocate new NVM structure
     19 * @dev: Device owning the NVM
     20 *
     21 * Allocates new NVM structure with unique @id and returns it. In case
     22 * of error returns ERR_PTR().
     23 */
     24struct tb_nvm *tb_nvm_alloc(struct device *dev)
     25{
     26	struct tb_nvm *nvm;
     27	int ret;
     28
     29	nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
     30	if (!nvm)
     31		return ERR_PTR(-ENOMEM);
     32
     33	ret = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL);
     34	if (ret < 0) {
     35		kfree(nvm);
     36		return ERR_PTR(ret);
     37	}
     38
     39	nvm->id = ret;
     40	nvm->dev = dev;
     41
     42	return nvm;
     43}
     44
     45/**
     46 * tb_nvm_add_active() - Adds active NVMem device to NVM
     47 * @nvm: NVM structure
     48 * @size: Size of the active NVM in bytes
     49 * @reg_read: Pointer to the function to read the NVM (passed directly to the
     50 *	      NVMem device)
     51 *
     52 * Registers new active NVmem device for @nvm. The @reg_read is called
     53 * directly from NVMem so it must handle possible concurrent access if
     54 * needed. The first parameter passed to @reg_read is @nvm structure.
     55 * Returns %0 in success and negative errno otherwise.
     56 */
     57int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read)
     58{
     59	struct nvmem_config config;
     60	struct nvmem_device *nvmem;
     61
     62	memset(&config, 0, sizeof(config));
     63
     64	config.name = "nvm_active";
     65	config.reg_read = reg_read;
     66	config.read_only = true;
     67	config.id = nvm->id;
     68	config.stride = 4;
     69	config.word_size = 4;
     70	config.size = size;
     71	config.dev = nvm->dev;
     72	config.owner = THIS_MODULE;
     73	config.priv = nvm;
     74
     75	nvmem = nvmem_register(&config);
     76	if (IS_ERR(nvmem))
     77		return PTR_ERR(nvmem);
     78
     79	nvm->active = nvmem;
     80	return 0;
     81}
     82
     83/**
     84 * tb_nvm_write_buf() - Write data to @nvm buffer
     85 * @nvm: NVM structure
     86 * @offset: Offset where to write the data
     87 * @val: Data buffer to write
     88 * @bytes: Number of bytes to write
     89 *
     90 * Helper function to cache the new NVM image before it is actually
     91 * written to the flash. Copies @bytes from @val to @nvm->buf starting
     92 * from @offset.
     93 */
     94int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
     95		     size_t bytes)
     96{
     97	if (!nvm->buf) {
     98		nvm->buf = vmalloc(NVM_MAX_SIZE);
     99		if (!nvm->buf)
    100			return -ENOMEM;
    101	}
    102
    103	nvm->flushed = false;
    104	nvm->buf_data_size = offset + bytes;
    105	memcpy(nvm->buf + offset, val, bytes);
    106	return 0;
    107}
    108
    109/**
    110 * tb_nvm_add_non_active() - Adds non-active NVMem device to NVM
    111 * @nvm: NVM structure
    112 * @size: Size of the non-active NVM in bytes
    113 * @reg_write: Pointer to the function to write the NVM (passed directly
    114 *	       to the NVMem device)
    115 *
    116 * Registers new non-active NVmem device for @nvm. The @reg_write is called
    117 * directly from NVMem so it must handle possible concurrent access if
    118 * needed. The first parameter passed to @reg_write is @nvm structure.
    119 * Returns %0 in success and negative errno otherwise.
    120 */
    121int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
    122			  nvmem_reg_write_t reg_write)
    123{
    124	struct nvmem_config config;
    125	struct nvmem_device *nvmem;
    126
    127	memset(&config, 0, sizeof(config));
    128
    129	config.name = "nvm_non_active";
    130	config.reg_write = reg_write;
    131	config.root_only = true;
    132	config.id = nvm->id;
    133	config.stride = 4;
    134	config.word_size = 4;
    135	config.size = size;
    136	config.dev = nvm->dev;
    137	config.owner = THIS_MODULE;
    138	config.priv = nvm;
    139
    140	nvmem = nvmem_register(&config);
    141	if (IS_ERR(nvmem))
    142		return PTR_ERR(nvmem);
    143
    144	nvm->non_active = nvmem;
    145	return 0;
    146}
    147
    148/**
    149 * tb_nvm_free() - Release NVM and its resources
    150 * @nvm: NVM structure to release
    151 *
    152 * Releases NVM and the NVMem devices if they were registered.
    153 */
    154void tb_nvm_free(struct tb_nvm *nvm)
    155{
    156	if (nvm) {
    157		nvmem_unregister(nvm->non_active);
    158		nvmem_unregister(nvm->active);
    159		vfree(nvm->buf);
    160		ida_simple_remove(&nvm_ida, nvm->id);
    161	}
    162	kfree(nvm);
    163}
    164
    165/**
    166 * tb_nvm_read_data() - Read data from NVM
    167 * @address: Start address on the flash
    168 * @buf: Buffer where the read data is copied
    169 * @size: Size of the buffer in bytes
    170 * @retries: Number of retries if block read fails
    171 * @read_block: Function that reads block from the flash
    172 * @read_block_data: Data passsed to @read_block
    173 *
    174 * This is a generic function that reads data from NVM or NVM like
    175 * device.
    176 *
    177 * Returns %0 on success and negative errno otherwise.
    178 */
    179int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
    180		     unsigned int retries, read_block_fn read_block,
    181		     void *read_block_data)
    182{
    183	do {
    184		unsigned int dwaddress, dwords, offset;
    185		u8 data[NVM_DATA_DWORDS * 4];
    186		size_t nbytes;
    187		int ret;
    188
    189		offset = address & 3;
    190		nbytes = min_t(size_t, size + offset, NVM_DATA_DWORDS * 4);
    191
    192		dwaddress = address / 4;
    193		dwords = ALIGN(nbytes, 4) / 4;
    194
    195		ret = read_block(read_block_data, dwaddress, data, dwords);
    196		if (ret) {
    197			if (ret != -ENODEV && retries--)
    198				continue;
    199			return ret;
    200		}
    201
    202		nbytes -= offset;
    203		memcpy(buf, data + offset, nbytes);
    204
    205		size -= nbytes;
    206		address += nbytes;
    207		buf += nbytes;
    208	} while (size > 0);
    209
    210	return 0;
    211}
    212
    213/**
    214 * tb_nvm_write_data() - Write data to NVM
    215 * @address: Start address on the flash
    216 * @buf: Buffer where the data is copied from
    217 * @size: Size of the buffer in bytes
    218 * @retries: Number of retries if the block write fails
    219 * @write_block: Function that writes block to the flash
    220 * @write_block_data: Data passwd to @write_block
    221 *
    222 * This is generic function that writes data to NVM or NVM like device.
    223 *
    224 * Returns %0 on success and negative errno otherwise.
    225 */
    226int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
    227		      unsigned int retries, write_block_fn write_block,
    228		      void *write_block_data)
    229{
    230	do {
    231		unsigned int offset, dwaddress;
    232		u8 data[NVM_DATA_DWORDS * 4];
    233		size_t nbytes;
    234		int ret;
    235
    236		offset = address & 3;
    237		nbytes = min_t(u32, size + offset, NVM_DATA_DWORDS * 4);
    238
    239		memcpy(data + offset, buf, nbytes);
    240
    241		dwaddress = address / 4;
    242		ret = write_block(write_block_data, dwaddress, data, nbytes / 4);
    243		if (ret) {
    244			if (ret == -ETIMEDOUT) {
    245				if (retries--)
    246					continue;
    247				ret = -EIO;
    248			}
    249			return ret;
    250		}
    251
    252		size -= nbytes;
    253		address += nbytes;
    254		buf += nbytes;
    255	} while (size > 0);
    256
    257	return 0;
    258}
    259
    260void tb_nvm_exit(void)
    261{
    262	ida_destroy(&nvm_ida);
    263}