xref: /linux/drivers/fpga/intel-m10-bmc-sec-update.c (revision da04fa8c40c339da7bb6de463ef83bc141d1111e)
1bdf86d0eSRuss Weight // SPDX-License-Identifier: GPL-2.0
2bdf86d0eSRuss Weight /*
3bdf86d0eSRuss Weight  * Intel MAX10 Board Management Controller Secure Update Driver
4bdf86d0eSRuss Weight  *
5bdf86d0eSRuss Weight  * Copyright (C) 2019-2022 Intel Corporation. All rights reserved.
6bdf86d0eSRuss Weight  *
7bdf86d0eSRuss Weight  */
8bdf86d0eSRuss Weight #include <linux/bitfield.h>
9bdf86d0eSRuss Weight #include <linux/device.h>
10bdf86d0eSRuss Weight #include <linux/firmware.h>
11bdf86d0eSRuss Weight #include <linux/mfd/intel-m10-bmc.h>
12bdf86d0eSRuss Weight #include <linux/mod_devicetable.h>
13bdf86d0eSRuss Weight #include <linux/module.h>
14bdf86d0eSRuss Weight #include <linux/platform_device.h>
15bdf86d0eSRuss Weight #include <linux/slab.h>
16bdf86d0eSRuss Weight 
17bdf86d0eSRuss Weight struct m10bmc_sec {
18bdf86d0eSRuss Weight 	struct device *dev;
19bdf86d0eSRuss Weight 	struct intel_m10bmc *m10bmc;
205cd339b3SRuss Weight 	struct fw_upload *fwl;
215cd339b3SRuss Weight 	char *fw_name;
225cd339b3SRuss Weight 	u32 fw_name_id;
235cd339b3SRuss Weight 	bool cancel_request;
24bdf86d0eSRuss Weight };
25bdf86d0eSRuss Weight 
265cd339b3SRuss Weight static DEFINE_XARRAY_ALLOC(fw_upload_xa);
275cd339b3SRuss Weight 
28bdf86d0eSRuss Weight /* Root Entry Hash (REH) support */
29bdf86d0eSRuss Weight #define REH_SHA256_SIZE		32
30bdf86d0eSRuss Weight #define REH_SHA384_SIZE		48
31bdf86d0eSRuss Weight #define REH_MAGIC		GENMASK(15, 0)
32bdf86d0eSRuss Weight #define REH_SHA_NUM_BYTES	GENMASK(31, 16)
33bdf86d0eSRuss Weight 
343e10c805SIlpo Järvinen static int m10bmc_sec_write(struct m10bmc_sec *sec, const u8 *buf, u32 offset, u32 size)
353e10c805SIlpo Järvinen {
363e10c805SIlpo Järvinen 	struct intel_m10bmc *m10bmc = sec->m10bmc;
373e10c805SIlpo Järvinen 	unsigned int stride = regmap_get_reg_stride(m10bmc->regmap);
383e10c805SIlpo Järvinen 	u32 write_count = size / stride;
393e10c805SIlpo Järvinen 	u32 leftover_offset = write_count * stride;
403e10c805SIlpo Järvinen 	u32 leftover_size = size - leftover_offset;
413e10c805SIlpo Järvinen 	u32 leftover_tmp = 0;
423e10c805SIlpo Järvinen 	int ret;
433e10c805SIlpo Järvinen 
443e10c805SIlpo Järvinen 	if (WARN_ON_ONCE(stride > sizeof(leftover_tmp)))
453e10c805SIlpo Järvinen 		return -EINVAL;
463e10c805SIlpo Järvinen 
473e10c805SIlpo Järvinen 	ret = regmap_bulk_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset,
483e10c805SIlpo Järvinen 				buf + offset, write_count);
493e10c805SIlpo Järvinen 	if (ret)
503e10c805SIlpo Järvinen 		return ret;
513e10c805SIlpo Järvinen 
523e10c805SIlpo Järvinen 	/* If size is not aligned to stride, handle the remainder bytes with regmap_write() */
533e10c805SIlpo Järvinen 	if (leftover_size) {
543e10c805SIlpo Järvinen 		memcpy(&leftover_tmp, buf + leftover_offset, leftover_size);
553e10c805SIlpo Järvinen 		ret = regmap_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset + leftover_offset,
563e10c805SIlpo Järvinen 				   leftover_tmp);
573e10c805SIlpo Järvinen 		if (ret)
583e10c805SIlpo Järvinen 			return ret;
593e10c805SIlpo Järvinen 	}
603e10c805SIlpo Järvinen 
613e10c805SIlpo Järvinen 	return 0;
623e10c805SIlpo Järvinen }
633e10c805SIlpo Järvinen 
643e10c805SIlpo Järvinen static int m10bmc_sec_read(struct m10bmc_sec *sec, u8 *buf, u32 addr, u32 size)
653e10c805SIlpo Järvinen {
663e10c805SIlpo Järvinen 	struct intel_m10bmc *m10bmc = sec->m10bmc;
673e10c805SIlpo Järvinen 	unsigned int stride = regmap_get_reg_stride(m10bmc->regmap);
683e10c805SIlpo Järvinen 	u32 read_count = size / stride;
693e10c805SIlpo Järvinen 	u32 leftover_offset = read_count * stride;
703e10c805SIlpo Järvinen 	u32 leftover_size = size - leftover_offset;
713e10c805SIlpo Järvinen 	u32 leftover_tmp;
723e10c805SIlpo Järvinen 	int ret;
733e10c805SIlpo Järvinen 
743e10c805SIlpo Järvinen 	if (WARN_ON_ONCE(stride > sizeof(leftover_tmp)))
753e10c805SIlpo Järvinen 		return -EINVAL;
763e10c805SIlpo Järvinen 
773e10c805SIlpo Järvinen 	ret = regmap_bulk_read(m10bmc->regmap, addr, buf, read_count);
783e10c805SIlpo Järvinen 	if (ret)
793e10c805SIlpo Järvinen 		return ret;
803e10c805SIlpo Järvinen 
813e10c805SIlpo Järvinen 	/* If size is not aligned to stride, handle the remainder bytes with regmap_read() */
823e10c805SIlpo Järvinen 	if (leftover_size) {
833e10c805SIlpo Järvinen 		ret = regmap_read(m10bmc->regmap, addr + leftover_offset, &leftover_tmp);
843e10c805SIlpo Järvinen 		if (ret)
853e10c805SIlpo Järvinen 			return ret;
863e10c805SIlpo Järvinen 		memcpy(buf + leftover_offset, &leftover_tmp, leftover_size);
873e10c805SIlpo Järvinen 	}
883e10c805SIlpo Järvinen 
893e10c805SIlpo Järvinen 	return 0;
903e10c805SIlpo Järvinen }
913e10c805SIlpo Järvinen 
923e10c805SIlpo Järvinen 
93bdf86d0eSRuss Weight static ssize_t
94bdf86d0eSRuss Weight show_root_entry_hash(struct device *dev, u32 exp_magic,
95bdf86d0eSRuss Weight 		     u32 prog_addr, u32 reh_addr, char *buf)
96bdf86d0eSRuss Weight {
97bdf86d0eSRuss Weight 	struct m10bmc_sec *sec = dev_get_drvdata(dev);
98bdf86d0eSRuss Weight 	int sha_num_bytes, i, ret, cnt = 0;
99bdf86d0eSRuss Weight 	u8 hash[REH_SHA384_SIZE];
100bdf86d0eSRuss Weight 	u32 magic;
101bdf86d0eSRuss Weight 
1023e10c805SIlpo Järvinen 	ret = m10bmc_sec_read(sec, (u8 *)&magic, prog_addr, sizeof(magic));
103bdf86d0eSRuss Weight 	if (ret)
104bdf86d0eSRuss Weight 		return ret;
105bdf86d0eSRuss Weight 
106bdf86d0eSRuss Weight 	if (FIELD_GET(REH_MAGIC, magic) != exp_magic)
107bdf86d0eSRuss Weight 		return sysfs_emit(buf, "hash not programmed\n");
108bdf86d0eSRuss Weight 
109bdf86d0eSRuss Weight 	sha_num_bytes = FIELD_GET(REH_SHA_NUM_BYTES, magic) / 8;
1103e10c805SIlpo Järvinen 	if (sha_num_bytes != REH_SHA256_SIZE &&
1113e10c805SIlpo Järvinen 	    sha_num_bytes != REH_SHA384_SIZE) {
112bdf86d0eSRuss Weight 		dev_err(sec->dev, "%s bad sha num bytes %d\n", __func__,
113bdf86d0eSRuss Weight 			sha_num_bytes);
114bdf86d0eSRuss Weight 		return -EINVAL;
115bdf86d0eSRuss Weight 	}
116bdf86d0eSRuss Weight 
1173e10c805SIlpo Järvinen 	ret = m10bmc_sec_read(sec, hash, reh_addr, sha_num_bytes);
118bdf86d0eSRuss Weight 	if (ret) {
1193e10c805SIlpo Järvinen 		dev_err(dev, "failed to read root entry hash\n");
120bdf86d0eSRuss Weight 		return ret;
121bdf86d0eSRuss Weight 	}
122bdf86d0eSRuss Weight 
123bdf86d0eSRuss Weight 	for (i = 0; i < sha_num_bytes; i++)
124bdf86d0eSRuss Weight 		cnt += sprintf(buf + cnt, "%02x", hash[i]);
125bdf86d0eSRuss Weight 	cnt += sprintf(buf + cnt, "\n");
126bdf86d0eSRuss Weight 
127bdf86d0eSRuss Weight 	return cnt;
128bdf86d0eSRuss Weight }
129bdf86d0eSRuss Weight 
1306052a005SIlpo Järvinen #define DEVICE_ATTR_SEC_REH_RO(_name)						\
131bdf86d0eSRuss Weight static ssize_t _name##_root_entry_hash_show(struct device *dev, \
132bdf86d0eSRuss Weight 					    struct device_attribute *attr, \
133bdf86d0eSRuss Weight 					    char *buf) \
1346052a005SIlpo Järvinen {										\
1356052a005SIlpo Järvinen 	struct m10bmc_sec *sec = dev_get_drvdata(dev);				\
1366052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;	\
1376052a005SIlpo Järvinen 										\
1386052a005SIlpo Järvinen 	return show_root_entry_hash(dev, csr_map->_name##_magic,		\
1396052a005SIlpo Järvinen 				    csr_map->_name##_prog_addr,			\
1406052a005SIlpo Järvinen 				    csr_map->_name##_reh_addr,			\
1416052a005SIlpo Järvinen 				    buf);					\
1426052a005SIlpo Järvinen }										\
143bdf86d0eSRuss Weight static DEVICE_ATTR_RO(_name##_root_entry_hash)
144bdf86d0eSRuss Weight 
1456052a005SIlpo Järvinen DEVICE_ATTR_SEC_REH_RO(bmc);
1466052a005SIlpo Järvinen DEVICE_ATTR_SEC_REH_RO(sr);
1476052a005SIlpo Järvinen DEVICE_ATTR_SEC_REH_RO(pr);
148bdf86d0eSRuss Weight 
1497f03d84aSRuss Weight #define CSK_BIT_LEN		128U
1507f03d84aSRuss Weight #define CSK_32ARRAY_SIZE	DIV_ROUND_UP(CSK_BIT_LEN, 32)
1517f03d84aSRuss Weight 
1527f03d84aSRuss Weight static ssize_t
1537f03d84aSRuss Weight show_canceled_csk(struct device *dev, u32 addr, char *buf)
1547f03d84aSRuss Weight {
1553e10c805SIlpo Järvinen 	unsigned int i, size = CSK_32ARRAY_SIZE * sizeof(u32);
1567f03d84aSRuss Weight 	struct m10bmc_sec *sec = dev_get_drvdata(dev);
1577f03d84aSRuss Weight 	DECLARE_BITMAP(csk_map, CSK_BIT_LEN);
1587f03d84aSRuss Weight 	__le32 csk_le32[CSK_32ARRAY_SIZE];
1597f03d84aSRuss Weight 	u32 csk32[CSK_32ARRAY_SIZE];
1607f03d84aSRuss Weight 	int ret;
1617f03d84aSRuss Weight 
1623e10c805SIlpo Järvinen 	ret = m10bmc_sec_read(sec, (u8 *)&csk_le32, addr, size);
1637f03d84aSRuss Weight 	if (ret) {
1643e10c805SIlpo Järvinen 		dev_err(sec->dev, "failed to read CSK vector\n");
1657f03d84aSRuss Weight 		return ret;
1667f03d84aSRuss Weight 	}
1677f03d84aSRuss Weight 
1687f03d84aSRuss Weight 	for (i = 0; i < CSK_32ARRAY_SIZE; i++)
1697f03d84aSRuss Weight 		csk32[i] = le32_to_cpu(((csk_le32[i])));
1707f03d84aSRuss Weight 
1717f03d84aSRuss Weight 	bitmap_from_arr32(csk_map, csk32, CSK_BIT_LEN);
1727f03d84aSRuss Weight 	bitmap_complement(csk_map, csk_map, CSK_BIT_LEN);
1737f03d84aSRuss Weight 	return bitmap_print_to_pagebuf(1, buf, csk_map, CSK_BIT_LEN);
1747f03d84aSRuss Weight }
1757f03d84aSRuss Weight 
1766052a005SIlpo Järvinen #define DEVICE_ATTR_SEC_CSK_RO(_name)						\
1777f03d84aSRuss Weight static ssize_t _name##_canceled_csks_show(struct device *dev, \
1787f03d84aSRuss Weight 					  struct device_attribute *attr, \
1797f03d84aSRuss Weight 					  char *buf) \
1806052a005SIlpo Järvinen {										\
1816052a005SIlpo Järvinen 	struct m10bmc_sec *sec = dev_get_drvdata(dev);				\
1826052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;	\
1836052a005SIlpo Järvinen 										\
1846052a005SIlpo Järvinen 	return show_canceled_csk(dev,						\
1856052a005SIlpo Järvinen 				 csr_map->_name##_prog_addr + CSK_VEC_OFFSET,	\
1866052a005SIlpo Järvinen 				 buf);						\
1876052a005SIlpo Järvinen }										\
1887f03d84aSRuss Weight static DEVICE_ATTR_RO(_name##_canceled_csks)
1897f03d84aSRuss Weight 
1907f03d84aSRuss Weight #define CSK_VEC_OFFSET 0x34
1917f03d84aSRuss Weight 
1926052a005SIlpo Järvinen DEVICE_ATTR_SEC_CSK_RO(bmc);
1936052a005SIlpo Järvinen DEVICE_ATTR_SEC_CSK_RO(sr);
1946052a005SIlpo Järvinen DEVICE_ATTR_SEC_CSK_RO(pr);
1957f03d84aSRuss Weight 
196154afa5cSRuss Weight #define FLASH_COUNT_SIZE 4096	/* count stored as inverted bit vector */
197154afa5cSRuss Weight 
198154afa5cSRuss Weight static ssize_t flash_count_show(struct device *dev,
199154afa5cSRuss Weight 				struct device_attribute *attr, char *buf)
200154afa5cSRuss Weight {
201154afa5cSRuss Weight 	struct m10bmc_sec *sec = dev_get_drvdata(dev);
2026052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
2033e10c805SIlpo Järvinen 	unsigned int num_bits;
204154afa5cSRuss Weight 	u8 *flash_buf;
205154afa5cSRuss Weight 	int cnt, ret;
206154afa5cSRuss Weight 
207154afa5cSRuss Weight 	num_bits = FLASH_COUNT_SIZE * 8;
208154afa5cSRuss Weight 
209468c9d92SRuss Weight 	flash_buf = kmalloc(FLASH_COUNT_SIZE, GFP_KERNEL);
210468c9d92SRuss Weight 	if (!flash_buf)
211468c9d92SRuss Weight 		return -ENOMEM;
212468c9d92SRuss Weight 
2133e10c805SIlpo Järvinen 	ret = m10bmc_sec_read(sec, flash_buf, csr_map->rsu_update_counter,
2143e10c805SIlpo Järvinen 			      FLASH_COUNT_SIZE);
215154afa5cSRuss Weight 	if (ret) {
2163e10c805SIlpo Järvinen 		dev_err(sec->dev, "failed to read flash count\n");
217154afa5cSRuss Weight 		goto exit_free;
218154afa5cSRuss Weight 	}
219154afa5cSRuss Weight 	cnt = num_bits - bitmap_weight((unsigned long *)flash_buf, num_bits);
220154afa5cSRuss Weight 
221154afa5cSRuss Weight exit_free:
222154afa5cSRuss Weight 	kfree(flash_buf);
223154afa5cSRuss Weight 
224154afa5cSRuss Weight 	return ret ? : sysfs_emit(buf, "%u\n", cnt);
225154afa5cSRuss Weight }
226154afa5cSRuss Weight static DEVICE_ATTR_RO(flash_count);
227154afa5cSRuss Weight 
228bdf86d0eSRuss Weight static struct attribute *m10bmc_security_attrs[] = {
229154afa5cSRuss Weight 	&dev_attr_flash_count.attr,
230bdf86d0eSRuss Weight 	&dev_attr_bmc_root_entry_hash.attr,
231bdf86d0eSRuss Weight 	&dev_attr_sr_root_entry_hash.attr,
232bdf86d0eSRuss Weight 	&dev_attr_pr_root_entry_hash.attr,
2337f03d84aSRuss Weight 	&dev_attr_sr_canceled_csks.attr,
2347f03d84aSRuss Weight 	&dev_attr_pr_canceled_csks.attr,
2357f03d84aSRuss Weight 	&dev_attr_bmc_canceled_csks.attr,
236bdf86d0eSRuss Weight 	NULL,
237bdf86d0eSRuss Weight };
238bdf86d0eSRuss Weight 
239bdf86d0eSRuss Weight static struct attribute_group m10bmc_security_attr_group = {
240bdf86d0eSRuss Weight 	.name = "security",
241bdf86d0eSRuss Weight 	.attrs = m10bmc_security_attrs,
242bdf86d0eSRuss Weight };
243bdf86d0eSRuss Weight 
244bdf86d0eSRuss Weight static const struct attribute_group *m10bmc_sec_attr_groups[] = {
245bdf86d0eSRuss Weight 	&m10bmc_security_attr_group,
246bdf86d0eSRuss Weight 	NULL,
247bdf86d0eSRuss Weight };
248bdf86d0eSRuss Weight 
2495cd339b3SRuss Weight static void log_error_regs(struct m10bmc_sec *sec, u32 doorbell)
2505cd339b3SRuss Weight {
2516052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
2525cd339b3SRuss Weight 	u32 auth_result;
2535cd339b3SRuss Weight 
2545cd339b3SRuss Weight 	dev_err(sec->dev, "RSU error status: 0x%08x\n", doorbell);
2555cd339b3SRuss Weight 
2566052a005SIlpo Järvinen 	if (!m10bmc_sys_read(sec->m10bmc, csr_map->auth_result, &auth_result))
2575cd339b3SRuss Weight 		dev_err(sec->dev, "RSU auth result: 0x%08x\n", auth_result);
2585cd339b3SRuss Weight }
2595cd339b3SRuss Weight 
260*da04fa8cSIlpo Järvinen static bool rsu_status_ok(u32 status)
261*da04fa8cSIlpo Järvinen {
262*da04fa8cSIlpo Järvinen 	return (status == RSU_STAT_NORMAL ||
263*da04fa8cSIlpo Järvinen 		status == RSU_STAT_NIOS_OK ||
264*da04fa8cSIlpo Järvinen 		status == RSU_STAT_USER_OK ||
265*da04fa8cSIlpo Järvinen 		status == RSU_STAT_FACTORY_OK);
266*da04fa8cSIlpo Järvinen }
267*da04fa8cSIlpo Järvinen 
268*da04fa8cSIlpo Järvinen static bool rsu_progress_done(u32 progress)
269*da04fa8cSIlpo Järvinen {
270*da04fa8cSIlpo Järvinen 	return (progress == RSU_PROG_IDLE ||
271*da04fa8cSIlpo Järvinen 		progress == RSU_PROG_RSU_DONE);
272*da04fa8cSIlpo Järvinen }
273*da04fa8cSIlpo Järvinen 
274*da04fa8cSIlpo Järvinen static bool rsu_progress_busy(u32 progress)
275*da04fa8cSIlpo Järvinen {
276*da04fa8cSIlpo Järvinen 	return (progress == RSU_PROG_AUTHENTICATING ||
277*da04fa8cSIlpo Järvinen 		progress == RSU_PROG_COPYING ||
278*da04fa8cSIlpo Järvinen 		progress == RSU_PROG_UPDATE_CANCEL ||
279*da04fa8cSIlpo Järvinen 		progress == RSU_PROG_PROGRAM_KEY_HASH);
280*da04fa8cSIlpo Järvinen }
281*da04fa8cSIlpo Järvinen 
2825cd339b3SRuss Weight static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec)
2835cd339b3SRuss Weight {
2846052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
2855cd339b3SRuss Weight 	u32 doorbell;
2865cd339b3SRuss Weight 	int ret;
2875cd339b3SRuss Weight 
2886052a005SIlpo Järvinen 	ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
2895cd339b3SRuss Weight 	if (ret)
2905cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
2915cd339b3SRuss Weight 
292*da04fa8cSIlpo Järvinen 	if (!rsu_progress_done(rsu_prog(doorbell))) {
2935cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
2945cd339b3SRuss Weight 		return FW_UPLOAD_ERR_BUSY;
2955cd339b3SRuss Weight 	}
2965cd339b3SRuss Weight 
2975cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
2985cd339b3SRuss Weight }
2995cd339b3SRuss Weight 
3005cd339b3SRuss Weight static inline bool rsu_start_done(u32 doorbell)
3015cd339b3SRuss Weight {
3025cd339b3SRuss Weight 	u32 status, progress;
3035cd339b3SRuss Weight 
3045cd339b3SRuss Weight 	if (doorbell & DRBL_RSU_REQUEST)
3055cd339b3SRuss Weight 		return false;
3065cd339b3SRuss Weight 
3075cd339b3SRuss Weight 	status = rsu_stat(doorbell);
3085cd339b3SRuss Weight 	if (status == RSU_STAT_ERASE_FAIL || status == RSU_STAT_WEAROUT)
3095cd339b3SRuss Weight 		return true;
3105cd339b3SRuss Weight 
3115cd339b3SRuss Weight 	progress = rsu_prog(doorbell);
312*da04fa8cSIlpo Järvinen 	if (!rsu_progress_done(progress))
3135cd339b3SRuss Weight 		return true;
3145cd339b3SRuss Weight 
3155cd339b3SRuss Weight 	return false;
3165cd339b3SRuss Weight }
3175cd339b3SRuss Weight 
3185cd339b3SRuss Weight static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec)
3195cd339b3SRuss Weight {
3206052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
3215cd339b3SRuss Weight 	u32 doorbell, status;
3225cd339b3SRuss Weight 	int ret;
3235cd339b3SRuss Weight 
3245cd339b3SRuss Weight 	ret = regmap_update_bits(sec->m10bmc->regmap,
3256052a005SIlpo Järvinen 				 csr_map->base + csr_map->doorbell,
3265cd339b3SRuss Weight 				 DRBL_RSU_REQUEST | DRBL_HOST_STATUS,
3275cd339b3SRuss Weight 				 DRBL_RSU_REQUEST |
3285cd339b3SRuss Weight 				 FIELD_PREP(DRBL_HOST_STATUS,
3295cd339b3SRuss Weight 					    HOST_STATUS_IDLE));
3305cd339b3SRuss Weight 	if (ret)
3315cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
3325cd339b3SRuss Weight 
3335cd339b3SRuss Weight 	ret = regmap_read_poll_timeout(sec->m10bmc->regmap,
3346052a005SIlpo Järvinen 				       csr_map->base + csr_map->doorbell,
3355cd339b3SRuss Weight 				       doorbell,
3365cd339b3SRuss Weight 				       rsu_start_done(doorbell),
3375cd339b3SRuss Weight 				       NIOS_HANDSHAKE_INTERVAL_US,
3385cd339b3SRuss Weight 				       NIOS_HANDSHAKE_TIMEOUT_US);
3395cd339b3SRuss Weight 
3405cd339b3SRuss Weight 	if (ret == -ETIMEDOUT) {
3415cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
3425cd339b3SRuss Weight 		return FW_UPLOAD_ERR_TIMEOUT;
3435cd339b3SRuss Weight 	} else if (ret) {
3445cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
3455cd339b3SRuss Weight 	}
3465cd339b3SRuss Weight 
3475cd339b3SRuss Weight 	status = rsu_stat(doorbell);
3485cd339b3SRuss Weight 	if (status == RSU_STAT_WEAROUT) {
3495cd339b3SRuss Weight 		dev_warn(sec->dev, "Excessive flash update count detected\n");
3505cd339b3SRuss Weight 		return FW_UPLOAD_ERR_WEAROUT;
3515cd339b3SRuss Weight 	} else if (status == RSU_STAT_ERASE_FAIL) {
3525cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
3535cd339b3SRuss Weight 		return FW_UPLOAD_ERR_HW_ERROR;
3545cd339b3SRuss Weight 	}
3555cd339b3SRuss Weight 
3565cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
3575cd339b3SRuss Weight }
3585cd339b3SRuss Weight 
3595cd339b3SRuss Weight static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec)
3605cd339b3SRuss Weight {
3616052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
3625cd339b3SRuss Weight 	unsigned long poll_timeout;
3635cd339b3SRuss Weight 	u32 doorbell, progress;
3645cd339b3SRuss Weight 	int ret;
3655cd339b3SRuss Weight 
3666052a005SIlpo Järvinen 	ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
3675cd339b3SRuss Weight 	if (ret)
3685cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
3695cd339b3SRuss Weight 
3705cd339b3SRuss Weight 	poll_timeout = jiffies + msecs_to_jiffies(RSU_PREP_TIMEOUT_MS);
3715cd339b3SRuss Weight 	while (rsu_prog(doorbell) == RSU_PROG_PREPARE) {
3725cd339b3SRuss Weight 		msleep(RSU_PREP_INTERVAL_MS);
3735cd339b3SRuss Weight 		if (time_after(jiffies, poll_timeout))
3745cd339b3SRuss Weight 			break;
3755cd339b3SRuss Weight 
3766052a005SIlpo Järvinen 		ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
3775cd339b3SRuss Weight 		if (ret)
3785cd339b3SRuss Weight 			return FW_UPLOAD_ERR_RW_ERROR;
3795cd339b3SRuss Weight 	}
3805cd339b3SRuss Weight 
3815cd339b3SRuss Weight 	progress = rsu_prog(doorbell);
3825cd339b3SRuss Weight 	if (progress == RSU_PROG_PREPARE) {
3835cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
3845cd339b3SRuss Weight 		return FW_UPLOAD_ERR_TIMEOUT;
3855cd339b3SRuss Weight 	} else if (progress != RSU_PROG_READY) {
3865cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
3875cd339b3SRuss Weight 		return FW_UPLOAD_ERR_HW_ERROR;
3885cd339b3SRuss Weight 	}
3895cd339b3SRuss Weight 
3905cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
3915cd339b3SRuss Weight }
3925cd339b3SRuss Weight 
3935cd339b3SRuss Weight static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec)
3945cd339b3SRuss Weight {
3956052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
3965cd339b3SRuss Weight 	u32 doorbell;
3975cd339b3SRuss Weight 	int ret;
3985cd339b3SRuss Weight 
3995cd339b3SRuss Weight 	ret = regmap_update_bits(sec->m10bmc->regmap,
4006052a005SIlpo Järvinen 				 csr_map->base + csr_map->doorbell,
4015cd339b3SRuss Weight 				 DRBL_HOST_STATUS,
4025cd339b3SRuss Weight 				 FIELD_PREP(DRBL_HOST_STATUS,
4035cd339b3SRuss Weight 					    HOST_STATUS_WRITE_DONE));
4045cd339b3SRuss Weight 	if (ret)
4055cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
4065cd339b3SRuss Weight 
4075cd339b3SRuss Weight 	ret = regmap_read_poll_timeout(sec->m10bmc->regmap,
4086052a005SIlpo Järvinen 				       csr_map->base + csr_map->doorbell,
4095cd339b3SRuss Weight 				       doorbell,
4105cd339b3SRuss Weight 				       rsu_prog(doorbell) != RSU_PROG_READY,
4115cd339b3SRuss Weight 				       NIOS_HANDSHAKE_INTERVAL_US,
4125cd339b3SRuss Weight 				       NIOS_HANDSHAKE_TIMEOUT_US);
4135cd339b3SRuss Weight 
4145cd339b3SRuss Weight 	if (ret == -ETIMEDOUT) {
4155cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
4165cd339b3SRuss Weight 		return FW_UPLOAD_ERR_TIMEOUT;
4175cd339b3SRuss Weight 	} else if (ret) {
4185cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
4195cd339b3SRuss Weight 	}
4205cd339b3SRuss Weight 
421*da04fa8cSIlpo Järvinen 	if (!rsu_status_ok(rsu_stat(doorbell))) {
4225cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
4235cd339b3SRuss Weight 		return FW_UPLOAD_ERR_HW_ERROR;
4245cd339b3SRuss Weight 	}
4255cd339b3SRuss Weight 
4265cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
4275cd339b3SRuss Weight }
4285cd339b3SRuss Weight 
4295cd339b3SRuss Weight static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell)
4305cd339b3SRuss Weight {
4316052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
4326052a005SIlpo Järvinen 
4336052a005SIlpo Järvinen 	if (m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, doorbell))
4345cd339b3SRuss Weight 		return -EIO;
4355cd339b3SRuss Weight 
436*da04fa8cSIlpo Järvinen 	if (!rsu_status_ok(rsu_stat(*doorbell)))
4375cd339b3SRuss Weight 		return -EINVAL;
4385cd339b3SRuss Weight 
439*da04fa8cSIlpo Järvinen 	if (rsu_progress_done(rsu_prog(*doorbell)))
4405cd339b3SRuss Weight 		return 0;
441*da04fa8cSIlpo Järvinen 
442*da04fa8cSIlpo Järvinen 	if (rsu_progress_busy(rsu_prog(*doorbell)))
4435cd339b3SRuss Weight 		return -EAGAIN;
444*da04fa8cSIlpo Järvinen 
4455cd339b3SRuss Weight 	return -EINVAL;
4465cd339b3SRuss Weight }
4475cd339b3SRuss Weight 
4485cd339b3SRuss Weight static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec)
4495cd339b3SRuss Weight {
4506052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
4515cd339b3SRuss Weight 	u32 doorbell;
4525cd339b3SRuss Weight 	int ret;
4535cd339b3SRuss Weight 
4546052a005SIlpo Järvinen 	ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
4555cd339b3SRuss Weight 	if (ret)
4565cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
4575cd339b3SRuss Weight 
4585cd339b3SRuss Weight 	if (rsu_prog(doorbell) != RSU_PROG_READY)
4595cd339b3SRuss Weight 		return FW_UPLOAD_ERR_BUSY;
4605cd339b3SRuss Weight 
4615cd339b3SRuss Weight 	ret = regmap_update_bits(sec->m10bmc->regmap,
4626052a005SIlpo Järvinen 				 csr_map->base + csr_map->doorbell,
4635cd339b3SRuss Weight 				 DRBL_HOST_STATUS,
4645cd339b3SRuss Weight 				 FIELD_PREP(DRBL_HOST_STATUS,
4655cd339b3SRuss Weight 					    HOST_STATUS_ABORT_RSU));
4665cd339b3SRuss Weight 	if (ret)
4675cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
4685cd339b3SRuss Weight 
4695cd339b3SRuss Weight 	return FW_UPLOAD_ERR_CANCELED;
4705cd339b3SRuss Weight }
4715cd339b3SRuss Weight 
4725cd339b3SRuss Weight static enum fw_upload_err m10bmc_sec_prepare(struct fw_upload *fwl,
4735cd339b3SRuss Weight 					     const u8 *data, u32 size)
4745cd339b3SRuss Weight {
4755cd339b3SRuss Weight 	struct m10bmc_sec *sec = fwl->dd_handle;
4765cd339b3SRuss Weight 	u32 ret;
4775cd339b3SRuss Weight 
4785cd339b3SRuss Weight 	sec->cancel_request = false;
4795cd339b3SRuss Weight 
4805cd339b3SRuss Weight 	if (!size || size > M10BMC_STAGING_SIZE)
4815cd339b3SRuss Weight 		return FW_UPLOAD_ERR_INVALID_SIZE;
4825cd339b3SRuss Weight 
4835cd339b3SRuss Weight 	ret = rsu_check_idle(sec);
4845cd339b3SRuss Weight 	if (ret != FW_UPLOAD_ERR_NONE)
4855cd339b3SRuss Weight 		return ret;
4865cd339b3SRuss Weight 
4875cd339b3SRuss Weight 	ret = rsu_update_init(sec);
4885cd339b3SRuss Weight 	if (ret != FW_UPLOAD_ERR_NONE)
4895cd339b3SRuss Weight 		return ret;
4905cd339b3SRuss Weight 
4915cd339b3SRuss Weight 	ret = rsu_prog_ready(sec);
4925cd339b3SRuss Weight 	if (ret != FW_UPLOAD_ERR_NONE)
4935cd339b3SRuss Weight 		return ret;
4945cd339b3SRuss Weight 
4955cd339b3SRuss Weight 	if (sec->cancel_request)
4965cd339b3SRuss Weight 		return rsu_cancel(sec);
4975cd339b3SRuss Weight 
4985cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
4995cd339b3SRuss Weight }
5005cd339b3SRuss Weight 
5015cd339b3SRuss Weight #define WRITE_BLOCK_SIZE 0x4000	/* Default write-block size is 0x4000 bytes */
5025cd339b3SRuss Weight 
5033e10c805SIlpo Järvinen static enum fw_upload_err m10bmc_sec_fw_write(struct fw_upload *fwl, const u8 *data,
5045cd339b3SRuss Weight 					      u32 offset, u32 size, u32 *written)
5055cd339b3SRuss Weight {
5065cd339b3SRuss Weight 	struct m10bmc_sec *sec = fwl->dd_handle;
5076052a005SIlpo Järvinen 	const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
5083e10c805SIlpo Järvinen 	struct intel_m10bmc *m10bmc = sec->m10bmc;
5093e10c805SIlpo Järvinen 	u32 blk_size, doorbell;
5105cd339b3SRuss Weight 	int ret;
5115cd339b3SRuss Weight 
5125cd339b3SRuss Weight 	if (sec->cancel_request)
5135cd339b3SRuss Weight 		return rsu_cancel(sec);
5145cd339b3SRuss Weight 
5153e10c805SIlpo Järvinen 	ret = m10bmc_sys_read(m10bmc, csr_map->doorbell, &doorbell);
5165cd339b3SRuss Weight 	if (ret) {
5175cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
5185cd339b3SRuss Weight 	} else if (rsu_prog(doorbell) != RSU_PROG_READY) {
5195cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
5205cd339b3SRuss Weight 		return FW_UPLOAD_ERR_HW_ERROR;
5215cd339b3SRuss Weight 	}
5225cd339b3SRuss Weight 
5233e10c805SIlpo Järvinen 	WARN_ON_ONCE(WRITE_BLOCK_SIZE % regmap_get_reg_stride(m10bmc->regmap));
5245cd339b3SRuss Weight 	blk_size = min_t(u32, WRITE_BLOCK_SIZE, size);
5253e10c805SIlpo Järvinen 	ret = m10bmc_sec_write(sec, data, offset, blk_size);
5265cd339b3SRuss Weight 	if (ret)
5275cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
5285cd339b3SRuss Weight 
5295cd339b3SRuss Weight 	*written = blk_size;
5305cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
5315cd339b3SRuss Weight }
5325cd339b3SRuss Weight 
5335cd339b3SRuss Weight static enum fw_upload_err m10bmc_sec_poll_complete(struct fw_upload *fwl)
5345cd339b3SRuss Weight {
5355cd339b3SRuss Weight 	struct m10bmc_sec *sec = fwl->dd_handle;
5365cd339b3SRuss Weight 	unsigned long poll_timeout;
5375cd339b3SRuss Weight 	u32 doorbell, result;
5385cd339b3SRuss Weight 	int ret;
5395cd339b3SRuss Weight 
5405cd339b3SRuss Weight 	if (sec->cancel_request)
5415cd339b3SRuss Weight 		return rsu_cancel(sec);
5425cd339b3SRuss Weight 
5435cd339b3SRuss Weight 	result = rsu_send_data(sec);
5445cd339b3SRuss Weight 	if (result != FW_UPLOAD_ERR_NONE)
5455cd339b3SRuss Weight 		return result;
5465cd339b3SRuss Weight 
5475cd339b3SRuss Weight 	poll_timeout = jiffies + msecs_to_jiffies(RSU_COMPLETE_TIMEOUT_MS);
5485cd339b3SRuss Weight 	do {
5495cd339b3SRuss Weight 		msleep(RSU_COMPLETE_INTERVAL_MS);
5505cd339b3SRuss Weight 		ret = rsu_check_complete(sec, &doorbell);
5515cd339b3SRuss Weight 	} while (ret == -EAGAIN && !time_after(jiffies, poll_timeout));
5525cd339b3SRuss Weight 
5535cd339b3SRuss Weight 	if (ret == -EAGAIN) {
5545cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
5555cd339b3SRuss Weight 		return FW_UPLOAD_ERR_TIMEOUT;
5565cd339b3SRuss Weight 	} else if (ret == -EIO) {
5575cd339b3SRuss Weight 		return FW_UPLOAD_ERR_RW_ERROR;
5585cd339b3SRuss Weight 	} else if (ret) {
5595cd339b3SRuss Weight 		log_error_regs(sec, doorbell);
5605cd339b3SRuss Weight 		return FW_UPLOAD_ERR_HW_ERROR;
5615cd339b3SRuss Weight 	}
5625cd339b3SRuss Weight 
5635cd339b3SRuss Weight 	return FW_UPLOAD_ERR_NONE;
5645cd339b3SRuss Weight }
5655cd339b3SRuss Weight 
5665cd339b3SRuss Weight /*
5675cd339b3SRuss Weight  * m10bmc_sec_cancel() may be called asynchronously with an on-going update.
5685cd339b3SRuss Weight  * All other functions are called sequentially in a single thread. To avoid
5695cd339b3SRuss Weight  * contention on register accesses, m10bmc_sec_cancel() must only update
5705cd339b3SRuss Weight  * the cancel_request flag. Other functions will check this flag and handle
5715cd339b3SRuss Weight  * the cancel request synchronously.
5725cd339b3SRuss Weight  */
5735cd339b3SRuss Weight static void m10bmc_sec_cancel(struct fw_upload *fwl)
5745cd339b3SRuss Weight {
5755cd339b3SRuss Weight 	struct m10bmc_sec *sec = fwl->dd_handle;
5765cd339b3SRuss Weight 
5775cd339b3SRuss Weight 	sec->cancel_request = true;
5785cd339b3SRuss Weight }
5795cd339b3SRuss Weight 
5805cd339b3SRuss Weight static void m10bmc_sec_cleanup(struct fw_upload *fwl)
5815cd339b3SRuss Weight {
5825cd339b3SRuss Weight 	struct m10bmc_sec *sec = fwl->dd_handle;
5835cd339b3SRuss Weight 
5845cd339b3SRuss Weight 	(void)rsu_cancel(sec);
5855cd339b3SRuss Weight }
5865cd339b3SRuss Weight 
5875cd339b3SRuss Weight static const struct fw_upload_ops m10bmc_ops = {
5885cd339b3SRuss Weight 	.prepare = m10bmc_sec_prepare,
5893e10c805SIlpo Järvinen 	.write = m10bmc_sec_fw_write,
5905cd339b3SRuss Weight 	.poll_complete = m10bmc_sec_poll_complete,
5915cd339b3SRuss Weight 	.cancel = m10bmc_sec_cancel,
5925cd339b3SRuss Weight 	.cleanup = m10bmc_sec_cleanup,
5935cd339b3SRuss Weight };
5945cd339b3SRuss Weight 
595bdf86d0eSRuss Weight #define SEC_UPDATE_LEN_MAX 32
596bdf86d0eSRuss Weight static int m10bmc_sec_probe(struct platform_device *pdev)
597bdf86d0eSRuss Weight {
5985cd339b3SRuss Weight 	char buf[SEC_UPDATE_LEN_MAX];
599bdf86d0eSRuss Weight 	struct m10bmc_sec *sec;
6005cd339b3SRuss Weight 	struct fw_upload *fwl;
6015cd339b3SRuss Weight 	unsigned int len;
6025cd339b3SRuss Weight 	int  ret;
603bdf86d0eSRuss Weight 
604bdf86d0eSRuss Weight 	sec = devm_kzalloc(&pdev->dev, sizeof(*sec), GFP_KERNEL);
605bdf86d0eSRuss Weight 	if (!sec)
606bdf86d0eSRuss Weight 		return -ENOMEM;
607bdf86d0eSRuss Weight 
608bdf86d0eSRuss Weight 	sec->dev = &pdev->dev;
609bdf86d0eSRuss Weight 	sec->m10bmc = dev_get_drvdata(pdev->dev.parent);
610bdf86d0eSRuss Weight 	dev_set_drvdata(&pdev->dev, sec);
611bdf86d0eSRuss Weight 
6125cd339b3SRuss Weight 	ret = xa_alloc(&fw_upload_xa, &sec->fw_name_id, sec,
6135cd339b3SRuss Weight 		       xa_limit_32b, GFP_KERNEL);
6145cd339b3SRuss Weight 	if (ret)
6155cd339b3SRuss Weight 		return ret;
6165cd339b3SRuss Weight 
6175cd339b3SRuss Weight 	len = scnprintf(buf, SEC_UPDATE_LEN_MAX, "secure-update%d",
6185cd339b3SRuss Weight 			sec->fw_name_id);
6195cd339b3SRuss Weight 	sec->fw_name = kmemdup_nul(buf, len, GFP_KERNEL);
6205cd339b3SRuss Weight 	if (!sec->fw_name)
6215cd339b3SRuss Weight 		return -ENOMEM;
6225cd339b3SRuss Weight 
6235cd339b3SRuss Weight 	fwl = firmware_upload_register(THIS_MODULE, sec->dev, sec->fw_name,
6245cd339b3SRuss Weight 				       &m10bmc_ops, sec);
6255cd339b3SRuss Weight 	if (IS_ERR(fwl)) {
6265cd339b3SRuss Weight 		dev_err(sec->dev, "Firmware Upload driver failed to start\n");
6275cd339b3SRuss Weight 		kfree(sec->fw_name);
6285cd339b3SRuss Weight 		xa_erase(&fw_upload_xa, sec->fw_name_id);
6295cd339b3SRuss Weight 		return PTR_ERR(fwl);
6305cd339b3SRuss Weight 	}
6315cd339b3SRuss Weight 
6325cd339b3SRuss Weight 	sec->fwl = fwl;
6335cd339b3SRuss Weight 	return 0;
6345cd339b3SRuss Weight }
6355cd339b3SRuss Weight 
6365cd339b3SRuss Weight static int m10bmc_sec_remove(struct platform_device *pdev)
6375cd339b3SRuss Weight {
6385cd339b3SRuss Weight 	struct m10bmc_sec *sec = dev_get_drvdata(&pdev->dev);
6395cd339b3SRuss Weight 
6405cd339b3SRuss Weight 	firmware_upload_unregister(sec->fwl);
6415cd339b3SRuss Weight 	kfree(sec->fw_name);
6425cd339b3SRuss Weight 	xa_erase(&fw_upload_xa, sec->fw_name_id);
6435cd339b3SRuss Weight 
644bdf86d0eSRuss Weight 	return 0;
645bdf86d0eSRuss Weight }
646bdf86d0eSRuss Weight 
647bdf86d0eSRuss Weight static const struct platform_device_id intel_m10bmc_sec_ids[] = {
648bdf86d0eSRuss Weight 	{
649bdf86d0eSRuss Weight 		.name = "n3000bmc-sec-update",
650bdf86d0eSRuss Weight 	},
651562d0bf2SRuss Weight 	{
652562d0bf2SRuss Weight 		.name = "d5005bmc-sec-update",
653562d0bf2SRuss Weight 	},
654bdf86d0eSRuss Weight 	{ }
655bdf86d0eSRuss Weight };
656bdf86d0eSRuss Weight MODULE_DEVICE_TABLE(platform, intel_m10bmc_sec_ids);
657bdf86d0eSRuss Weight 
658bdf86d0eSRuss Weight static struct platform_driver intel_m10bmc_sec_driver = {
659bdf86d0eSRuss Weight 	.probe = m10bmc_sec_probe,
6605cd339b3SRuss Weight 	.remove = m10bmc_sec_remove,
661bdf86d0eSRuss Weight 	.driver = {
662bdf86d0eSRuss Weight 		.name = "intel-m10bmc-sec-update",
663bdf86d0eSRuss Weight 		.dev_groups = m10bmc_sec_attr_groups,
664bdf86d0eSRuss Weight 	},
665bdf86d0eSRuss Weight 	.id_table = intel_m10bmc_sec_ids,
666bdf86d0eSRuss Weight };
667bdf86d0eSRuss Weight module_platform_driver(intel_m10bmc_sec_driver);
668bdf86d0eSRuss Weight 
669bdf86d0eSRuss Weight MODULE_AUTHOR("Intel Corporation");
670bdf86d0eSRuss Weight MODULE_DESCRIPTION("Intel MAX10 BMC Secure Update");
671bdf86d0eSRuss Weight MODULE_LICENSE("GPL");
672