| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Microchip Polarfire SoC "Auto Update" FPGA reprogramming. | |
| * | |
| * Documentation of this functionality is available in the "PolarFire® FPGA and | |
| * PolarFire SoC FPGA Programming" User Guide. | |
| * | |
| * Copyright (c) 2022-2023 Microchip Corporation. All rights reserved. | |
| * | |
| * Author: Conor Dooley <conor.dooley@microchip.com> | |
| */ | |
| #include <linux/cleanup.h> | |
| #include <linux/debugfs.h> | |
| #include <linux/firmware.h> | |
| #include <linux/math.h> | |
| #include <linux/module.h> | |
| #include <linux/mtd/mtd.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/sizes.h> | |
| #include <soc/microchip/mpfs.h> | |
| #define AUTO_UPDATE_DEFAULT_MBOX_OFFSET 0u | |
| #define AUTO_UPDATE_DEFAULT_RESP_OFFSET 0u | |
| #define AUTO_UPDATE_FEATURE_CMD_OPCODE 0x05u | |
| #define AUTO_UPDATE_FEATURE_CMD_DATA_SIZE 0u | |
| #define AUTO_UPDATE_FEATURE_RESP_SIZE 33u | |
| #define AUTO_UPDATE_FEATURE_CMD_DATA NULL | |
| #define AUTO_UPDATE_FEATURE_ENABLED BIT(5) | |
| #define AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE 0x22u | |
| #define AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE 0u | |
| #define AUTO_UPDATE_AUTHENTICATE_RESP_SIZE 1u | |
| #define AUTO_UPDATE_AUTHENTICATE_CMD_DATA NULL | |
| #define AUTO_UPDATE_PROGRAM_CMD_OPCODE 0x46u | |
| #define AUTO_UPDATE_PROGRAM_CMD_DATA_SIZE 0u | |
| #define AUTO_UPDATE_PROGRAM_RESP_SIZE 1u | |
| #define AUTO_UPDATE_PROGRAM_CMD_DATA NULL | |
| /* | |
| * SPI Flash layout example: | |
| * |------------------------------| 0x0000000 | |
| * | 1 KiB | | |
| * | SPI "directories" | | |
| * |------------------------------| 0x0000400 | |
| * | 1 MiB | | |
| * | Reserved area | | |
| * | Used for bitstream info | | |
| * |------------------------------| 0x0100400 | |
| * | 20 MiB | | |
| * | Golden Image | | |
| * |------------------------------| 0x1500400 | |
| * | 20 MiB | | |
| * | Auto Upgrade Image | | |
| * |------------------------------| 0x2900400 | |
| * | 20 MiB | | |
| * | Reserved for multi-image IAP | | |
| * | Unused for Auto Upgrade | | |
| * |------------------------------| 0x3D00400 | |
| * | ? B | | |
| * | Unused | | |
| * |------------------------------| 0x? | |
| */ | |
| #define AUTO_UPDATE_DIRECTORY_BASE 0u | |
| #define AUTO_UPDATE_DIRECTORY_WIDTH 4u | |
| #define AUTO_UPDATE_GOLDEN_INDEX 0u | |
| #define AUTO_UPDATE_UPGRADE_INDEX 1u | |
| #define AUTO_UPDATE_BLANK_INDEX 2u | |
| #define AUTO_UPDATE_GOLDEN_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_GOLDEN_INDEX) | |
| #define AUTO_UPDATE_UPGRADE_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_UPGRADE_INDEX) | |
| #define AUTO_UPDATE_BLANK_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_BLANK_INDEX) | |
| #define AUTO_UPDATE_DIRECTORY_SIZE SZ_1K | |
| #define AUTO_UPDATE_INFO_BASE AUTO_UPDATE_DIRECTORY_SIZE | |
| #define AUTO_UPDATE_INFO_SIZE SZ_1M | |
| #define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_INFO_SIZE) | |
| struct mpfs_auto_update_priv { | |
| struct mpfs_sys_controller *sys_controller; | |
| struct device *dev; | |
| struct mtd_info *flash; | |
| struct fw_upload *fw_uploader; | |
| size_t size_per_bitstream; | |
| bool cancel_request; | |
| }; | |
| static bool mpfs_auto_update_is_bitstream_info(const u8 *data, u32 size) | |
| { | |
| if (size < 4) | |
| return false; | |
| if (data[0] == 0x4d && data[1] == 0x43 && data[2] == 0x48 && data[3] == 0x50) | |
| return true; | |
| return false; | |
| } | |
| static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader, const u8 *data, | |
| u32 size) | |
| { | |
| struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; | |
| size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; | |
| /* | |
| * Verifying the Golden Image is idealistic. It will be evaluated | |
| * against the currently programmed image and thus may fail - due to | |
| * either rollback protection (if its an older version than that in use) | |
| * or if the version is the same as that of the in-use image. | |
| * Extracting the information as to why a failure occurred is not | |
| * currently possible due to limitations of the system controller | |
| * driver. If those are fixed, verification of the Golden Image should | |
| * be added here. | |
| */ | |
| erase_size = round_up(erase_size, (u64)priv->flash->erasesize); | |
| /* | |
| * We need to calculate if we have enough space in the flash for the | |
| * new image. | |
| * First, chop off the first 1 KiB as it's reserved for the directory. | |
| * The 1 MiB reserved for design info needs to be ignored also. | |
| * All that remains is carved into 3 & rounded down to the erasesize. | |
| * If this is smaller than the image size, we abort. | |
| * There's also no need to consume more than 20 MiB per image. | |
| */ | |
| priv->size_per_bitstream = priv->flash->size - SZ_1K - SZ_1M; | |
| priv->size_per_bitstream = round_down(priv->size_per_bitstream / 3, erase_size); | |
| if (priv->size_per_bitstream > 20 * SZ_1M) | |
| priv->size_per_bitstream = 20 * SZ_1M; | |
| if (priv->size_per_bitstream < size) { | |
| dev_err(priv->dev, | |
| "flash device has insufficient capacity to store this bitstream\n"); | |
| return FW_UPLOAD_ERR_INVALID_SIZE; | |
| } | |
| priv->cancel_request = false; | |
| return FW_UPLOAD_ERR_NONE; | |
| } | |
| static void mpfs_auto_update_cancel(struct fw_upload *fw_uploader) | |
| { | |
| struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; | |
| priv->cancel_request = true; | |
| } | |
| static enum fw_upload_err mpfs_auto_update_poll_complete(struct fw_upload *fw_uploader) | |
| { | |
| return FW_UPLOAD_ERR_NONE; | |
| } | |
| static int mpfs_auto_update_verify_image(struct fw_upload *fw_uploader) | |
| { | |
| struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; | |
| u32 *response_msg __free(kfree) = | |
| kzalloc(AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), GFP_KERNEL); | |
| struct mpfs_mss_response *response __free(kfree) = | |
| kzalloc_obj(struct mpfs_mss_response); | |
| struct mpfs_mss_msg *message __free(kfree) = | |
| kzalloc_obj(struct mpfs_mss_msg); | |
| int ret; | |
| if (!response_msg || !response || !message) | |
| return -ENOMEM; | |
| /* | |
| * The system controller can verify that an image in the flash is valid. | |
| * Rather than duplicate the check in this driver, call the relevant | |
| * service from the system controller instead. | |
| * This service has no command data and no response data. It overloads | |
| * mbox_offset with the image index in the flash's SPI directory where | |
| * the bitstream is located. | |
| */ | |
| response->resp_msg = response_msg; | |
| response->resp_size = AUTO_UPDATE_AUTHENTICATE_RESP_SIZE; | |
| message->cmd_opcode = AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE; | |
| message->cmd_data_size = AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE; | |
| message->response = response; | |
| message->cmd_data = AUTO_UPDATE_AUTHENTICATE_CMD_DATA; | |
| message->mbox_offset = AUTO_UPDATE_UPGRADE_INDEX; | |
| message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET; | |
| dev_info(priv->dev, "Running verification of Upgrade Image\n"); | |
| ret = mpfs_blocking_transaction(priv->sys_controller, message); | |
| if (ret | response->resp_status) { | |
| dev_warn(priv->dev, "Verification of Upgrade Image failed!\n"); | |
| return ret ? ret : -EBADMSG; | |
| } | |
| dev_info(priv->dev, "Verification of Upgrade Image passed!\n"); | |
| return 0; | |
| } | |
| static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv, | |
| u32 image_address, loff_t directory_address) | |
| { | |
| struct erase_info erase; | |
| size_t erase_size = round_up(AUTO_UPDATE_DIRECTORY_SIZE, (u64)priv->flash->erasesize); | |
| size_t bytes_written = 0, bytes_read = 0; | |
| char *buffer __free(kfree) = kzalloc(erase_size, GFP_KERNEL); | |
| int ret; | |
| if (!buffer) | |
| return -ENOMEM; | |
| erase.addr = AUTO_UPDATE_DIRECTORY_BASE; | |
| erase.len = erase_size; | |
| /* | |
| * We need to write the "SPI DIRECTORY" to the first 1 KiB, telling | |
| * the system controller where to find the actual bitstream. Since | |
| * this is spi-nor, we have to read the first eraseblock, erase that | |
| * portion of the flash, modify the data and then write it back. | |
| * There's no need to do this though if things are already the way they | |
| * should be, so check and save the write in that case. | |
| */ | |
| ret = mtd_read(priv->flash, AUTO_UPDATE_DIRECTORY_BASE, erase_size, &bytes_read, | |
| (u_char *)buffer); | |
| if (ret) | |
| return ret; | |
| if (bytes_read != erase_size) | |
| return -EIO; | |
| if ((*(u32 *)(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY) == image_address) && | |
| !(*(u32 *)(buffer + AUTO_UPDATE_BLANK_DIRECTORY))) | |
| return 0; | |
| ret = mtd_erase(priv->flash, &erase); | |
| if (ret) | |
| return ret; | |
| /* | |
| * Populate the image address and then zero out the next directory so | |
| * that the system controller doesn't complain if in "Single Image" | |
| * mode. | |
| */ | |
| memcpy(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY, &image_address, | |
| AUTO_UPDATE_DIRECTORY_WIDTH); | |
| memset(buffer + AUTO_UPDATE_BLANK_DIRECTORY, 0x0, AUTO_UPDATE_DIRECTORY_WIDTH); | |
| dev_info(priv->dev, "Writing the image address (0x%x) to the flash directory (0x%llx)\n", | |
| image_address, directory_address); | |
| ret = mtd_write(priv->flash, 0x0, erase_size, &bytes_written, (u_char *)buffer); | |
| if (ret) | |
| return ret; | |
| if (bytes_written != erase_size) | |
| return -EIO; | |
| return 0; | |
| } | |
| static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const u8 *data, | |
| u32 offset, u32 size, u32 *written) | |
| { | |
| struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; | |
| struct erase_info erase; | |
| loff_t directory_address = AUTO_UPDATE_UPGRADE_DIRECTORY; | |
| size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; | |
| size_t bytes_written = 0; | |
| bool is_info = mpfs_auto_update_is_bitstream_info(data, size); | |
| u32 image_address; | |
| int ret; | |
| erase_size = round_up(erase_size, (u64)priv->flash->erasesize); | |
| if (is_info) | |
| image_address = AUTO_UPDATE_INFO_BASE; | |
| else | |
| image_address = AUTO_UPDATE_BITSTREAM_BASE + | |
| AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream; | |
| /* | |
| * For bitstream info, the descriptor is written to a fixed offset, | |
| * so there is no need to set the image address. | |
| */ | |
| if (!is_info) { | |
| ret = mpfs_auto_update_set_image_address(priv, image_address, directory_address); | |
| if (ret) { | |
| dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret); | |
| return ret; | |
| } | |
| } else { | |
| if (size > AUTO_UPDATE_INFO_SIZE) { | |
| dev_err(priv->dev, "bitstream info exceeds permitted size\n"); | |
| return -ENOSPC; | |
| } | |
| } | |
| /* | |
| * Now the .spi image itself can be written to the flash. Preservation | |
| * of contents here is not important here, unlike the spi "directory" | |
| * which must be RMWed. | |
| */ | |
| erase.len = round_up(size, (size_t)priv->flash->erasesize); | |
| erase.addr = image_address; | |
| dev_info(priv->dev, "Erasing the flash at address (0x%x)\n", image_address); | |
| ret = mtd_erase(priv->flash, &erase); | |
| if (ret) | |
| return ret; | |
| /* | |
| * No parsing etc of the bitstream is required. The system controller | |
| * will do all of that itself - including verifying that the bitstream | |
| * is valid. | |
| */ | |
| dev_info(priv->dev, "Writing the image to the flash at address (0x%x)\n", image_address); | |
| ret = mtd_write(priv->flash, (loff_t)image_address, size, &bytes_written, data); | |
| if (ret) | |
| return ret; | |
| if (bytes_written != size) | |
| return -EIO; | |
| *written = bytes_written; | |
| dev_info(priv->dev, "Wrote 0x%zx bytes to the flash\n", bytes_written); | |
| return 0; | |
| } | |
| static enum fw_upload_err mpfs_auto_update_write(struct fw_upload *fw_uploader, const u8 *data, | |
| u32 offset, u32 size, u32 *written) | |
| { | |
| struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; | |
| int ret; | |
| ret = mpfs_auto_update_write_bitstream(fw_uploader, data, offset, size, written); | |
| if (ret) | |
| return FW_UPLOAD_ERR_RW_ERROR; | |
| if (priv->cancel_request) | |
| return FW_UPLOAD_ERR_CANCELED; | |
| if (mpfs_auto_update_is_bitstream_info(data, size)) | |
| return FW_UPLOAD_ERR_NONE; | |
| ret = mpfs_auto_update_verify_image(fw_uploader); | |
| if (ret) | |
| return FW_UPLOAD_ERR_FW_INVALID; | |
| return FW_UPLOAD_ERR_NONE; | |
| } | |
| static const struct fw_upload_ops mpfs_auto_update_ops = { | |
| .prepare = mpfs_auto_update_prepare, | |
| .write = mpfs_auto_update_write, | |
| .poll_complete = mpfs_auto_update_poll_complete, | |
| .cancel = mpfs_auto_update_cancel, | |
| }; | |
| static int mpfs_auto_update_available(struct mpfs_auto_update_priv *priv) | |
| { | |
| u32 *response_msg __free(kfree) = | |
| kzalloc(AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), GFP_KERNEL); | |
| struct mpfs_mss_response *response __free(kfree) = | |
| kzalloc_obj(struct mpfs_mss_response); | |
| struct mpfs_mss_msg *message __free(kfree) = | |
| kzalloc_obj(struct mpfs_mss_msg); | |
| int ret; | |
| if (!response_msg || !response || !message) | |
| return -ENOMEM; | |
| /* | |
| * To verify that Auto Update is possible, the "Query Security Service | |
| * Request" is performed. | |
| * This service has no command data & does not overload mbox_offset. | |
| */ | |
| response->resp_msg = response_msg; | |
| response->resp_size = AUTO_UPDATE_FEATURE_RESP_SIZE; | |
| message->cmd_opcode = AUTO_UPDATE_FEATURE_CMD_OPCODE; | |
| message->cmd_data_size = AUTO_UPDATE_FEATURE_CMD_DATA_SIZE; | |
| message->response = response; | |
| message->cmd_data = AUTO_UPDATE_FEATURE_CMD_DATA; | |
| message->mbox_offset = AUTO_UPDATE_DEFAULT_MBOX_OFFSET; | |
| message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET; | |
| ret = mpfs_blocking_transaction(priv->sys_controller, message); | |
| if (ret) | |
| return ret; | |
| /* | |
| * Currently, the system controller's firmware does not generate any | |
| * interrupts for failed services, so mpfs_blocking_transaction() should | |
| * time out & therefore return an error. | |
| * Hitting this check is highly unlikely at present, but if the system | |
| * controller's behaviour changes so that it does generate interrupts | |
| * for failed services, it will be required. | |
| */ | |
| if (response->resp_status) | |
| return -EIO; | |
| /* | |
| * Bit 5 of byte 1 is "UL_IAP" & if it is set, Auto Update is | |
| * not possible. | |
| */ | |
| if ((((u8 *)response_msg)[1] & AUTO_UPDATE_FEATURE_ENABLED)) | |
| return -EPERM; | |
| return 0; | |
| } | |
| static int mpfs_auto_update_probe(struct platform_device *pdev) | |
| { | |
| struct device *dev = &pdev->dev; | |
| struct mpfs_auto_update_priv *priv; | |
| struct fw_upload *fw_uploader; | |
| int ret; | |
| priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
| if (!priv) | |
| return -ENOMEM; | |
| priv->sys_controller = mpfs_sys_controller_get(dev); | |
| if (IS_ERR(priv->sys_controller)) | |
| return dev_err_probe(dev, PTR_ERR(priv->sys_controller), | |
| "Could not register as a sub device of the system controller\n"); | |
| priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller); | |
| if (IS_ERR_OR_NULL(priv->flash)) { | |
| dev_dbg(dev, "No flash connected to the system controller, auto-update not supported\n"); | |
| return -ENODEV; | |
| } | |
| priv->dev = dev; | |
| platform_set_drvdata(pdev, priv); | |
| ret = mpfs_auto_update_available(priv); | |
| if (ret) | |
| return dev_err_probe(dev, ret, | |
| "The current bitstream does not support auto-update\n"); | |
| fw_uploader = firmware_upload_register(THIS_MODULE, dev, "mpfs-auto-update", | |
| &mpfs_auto_update_ops, priv); | |
| if (IS_ERR(fw_uploader)) | |
| return dev_err_probe(dev, PTR_ERR(fw_uploader), | |
| "Failed to register the bitstream uploader\n"); | |
| priv->fw_uploader = fw_uploader; | |
| return 0; | |
| } | |
| static void mpfs_auto_update_remove(struct platform_device *pdev) | |
| { | |
| struct mpfs_auto_update_priv *priv = platform_get_drvdata(pdev); | |
| firmware_upload_unregister(priv->fw_uploader); | |
| } | |
| static struct platform_driver mpfs_auto_update_driver = { | |
| .driver = { | |
| .name = "mpfs-auto-update", | |
| }, | |
| .probe = mpfs_auto_update_probe, | |
| .remove = mpfs_auto_update_remove, | |
| }; | |
| module_platform_driver(mpfs_auto_update_driver); | |
| MODULE_LICENSE("GPL"); | |
| MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); | |
| MODULE_DESCRIPTION("PolarFire SoC Auto Update FPGA reprogramming"); |