drivers/thunderbolt/nvm.c

Source file repositories/reference/linux-study-clean/drivers/thunderbolt/nvm.c

File Facts

System
Linux kernel
Corpus path
drivers/thunderbolt/nvm.c
Extension
.c
Size
15688 bytes
Lines
645
Domain
Driver Families
Bucket
drivers/thunderbolt
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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;
	const struct tb_nvm_vendor_ops *vops;
};

static int intel_switch_nvm_version(struct tb_nvm *nvm)
{
	struct tb_switch *sw = tb_to_switch(nvm->dev);
	u32 val, nvm_size, hdr_size;
	int ret;

	/*
	 * 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;

	hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K;
	nvm_size = (SZ_1M << (val & 7)) / 8;
	nvm_size = (nvm_size - hdr_size) / 2;

	ret = tb_switch_nvm_read(sw, INTEL_NVM_VERSION, &val, sizeof(val));
	if (ret)
		return ret;

	nvm->major = (val >> 16) & 0xff;
	nvm->minor = (val >> 8) & 0xff;
	nvm->active_size = nvm_size;

	return 0;
}

static int intel_switch_nvm_validate(struct tb_nvm *nvm)
{
	struct tb_switch *sw = tb_to_switch(nvm->dev);
	unsigned int image_size, hdr_size;
	u16 ds_size, device_id;
	u8 *buf = nvm->buf;

	image_size = nvm->buf_data_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;

	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)

Annotation

Implementation Notes