/** * struct tb_nvm_vendor_ops - Vendor specific NVM operations * @read_version: Reads out NVM version from the flash * @validate: Validates the NVM image before update (optional) * @write_headers: Writes headers before the rest of the image (optional)
*/ struct tb_nvm_vendor_ops { int (*read_version)(struct tb_nvm *nvm); int (*validate)(struct tb_nvm *nvm); int (*write_headers)(struct tb_nvm *nvm);
};
/** * struct tb_nvm_vendor - Vendor to &struct tb_nvm_vendor_ops mapping * @vendor: Vendor ID * @vops: Vendor specific NVM operations * * Maps vendor ID to NVM vendor operations. If there is no mapping then * NVM firmware upgrade is disabled for the device.
*/ struct tb_nvm_vendor {
u16 vendor; conststruct tb_nvm_vendor_ops *vops;
};
/* * If the switch is in safe-mode the only accessible portion of * the NVM is the non-active one where userspace is expected to * write new functional NVM.
*/ if (sw->safe_mode) return 0;
ret = tb_switch_nvm_read(sw, INTEL_NVM_FLASH_SIZE, &val, sizeof(val)); if (ret) return ret;
/* * FARB pointer must point inside the image and must at least * contain parts of the digital section we will be reading here.
*/
hdr_size = (*(u32 *)buf) & 0xffffff; if (hdr_size + INTEL_NVM_DEVID + 2 >= image_size) return -EINVAL;
/* Digital section start should be aligned to 4k page */ if (!IS_ALIGNED(hdr_size, SZ_4K)) return -EINVAL;
/* * Read digital section size and check that it also fits inside * the image.
*/
ds_size = *(u16 *)(buf + hdr_size); if (ds_size >= image_size) return -EINVAL;
if (sw->safe_mode) return 0;
/* * Make sure the device ID in the image matches the one * we read from the switch config space.
*/
device_id = *(u16 *)(buf + hdr_size + INTEL_NVM_DEVID); if (device_id != sw->config.device_id) return -EINVAL;
/* Skip headers in the image */
nvm->buf_data_start = buf + hdr_size;
nvm->buf_data_size = image_size - hdr_size;
/* * FARB pointer must point inside the image and must at least * contain parts of the digital section we will be reading here.
*/
hdr_size = (*(u32 *)buf) & 0xffffff; if (hdr_size + INTEL_NVM_DEVID + 2 >= image_size) return -EINVAL;
/* Digital section start should be aligned to 4k page */ if (!IS_ALIGNED(hdr_size, SZ_4K)) return -EINVAL;
/* * Read digital section size and check that it also fits inside * the image.
*/
ds_size = *(u16 *)(buf + hdr_size); if (ds_size >= image_size) return -EINVAL;
/* * Make sure the device ID in the image matches the retimer * hardware.
*/
device = *(u16 *)(buf + hdr_size + INTEL_NVM_DEVID); if (device != rt->device) return -EINVAL;
/* Skip headers in the image */
nvm->buf_data_start = buf + hdr_size;
nvm->buf_data_size = image_size - hdr_size;
/** * tb_nvm_alloc() - Allocate new NVM structure * @dev: Device owning the NVM * * Allocates new NVM structure with unique @id and returns it. In case * of error returns ERR_PTR(). Specifically returns %-EOPNOTSUPP if the * NVM format of the @dev is not known by the kernel.
*/ struct tb_nvm *tb_nvm_alloc(struct device *dev)
{ conststruct tb_nvm_vendor_ops *vops = NULL; struct tb_nvm *nvm; int ret, i;
if (tb_is_switch(dev)) { conststruct tb_switch *sw = tb_to_switch(dev);
for (i = 0; i < ARRAY_SIZE(switch_nvm_vendors); i++) { conststruct tb_nvm_vendor *v = &switch_nvm_vendors[i];
if (!vops) {
dev_dbg(dev, "retimer NVM format of vendor %#x unknown\n",
rt->vendor); return ERR_PTR(-EOPNOTSUPP);
}
} else { return ERR_PTR(-EOPNOTSUPP);
}
nvm = kzalloc(sizeof(*nvm), GFP_KERNEL); if (!nvm) return ERR_PTR(-ENOMEM);
ret = ida_alloc(&nvm_ida, GFP_KERNEL); if (ret < 0) {
kfree(nvm); return ERR_PTR(ret);
}
nvm->id = ret;
nvm->dev = dev;
nvm->vops = vops;
return nvm;
}
/** * tb_nvm_read_version() - Read and populate NVM version * @nvm: NVM structure * * Uses vendor specific means to read out and fill in the existing * active NVM version. Returns %0 in case of success and negative errno * otherwise.
*/ int tb_nvm_read_version(struct tb_nvm *nvm)
{ conststruct tb_nvm_vendor_ops *vops = nvm->vops;
if (vops && vops->read_version) return vops->read_version(nvm);
return -EOPNOTSUPP;
}
/** * tb_nvm_validate() - Validate new NVM image * @nvm: NVM structure * * Runs vendor specific validation over the new NVM image and if all * checks pass returns %0. As side effect updates @nvm->buf_data_start * and @nvm->buf_data_size fields to match the actual data to be written * to the NVM. * * If the validation does not pass then returns negative errno.
*/ int tb_nvm_validate(struct tb_nvm *nvm)
{ conststruct tb_nvm_vendor_ops *vops = nvm->vops; unsignedint image_size;
u8 *buf = nvm->buf;
if (!buf) return -EINVAL; if (!vops) return -EOPNOTSUPP;
/* Just do basic image size checks */
image_size = nvm->buf_data_size; if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) return -EINVAL;
/* * Set the default data start in the buffer. The validate method * below can change this if needed.
*/
nvm->buf_data_start = buf;
/** * tb_nvm_write_headers() - Write headers before the rest of the image * @nvm: NVM structure * * If the vendor NVM format requires writing headers before the rest of * the image, this function does that. Can be called even if the device * does not need this. * * Returns %0 in case of success and negative errno otherwise.
*/ int tb_nvm_write_headers(struct tb_nvm *nvm)
{ conststruct tb_nvm_vendor_ops *vops = nvm->vops;
/** * tb_nvm_add_active() - Adds active NVMem device to NVM * @nvm: NVM structure * @reg_read: Pointer to the function to read the NVM (passed directly to the * NVMem device) * * Registers new active NVmem device for @nvm. The @reg_read is called * directly from NVMem so it must handle possible concurrent access if * needed. The first parameter passed to @reg_read is @nvm structure. * Returns %0 in success and negative errno otherwise.
*/ int tb_nvm_add_active(struct tb_nvm *nvm, nvmem_reg_read_t reg_read)
{ struct nvmem_config config; struct nvmem_device *nvmem;
nvmem = nvmem_register(&config); if (IS_ERR(nvmem)) return PTR_ERR(nvmem);
nvm->active = nvmem; return 0;
}
/** * tb_nvm_write_buf() - Write data to @nvm buffer * @nvm: NVM structure * @offset: Offset where to write the data * @val: Data buffer to write * @bytes: Number of bytes to write * * Helper function to cache the new NVM image before it is actually * written to the flash. Copies @bytes from @val to @nvm->buf starting * from @offset.
*/ int tb_nvm_write_buf(struct tb_nvm *nvm, unsignedint offset, void *val,
size_t bytes)
{ if (!nvm->buf) {
nvm->buf = vmalloc(NVM_MAX_SIZE); if (!nvm->buf) return -ENOMEM;
}
/** * tb_nvm_add_non_active() - Adds non-active NVMem device to NVM * @nvm: NVM structure * @reg_write: Pointer to the function to write the NVM (passed directly * to the NVMem device) * * Registers new non-active NVmem device for @nvm. The @reg_write is called * directly from NVMem so it must handle possible concurrent access if * needed. The first parameter passed to @reg_write is @nvm structure. * The size of the NVMem device is set to %NVM_MAX_SIZE. * * Returns %0 in success and negative errno otherwise.
*/ int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write)
{ struct nvmem_config config; struct nvmem_device *nvmem;
nvmem = nvmem_register(&config); if (IS_ERR(nvmem)) return PTR_ERR(nvmem);
nvm->non_active = nvmem; return 0;
}
/** * tb_nvm_free() - Release NVM and its resources * @nvm: NVM structure to release * * Releases NVM and the NVMem devices if they were registered.
*/ void tb_nvm_free(struct tb_nvm *nvm)
{ if (nvm) {
nvmem_unregister(nvm->non_active);
nvmem_unregister(nvm->active);
vfree(nvm->buf);
ida_free(&nvm_ida, nvm->id);
}
kfree(nvm);
}
/** * tb_nvm_read_data() - Read data from NVM * @address: Start address on the flash * @buf: Buffer where the read data is copied * @size: Size of the buffer in bytes * @retries: Number of retries if block read fails * @read_block: Function that reads block from the flash * @read_block_data: Data passsed to @read_block * * This is a generic function that reads data from NVM or NVM like * device. * * Returns %0 on success and negative errno otherwise.
*/ int tb_nvm_read_data(unsignedint address, void *buf, size_t size, unsignedint retries, read_block_fn read_block, void *read_block_data)
{ do { unsignedint dwaddress, dwords, offset;
u8 data[NVM_DATA_DWORDS * 4];
size_t nbytes; int ret;
/** * tb_nvm_write_data() - Write data to NVM * @address: Start address on the flash * @buf: Buffer where the data is copied from * @size: Size of the buffer in bytes * @retries: Number of retries if the block write fails * @write_block: Function that writes block to the flash * @write_block_data: Data passed to @write_block * * This is generic function that writes data to NVM or NVM like device. * * Returns %0 on success and negative errno otherwise.
*/ int tb_nvm_write_data(unsignedint address, constvoid *buf, size_t size, unsignedint retries, write_block_fn write_block, void *write_block_data)
{ do { unsignedint offset, dwaddress;
u8 data[NVM_DATA_DWORDS * 4];
size_t nbytes; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.