// 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 */ #include #include #include #include #include #include #include #include #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_RESERVED_SIZE SZ_1M #define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_RESERVED_SIZE) #define AUTO_UPDATE_TIMEOUT_MS 60000 struct mpfs_auto_update_priv { struct mpfs_sys_controller *sys_controller; struct device *dev; struct mtd_info *flash; struct fw_upload *fw_uploader; struct completion programming_complete; size_t size_per_bitstream; bool cancel_request; }; 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. */ priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller); if (!priv->flash) return FW_UPLOAD_ERR_HW_ERROR; 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) { struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; int ret; /* * There is no meaningful way to get the status of the programming while * it is in progress, so attempting anything other than waiting for it * to complete would be misplaced. */ ret = wait_for_completion_timeout(&priv->programming_complete, msecs_to_jiffies(AUTO_UPDATE_TIMEOUT_MS)); if (ret) return FW_UPLOAD_ERR_TIMEOUT; 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; struct mpfs_mss_response *response; struct mpfs_mss_msg *message; u32 *response_msg; int ret; response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), GFP_KERNEL); if (!response_msg) return -ENOMEM; response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL); if (!response) { ret = -ENOMEM; goto free_response_msg; } message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL); if (!message) { ret = -ENOMEM; goto free_response; } /* * 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"); ret = ret ? ret : -EBADMSG; } dev_info(priv->dev, "Verification of Upgrade Image passed!\n"); devm_kfree(priv->dev, message); free_response: devm_kfree(priv->dev, response); free_response_msg: devm_kfree(priv->dev, response_msg); return ret; } static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv, char *buffer, u32 image_address, loff_t directory_address) { struct erase_info erase; size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; size_t bytes_written = 0, bytes_read = 0; int ret; erase_size = round_up(erase_size, (u64)priv->flash->erasesize); 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 (%x) to the flash directory (%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 ret; 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; char *buffer; loff_t directory_address = AUTO_UPDATE_UPGRADE_DIRECTORY; size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; size_t bytes_written = 0; u32 image_address; int ret; erase_size = round_up(erase_size, (u64)priv->flash->erasesize); image_address = AUTO_UPDATE_BITSTREAM_BASE + AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream; buffer = devm_kzalloc(priv->dev, erase_size, GFP_KERNEL); if (!buffer) return -ENOMEM; ret = mpfs_auto_update_set_image_address(priv, buffer, image_address, directory_address); if (ret) { dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret); goto out; } /* * 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 (%x)\n", image_address); ret = mtd_erase(priv->flash, &erase); if (ret) goto out; /* * 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 (%x)\n", image_address); ret = mtd_write(priv->flash, (loff_t)image_address, size, &bytes_written, data); if (ret) goto out; if (bytes_written != size) { ret = -EIO; goto out; } *written = bytes_written; out: devm_kfree(priv->dev, buffer); return ret; } 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; enum fw_upload_err err = FW_UPLOAD_ERR_NONE; int ret; reinit_completion(&priv->programming_complete); ret = mpfs_auto_update_write_bitstream(fw_uploader, data, offset, size, written); if (ret) { err = FW_UPLOAD_ERR_RW_ERROR; goto out; } if (priv->cancel_request) { err = FW_UPLOAD_ERR_CANCELED; goto out; } ret = mpfs_auto_update_verify_image(fw_uploader); if (ret) err = FW_UPLOAD_ERR_FW_INVALID; out: complete(&priv->programming_complete); return err; } 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) { struct mpfs_mss_response *response; struct mpfs_mss_msg *message; u32 *response_msg; int ret; response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), GFP_KERNEL); if (!response_msg) return -ENOMEM; response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL); if (!response) return -ENOMEM; message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL); if (!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_Auto Update" & if it is set, Auto Update is * not possible. */ if (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->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"); init_completion(&priv->programming_complete); 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_new = mpfs_auto_update_remove, }; module_platform_driver(mpfs_auto_update_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Conor Dooley "); MODULE_DESCRIPTION("PolarFire SoC Auto Update FPGA reprogramming");