diff options
Diffstat (limited to 'drivers/firmware')
79 files changed, 2229 insertions, 1996 deletions
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index d5e86ef40b89..fa85c64d3ded 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -36,81 +36,6 @@ #include "common.h" #define FFA_DRIVER_VERSION FFA_VERSION_1_0 - -#define FFA_SMC(calling_convention, func_num) \ - ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, (calling_convention), \ - ARM_SMCCC_OWNER_STANDARD, (func_num)) - -#define FFA_SMC_32(func_num) FFA_SMC(ARM_SMCCC_SMC_32, (func_num)) -#define FFA_SMC_64(func_num) FFA_SMC(ARM_SMCCC_SMC_64, (func_num)) - -#define FFA_ERROR FFA_SMC_32(0x60) -#define FFA_SUCCESS FFA_SMC_32(0x61) -#define FFA_INTERRUPT FFA_SMC_32(0x62) -#define FFA_VERSION FFA_SMC_32(0x63) -#define FFA_FEATURES FFA_SMC_32(0x64) -#define FFA_RX_RELEASE FFA_SMC_32(0x65) -#define FFA_RXTX_MAP FFA_SMC_32(0x66) -#define FFA_FN64_RXTX_MAP FFA_SMC_64(0x66) -#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) -#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) -#define FFA_ID_GET FFA_SMC_32(0x69) -#define FFA_MSG_POLL FFA_SMC_32(0x6A) -#define FFA_MSG_WAIT FFA_SMC_32(0x6B) -#define FFA_YIELD FFA_SMC_32(0x6C) -#define FFA_RUN FFA_SMC_32(0x6D) -#define FFA_MSG_SEND FFA_SMC_32(0x6E) -#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_32(0x6F) -#define FFA_FN64_MSG_SEND_DIRECT_REQ FFA_SMC_64(0x6F) -#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_32(0x70) -#define FFA_FN64_MSG_SEND_DIRECT_RESP FFA_SMC_64(0x70) -#define FFA_MEM_DONATE FFA_SMC_32(0x71) -#define FFA_FN64_MEM_DONATE FFA_SMC_64(0x71) -#define FFA_MEM_LEND FFA_SMC_32(0x72) -#define FFA_FN64_MEM_LEND FFA_SMC_64(0x72) -#define FFA_MEM_SHARE FFA_SMC_32(0x73) -#define FFA_FN64_MEM_SHARE FFA_SMC_64(0x73) -#define FFA_MEM_RETRIEVE_REQ FFA_SMC_32(0x74) -#define FFA_FN64_MEM_RETRIEVE_REQ FFA_SMC_64(0x74) -#define FFA_MEM_RETRIEVE_RESP FFA_SMC_32(0x75) -#define FFA_MEM_RELINQUISH FFA_SMC_32(0x76) -#define FFA_MEM_RECLAIM FFA_SMC_32(0x77) -#define FFA_MEM_OP_PAUSE FFA_SMC_32(0x78) -#define FFA_MEM_OP_RESUME FFA_SMC_32(0x79) -#define FFA_MEM_FRAG_RX FFA_SMC_32(0x7A) -#define FFA_MEM_FRAG_TX FFA_SMC_32(0x7B) -#define FFA_NORMAL_WORLD_RESUME FFA_SMC_32(0x7C) - -/* - * For some calls it is necessary to use SMC64 to pass or return 64-bit values. - * For such calls FFA_FN_NATIVE(name) will choose the appropriate - * (native-width) function ID. - */ -#ifdef CONFIG_64BIT -#define FFA_FN_NATIVE(name) FFA_FN64_##name -#else -#define FFA_FN_NATIVE(name) FFA_##name -#endif - -/* FFA error codes. */ -#define FFA_RET_SUCCESS (0) -#define FFA_RET_NOT_SUPPORTED (-1) -#define FFA_RET_INVALID_PARAMETERS (-2) -#define FFA_RET_NO_MEMORY (-3) -#define FFA_RET_BUSY (-4) -#define FFA_RET_INTERRUPTED (-5) -#define FFA_RET_DENIED (-6) -#define FFA_RET_RETRY (-7) -#define FFA_RET_ABORTED (-8) - -#define MAJOR_VERSION_MASK GENMASK(30, 16) -#define MINOR_VERSION_MASK GENMASK(15, 0) -#define MAJOR_VERSION(x) ((u16)(FIELD_GET(MAJOR_VERSION_MASK, (x)))) -#define MINOR_VERSION(x) ((u16)(FIELD_GET(MINOR_VERSION_MASK, (x)))) -#define PACK_VERSION_INFO(major, minor) \ - (FIELD_PREP(MAJOR_VERSION_MASK, (major)) | \ - FIELD_PREP(MINOR_VERSION_MASK, (minor))) -#define FFA_VERSION_1_0 PACK_VERSION_INFO(1, 0) #define FFA_MIN_VERSION FFA_VERSION_1_0 #define SENDER_ID_MASK GENMASK(31, 16) @@ -121,12 +46,6 @@ (FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r))) /* - * FF-A specification mentions explicitly about '4K pages'. This should - * not be confused with the kernel PAGE_SIZE, which is the translation - * granule kernel is configured and may be one among 4K, 16K and 64K. - */ -#define FFA_PAGE_SIZE SZ_4K -/* * Keeping RX TX buffer size as 4K for now * 64K may be preferred to keep it min a page in 64K PAGE_SIZE config */ @@ -178,9 +97,9 @@ static struct ffa_drv_info *drv_info; */ static u32 ffa_compatible_version_find(u32 version) { - u16 major = MAJOR_VERSION(version), minor = MINOR_VERSION(version); - u16 drv_major = MAJOR_VERSION(FFA_DRIVER_VERSION); - u16 drv_minor = MINOR_VERSION(FFA_DRIVER_VERSION); + u16 major = FFA_MAJOR_VERSION(version), minor = FFA_MINOR_VERSION(version); + u16 drv_major = FFA_MAJOR_VERSION(FFA_DRIVER_VERSION); + u16 drv_minor = FFA_MINOR_VERSION(FFA_DRIVER_VERSION); if ((major < drv_major) || (major == drv_major && minor <= drv_minor)) return version; @@ -204,16 +123,16 @@ static int ffa_version_check(u32 *version) if (ver.a0 < FFA_MIN_VERSION) { pr_err("Incompatible v%d.%d! Earliest supported v%d.%d\n", - MAJOR_VERSION(ver.a0), MINOR_VERSION(ver.a0), - MAJOR_VERSION(FFA_MIN_VERSION), - MINOR_VERSION(FFA_MIN_VERSION)); + FFA_MAJOR_VERSION(ver.a0), FFA_MINOR_VERSION(ver.a0), + FFA_MAJOR_VERSION(FFA_MIN_VERSION), + FFA_MINOR_VERSION(FFA_MIN_VERSION)); return -EINVAL; } - pr_info("Driver version %d.%d\n", MAJOR_VERSION(FFA_DRIVER_VERSION), - MINOR_VERSION(FFA_DRIVER_VERSION)); - pr_info("Firmware version %d.%d found\n", MAJOR_VERSION(ver.a0), - MINOR_VERSION(ver.a0)); + pr_info("Driver version %d.%d\n", FFA_MAJOR_VERSION(FFA_DRIVER_VERSION), + FFA_MINOR_VERSION(FFA_DRIVER_VERSION)); + pr_info("Firmware version %d.%d found\n", FFA_MAJOR_VERSION(ver.a0), + FFA_MINOR_VERSION(ver.a0)); *version = ffa_compatible_version_find(ver.a0); return 0; diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index d4e23101448a..35bb70724d44 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -216,9 +216,20 @@ void scmi_device_destroy(struct scmi_device *scmi_dev) device_unregister(&scmi_dev->dev); } +void scmi_device_link_add(struct device *consumer, struct device *supplier) +{ + struct device_link *link; + + link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER); + + WARN_ON(!link); +} + void scmi_set_handle(struct scmi_device *scmi_dev) { scmi_dev->handle = scmi_handle_get(&scmi_dev->dev); + if (scmi_dev->handle) + scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev); } int scmi_protocol_register(const struct scmi_protocol *proto) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 61aba7447c32..a1c0154c31c6 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -97,6 +97,7 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) struct scmi_revision_info * scmi_revision_area_get(const struct scmi_protocol_handle *ph); int scmi_handle_put(const struct scmi_handle *handle); +void scmi_device_link_add(struct device *consumer, struct device *supplier); struct scmi_handle *scmi_handle_get(struct device *dev); void scmi_set_handle(struct scmi_device *scmi_dev); void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, @@ -117,6 +118,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id); * * @dev: Reference to device in the SCMI hierarchy corresponding to this * channel + * @rx_timeout_ms: The configured RX timeout in milliseconds. * @handle: Pointer to SCMI entity handle * @no_completion_irq: Flag to indicate that this channel has no completion * interrupt mechanism for synchronous commands. @@ -126,6 +128,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id); */ struct scmi_chan_info { struct device *dev; + unsigned int rx_timeout_ms; struct scmi_handle *handle; bool no_completion_irq; void *transport_info; @@ -232,7 +235,7 @@ void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); struct scmi_shared_mem; void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, - struct scmi_xfer *xfer); + struct scmi_xfer *xfer, struct scmi_chan_info *cinfo); u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 609ebedee9cb..f818d00bb2c6 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2013,6 +2013,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device *dev, return -ENOMEM; cinfo->dev = dev; + cinfo->rx_timeout_ms = info->desc->max_rx_timeout_ms; ret = info->desc->ops->chan_setup(cinfo, info->dev, tx); if (ret) @@ -2044,8 +2045,12 @@ scmi_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) { int ret = scmi_chan_setup(info, dev, prot_id, true); - if (!ret) /* Rx is optional, hence no error check */ - scmi_chan_setup(info, dev, prot_id, false); + if (!ret) { + /* Rx is optional, report only memory errors */ + ret = scmi_chan_setup(info, dev, prot_id, false); + if (ret && ret != -ENOMEM) + ret = 0; + } return ret; } @@ -2273,10 +2278,16 @@ int scmi_protocol_device_request(const struct scmi_device_id *id_table) sdev = scmi_get_protocol_device(child, info, id_table->protocol_id, id_table->name); - /* Set handle if not already set: device existed */ - if (sdev && !sdev->handle) - sdev->handle = - scmi_handle_get_from_info_unlocked(info); + if (sdev) { + /* Set handle if not already set: device existed */ + if (!sdev->handle) + sdev->handle = + scmi_handle_get_from_info_unlocked(info); + /* Relink consumer and suppliers */ + if (sdev->handle) + scmi_device_link_add(&sdev->dev, + sdev->handle->dev); + } } else { dev_err(info->dev, "Failed. SCMI protocol %d not active.\n", @@ -2475,20 +2486,17 @@ void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id) static int scmi_remove(struct platform_device *pdev) { - int ret = 0, id; + int ret, id; struct scmi_info *info = platform_get_drvdata(pdev); struct device_node *child; mutex_lock(&scmi_list_mutex); if (info->users) - ret = -EBUSY; - else - list_del(&info->node); + dev_warn(&pdev->dev, + "Still active SCMI users will be forcibly unbound.\n"); + list_del(&info->node); mutex_unlock(&scmi_list_mutex); - if (ret) - return ret; - scmi_notification_exit(&info->handle); mutex_lock(&info->protocols_mtx); @@ -2500,7 +2508,11 @@ static int scmi_remove(struct platform_device *pdev) idr_destroy(&info->active_protocols); /* Safe to free channels since no more users */ - return scmi_cleanup_txrx_channels(info); + ret = scmi_cleanup_txrx_channels(info); + if (ret) + dev_warn(&pdev->dev, "Failed to cleanup SCMI channels.\n"); + + return 0; } static ssize_t protocol_version_show(struct device *dev, @@ -2571,6 +2583,7 @@ MODULE_DEVICE_TABLE(of, scmi_of_match); static struct platform_driver scmi_driver = { .driver = { .name = "arm-scmi", + .suppress_bind_attrs = true, .of_match_table = scmi_of_match, .dev_groups = versions_groups, }, diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 08ff4d110beb..1e40cb035044 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -36,7 +36,7 @@ static void tx_prepare(struct mbox_client *cl, void *m) { struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); - shmem_tx_prepare(smbox->shmem, m); + shmem_tx_prepare(smbox->shmem, m, smbox->cinfo); } static void rx_callback(struct mbox_client *cl, void *m) diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c index f42dad997ac9..2a7aeab40e54 100644 --- a/drivers/firmware/arm_scmi/optee.c +++ b/drivers/firmware/arm_scmi/optee.c @@ -498,7 +498,7 @@ static int scmi_optee_send_message(struct scmi_chan_info *cinfo, msg_tx_prepare(channel->req.msg, xfer); ret = invoke_process_msg_channel(channel, msg_command_size(xfer)); } else { - shmem_tx_prepare(channel->req.shmem, xfer); + shmem_tx_prepare(channel->req.shmem, xfer, cinfo); ret = invoke_process_smt_channel(channel); } diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c index 0e3eaea5d852..1dfe534b8518 100644 --- a/drivers/firmware/arm_scmi/shmem.c +++ b/drivers/firmware/arm_scmi/shmem.c @@ -5,10 +5,13 @@ * Copyright (C) 2019 ARM Ltd. */ +#include <linux/ktime.h> #include <linux/io.h> #include <linux/processor.h> #include <linux/types.h> +#include <asm-generic/bug.h> + #include "common.h" /* @@ -30,16 +33,36 @@ struct scmi_shared_mem { }; void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, - struct scmi_xfer *xfer) + struct scmi_xfer *xfer, struct scmi_chan_info *cinfo) { + ktime_t stop; + /* * Ideally channel must be free by now unless OS timeout last * request and platform continued to process the same, wait * until it releases the shared memory, otherwise we may endup - * overwriting its response with new message payload or vice-versa + * overwriting its response with new message payload or vice-versa. + * Giving up anyway after twice the expected channel timeout so as + * not to bail-out on intermittent issues where the platform is + * occasionally a bit slower to answer. + * + * Note that after a timeout is detected we bail-out and carry on but + * the transport functionality is probably permanently compromised: + * this is just to ease debugging and avoid complete hangs on boot + * due to a misbehaving SCMI firmware. */ - spin_until_cond(ioread32(&shmem->channel_status) & - SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); + stop = ktime_add_ms(ktime_get(), 2 * cinfo->rx_timeout_ms); + spin_until_cond((ioread32(&shmem->channel_status) & + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) || + ktime_after(ktime_get(), stop)); + if (!(ioread32(&shmem->channel_status) & + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) { + WARN_ON_ONCE(1); + dev_err(cinfo->dev, + "Timeout waiting for a free TX channel !\n"); + return; + } + /* Mark channel busy + clear error */ iowrite32(0x0, &shmem->channel_status); iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 745acfdd0b3d..87a7b13cf868 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -188,7 +188,7 @@ static int smc_send_message(struct scmi_chan_info *cinfo, */ smc_channel_lock_acquire(scmi_info, xfer); - shmem_tx_prepare(scmi_info->shmem, xfer); + shmem_tx_prepare(scmi_info->shmem, xfer, cinfo); arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); diff --git a/drivers/firmware/arm_scmi/virtio.c b/drivers/firmware/arm_scmi/virtio.c index 14709dbc96a1..33c9b81a55cd 100644 --- a/drivers/firmware/arm_scmi/virtio.c +++ b/drivers/firmware/arm_scmi/virtio.c @@ -148,7 +148,6 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch) { unsigned long flags; DECLARE_COMPLETION_ONSTACK(vioch_shutdown_done); - void *deferred_wq = NULL; /* * Prepare to wait for the last release if not already released @@ -162,16 +161,11 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch) vioch->shutdown_done = &vioch_shutdown_done; virtio_break_device(vioch->vqueue->vdev); - if (!vioch->is_rx && vioch->deferred_tx_wq) { - deferred_wq = vioch->deferred_tx_wq; + if (!vioch->is_rx && vioch->deferred_tx_wq) /* Cannot be kicked anymore after this...*/ vioch->deferred_tx_wq = NULL; - } spin_unlock_irqrestore(&vioch->lock, flags); - if (deferred_wq) - destroy_workqueue(deferred_wq); - scmi_vio_channel_release(vioch); /* Let any possibly concurrent RX path release the channel */ @@ -416,6 +410,11 @@ static bool virtio_chan_available(struct device *dev, int idx) return vioch && !vioch->cinfo; } +static void scmi_destroy_tx_workqueue(void *deferred_tx_wq) +{ + destroy_workqueue(deferred_tx_wq); +} + static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) { @@ -430,6 +429,8 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, /* Setup a deferred worker for polling. */ if (tx && !vioch->deferred_tx_wq) { + int ret; + vioch->deferred_tx_wq = alloc_workqueue(dev_name(&scmi_vdev->dev), WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, @@ -437,6 +438,11 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, if (!vioch->deferred_tx_wq) return -ENOMEM; + ret = devm_add_action_or_reset(dev, scmi_destroy_tx_workqueue, + vioch->deferred_tx_wq); + if (ret) + return ret; + INIT_WORK(&vioch->deferred_tx_work, scmi_vio_deferred_tx_worker); } @@ -444,12 +450,12 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, for (i = 0; i < vioch->max_msg; i++) { struct scmi_vio_msg *msg; - msg = devm_kzalloc(cinfo->dev, sizeof(*msg), GFP_KERNEL); + msg = devm_kzalloc(dev, sizeof(*msg), GFP_KERNEL); if (!msg) return -ENOMEM; if (tx) { - msg->request = devm_kzalloc(cinfo->dev, + msg->request = devm_kzalloc(dev, VIRTIO_SCMI_MAX_PDU_SIZE, GFP_KERNEL); if (!msg->request) @@ -458,7 +464,7 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, refcount_set(&msg->users, 1); } - msg->input = devm_kzalloc(cinfo->dev, VIRTIO_SCMI_MAX_PDU_SIZE, + msg->input = devm_kzalloc(dev, VIRTIO_SCMI_MAX_PDU_SIZE, GFP_KERNEL); if (!msg->input) return -ENOMEM; diff --git a/drivers/firmware/broadcom/bcm47xx_nvram.c b/drivers/firmware/broadcom/bcm47xx_nvram.c index bd235833b687..5f47dbf4889a 100644 --- a/drivers/firmware/broadcom/bcm47xx_nvram.c +++ b/drivers/firmware/broadcom/bcm47xx_nvram.c @@ -110,6 +110,24 @@ found: return 0; } +int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size) +{ + if (nvram_len) { + pr_warn("nvram already initialized\n"); + return -EEXIST; + } + + if (!bcm47xx_nvram_is_valid(nvram_start)) { + pr_err("No valid NVRAM found\n"); + return -ENOENT; + } + + bcm47xx_nvram_copy(nvram_start, res_size); + + return 0; +} +EXPORT_SYMBOL_GPL(bcm47xx_nvram_init_from_iomem); + /* * On bcm47xx we need access to the NVRAM very early, so we can't use mtd * subsystem to access flash. We can't even use platform device / driver to diff --git a/drivers/firmware/cirrus/Kconfig b/drivers/firmware/cirrus/Kconfig index f9503cb481d2..3ccbe14e4b0c 100644 --- a/drivers/firmware/cirrus/Kconfig +++ b/drivers/firmware/cirrus/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -config CS_DSP +config FW_CS_DSP tristate default n diff --git a/drivers/firmware/cirrus/Makefile b/drivers/firmware/cirrus/Makefile index f074e2638c9c..b91318ca0ff4 100644 --- a/drivers/firmware/cirrus/Makefile +++ b/drivers/firmware/cirrus/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 # -obj-$(CONFIG_CS_DSP) += cs_dsp.o +obj-$(CONFIG_FW_CS_DSP) += cs_dsp.o diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 81cc3d0f6eec..f558b390fbfe 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -369,7 +369,7 @@ const char *cs_dsp_mem_region_name(unsigned int type) return NULL; } } -EXPORT_SYMBOL_GPL(cs_dsp_mem_region_name); +EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, FW_CS_DSP); #ifdef CONFIG_DEBUG_FS static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s) @@ -480,7 +480,7 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) dsp->debugfs_root = root; } -EXPORT_SYMBOL_GPL(cs_dsp_init_debugfs); +EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP); /** * cs_dsp_cleanup_debugfs() - Removes DSP representation from debugfs @@ -492,17 +492,17 @@ void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) debugfs_remove_recursive(dsp->debugfs_root); dsp->debugfs_root = NULL; } -EXPORT_SYMBOL_GPL(cs_dsp_cleanup_debugfs); +EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); #else void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) { } -EXPORT_SYMBOL_GPL(cs_dsp_init_debugfs); +EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP); void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) { } -EXPORT_SYMBOL_GPL(cs_dsp_cleanup_debugfs); +EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); static inline void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s) @@ -710,7 +710,7 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int return -ETIMEDOUT; } -EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_acked_control); +EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_acked_control, FW_CS_DSP); static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, unsigned int off, const void *buf, size_t len) @@ -752,7 +752,7 @@ static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, * * Must be called with pwr_lock held. * - * Return: Zero for success, a negative number on error. + * Return: < 0 on error, 1 when the control value changed and 0 when it has not. */ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, unsigned int off, const void *buf, size_t len) @@ -767,18 +767,25 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, if (len + off * sizeof(u32) > ctl->len) return -EINVAL; - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { ret = -EPERM; - else if (buf != ctl->cache) - memcpy(ctl->cache + off * sizeof(u32), buf, len); + } else if (buf != ctl->cache) { + if (memcmp(ctl->cache + off * sizeof(u32), buf, len)) + memcpy(ctl->cache + off * sizeof(u32), buf, len); + else + return 0; + } ctl->set = 1; if (ctl->enabled && ctl->dsp->running) ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); - return ret; + if (ret < 0) + return ret; + + return 1; } -EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_ctrl); +EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_ctrl, FW_CS_DSP); static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, unsigned int off, void *buf, size_t len) @@ -850,7 +857,7 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, return ret; } -EXPORT_SYMBOL_GPL(cs_dsp_coeff_read_ctrl); +EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_read_ctrl, FW_CS_DSP); static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) { @@ -1493,7 +1500,7 @@ struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, in return rslt; } -EXPORT_SYMBOL_GPL(cs_dsp_get_ctl); +EXPORT_SYMBOL_NS_GPL(cs_dsp_get_ctl, FW_CS_DSP); static void cs_dsp_ctl_fixup_base(struct cs_dsp *dsp, const struct cs_dsp_alg_region *alg_region) @@ -1583,7 +1590,7 @@ struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, return NULL; } -EXPORT_SYMBOL_GPL(cs_dsp_find_alg_region); +EXPORT_SYMBOL_NS_GPL(cs_dsp_find_alg_region, FW_CS_DSP); static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, int type, __be32 id, @@ -2217,7 +2224,7 @@ int cs_dsp_adsp1_init(struct cs_dsp *dsp) return cs_dsp_common_init(dsp); } -EXPORT_SYMBOL_GPL(cs_dsp_adsp1_init); +EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_init, FW_CS_DSP); /** * cs_dsp_adsp1_power_up() - Load and start the named firmware @@ -2309,7 +2316,7 @@ err_mutex: mutex_unlock(&dsp->pwr_lock); return ret; } -EXPORT_SYMBOL_GPL(cs_dsp_adsp1_power_up); +EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_up, FW_CS_DSP); /** * cs_dsp_adsp1_power_down() - Halts the DSP @@ -2341,7 +2348,7 @@ void cs_dsp_adsp1_power_down(struct cs_dsp *dsp) mutex_unlock(&dsp->pwr_lock); } -EXPORT_SYMBOL_GPL(cs_dsp_adsp1_power_down); +EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_down, FW_CS_DSP); static int cs_dsp_adsp2v2_enable_core(struct cs_dsp *dsp) { @@ -2493,7 +2500,7 @@ int cs_dsp_set_dspclk(struct cs_dsp *dsp, unsigned int freq) return ret; } -EXPORT_SYMBOL_GPL(cs_dsp_set_dspclk); +EXPORT_SYMBOL_NS_GPL(cs_dsp_set_dspclk, FW_CS_DSP); static void cs_dsp_stop_watchdog(struct cs_dsp *dsp) { @@ -2583,7 +2590,7 @@ err_mutex: return ret; } -EXPORT_SYMBOL_GPL(cs_dsp_power_up); +EXPORT_SYMBOL_NS_GPL(cs_dsp_power_up, FW_CS_DSP); /** * cs_dsp_power_down() - Powers-down the DSP @@ -2617,7 +2624,7 @@ void cs_dsp_power_down(struct cs_dsp *dsp) cs_dsp_dbg(dsp, "Shutdown complete\n"); } -EXPORT_SYMBOL_GPL(cs_dsp_power_down); +EXPORT_SYMBOL_NS_GPL(cs_dsp_power_down, FW_CS_DSP); static int cs_dsp_adsp2_start_core(struct cs_dsp *dsp) { @@ -2703,7 +2710,7 @@ err: return ret; } -EXPORT_SYMBOL_GPL(cs_dsp_run); +EXPORT_SYMBOL_NS_GPL(cs_dsp_run, FW_CS_DSP); /** * cs_dsp_stop() - Stops the firmware @@ -2742,7 +2749,7 @@ void cs_dsp_stop(struct cs_dsp *dsp) cs_dsp_dbg(dsp, "Execution stopped\n"); } -EXPORT_SYMBOL_GPL(cs_dsp_stop); +EXPORT_SYMBOL_NS_GPL(cs_dsp_stop, FW_CS_DSP); static int cs_dsp_halo_start_core(struct cs_dsp *dsp) { @@ -2804,7 +2811,7 @@ int cs_dsp_adsp2_init(struct cs_dsp *dsp) return cs_dsp_common_init(dsp); } -EXPORT_SYMBOL_GPL(cs_dsp_adsp2_init); +EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_init, FW_CS_DSP); /** * cs_dsp_halo_init() - Initialise a cs_dsp structure representing a HALO Core DSP @@ -2818,7 +2825,7 @@ int cs_dsp_halo_init(struct cs_dsp *dsp) return cs_dsp_common_init(dsp); } -EXPORT_SYMBOL_GPL(cs_dsp_halo_init); +EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_init, FW_CS_DSP); /** * cs_dsp_remove() - Clean a cs_dsp before deletion @@ -2838,7 +2845,7 @@ void cs_dsp_remove(struct cs_dsp *dsp) cs_dsp_free_ctl_blk(ctl); } } -EXPORT_SYMBOL_GPL(cs_dsp_remove); +EXPORT_SYMBOL_NS_GPL(cs_dsp_remove, FW_CS_DSP); /** * cs_dsp_read_raw_data_block() - Reads a block of data from DSP memory @@ -2875,7 +2882,7 @@ int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int me return 0; } -EXPORT_SYMBOL_GPL(cs_dsp_read_raw_data_block); +EXPORT_SYMBOL_NS_GPL(cs_dsp_read_raw_data_block, FW_CS_DSP); /** * cs_dsp_read_data_word() - Reads a word from DSP memory @@ -2899,7 +2906,7 @@ int cs_dsp_read_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_add return 0; } -EXPORT_SYMBOL_GPL(cs_dsp_read_data_word); +EXPORT_SYMBOL_NS_GPL(cs_dsp_read_data_word, FW_CS_DSP); /** * cs_dsp_write_data_word() - Writes a word to DSP memory @@ -2925,7 +2932,7 @@ int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_ad return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); } -EXPORT_SYMBOL_GPL(cs_dsp_write_data_word); +EXPORT_SYMBOL_NS_GPL(cs_dsp_write_data_word, FW_CS_DSP); /** * cs_dsp_remove_padding() - Convert unpacked words to packed bytes @@ -2949,7 +2956,7 @@ void cs_dsp_remove_padding(u32 *buf, int nwords) *pack_out++ = (u8)(word >> 16); } } -EXPORT_SYMBOL_GPL(cs_dsp_remove_padding); +EXPORT_SYMBOL_NS_GPL(cs_dsp_remove_padding, FW_CS_DSP); /** * cs_dsp_adsp2_bus_error() - Handle a DSP bus error interrupt @@ -3019,7 +3026,7 @@ void cs_dsp_adsp2_bus_error(struct cs_dsp *dsp) error: mutex_unlock(&dsp->pwr_lock); } -EXPORT_SYMBOL_GPL(cs_dsp_adsp2_bus_error); +EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_bus_error, FW_CS_DSP); /** * cs_dsp_halo_bus_error() - Handle a DSP bus error interrupt @@ -3079,7 +3086,7 @@ void cs_dsp_halo_bus_error(struct cs_dsp *dsp) exit_unlock: mutex_unlock(&dsp->pwr_lock); } -EXPORT_SYMBOL_GPL(cs_dsp_halo_bus_error); +EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_bus_error, FW_CS_DSP); /** * cs_dsp_halo_wdt_expire() - Handle DSP watchdog expiry @@ -3099,7 +3106,7 @@ void cs_dsp_halo_wdt_expire(struct cs_dsp *dsp) mutex_unlock(&dsp->pwr_lock); } -EXPORT_SYMBOL_GPL(cs_dsp_halo_wdt_expire); +EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_wdt_expire, FW_CS_DSP); static const struct cs_dsp_ops cs_dsp_adsp1_ops = { .validate_version = cs_dsp_validate_version, @@ -3221,7 +3228,7 @@ int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val) return 0; } -EXPORT_SYMBOL_GPL(cs_dsp_chunk_write); +EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_write, FW_CS_DSP); /** * cs_dsp_chunk_flush() - Pad remaining data with zero and commit to chunk @@ -3240,7 +3247,7 @@ int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch) return cs_dsp_chunk_write(ch, CS_DSP_DATA_WORD_BITS - ch->cachebits, 0); } -EXPORT_SYMBOL_GPL(cs_dsp_chunk_flush); +EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_flush, FW_CS_DSP); /** * cs_dsp_chunk_read() - Parse data from a DSP memory chunk @@ -3282,7 +3289,7 @@ int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) return result; } -EXPORT_SYMBOL_GPL(cs_dsp_chunk_read); +EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP); MODULE_DESCRIPTION("Cirrus Logic DSP Support"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index 940ddf916202..5f3a3e913d28 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -155,7 +155,7 @@ static const struct attribute_group* sys_dmi_attribute_groups[] = { NULL }; -static int dmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +static int dmi_dev_uevent(const struct device *dev, struct kobj_uevent_env *env) { ssize_t len; diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 5b79a4a4a88d..043ca31c114e 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -26,39 +26,6 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE backend for pstore by default. This setting can be overridden using the efivars module's pstore_disable parameter. -config EFI_RUNTIME_MAP - bool "Export efi runtime maps to sysfs" - depends on X86 && EFI && KEXEC_CORE - default y - help - Export efi runtime memory maps to /sys/firmware/efi/runtime-map. - That memory map is used for example by kexec to set up efi virtual - mapping the 2nd kernel, but can also be used for debugging purposes. - - See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. - -config EFI_FAKE_MEMMAP - bool "Enable EFI fake memory map" - depends on EFI && X86 - default n - help - Saying Y here will enable "efi_fake_mem" boot option. - By specifying this parameter, you can add arbitrary attribute - to specific memory range by updating original (firmware provided) - EFI memmap. - This is useful for debugging of EFI memmap related feature. - e.g. Address Range Mirroring feature. - -config EFI_MAX_FAKE_MEM - int "maximum allowable number of ranges in efi_fake_mem boot option" - depends on EFI_FAKE_MEMMAP - range 1 128 - default 8 - help - Maximum allowable number of ranges in efi_fake_mem boot option. - Ranges can be set up to this value using comma-separated list. - The default value is 8. - config EFI_SOFT_RESERVE bool "Reserve EFI Specific Purpose Memory" depends on EFI && EFI_STUB && ACPI_HMAT @@ -124,28 +91,6 @@ config EFI_ZBOOT is supported by the encapsulated image. (The compression algorithm used is described in the zboot image header) -config EFI_ZBOOT_SIGNED - def_bool y - depends on EFI_ZBOOT_SIGNING_CERT != "" - depends on EFI_ZBOOT_SIGNING_KEY != "" - -config EFI_ZBOOT_SIGNING - bool "Sign the EFI decompressor for UEFI secure boot" - depends on EFI_ZBOOT - help - Use the 'sbsign' command line tool (which must exist on the host - path) to sign both the EFI decompressor PE/COFF image, as well as the - encapsulated PE/COFF image, which is subsequently compressed and - wrapped by the former image. - -config EFI_ZBOOT_SIGNING_CERT - string "Certificate to use for signing the compressed EFI boot image" - depends on EFI_ZBOOT_SIGNING - -config EFI_ZBOOT_SIGNING_KEY - string "Private key to use for signing the compressed EFI boot image" - depends on EFI_ZBOOT_SIGNING - config EFI_ARMSTUB_DTB_LOADER bool "Enable the DTB loader" depends on EFI_GENERIC_STUB && !RISCV && !LOONGARCH @@ -161,18 +106,6 @@ config EFI_ARMSTUB_DTB_LOADER functionality for bootloaders that do not have such support this option is necessary. -config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER - bool "Enable the command line initrd loader" if !X86 - depends on EFI_STUB && (EFI_GENERIC_STUB || X86) - default y if X86 - depends on !RISCV && !LOONGARCH - help - Select this config option to add support for the initrd= command - line parameter, allowing an initrd that resides on the same volume - as the kernel image to be loaded into memory. - - This method is deprecated. - config EFI_BOOTLOADER_CONTROL tristate "EFI Bootloader Control" select UCS2_STRING diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 8d151e332584..b51f2a4c821e 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -19,11 +19,9 @@ endif obj-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdtparams.o obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o -obj-$(CONFIG_UEFI_CPER) += cper.o -obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o +obj-$(CONFIG_UEFI_CPER) += cper.o cper_cxl.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o subdir-$(CONFIG_EFI_STUB) += libstub -obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_map.o obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o @@ -32,9 +30,6 @@ obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o obj-$(CONFIG_LOAD_UEFI_KEYS) += mokvar-table.o -fake_map-y += fake_mem.o -fake_map-$(CONFIG_X86) += x86_fake_mem.o - obj-$(CONFIG_SYSFB) += sysfb_efi.o arm-obj-$(CONFIG_EFI) := efi-init.o arm-runtime.o diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 3359ae2adf24..83f5bb57fa4c 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -25,14 +25,14 @@ #include <asm/mmu.h> #include <asm/pgalloc.h> -#if defined(CONFIG_PTDUMP_DEBUGFS) && defined(CONFIG_ARM64) +#if defined(CONFIG_PTDUMP_DEBUGFS) || defined(CONFIG_ARM_PTDUMP_DEBUGFS) #include <asm/ptdump.h> static struct ptdump_info efi_ptdump_info = { .mm = &efi_mm, .markers = (struct addr_marker[]){ { 0, "UEFI runtime start" }, - { DEFAULT_MAP_WINDOW_64, "UEFI runtime end" }, + { EFI_RUNTIME_MAP_END, "UEFI runtime end" }, { -1, NULL } }, .base_addr = 0, @@ -63,7 +63,7 @@ static bool __init efi_virtmap_init(void) if (!(md->attribute & EFI_MEMORY_RUNTIME)) continue; - if (md->virt_addr == 0) + if (md->virt_addr == U64_MAX) return false; ret = efi_create_mapping(&efi_mm, md); diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index e4e5ea7ce910..35c37f667781 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -24,6 +24,7 @@ #include <linux/bcd.h> #include <acpi/ghes.h> #include <ras/ras_event.h> +#include "cper_cxl.h" /* * CPER record ID need to be unique even after reboot, because record @@ -290,6 +291,7 @@ int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) return n; } +EXPORT_SYMBOL_GPL(cper_mem_err_location); int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) { @@ -310,6 +312,7 @@ int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) return n; } +EXPORT_SYMBOL_GPL(cper_dimm_err_location); void cper_mem_err_pack(const struct cper_sec_mem_err *mem, struct cper_mem_err_compact *cmem) @@ -331,6 +334,7 @@ void cper_mem_err_pack(const struct cper_sec_mem_err *mem, cmem->mem_array_handle = mem->mem_array_handle; cmem->mem_dev_handle = mem->mem_dev_handle; } +EXPORT_SYMBOL_GPL(cper_mem_err_pack); const char *cper_mem_err_unpack(struct trace_seq *p, struct cper_mem_err_compact *cmem) @@ -595,6 +599,14 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata cper_print_fw_err(newpfx, gdata, fw_err); else goto err_section_too_small; + } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { + struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata); + + printk("%ssection_type: CXL Protocol Error\n", newpfx); + if (gdata->error_data_length >= sizeof(*prot_err)) + cper_print_prot_err(newpfx, prot_err); + else + goto err_section_too_small; } else { const void *err = acpi_hest_get_payload(gdata); diff --git a/drivers/firmware/efi/cper_cxl.c b/drivers/firmware/efi/cper_cxl.c new file mode 100644 index 000000000000..53e435c4f310 --- /dev/null +++ b/drivers/firmware/efi/cper_cxl.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UEFI Common Platform Error Record (CPER) support for CXL Section. + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Author: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com> + */ + +#include <linux/cper.h> +#include "cper_cxl.h" +#include <linux/cxl_err.h> + +#define PROT_ERR_VALID_AGENT_TYPE BIT_ULL(0) +#define PROT_ERR_VALID_AGENT_ADDRESS BIT_ULL(1) +#define PROT_ERR_VALID_DEVICE_ID BIT_ULL(2) +#define PROT_ERR_VALID_SERIAL_NUMBER BIT_ULL(3) +#define PROT_ERR_VALID_CAPABILITY BIT_ULL(4) +#define PROT_ERR_VALID_DVSEC BIT_ULL(5) +#define PROT_ERR_VALID_ERROR_LOG BIT_ULL(6) + +static const char * const prot_err_agent_type_strs[] = { + "Restricted CXL Device", + "Restricted CXL Host Downstream Port", + "CXL Device", + "CXL Logical Device", + "CXL Fabric Manager managed Logical Device", + "CXL Root Port", + "CXL Downstream Switch Port", + "CXL Upstream Switch Port", +}; + +/* + * The layout of the enumeration and the values matches CXL Agent Type + * field in the UEFI 2.10 Section N.2.13, + */ +enum { + RCD, /* Restricted CXL Device */ + RCH_DP, /* Restricted CXL Host Downstream Port */ + DEVICE, /* CXL Device */ + LD, /* CXL Logical Device */ + FMLD, /* CXL Fabric Manager managed Logical Device */ + RP, /* CXL Root Port */ + DSP, /* CXL Downstream Switch Port */ + USP, /* CXL Upstream Switch Port */ +}; + +void cper_print_prot_err(const char *pfx, const struct cper_sec_prot_err *prot_err) +{ + if (prot_err->valid_bits & PROT_ERR_VALID_AGENT_TYPE) + pr_info("%s agent_type: %d, %s\n", pfx, prot_err->agent_type, + prot_err->agent_type < ARRAY_SIZE(prot_err_agent_type_strs) + ? prot_err_agent_type_strs[prot_err->agent_type] + : "unknown"); + + if (prot_err->valid_bits & PROT_ERR_VALID_AGENT_ADDRESS) { + switch (prot_err->agent_type) { + /* + * According to UEFI 2.10 Section N.2.13, the term CXL Device + * is used to refer to Restricted CXL Device, CXL Device, CXL + * Logical Device or a CXL Fabric Manager Managed Logical + * Device. + */ + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + pr_info("%s agent_address: %04x:%02x:%02x.%x\n", + pfx, prot_err->agent_addr.segment, + prot_err->agent_addr.bus, + prot_err->agent_addr.device, + prot_err->agent_addr.function); + break; + case RCH_DP: + pr_info("%s rcrb_base_address: 0x%016llx\n", pfx, + prot_err->agent_addr.rcrb_base_addr); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_DEVICE_ID) { + const __u8 *class_code; + + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + pr_info("%s slot: %d\n", pfx, + prot_err->device_id.slot >> CPER_PCIE_SLOT_SHIFT); + pr_info("%s vendor_id: 0x%04x, device_id: 0x%04x\n", + pfx, prot_err->device_id.vendor_id, + prot_err->device_id.device_id); + pr_info("%s sub_vendor_id: 0x%04x, sub_device_id: 0x%04x\n", + pfx, prot_err->device_id.subsystem_vendor_id, + prot_err->device_id.subsystem_id); + class_code = prot_err->device_id.class_code; + pr_info("%s class_code: %02x%02x\n", pfx, + class_code[1], class_code[0]); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_SERIAL_NUMBER) { + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + pr_info("%s lower_dw: 0x%08x, upper_dw: 0x%08x\n", pfx, + prot_err->dev_serial_num.lower_dw, + prot_err->dev_serial_num.upper_dw); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_CAPABILITY) { + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, + prot_err->capability, + sizeof(prot_err->capability), 0); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_DVSEC) { + pr_info("%s DVSEC length: 0x%04x\n", pfx, prot_err->dvsec_len); + + pr_info("%s CXL DVSEC:\n", pfx); + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, (prot_err + 1), + prot_err->dvsec_len, 0); + } + + if (prot_err->valid_bits & PROT_ERR_VALID_ERROR_LOG) { + size_t size = sizeof(*prot_err) + prot_err->dvsec_len; + struct cxl_ras_capability_regs *cxl_ras; + + pr_info("%s Error log length: 0x%04x\n", pfx, prot_err->err_len); + + pr_info("%s CXL Error Log:\n", pfx); + cxl_ras = (struct cxl_ras_capability_regs *)((long)prot_err + size); + pr_info("%s cxl_ras_uncor_status: 0x%08x", pfx, + cxl_ras->uncor_status); + pr_info("%s cxl_ras_uncor_mask: 0x%08x\n", pfx, + cxl_ras->uncor_mask); + pr_info("%s cxl_ras_uncor_severity: 0x%08x\n", pfx, + cxl_ras->uncor_severity); + pr_info("%s cxl_ras_cor_status: 0x%08x", pfx, + cxl_ras->cor_status); + pr_info("%s cxl_ras_cor_mask: 0x%08x\n", pfx, + cxl_ras->cor_mask); + pr_info("%s cap_control: 0x%08x\n", pfx, + cxl_ras->cap_control); + pr_info("%s Header Log Registers:\n", pfx); + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, cxl_ras->header_log, + sizeof(cxl_ras->header_log), 0); + } +} diff --git a/drivers/firmware/efi/cper_cxl.h b/drivers/firmware/efi/cper_cxl.h new file mode 100644 index 000000000000..86bfcf7909ec --- /dev/null +++ b/drivers/firmware/efi/cper_cxl.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UEFI Common Platform Error Record (CPER) support for CXL Section. + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Author: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com> + */ + +#ifndef LINUX_CPER_CXL_H +#define LINUX_CPER_CXL_H + +/* CXL Protocol Error Section */ +#define CPER_SEC_CXL_PROT_ERR \ + GUID_INIT(0x80B9EFB4, 0x52B5, 0x4DE3, 0xA7, 0x77, 0x68, 0x78, \ + 0x4B, 0x77, 0x10, 0x48) + +#pragma pack(1) + +/* Compute Express Link Protocol Error Section, UEFI v2.10 sec N.2.13 */ +struct cper_sec_prot_err { + u64 valid_bits; + u8 agent_type; + u8 reserved[7]; + + /* + * Except for RCH Downstream Port, all the remaining CXL Agent + * types are uniquely identified by the PCIe compatible SBDF number. + */ + union { + u64 rcrb_base_addr; + struct { + u8 function; + u8 device; + u8 bus; + u16 segment; + u8 reserved_1[3]; + }; + } agent_addr; + + struct { + u16 vendor_id; + u16 device_id; + u16 subsystem_vendor_id; + u16 subsystem_id; + u8 class_code[2]; + u16 slot; + u8 reserved_1[4]; + } device_id; + + struct { + u32 lower_dw; + u32 upper_dw; + } dev_serial_num; + + u8 capability[60]; + u16 dvsec_len; + u16 err_len; + u8 reserved_2[4]; +}; + +#pragma pack() + +void cper_print_prot_err(const char *pfx, const struct cper_sec_prot_err *prot_err); + +#endif //__CPER_CXL_ diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c index a52236e11e5f..4d6c5327471a 100644 --- a/drivers/firmware/efi/earlycon.c +++ b/drivers/firmware/efi/earlycon.c @@ -29,8 +29,8 @@ static void *efi_fb; */ static int __init efi_earlycon_remap_fb(void) { - /* bail if there is no bootconsole or it has been disabled already */ - if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) + /* bail if there is no bootconsole or it was unregistered already */ + if (!earlycon_console || !console_is_registered(earlycon_console)) return 0; efi_fb = memremap(fb_base, screen_info.lfb_size, @@ -42,8 +42,8 @@ early_initcall(efi_earlycon_remap_fb); static int __init efi_earlycon_unmap_fb(void) { - /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ - if (efi_fb && !(earlycon_console->flags & CON_ENABLED)) + /* unmap the bootconsole fb unless keep_bootcon left it registered */ + if (efi_fb && !console_is_registered(earlycon_console)) memunmap(efi_fb); return 0; } diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index 2fd770b499a3..1639159493e3 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -22,6 +22,8 @@ #include <asm/efi.h> +unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; + static int __init is_memory(efi_memory_desc_t *md) { if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC)) @@ -55,9 +57,22 @@ extern __weak const efi_config_table_type_t efi_arch_tables[]; static void __init init_screen_info(void) { - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI && - memblock_is_map_memory(screen_info.lfb_base)) - memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size); + struct screen_info *si; + + if (screen_info_table != EFI_INVALID_TABLE_ADDR) { + si = early_memremap(screen_info_table, sizeof(*si)); + if (!si) { + pr_err("Could not map screen_info config table\n"); + return; + } + screen_info = *si; + memset(si, 0, sizeof(*si)); + early_memunmap(si, sizeof(*si)); + + if (memblock_is_map_memory(screen_info.lfb_base)) + memblock_mark_nomap(screen_info.lfb_base, + screen_info.lfb_size); + } } static int __init uefi_init(u64 efi_system_table) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 3bddc152fcd4..e7b9ec6f8a86 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -10,7 +10,9 @@ MODULE_IMPORT_NS(EFIVAR); #define DUMP_NAME_LEN 66 -#define EFIVARS_DATA_SIZE_MAX 1024 +static unsigned int record_size = 1024; +module_param(record_size, uint, 0444); +MODULE_PARM_DESC(record_size, "size of each pstore UEFI var (in bytes, min/default=1024)"); static bool efivars_pstore_disable = IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); @@ -30,7 +32,7 @@ static int efi_pstore_open(struct pstore_info *psi) if (err) return err; - psi->data = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); + psi->data = kzalloc(record_size, GFP_KERNEL); if (!psi->data) return -ENOMEM; @@ -52,7 +54,7 @@ static inline u64 generic_id(u64 timestamp, unsigned int part, int count) static int efi_pstore_read_func(struct pstore_record *record, efi_char16_t *varname) { - unsigned long wlen, size = EFIVARS_DATA_SIZE_MAX; + unsigned long wlen, size = record_size; char name[DUMP_NAME_LEN], data_type; efi_status_t status; int cnt; @@ -133,7 +135,7 @@ static ssize_t efi_pstore_read(struct pstore_record *record) efi_status_t status; for (;;) { - varname_size = EFIVARS_DATA_SIZE_MAX; + varname_size = 1024; /* * If this is the first read() call in the pstore enumeration, @@ -207,7 +209,7 @@ static int efi_pstore_erase(struct pstore_record *record) static struct pstore_info efi_pstore_info = { .owner = THIS_MODULE, - .name = "efi", + .name = KBUILD_MODNAME, .flags = PSTORE_FLAGS_DMESG, .open = efi_pstore_open, .close = efi_pstore_close, @@ -224,11 +226,20 @@ static __init int efivars_pstore_init(void) if (efivars_pstore_disable) return 0; - efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); + /* + * Notice that 1024 is the minimum here to prevent issues with + * decompression algorithms that were spotted during tests; + * even in the case of not using compression, smaller values would + * just pollute more the pstore FS with many small collected files. + */ + if (record_size < 1024) + record_size = 1024; + + efi_pstore_info.buf = kmalloc(record_size, GFP_KERNEL); if (!efi_pstore_info.buf) return -ENOMEM; - efi_pstore_info.bufsize = 1024; + efi_pstore_info.bufsize = record_size; if (pstore_register(&efi_pstore_info)) { kfree(efi_pstore_info.buf); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 9624735f1575..31a4090c66b3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -58,6 +58,8 @@ static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; +extern unsigned long screen_info_table; + struct mm_struct efi_mm = { .mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock), .mm_users = ATOMIC_INIT(2), @@ -271,6 +273,8 @@ static __init int efivar_ssdt_load(void) acpi_status ret = acpi_load_table(data, NULL); if (ret) pr_err("failed to load table: %u\n", ret); + else + continue; } else { pr_err("failed to get var data: 0x%lx\n", status); } @@ -335,6 +339,24 @@ static void __init efi_debugfs_init(void) static inline void efi_debugfs_init(void) {} #endif +static void refresh_nv_rng_seed(struct work_struct *work) +{ + u8 seed[EFI_RANDOM_SEED_SIZE]; + + get_random_bytes(seed, sizeof(seed)); + efi.set_variable(L"RandomSeed", &LINUX_EFI_RANDOM_SEED_TABLE_GUID, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, sizeof(seed), seed); + memzero_explicit(seed, sizeof(seed)); +} +static int refresh_nv_rng_seed_notification(struct notifier_block *nb, unsigned long action, void *data) +{ + static DECLARE_WORK(work, refresh_nv_rng_seed); + schedule_work(&work); + return NOTIFY_DONE; +} +static struct notifier_block refresh_nv_rng_seed_nb = { .notifier_call = refresh_nv_rng_seed_notification }; + /* * We register the efi subsystem with the firmware subsystem and the * efivars subsystem with the efi subsystem, if the system was booted with @@ -392,10 +414,6 @@ static int __init efisubsys_init(void) goto err_unregister; } - error = efi_runtime_map_init(efi_kobj); - if (error) - goto err_remove_group; - /* and the standard mountpoint for efivarfs */ error = sysfs_create_mount_point(efi_kobj, "efivars"); if (error) { @@ -411,6 +429,7 @@ static int __init efisubsys_init(void) platform_device_register_simple("efi_secret", 0, NULL, 0); #endif + execute_with_initialized_rng(&refresh_nv_rng_seed_nb); return 0; err_remove_group: @@ -421,6 +440,7 @@ err_unregister: generic_ops_unregister(); err_put: kobject_put(efi_kobj); + efi_kobj = NULL; destroy_workqueue(efi_rts_wq); return error; } @@ -545,6 +565,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { #ifdef CONFIG_EFI_COCO_SECRET {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, #endif +#ifdef CONFIG_EFI_GENERIC_STUB + {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, +#endif {}, }; @@ -609,7 +632,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed)); if (seed != NULL) { - size = READ_ONCE(seed->size); + size = min_t(u32, seed->size, SZ_1K); // sanity check early_memunmap(seed, sizeof(*seed)); } else { pr_err("Could not map UEFI random seed!\n"); @@ -618,8 +641,8 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed) + size); if (seed != NULL) { - pr_notice("seeding entropy pool\n"); add_bootloader_randomness(seed->bits, size); + memzero_explicit(seed->bits, size); early_memunmap(seed, sizeof(*seed) + size); } else { pr_err("Could not map UEFI random seed!\n"); diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c deleted file mode 100644 index 6e0f34a38171..000000000000 --- a/drivers/firmware/efi/fake_mem.c +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * fake_mem.c - * - * Copyright (C) 2015 FUJITSU LIMITED - * Author: Taku Izumi <izumi.taku@jp.fujitsu.com> - * - * This code introduces new boot option named "efi_fake_mem" - * By specifying this parameter, you can add arbitrary attribute to - * specific memory range by updating original (firmware provided) EFI - * memmap. - */ - -#include <linux/kernel.h> -#include <linux/efi.h> -#include <linux/init.h> -#include <linux/memblock.h> -#include <linux/types.h> -#include <linux/sort.h> -#include "fake_mem.h" - -struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; -int nr_fake_mem; - -static int __init cmp_fake_mem(const void *x1, const void *x2) -{ - const struct efi_mem_range *m1 = x1; - const struct efi_mem_range *m2 = x2; - - if (m1->range.start < m2->range.start) - return -1; - if (m1->range.start > m2->range.start) - return 1; - return 0; -} - -static void __init efi_fake_range(struct efi_mem_range *efi_range) -{ - struct efi_memory_map_data data = { 0 }; - int new_nr_map = efi.memmap.nr_map; - efi_memory_desc_t *md; - void *new_memmap; - - /* count up the number of EFI memory descriptor */ - for_each_efi_memory_desc(md) - new_nr_map += efi_memmap_split_count(md, &efi_range->range); - - /* allocate memory for new EFI memmap */ - if (efi_memmap_alloc(new_nr_map, &data) != 0) - return; - - /* create new EFI memmap */ - new_memmap = early_memremap(data.phys_map, data.size); - if (!new_memmap) { - __efi_memmap_free(data.phys_map, data.size, data.flags); - return; - } - - efi_memmap_insert(&efi.memmap, new_memmap, efi_range); - - /* swap into new EFI memmap */ - early_memunmap(new_memmap, data.size); - - efi_memmap_install(&data); -} - -void __init efi_fake_memmap(void) -{ - int i; - - if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) - return; - - for (i = 0; i < nr_fake_mem; i++) - efi_fake_range(&efi_fake_mems[i]); - - /* print new EFI memmap */ - efi_print_memmap(); -} - -static int __init setup_fake_mem(char *p) -{ - u64 start = 0, mem_size = 0, attribute = 0; - int i; - - if (!p) - return -EINVAL; - - while (*p != '\0') { - mem_size = memparse(p, &p); - if (*p == '@') - start = memparse(p+1, &p); - else - break; - - if (*p == ':') - attribute = simple_strtoull(p+1, &p, 0); - else - break; - - if (nr_fake_mem >= EFI_MAX_FAKEMEM) - break; - - efi_fake_mems[nr_fake_mem].range.start = start; - efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1; - efi_fake_mems[nr_fake_mem].attribute = attribute; - nr_fake_mem++; - - if (*p == ',') - p++; - } - - sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), - cmp_fake_mem, NULL); - - for (i = 0; i < nr_fake_mem; i++) - pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", - efi_fake_mems[i].attribute, efi_fake_mems[i].range.start, - efi_fake_mems[i].range.end); - - return *p == '\0' ? 0 : -EINVAL; -} - -early_param("efi_fake_mem", setup_fake_mem); diff --git a/drivers/firmware/efi/fake_mem.h b/drivers/firmware/efi/fake_mem.h deleted file mode 100644 index d52791af4b18..000000000000 --- a/drivers/firmware/efi/fake_mem.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __EFI_FAKE_MEM_H__ -#define __EFI_FAKE_MEM_H__ -#include <asm/efi.h> - -#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM - -extern struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; -extern int nr_fake_mem; -#endif /* __EFI_FAKE_MEM_H__ */ diff --git a/drivers/firmware/efi/fdtparams.c b/drivers/firmware/efi/fdtparams.c index e901f8564ca0..0ec83ba58097 100644 --- a/drivers/firmware/efi/fdtparams.c +++ b/drivers/firmware/efi/fdtparams.c @@ -30,11 +30,13 @@ static __initconst const char name[][22] = { static __initconst const struct { const char path[17]; + u8 paravirt; const char params[PARAMCOUNT][26]; } dt_params[] = { { #ifdef CONFIG_XEN // <-------17------> .path = "/hypervisor/uefi", + .paravirt = 1, .params = { [SYSTAB] = "xen,uefi-system-table", [MMBASE] = "xen,uefi-mmap-start", @@ -121,6 +123,8 @@ u64 __init efi_get_fdt_params(struct efi_memory_map_data *mm) pr_err("Can't find property '%s' in DT!\n", pname); return 0; } + if (dt_params[i].paravirt) + set_bit(EFI_PARAVIRT, &efi.flags); return systab; } notfound: diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index b1601aad7e1a..be8b8c6e8b40 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -5,6 +5,10 @@ # things like ftrace and stack-protector are likely to cause trouble if left # enabled, even if doing so doesn't break the build. # + +# non-x86 reuses KBUILD_CFLAGS, x86 does not +cflags-y := $(KBUILD_CFLAGS) + cflags-$(CONFIG_X86_32) := -march=i386 cflags-$(CONFIG_X86_64) := -mcmodel=small cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ @@ -18,20 +22,20 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # disable the stackleak plugin -cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpie $(DISABLE_STACKLEAK_PLUGIN) \ +cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ + -fno-unwind-tables -fno-asynchronous-unwind-tables \ $(call cc-option,-mbranch-protection=none) -cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fno-builtin -fpic \ +cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ + -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ + -DEFI_HAVE_STRCMP -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) -cflags-$(CONFIG_RISCV) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpic -cflags-$(CONFIG_LOONGARCH) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpie +cflags-$(CONFIG_RISCV) += -fpic +cflags-$(CONFIG_LOONGARCH) += -fpie cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt -KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \ +KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \ + -Os -DDISABLE_BRANCH_PROFILING \ -include $(srctree)/include/linux/hidden.h \ -D__NO_FORTIFY \ -ffreestanding \ @@ -67,7 +71,7 @@ KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ skip_spaces.o lib-cmdline.o lib-ctype.o \ - alignedmem.o relocate.o vsprintf.o + alignedmem.o relocate.o printk.o vsprintf.o # include the stub's libfdt dependencies from lib/ when needed libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \ @@ -79,13 +83,14 @@ lib-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdt.o \ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o +lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ + screen_info.o efi-stub-entry.o lib-$(CONFIG_ARM) += arm32-stub.o -lib-$(CONFIG_ARM64) += arm64-stub.o +lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o arm64-entry.o smbios.o lib-$(CONFIG_X86) += x86-stub.o -lib-$(CONFIG_RISCV) += riscv-stub.o -lib-$(CONFIG_LOONGARCH) += loongarch-stub.o +lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o +lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) @@ -136,7 +141,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS # STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ -STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS +STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS64 # For RISC-V, we don't need anything special other than arm64. Keep all the # symbols in .init section and make sure that no absolute symbols references diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 35f234ad8738..43e9a4cab9f5 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -10,38 +10,25 @@ comp-type-$(CONFIG_KERNEL_LZO) := lzo comp-type-$(CONFIG_KERNEL_XZ) := xzkern comp-type-$(CONFIG_KERNEL_ZSTD) := zstd22 -# in GZIP, the appended le32 carrying the uncompressed size is part of the -# format, but in other cases, we just append it at the end for convenience, -# causing the original tools to complain when checking image integrity. -# So disregard it when calculating the payload size in the zimage header. -zboot-method-y := $(comp-type-y)_with_size -zboot-size-len-y := 4 - -zboot-method-$(CONFIG_KERNEL_GZIP) := gzip -zboot-size-len-$(CONFIG_KERNEL_GZIP) := 0 - -quiet_cmd_sbsign = SBSIGN $@ - cmd_sbsign = sbsign --out $@ $< \ - --key $(CONFIG_EFI_ZBOOT_SIGNING_KEY) \ - --cert $(CONFIG_EFI_ZBOOT_SIGNING_CERT) - -$(obj)/$(EFI_ZBOOT_PAYLOAD).signed: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE - $(call if_changed,sbsign) - -ZBOOT_PAYLOAD-y := $(EFI_ZBOOT_PAYLOAD) -ZBOOT_PAYLOAD-$(CONFIG_EFI_ZBOOT_SIGNED) := $(EFI_ZBOOT_PAYLOAD).signed - -$(obj)/vmlinuz: $(obj)/$(ZBOOT_PAYLOAD-y) FORCE - $(call if_changed,$(zboot-method-y)) +# Copy the SizeOfHeaders, SizeOfCode and SizeOfImage fields from the payload to +# the end of the compressed image. Note that this presupposes a PE header +# offset of 64 bytes, which is what arm64, RISC-V and LoongArch use. +quiet_cmd_compwithsize = $(quiet_cmd_$(comp-type-y)) + cmd_compwithsize = $(cmd_$(comp-type-y)) && ( \ + dd status=none if=$< bs=4 count=1 skip=37 ; \ + dd status=none if=$< bs=4 count=1 skip=23 ; \ + dd status=none if=$< bs=4 count=1 skip=36 ) >> $@ + +$(obj)/vmlinuz: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE + $(call if_changed,compwithsize) OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \ - --rename-section .data=.gzdata,load,alloc,readonly,contents + --rename-section .data=.gzdata,load,alloc,readonly,contents $(obj)/vmlinuz.o: $(obj)/vmlinuz FORCE $(call if_changed,objcopy) AFLAGS_zboot-header.o += -DMACHINE_TYPE=IMAGE_FILE_MACHINE_$(EFI_ZBOOT_MACH_TYPE) \ -DZBOOT_EFI_PATH="\"$(realpath $(obj)/vmlinuz.efi.elf)\"" \ - -DZBOOT_SIZE_LEN=$(zboot-size-len-y) \ -DCOMP_TYPE="\"$(comp-type-y)\"" $(obj)/zboot-header.o: $(srctree)/drivers/firmware/efi/libstub/zboot-header.S FORCE @@ -53,18 +40,8 @@ LDFLAGS_vmlinuz.efi.elf := -T $(srctree)/drivers/firmware/efi/libstub/zboot.lds $(obj)/vmlinuz.efi.elf: $(obj)/vmlinuz.o $(ZBOOT_DEPS) FORCE $(call if_changed,ld) -ZBOOT_EFI-y := vmlinuz.efi -ZBOOT_EFI-$(CONFIG_EFI_ZBOOT_SIGNED) := vmlinuz.efi.unsigned - -OBJCOPYFLAGS_$(ZBOOT_EFI-y) := -O binary -$(obj)/$(ZBOOT_EFI-y): $(obj)/vmlinuz.efi.elf FORCE +OBJCOPYFLAGS_vmlinuz.efi := -O binary +$(obj)/vmlinuz.efi: $(obj)/vmlinuz.efi.elf FORCE $(call if_changed,objcopy) -targets += zboot-header.o vmlinuz vmlinuz.o vmlinuz.efi.elf vmlinuz.efi - -ifneq ($(CONFIG_EFI_ZBOOT_SIGNED),) -$(obj)/vmlinuz.efi: $(obj)/vmlinuz.efi.unsigned FORCE - $(call if_changed,sbsign) -endif - -targets += $(EFI_ZBOOT_PAYLOAD).signed vmlinuz.efi.unsigned +targets += zboot-header.o vmlinuz.o vmlinuz.efi.elf vmlinuz.efi diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c index 1de9878ddd3a..6b83c492c3b8 100644 --- a/drivers/firmware/efi/libstub/alignedmem.c +++ b/drivers/firmware/efi/libstub/alignedmem.c @@ -22,12 +22,15 @@ * Return: status code */ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, - unsigned long max, unsigned long align) + unsigned long max, unsigned long align, + int memory_type) { efi_physical_addr_t alloc_addr; efi_status_t status; int slack; + max = min(max, EFI_ALLOC_LIMIT); + if (align < EFI_ALLOC_ALIGN) align = EFI_ALLOC_ALIGN; @@ -36,7 +39,7 @@ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, slack = align / EFI_PAGE_SIZE - 1; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, - EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, + memory_type, size / EFI_PAGE_SIZE + slack, &alloc_addr); if (status != EFI_SUCCESS) return status; diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index 0131e3aaa605..1073dd947516 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -76,43 +76,6 @@ void efi_handle_post_ebs_state(void) &efi_entry_state->sctlr_after_ebs); } -static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID; - -struct screen_info *alloc_screen_info(void) -{ - struct screen_info *si; - efi_status_t status; - - /* - * Unlike on arm64, where we can directly fill out the screen_info - * structure from the stub, we need to allocate a buffer to hold - * its contents while we hand over to the kernel proper from the - * decompressor. - */ - status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, - sizeof(*si), (void **)&si); - - if (status != EFI_SUCCESS) - return NULL; - - status = efi_bs_call(install_configuration_table, - &screen_info_guid, si); - if (status == EFI_SUCCESS) - return si; - - efi_bs_call(free_pool, si); - return NULL; -} - -void free_screen_info(struct screen_info *si) -{ - if (!si) - return; - - efi_bs_call(install_configuration_table, &screen_info_guid, NULL); - efi_bs_call(free_pool, si); -} - efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, diff --git a/drivers/firmware/efi/libstub/arm64-entry.S b/drivers/firmware/efi/libstub/arm64-entry.S new file mode 100644 index 000000000000..b5c17e89a4fc --- /dev/null +++ b/drivers/firmware/efi/libstub/arm64-entry.S @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * EFI entry point. + * + * Copyright (C) 2013, 2014 Red Hat, Inc. + * Author: Mark Salter <msalter@redhat.com> + */ +#include <linux/linkage.h> +#include <asm/assembler.h> + + /* + * The entrypoint of a arm64 bare metal image is at offset #0 of the + * image, so this is a reasonable default for primary_entry_offset. + * Only when the EFI stub is integrated into the core kernel, it is not + * guaranteed that the PE/COFF header has been copied to memory too, so + * in this case, primary_entry_offset should be overridden by the + * linker and point to primary_entry() directly. + */ + .weak primary_entry_offset + +SYM_CODE_START(efi_enter_kernel) + /* + * efi_pe_entry() will have copied the kernel image if necessary and we + * end up here with device tree address in x1 and the kernel entry + * point stored in x0. Save those values in registers which are + * callee preserved. + */ + ldr w2, =primary_entry_offset + add x19, x0, x2 // relocated Image entrypoint + + mov x0, x1 // DTB address + mov x1, xzr + mov x2, xzr + mov x3, xzr + + /* + * Clean the remainder of this routine to the PoC + * so that we can safely disable the MMU and caches. + */ + adr x4, 1f + dc civac, x4 + dsb sy + + /* Turn off Dcache and MMU */ + mrs x4, CurrentEL + cmp x4, #CurrentEL_EL2 + mrs x4, sctlr_el1 + b.ne 0f + mrs x4, sctlr_el2 +0: bic x4, x4, #SCTLR_ELx_M + bic x4, x4, #SCTLR_ELx_C + b.eq 1f + b 2f + + .balign 32 +1: pre_disable_mmu_workaround + msr sctlr_el2, x4 + isb + br x19 // jump to kernel entrypoint + +2: pre_disable_mmu_workaround + msr sctlr_el1, x4 + isb + br x19 // jump to kernel entrypoint + + .org 1b + 32 +SYM_CODE_END(efi_enter_kernel) diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 259e4b852d63..7327b98d8e3f 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -11,37 +11,9 @@ #include <asm/efi.h> #include <asm/memory.h> #include <asm/sections.h> -#include <asm/sysreg.h> #include "efistub.h" -efi_status_t check_platform_features(void) -{ - u64 tg; - - /* - * If we have 48 bits of VA space for TTBR0 mappings, we can map the - * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is - * unnecessary. - */ - if (VA_BITS_MIN >= 48) - efi_novamap = true; - - /* UEFI mandates support for 4 KB granularity, no need to check */ - if (IS_ENABLED(CONFIG_ARM64_4K_PAGES)) - return EFI_SUCCESS; - - tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf; - if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) { - if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) - efi_err("This 64 KB granular kernel is not supported by your CPU\n"); - else - efi_err("This 16 KB granular kernel is not supported by your CPU\n"); - return EFI_UNSUPPORTED; - } - return EFI_SUCCESS; -} - /* * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail * to provide space, and fail to zero it). Check for this condition by double @@ -88,16 +60,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_status_t status; unsigned long kernel_size, kernel_memsize = 0; u32 phys_seed = 0; - - /* - * Although relocatable kernels can fix up the misalignment with - * respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are - * subtly out of sync with those recorded in the vmlinux when kaslr is - * disabled but the image required relocation anyway. Therefore retain - * 2M alignment if KASLR was explicitly disabled, even if it was not - * going to be activated to begin with. - */ - u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN; + u64 min_kimg_align = efi_get_kimg_min_align(); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; @@ -139,7 +102,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, * locate the kernel at a randomized offset in physical memory. */ status = efi_random_alloc(*reserve_size, min_kimg_align, - reserve_addr, phys_seed); + reserve_addr, phys_seed, + EFI_LOADER_CODE); if (status != EFI_SUCCESS) efi_warn("efi_random_alloc() failed: 0x%lx\n", status); } else { @@ -149,18 +113,20 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, if (status != EFI_SUCCESS) { if (!check_image_region((u64)_text, kernel_memsize)) { efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n"); - } else if (IS_ALIGNED((u64)_text, min_kimg_align)) { + } else if (IS_ALIGNED((u64)_text, min_kimg_align) && + (u64)_end < EFI_ALLOC_LIMIT) { /* * Just execute from wherever we were loaded by the - * UEFI PE/COFF loader if the alignment is suitable. + * UEFI PE/COFF loader if the placement is suitable. */ *image_addr = (u64)_text; *reserve_size = 0; - return EFI_SUCCESS; + goto clean_image_to_poc; } status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, - ULONG_MAX, min_kimg_align); + ULONG_MAX, min_kimg_align, + EFI_LOADER_CODE); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); @@ -172,5 +138,13 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, *image_addr = *reserve_addr; memcpy((void *)*image_addr, _text, kernel_size); +clean_image_to_poc: + /* + * Clean the copied Image to the PoC, and ensure it is not shadowed by + * stale icache entries from before relocation. + */ + dcache_clean_poc(*image_addr, *image_addr + kernel_size); + asm("ic ialluis"); + return EFI_SUCCESS; } diff --git a/drivers/firmware/efi/libstub/arm64.c b/drivers/firmware/efi/libstub/arm64.c new file mode 100644 index 000000000000..ff2d18c42ee7 --- /dev/null +++ b/drivers/firmware/efi/libstub/arm64.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org> + * + * This file implements the EFI boot stub for the arm64 kernel. + * Adapted from ARM version by Mark Salter <msalter@redhat.com> + */ + + +#include <linux/efi.h> +#include <asm/efi.h> +#include <asm/memory.h> +#include <asm/sysreg.h> + +#include "efistub.h" + +static bool system_needs_vamap(void) +{ + const u8 *type1_family = efi_get_smbios_string(1, family); + + /* + * Ampere Altra machines crash in SetTime() if SetVirtualAddressMap() + * has not been called prior. + */ + if (!type1_family || strcmp(type1_family, "Altra")) + return false; + + efi_warn("Working around broken SetVirtualAddressMap()\n"); + return true; +} + +efi_status_t check_platform_features(void) +{ + u64 tg; + + /* + * If we have 48 bits of VA space for TTBR0 mappings, we can map the + * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is + * unnecessary. + */ + if (VA_BITS_MIN >= 48 && !system_needs_vamap()) + efi_novamap = true; + + /* UEFI mandates support for 4 KB granularity, no need to check */ + if (IS_ENABLED(CONFIG_ARM64_4K_PAGES)) + return EFI_SUCCESS; + + tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf; + if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) { + if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) + efi_err("This 64 KB granular kernel is not supported by your CPU\n"); + else + efi_err("This 16 KB granular kernel is not supported by your CPU\n"); + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} + +void efi_cache_sync_image(unsigned long image_base, + unsigned long alloc_size, + unsigned long code_size) +{ + u32 ctr = read_cpuid_effective_cachetype(); + u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr, + CTR_EL0_DminLine_SHIFT); + + do { + asm("dc civac, %0" :: "r"(image_base)); + image_base += lsize; + alloc_size -= lsize; + } while (alloc_size >= lsize); + + asm("ic ialluis"); + dsb(ish); + isb(); +} diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c new file mode 100644 index 000000000000..5245c4f031c0 --- /dev/null +++ b/drivers/firmware/efi/libstub/efi-stub-entry.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/efi.h> +#include <asm/efi.h> + +#include "efistub.h" + +/* + * EFI entry point for the generic EFI stub used by ARM, arm64, RISC-V and + * LoongArch. This is the entrypoint that is described in the PE/COFF header + * of the core kernel. + */ +efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, + efi_system_table_t *systab) +{ + efi_loaded_image_t *image; + efi_status_t status; + unsigned long image_addr; + unsigned long image_size = 0; + /* addr/point and size pairs for memory management*/ + char *cmdline_ptr = NULL; + efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; + unsigned long reserve_addr = 0; + unsigned long reserve_size = 0; + + WRITE_ONCE(efi_system_table, systab); + + /* Check if we were booted by the EFI firmware */ + if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + return EFI_INVALID_PARAMETER; + + /* + * Get a handle to the loaded image protocol. This is used to get + * information about the running image, such as size and the command + * line. + */ + status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, + (void *)&image); + if (status != EFI_SUCCESS) { + efi_err("Failed to get loaded image protocol\n"); + return status; + } + + status = efi_handle_cmdline(image, &cmdline_ptr); + if (status != EFI_SUCCESS) + return status; + + efi_info("Booting Linux Kernel...\n"); + + status = handle_kernel_image(&image_addr, &image_size, + &reserve_addr, + &reserve_size, + image, handle); + if (status != EFI_SUCCESS) { + efi_err("Failed to relocate kernel\n"); + return status; + } + + status = efi_stub_common(handle, image, image_addr, cmdline_ptr); + + efi_free(image_size, image_addr); + efi_free(reserve_size, reserve_addr); + + return status; +} diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 0c493521b25b..f5a4bdacac64 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -9,10 +9,8 @@ #include <linux/stdarg.h> -#include <linux/ctype.h> #include <linux/efi.h> #include <linux/kernel.h> -#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */ #include <asm/efi.h> #include <asm/setup.h> @@ -20,7 +18,6 @@ bool efi_nochunk; bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); -int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; bool efi_novamap; static bool efi_noinitrd; @@ -33,146 +30,6 @@ bool __pure __efi_soft_reserve_enabled(void) } /** - * efi_char16_puts() - Write a UCS-2 encoded string to the console - * @str: UCS-2 encoded string - */ -void efi_char16_puts(efi_char16_t *str) -{ - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, str); -} - -static -u32 utf8_to_utf32(const u8 **s8) -{ - u32 c32; - u8 c0, cx; - size_t clen, i; - - c0 = cx = *(*s8)++; - /* - * The position of the most-significant 0 bit gives us the length of - * a multi-octet encoding. - */ - for (clen = 0; cx & 0x80; ++clen) - cx <<= 1; - /* - * If the 0 bit is in position 8, this is a valid single-octet - * encoding. If the 0 bit is in position 7 or positions 1-3, the - * encoding is invalid. - * In either case, we just return the first octet. - */ - if (clen < 2 || clen > 4) - return c0; - /* Get the bits from the first octet. */ - c32 = cx >> clen--; - for (i = 0; i < clen; ++i) { - /* Trailing octets must have 10 in most significant bits. */ - cx = (*s8)[i] ^ 0x80; - if (cx & 0xc0) - return c0; - c32 = (c32 << 6) | cx; - } - /* - * Check for validity: - * - The character must be in the Unicode range. - * - It must not be a surrogate. - * - It must be encoded using the correct number of octets. - */ - if (c32 > 0x10ffff || - (c32 & 0xf800) == 0xd800 || - clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) - return c0; - *s8 += clen; - return c32; -} - -/** - * efi_puts() - Write a UTF-8 encoded string to the console - * @str: UTF-8 encoded string - */ -void efi_puts(const char *str) -{ - efi_char16_t buf[128]; - size_t pos = 0, lim = ARRAY_SIZE(buf); - const u8 *s8 = (const u8 *)str; - u32 c32; - - while (*s8) { - if (*s8 == '\n') - buf[pos++] = L'\r'; - c32 = utf8_to_utf32(&s8); - if (c32 < 0x10000) { - /* Characters in plane 0 use a single word. */ - buf[pos++] = c32; - } else { - /* - * Characters in other planes encode into a surrogate - * pair. - */ - buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); - buf[pos++] = 0xdc00 + (c32 & 0x3ff); - } - if (*s8 == '\0' || pos >= lim - 2) { - buf[pos] = L'\0'; - efi_char16_puts(buf); - pos = 0; - } - } -} - -/** - * efi_printk() - Print a kernel message - * @fmt: format string - * - * The first letter of the format string is used to determine the logging level - * of the message. If the level is less then the current EFI logging level, the - * message is suppressed. The message will be truncated to 255 bytes. - * - * Return: number of printed characters - */ -int efi_printk(const char *fmt, ...) -{ - char printf_buf[256]; - va_list args; - int printed; - int loglevel = printk_get_level(fmt); - - switch (loglevel) { - case '0' ... '9': - loglevel -= '0'; - break; - default: - /* - * Use loglevel -1 for cases where we just want to print to - * the screen. - */ - loglevel = -1; - break; - } - - if (loglevel >= efi_loglevel) - return 0; - - if (loglevel >= 0) - efi_puts("EFI stub: "); - - fmt = printk_skip_level(fmt); - - va_start(args, fmt); - printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); - va_end(args); - - efi_puts(printf_buf); - if (printed >= sizeof(printf_buf)) { - efi_puts("[Message truncated]\n"); - return -1; - } - - return printed; -} - -/** * efi_parse_options() - Parse EFI command line options * @cmdline: kernel command line * @@ -626,8 +483,8 @@ static const struct { /** * efi_load_initrd_dev_path() - load the initrd from the Linux initrd device path - * @load_addr: pointer to store the address where the initrd was loaded - * @load_size: pointer to store the size of the loaded initrd + * @initrd: pointer of struct to store the address where the initrd was loaded + * and the size of the loaded initrd * @max: upper limit for the initrd memory allocation * * Return: @@ -681,8 +538,7 @@ efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, unsigned long soft_limit, unsigned long hard_limit) { - if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) || - (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) + if (image == NULL) return EFI_UNSUPPORTED; return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index cf474f0dd261..2955c1ac6a36 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -35,15 +35,6 @@ * as well to minimize the code churn. */ #define EFI_RT_VIRTUAL_BASE SZ_512M -#define EFI_RT_VIRTUAL_SIZE SZ_512M - -#ifdef CONFIG_ARM64 -# define EFI_RT_VIRTUAL_LIMIT DEFAULT_MAP_WINDOW_64 -#elif defined(CONFIG_RISCV) || defined(CONFIG_LOONGARCH) -# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE_MIN -#else /* Only if TASK_SIZE is a constant */ -# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE -#endif /* * Some architectures map the EFI regions into the kernel's linear map using a @@ -56,6 +47,15 @@ static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0); +struct screen_info * __weak alloc_screen_info(void) +{ + return &screen_info; +} + +void __weak free_screen_info(struct screen_info *si) +{ +} + static struct screen_info *setup_graphics(void) { efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; @@ -115,62 +115,21 @@ static u32 get_supported_rt_services(void) return supported; } -/* - * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint - * that is described in the PE/COFF header. Most of the code is the same - * for both archictectures, with the arch-specific code provided in the - * handle_kernel_image() function. - */ -efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, - efi_system_table_t *sys_table_arg) +efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr) { - efi_loaded_image_t *image; - efi_status_t status; - unsigned long image_addr; - unsigned long image_size = 0; - /* addr/point and size pairs for memory management*/ - char *cmdline_ptr = NULL; int cmdline_size = 0; - efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; - unsigned long reserve_addr = 0; - unsigned long reserve_size = 0; - struct screen_info *si; - efi_properties_table_t *prop_tbl; - - efi_system_table = sys_table_arg; - - /* Check if we were booted by the EFI firmware */ - if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - status = EFI_INVALID_PARAMETER; - goto fail; - } - - status = check_platform_features(); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Get a handle to the loaded image protocol. This is used to get - * information about the running image, such as size and the command - * line. - */ - status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, - (void *)&image); - if (status != EFI_SUCCESS) { - efi_err("Failed to get loaded image protocol\n"); - goto fail; - } + efi_status_t status; + char *cmdline; /* * Get the command line from EFI, using the LOADED_IMAGE * protocol. We are going to copy the command line into the * device tree, so this can be allocated anywhere. */ - cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); - if (!cmdline_ptr) { + cmdline = efi_convert_cmdline(image, &cmdline_size); + if (!cmdline) { efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); - status = EFI_OUT_OF_RESOURCES; - goto fail; + return EFI_OUT_OF_RESOURCES; } if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || @@ -184,25 +143,34 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, } if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) { - status = efi_parse_options(cmdline_ptr); + status = efi_parse_options(cmdline); if (status != EFI_SUCCESS) { efi_err("Failed to parse options\n"); goto fail_free_cmdline; } } - efi_info("Booting Linux Kernel...\n"); + *cmdline_ptr = cmdline; + return EFI_SUCCESS; - si = setup_graphics(); +fail_free_cmdline: + efi_bs_call(free_pool, cmdline_ptr); + return status; +} - status = handle_kernel_image(&image_addr, &image_size, - &reserve_addr, - &reserve_size, - image, handle); - if (status != EFI_SUCCESS) { - efi_err("Failed to relocate kernel\n"); - goto fail_free_screeninfo; - } +efi_status_t efi_stub_common(efi_handle_t handle, + efi_loaded_image_t *image, + unsigned long image_addr, + char *cmdline_ptr) +{ + struct screen_info *si; + efi_status_t status; + + status = check_platform_features(); + if (status != EFI_SUCCESS) + return status; + + si = setup_graphics(); efi_retrieve_tpm2_eventlog(); @@ -214,53 +182,15 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_random_get_seed(); - /* - * If the NX PE data feature is enabled in the properties table, we - * should take care not to create a virtual mapping that changes the - * relative placement of runtime services code and data regions, as - * they may belong to the same PE/COFF executable image in memory. - * The easiest way to achieve that is to simply use a 1:1 mapping. - */ - prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID); - flat_va_mapping |= prop_tbl && - (prop_tbl->memory_protection_attribute & - EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); - /* force efi_novamap if SetVirtualAddressMap() is unsupported */ efi_novamap |= !(get_supported_rt_services() & EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP); - /* hibernation expects the runtime regions to stay in the same place */ - if (!IS_ENABLED(CONFIG_HIBERNATION) && !efi_nokaslr && !flat_va_mapping) { - /* - * Randomize the base of the UEFI runtime services region. - * Preserve the 2 MB alignment of the region by taking a - * shift of 21 bit positions into account when scaling - * the headroom value using a 32-bit random value. - */ - static const u64 headroom = EFI_RT_VIRTUAL_LIMIT - - EFI_RT_VIRTUAL_BASE - - EFI_RT_VIRTUAL_SIZE; - u32 rnd; - - status = efi_get_random_bytes(sizeof(rnd), (u8 *)&rnd); - if (status == EFI_SUCCESS) { - virtmap_base = EFI_RT_VIRTUAL_BASE + - (((headroom >> 21) * rnd) >> (32 - 21)); - } - } - install_memreserve_table(); status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr); - efi_free(image_size, image_addr); - efi_free(reserve_size, reserve_addr); -fail_free_screeninfo: free_screen_info(si); -fail_free_cmdline: - efi_bs_call(free_pool, cmdline_ptr); -fail: return status; } diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index a30fb5d8ef05..5b8f2c411ed8 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -29,6 +29,10 @@ #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #endif +#ifndef EFI_ALLOC_LIMIT +#define EFI_ALLOC_LIMIT ULONG_MAX +#endif + extern bool efi_nochunk; extern bool efi_nokaslr; extern int efi_loglevel; @@ -44,15 +48,23 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, #ifndef ARCH_HAS_EFISTUB_WRAPPERS -#define efi_is_native() (true) -#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__) -#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__) -#define efi_dxe_call(func, ...) efi_dxe_table->func(__VA_ARGS__) -#define efi_table_attr(inst, attr) (inst->attr) -#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) +#define efi_is_native() (true) +#define efi_table_attr(inst, attr) (inst)->attr +#define efi_fn_call(inst, func, ...) (inst)->func(__VA_ARGS__) #endif +#define efi_call_proto(inst, func, ...) ({ \ + __typeof__(inst) __inst = (inst); \ + efi_fn_call(__inst, func, __inst, ##__VA_ARGS__); \ +}) +#define efi_bs_call(func, ...) \ + efi_fn_call(efi_table_attr(efi_system_table, boottime), func, ##__VA_ARGS__) +#define efi_rt_call(func, ...) \ + efi_fn_call(efi_table_attr(efi_system_table, runtime), func, ##__VA_ARGS__) +#define efi_dxe_call(func, ...) \ + efi_fn_call(efi_dxe_table, func, ##__VA_ARGS__) + #define efi_info(fmt, ...) \ efi_printk(KERN_INFO fmt, ##__VA_ARGS__) #define efi_warn(fmt, ...) \ @@ -179,6 +191,21 @@ union efi_device_path_to_text_protocol { typedef union efi_device_path_to_text_protocol efi_device_path_to_text_protocol_t; +union efi_device_path_from_text_protocol { + struct { + efi_device_path_protocol_t * + (__efiapi *convert_text_to_device_node)(const efi_char16_t *); + efi_device_path_protocol_t * + (__efiapi *convert_text_to_device_path)(const efi_char16_t *); + }; + struct { + u32 convert_text_to_device_node; + u32 convert_text_to_device_path; + } mixed_mode; +}; + +typedef union efi_device_path_from_text_protocol efi_device_path_from_text_protocol_t; + typedef void *efi_event_t; /* Note that notifications won't work in mixed mode */ typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *); @@ -572,36 +599,63 @@ typedef struct { efi_char16_t filename[]; } efi_file_info_t; -typedef struct efi_file_protocol efi_file_protocol_t; - -struct efi_file_protocol { - u64 revision; - efi_status_t (__efiapi *open) (efi_file_protocol_t *, - efi_file_protocol_t **, - efi_char16_t *, u64, u64); - efi_status_t (__efiapi *close) (efi_file_protocol_t *); - efi_status_t (__efiapi *delete) (efi_file_protocol_t *); - efi_status_t (__efiapi *read) (efi_file_protocol_t *, - unsigned long *, void *); - efi_status_t (__efiapi *write) (efi_file_protocol_t *, - unsigned long, void *); - efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, u64 *); - efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, u64); - efi_status_t (__efiapi *get_info) (efi_file_protocol_t *, - efi_guid_t *, unsigned long *, - void *); - efi_status_t (__efiapi *set_info) (efi_file_protocol_t *, - efi_guid_t *, unsigned long, - void *); - efi_status_t (__efiapi *flush) (efi_file_protocol_t *); +typedef union efi_file_protocol efi_file_protocol_t; + +union efi_file_protocol { + struct { + u64 revision; + efi_status_t (__efiapi *open) (efi_file_protocol_t *, + efi_file_protocol_t **, + efi_char16_t *, u64, + u64); + efi_status_t (__efiapi *close) (efi_file_protocol_t *); + efi_status_t (__efiapi *delete) (efi_file_protocol_t *); + efi_status_t (__efiapi *read) (efi_file_protocol_t *, + unsigned long *, + void *); + efi_status_t (__efiapi *write) (efi_file_protocol_t *, + unsigned long, void *); + efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, + u64 *); + efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, + u64); + efi_status_t (__efiapi *get_info) (efi_file_protocol_t *, + efi_guid_t *, + unsigned long *, + void *); + efi_status_t (__efiapi *set_info) (efi_file_protocol_t *, + efi_guid_t *, + unsigned long, + void *); + efi_status_t (__efiapi *flush) (efi_file_protocol_t *); + }; + struct { + u64 revision; + u32 open; + u32 close; + u32 delete; + u32 read; + u32 write; + u32 get_position; + u32 set_position; + u32 get_info; + u32 set_info; + u32 flush; + } mixed_mode; }; -typedef struct efi_simple_file_system_protocol efi_simple_file_system_protocol_t; +typedef union efi_simple_file_system_protocol efi_simple_file_system_protocol_t; -struct efi_simple_file_system_protocol { - u64 revision; - int (__efiapi *open_volume)(efi_simple_file_system_protocol_t *, - efi_file_protocol_t **); +union efi_simple_file_system_protocol { + struct { + u64 revision; + efi_status_t (__efiapi *open_volume)(efi_simple_file_system_protocol_t *, + efi_file_protocol_t **); + }; + struct { + u64 revision; + u32 open_volume; + } mixed_mode; }; #define EFI_FILE_MODE_READ 0x0000000000000001 @@ -880,7 +934,10 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); efi_status_t efi_random_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long random_seed); + unsigned long *addr, unsigned long random_seed, + int memory_type); + +efi_status_t efi_random_get_seed(void); efi_status_t check_platform_features(void); @@ -905,7 +962,8 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, unsigned long max); efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, - unsigned long max, unsigned long align); + unsigned long max, unsigned long align, + int memory_type); efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, unsigned long *addr, unsigned long min); @@ -958,6 +1016,14 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_loaded_image_t *image, efi_handle_t image_handle); +/* shared entrypoint between the normal stub and the zboot stub */ +efi_status_t efi_stub_common(efi_handle_t handle, + efi_loaded_image_t *image, + unsigned long image_addr, + char *cmdline_ptr); + +efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr); + asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt_addr, unsigned long fdt_size); @@ -975,4 +1041,39 @@ efi_enable_reset_attack_mitigation(void) { } void efi_retrieve_tpm2_eventlog(void); +struct screen_info *alloc_screen_info(void); +void free_screen_info(struct screen_info *si); + +void efi_cache_sync_image(unsigned long image_base, + unsigned long alloc_size, + unsigned long code_size); + +struct efi_smbios_record { + u8 type; + u8 length; + u16 handle; +}; + +struct efi_smbios_type1_record { + struct efi_smbios_record header; + + u8 manufacturer; + u8 product_name; + u8 version; + u8 serial_number; + efi_guid_t uuid; + u8 wakeup_type; + u8 sku_number; + u8 family; +}; + +#define efi_get_smbios_string(__type, __name) ({ \ + int size = sizeof(struct efi_smbios_type ## __type ## _record); \ + int off = offsetof(struct efi_smbios_type ## __type ## _record, \ + __name); \ + __efi_get_smbios_string(__type, off, size); \ +}) + +const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize); + #endif diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 4f4d98e51fbf..70e9789ff9de 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -313,16 +313,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, /* * Set the virtual address field of all - * EFI_MEMORY_RUNTIME entries to 0. This will signal - * the incoming kernel that no virtual translation has - * been installed. + * EFI_MEMORY_RUNTIME entries to U64_MAX. This will + * signal the incoming kernel that no virtual + * translation has been installed. */ for (l = 0; l < priv.boot_memmap->map_size; l += priv.boot_memmap->desc_size) { p = (void *)priv.boot_memmap->map + l; if (p->attribute & EFI_MEMORY_RUNTIME) - p->virt_addr = 0; + p->virt_addr = U64_MAX; } } return EFI_SUCCESS; diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index f756c61396e9..d6a025df07dc 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -43,18 +43,26 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, efi_file_protocol_t *fh; unsigned long info_sz; efi_status_t status; + efi_char16_t *c; - status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); + /* Replace UNIX dir separators with EFI standard ones */ + for (c = fi->filename; *c != L'\0'; c++) { + if (*c == L'/') + *c = L'\\'; + } + + status = efi_call_proto(volume, open, &fh, fi->filename, + EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { efi_err("Failed to open file: %ls\n", fi->filename); return status; } info_sz = sizeof(struct finfo); - status = fh->get_info(fh, &info_guid, &info_sz, fi); + status = efi_call_proto(fh, get_info, &info_guid, &info_sz, fi); if (status != EFI_SUCCESS) { efi_err("Failed to get file info\n"); - fh->close(fh); + efi_call_proto(fh, close); return status; } @@ -66,36 +74,18 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, static efi_status_t efi_open_volume(efi_loaded_image_t *image, efi_file_protocol_t **fh) { - struct efi_vendor_dev_path *dp = image->file_path; - efi_guid_t li_proto = LOADED_IMAGE_PROTOCOL_GUID; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; efi_simple_file_system_protocol_t *io; efi_status_t status; - // If we are using EFI zboot, we should look for the file system - // protocol on the parent image's handle instead - if (IS_ENABLED(CONFIG_EFI_ZBOOT) && - image->parent_handle != NULL && - dp != NULL && - dp->header.type == EFI_DEV_MEDIA && - dp->header.sub_type == EFI_DEV_MEDIA_VENDOR && - !efi_guidcmp(dp->vendorguid, LINUX_EFI_ZBOOT_MEDIA_GUID)) { - status = efi_bs_call(handle_protocol, image->parent_handle, - &li_proto, (void *)&image); - if (status != EFI_SUCCESS) { - efi_err("Failed to locate parent image handle\n"); - return status; - } - } - - status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto, - (void **)&io); + status = efi_bs_call(handle_protocol, efi_table_attr(image, device_handle), + &fs_proto, (void **)&io); if (status != EFI_SUCCESS) { efi_err("Failed to handle fs_proto\n"); return status; } - status = io->open_volume(io, fh); + status = efi_call_proto(io, open_volume, fh); if (status != EFI_SUCCESS) efi_err("Failed to open volume\n"); @@ -129,16 +119,62 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, if (c == L'\0' || c == L'\n' || c == L' ') break; - else if (c == L'/') - /* Replace UNIX dir separators with EFI standard ones */ - *result++ = L'\\'; - else - *result++ = c; + *result++ = c; } *result = L'\0'; return i; } +static efi_status_t efi_open_device_path(efi_file_protocol_t **volume, + struct finfo *fi) +{ + efi_guid_t text_to_dp_guid = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; + static efi_device_path_from_text_protocol_t *text_to_dp = NULL; + efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + efi_device_path_protocol_t *initrd_dp; + efi_simple_file_system_protocol_t *io; + struct efi_file_path_dev_path *fpath; + efi_handle_t handle; + efi_status_t status; + + /* See if the text to device path protocol exists */ + if (!text_to_dp && + efi_bs_call(locate_protocol, &text_to_dp_guid, NULL, + (void **)&text_to_dp) != EFI_SUCCESS) + return EFI_UNSUPPORTED; + + + /* Convert the filename wide string into a device path */ + initrd_dp = efi_fn_call(text_to_dp, convert_text_to_device_path, + fi->filename); + + /* Check whether the device path in question implements simple FS */ + if ((efi_bs_call(locate_device_path, &fs_proto, &initrd_dp, &handle) ?: + efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io)) + != EFI_SUCCESS) + return EFI_NOT_FOUND; + + /* Check whether the remaining device path is a file device path */ + if (initrd_dp->type != EFI_DEV_MEDIA || + initrd_dp->sub_type != EFI_DEV_MEDIA_FILE) { + efi_warn("Unexpected device path node type: (%x, %x)\n", + initrd_dp->type, initrd_dp->sub_type); + return EFI_LOAD_ERROR; + } + + /* Copy the remaining file path into the fi structure */ + fpath = (struct efi_file_path_dev_path *)initrd_dp; + memcpy(fi->filename, fpath->filename, + min(sizeof(fi->filename), + fpath->header.length - sizeof(fpath->header))); + + status = efi_call_proto(io, open_volume, volume); + if (status != EFI_SUCCESS) + efi_err("Failed to open volume\n"); + + return status; +} + /* * Check the cmdline for a LILO-style file= arguments. * @@ -153,8 +189,8 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, unsigned long *load_addr, unsigned long *load_size) { - const efi_char16_t *cmdline = image->load_options; - u32 cmdline_len = image->load_options_size; + const efi_char16_t *cmdline = efi_table_attr(image, load_options); + u32 cmdline_len = efi_table_attr(image, load_options_size); unsigned long efi_chunk_size = ULONG_MAX; efi_file_protocol_t *volume = NULL; efi_file_protocol_t *file; @@ -188,11 +224,13 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, cmdline += offset; cmdline_len -= offset; - if (!volume) { + status = efi_open_device_path(&volume, &fi); + if (status == EFI_UNSUPPORTED || status == EFI_NOT_FOUND) + /* try the volume that holds the kernel itself */ status = efi_open_volume(image, &volume); - if (status != EFI_SUCCESS) - return status; - } + + if (status != EFI_SUCCESS) + goto err_free_alloc; status = efi_open_file(volume, &fi, &file, &size); if (status != EFI_SUCCESS) @@ -240,7 +278,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, while (size) { unsigned long chunksize = min(size, efi_chunk_size); - status = file->read(file, &chunksize, addr); + status = efi_call_proto(file, read, &chunksize, addr); if (status != EFI_SUCCESS) { efi_err("Failed to read file\n"); goto err_close_file; @@ -248,24 +286,24 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, addr += chunksize; size -= chunksize; } - file->close(file); + efi_call_proto(file, close); + efi_call_proto(volume, close); } while (offset > 0); *load_addr = alloc_addr; *load_size = alloc_size; - if (volume) - volume->close(volume); - if (*load_size == 0) return EFI_NOT_READY; return EFI_SUCCESS; err_close_file: - file->close(file); + efi_call_proto(file, close); err_close_volume: - volume->close(volume); + efi_call_proto(volume, close); + +err_free_alloc: efi_free(alloc_size, alloc_addr); return status; } diff --git a/drivers/firmware/efi/libstub/intrinsics.c b/drivers/firmware/efi/libstub/intrinsics.c index a04ab39292b6..965e734f6f98 100644 --- a/drivers/firmware/efi/libstub/intrinsics.c +++ b/drivers/firmware/efi/libstub/intrinsics.c @@ -28,3 +28,21 @@ void *memset(void *dst, int c, size_t len) efi_bs_call(set_mem, dst, len, c & U8_MAX); return dst; } + +/** + * memcmp - Compare two areas of memory + * @cs: One area of memory + * @ct: Another area of memory + * @count: The size of the area. + */ +#undef memcmp +int memcmp(const void *cs, const void *ct, size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c index 32329f2a92f9..eee7ed43cdfb 100644 --- a/drivers/firmware/efi/libstub/loongarch-stub.c +++ b/drivers/firmware/efi/libstub/loongarch-stub.c @@ -9,18 +9,10 @@ #include <asm/addrspace.h> #include "efistub.h" -typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline, - unsigned long systab); - extern int kernel_asize; extern int kernel_fsize; extern int kernel_offset; -extern kernel_entry_t kernel_entry; - -efi_status_t check_platform_features(void) -{ - return EFI_SUCCESS; -} +extern int kernel_entry; efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, @@ -29,74 +21,33 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_loaded_image_t *image, efi_handle_t image_handle) { + int nr_pages = round_up(kernel_asize, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; + efi_physical_addr_t kernel_addr = EFI_KIMG_PREFERRED_ADDRESS; efi_status_t status; - unsigned long kernel_addr = 0; - - kernel_addr = (unsigned long)&kernel_offset - kernel_offset; - - status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize, - PHYSADDR(VMLINUX_LOAD_ADDRESS), SZ_2M, 0x0); - - *image_addr = kernel_addr; - *image_size = kernel_asize; - - return status; -} - -struct exit_boot_struct { - efi_memory_desc_t *runtime_map; - int runtime_entry_count; -}; - -static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) -{ - struct exit_boot_struct *p = priv; /* - * Update the memory map with virtual addresses. The function will also - * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME - * entries so that we can pass it straight to SetVirtualAddressMap() + * Allocate space for the kernel image at the preferred offset. This is + * the only location in memory from where we can execute the image, so + * no point in falling back to another allocation. */ - efi_get_virtmap(map->map, map->map_size, map->desc_size, - p->runtime_map, &p->runtime_entry_count); - - return EFI_SUCCESS; -} - -efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, - unsigned long kernel_addr, char *cmdline_ptr) -{ - kernel_entry_t real_kernel_entry; - struct exit_boot_struct priv; - unsigned long desc_size; - efi_status_t status; - u32 desc_ver; - - status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); - if (status != EFI_SUCCESS) { - efi_err("Unable to retrieve UEFI memory map.\n"); - return status; - } - - efi_info("Exiting boot services\n"); - - efi_novamap = false; - status = efi_exit_boot_services(handle, &priv, exit_boot_func); + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &kernel_addr); if (status != EFI_SUCCESS) return status; - /* Install the new virtual address map */ - efi_rt_call(set_virtual_address_map, - priv.runtime_entry_count * desc_size, desc_size, - desc_ver, priv.runtime_map); + *image_addr = EFI_KIMG_PREFERRED_ADDRESS; + *image_size = kernel_asize; - /* Config Direct Mapping */ - csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); - csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); + memcpy((void *)EFI_KIMG_PREFERRED_ADDRESS, + (void *)&kernel_offset - kernel_offset, + kernel_fsize); - real_kernel_entry = (kernel_entry_t) - ((unsigned long)&kernel_entry - kernel_addr + VMLINUX_LOAD_ADDRESS); + return status; +} + +unsigned long kernel_entry_address(void) +{ + unsigned long base = (unsigned long)&kernel_offset - kernel_offset; - real_kernel_entry(true, (unsigned long)cmdline_ptr, - (unsigned long)efi_system_table); + return (unsigned long)&kernel_entry - base + VMLINUX_LOAD_ADDRESS; } diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c new file mode 100644 index 000000000000..807cba2693fc --- /dev/null +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Yun Liu <liuyun@loongson.cn> + * Huacai Chen <chenhuacai@loongson.cn> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include <asm/efi.h> +#include <asm/addrspace.h> +#include "efistub.h" + +typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline, + unsigned long systab); + +efi_status_t check_platform_features(void) +{ + return EFI_SUCCESS; +} + +struct exit_boot_struct { + efi_memory_desc_t *runtime_map; + int runtime_entry_count; +}; + +static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) +{ + struct exit_boot_struct *p = priv; + + /* + * Update the memory map with virtual addresses. The function will also + * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME + * entries so that we can pass it straight to SetVirtualAddressMap() + */ + efi_get_virtmap(map->map, map->map_size, map->desc_size, + p->runtime_map, &p->runtime_entry_count); + + return EFI_SUCCESS; +} + +unsigned long __weak kernel_entry_address(void) +{ + return *(unsigned long *)(PHYSADDR(VMLINUX_LOAD_ADDRESS) + 8); +} + +efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, + unsigned long kernel_addr, char *cmdline_ptr) +{ + kernel_entry_t real_kernel_entry; + struct exit_boot_struct priv; + unsigned long desc_size; + efi_status_t status; + u32 desc_ver; + + status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); + if (status != EFI_SUCCESS) { + efi_err("Unable to retrieve UEFI memory map.\n"); + return status; + } + + efi_info("Exiting boot services\n"); + + efi_novamap = false; + status = efi_exit_boot_services(handle, &priv, exit_boot_func); + if (status != EFI_SUCCESS) + return status; + + /* Install the new virtual address map */ + efi_rt_call(set_virtual_address_map, + priv.runtime_entry_count * desc_size, desc_size, + desc_ver, priv.runtime_map); + + /* Config Direct Mapping */ + csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); + csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); + + real_kernel_entry = (void *)kernel_entry_address(); + + real_kernel_entry(true, (unsigned long)cmdline_ptr, + (unsigned long)efi_system_table); +} diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 45841ef55a9f..4f1fa302234d 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -89,9 +89,12 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, efi_physical_addr_t alloc_addr; efi_status_t status; + max = min(max, EFI_ALLOC_LIMIT); + if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE) return efi_allocate_pages_aligned(size, addr, max, - EFI_ALLOC_ALIGN); + EFI_ALLOC_ALIGN, + EFI_LOADER_DATA); alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, diff --git a/drivers/firmware/efi/libstub/printk.c b/drivers/firmware/efi/libstub/printk.c new file mode 100644 index 000000000000..3a67a2cea7bd --- /dev/null +++ b/drivers/firmware/efi/libstub/printk.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/stdarg.h> + +#include <linux/ctype.h> +#include <linux/efi.h> +#include <linux/kernel.h> +#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */ +#include <asm/efi.h> +#include <asm/setup.h> + +#include "efistub.h" + +int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; + +/** + * efi_char16_puts() - Write a UCS-2 encoded string to the console + * @str: UCS-2 encoded string + */ +void efi_char16_puts(efi_char16_t *str) +{ + efi_call_proto(efi_table_attr(efi_system_table, con_out), + output_string, str); +} + +static +u32 utf8_to_utf32(const u8 **s8) +{ + u32 c32; + u8 c0, cx; + size_t clen, i; + + c0 = cx = *(*s8)++; + /* + * The position of the most-significant 0 bit gives us the length of + * a multi-octet encoding. + */ + for (clen = 0; cx & 0x80; ++clen) + cx <<= 1; + /* + * If the 0 bit is in position 8, this is a valid single-octet + * encoding. If the 0 bit is in position 7 or positions 1-3, the + * encoding is invalid. + * In either case, we just return the first octet. + */ + if (clen < 2 || clen > 4) + return c0; + /* Get the bits from the first octet. */ + c32 = cx >> clen--; + for (i = 0; i < clen; ++i) { + /* Trailing octets must have 10 in most significant bits. */ + cx = (*s8)[i] ^ 0x80; + if (cx & 0xc0) + return c0; + c32 = (c32 << 6) | cx; + } + /* + * Check for validity: + * - The character must be in the Unicode range. + * - It must not be a surrogate. + * - It must be encoded using the correct number of octets. + */ + if (c32 > 0x10ffff || + (c32 & 0xf800) == 0xd800 || + clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) + return c0; + *s8 += clen; + return c32; +} + +/** + * efi_puts() - Write a UTF-8 encoded string to the console + * @str: UTF-8 encoded string + */ +void efi_puts(const char *str) +{ + efi_char16_t buf[128]; + size_t pos = 0, lim = ARRAY_SIZE(buf); + const u8 *s8 = (const u8 *)str; + u32 c32; + + while (*s8) { + if (*s8 == '\n') + buf[pos++] = L'\r'; + c32 = utf8_to_utf32(&s8); + if (c32 < 0x10000) { + /* Characters in plane 0 use a single word. */ + buf[pos++] = c32; + } else { + /* + * Characters in other planes encode into a surrogate + * pair. + */ + buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); + buf[pos++] = 0xdc00 + (c32 & 0x3ff); + } + if (*s8 == '\0' || pos >= lim - 2) { + buf[pos] = L'\0'; + efi_char16_puts(buf); + pos = 0; + } + } +} + +/** + * efi_printk() - Print a kernel message + * @fmt: format string + * + * The first letter of the format string is used to determine the logging level + * of the message. If the level is less then the current EFI logging level, the + * message is suppressed. The message will be truncated to 255 bytes. + * + * Return: number of printed characters + */ +int efi_printk(const char *fmt, ...) +{ + char printf_buf[256]; + va_list args; + int printed; + int loglevel = printk_get_level(fmt); + + switch (loglevel) { + case '0' ... '9': + loglevel -= '0'; + break; + default: + /* + * Use loglevel -1 for cases where we just want to print to + * the screen. + */ + loglevel = -1; + break; + } + + if (loglevel >= efi_loglevel) + return 0; + + if (loglevel >= 0) + efi_puts("EFI stub: "); + + fmt = printk_skip_level(fmt); + + va_start(args, fmt); + printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); + va_end(args); + + efi_puts(printf_buf); + if (printed >= sizeof(printf_buf)) { + efi_puts("[Message truncated]\n"); + return -1; + } + + return printed; +} diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index 24aa37535372..7109b8a2dcba 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -67,42 +67,113 @@ efi_status_t efi_random_get_seed(void) efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; + struct linux_efi_random_seed *prev_seed, *seed = NULL; + int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE; + unsigned long nv_seed_size = 0, offset = 0; efi_rng_protocol_t *rng = NULL; - struct linux_efi_random_seed *seed = NULL; efi_status_t status; status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); if (status != EFI_SUCCESS) - return status; + seed_size = 0; - status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, - sizeof(*seed) + EFI_RANDOM_SEED_SIZE, - (void **)&seed); - if (status != EFI_SUCCESS) + // Call GetVariable() with a zero length buffer to obtain the size + get_efi_var(L"RandomSeed", &rng_table_guid, NULL, &nv_seed_size, NULL); + if (!seed_size && !nv_seed_size) return status; - status = efi_call_proto(rng, get_rng, &rng_algo_raw, - EFI_RANDOM_SEED_SIZE, seed->bits); + seed_size += nv_seed_size; + + /* + * Check whether a seed was provided by a prior boot stage. In that + * case, instead of overwriting it, let's create a new buffer that can + * hold both, and concatenate the existing and the new seeds. + * Note that we should read the seed size with caution, in case the + * table got corrupted in memory somehow. + */ + prev_seed = get_efi_config_table(rng_table_guid); + if (prev_seed && prev_seed->size <= 512U) { + prev_seed_size = prev_seed->size; + seed_size += prev_seed_size; + } + + /* + * Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the + * allocation will survive a kexec reboot (although we refresh the seed + * beforehand) + */ + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, + struct_size(seed, bits, seed_size), + (void **)&seed); + if (status != EFI_SUCCESS) { + efi_warn("Failed to allocate memory for RNG seed.\n"); + goto err_warn; + } - if (status == EFI_UNSUPPORTED) - /* - * Use whatever algorithm we have available if the raw algorithm - * is not implemented. - */ - status = efi_call_proto(rng, get_rng, NULL, + if (rng) { + status = efi_call_proto(rng, get_rng, &rng_algo_raw, EFI_RANDOM_SEED_SIZE, seed->bits); - if (status != EFI_SUCCESS) + if (status == EFI_UNSUPPORTED) + /* + * Use whatever algorithm we have available if the raw algorithm + * is not implemented. + */ + status = efi_call_proto(rng, get_rng, NULL, + EFI_RANDOM_SEED_SIZE, seed->bits); + + if (status == EFI_SUCCESS) + offset = EFI_RANDOM_SEED_SIZE; + } + + if (nv_seed_size) { + status = get_efi_var(L"RandomSeed", &rng_table_guid, NULL, + &nv_seed_size, seed->bits + offset); + + if (status == EFI_SUCCESS) + /* + * We delete the seed here, and /hope/ that this causes + * EFI to also zero out its representation on disk. + * This is somewhat idealistic, but overwriting the + * variable with zeros is likely just as fraught too. + * TODO: in the future, maybe we can hash it forward + * instead, and write a new seed. + */ + status = set_efi_var(L"RandomSeed", &rng_table_guid, 0, + 0, NULL); + + if (status == EFI_SUCCESS) + offset += nv_seed_size; + else + memzero_explicit(seed->bits + offset, nv_seed_size); + } + + if (!offset) goto err_freepool; - seed->size = EFI_RANDOM_SEED_SIZE; + if (prev_seed_size) { + memcpy(seed->bits + offset, prev_seed->bits, prev_seed_size); + offset += prev_seed_size; + } + + seed->size = offset; status = efi_bs_call(install_configuration_table, &rng_table_guid, seed); if (status != EFI_SUCCESS) goto err_freepool; + if (prev_seed_size) { + /* wipe and free the old seed if we managed to install the new one */ + memzero_explicit(prev_seed->bits, prev_seed_size); + efi_bs_call(free_pool, prev_seed); + } return EFI_SUCCESS; err_freepool: + memzero_explicit(seed, struct_size(seed, bits, seed_size)); efi_bs_call(free_pool, seed); + efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL or EFI variable\n"); +err_warn: + if (prev_seed) + efi_warn("Retaining bootloader-supplied seed only"); return status; } diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 9fb5869896be..1692d19ae80f 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -29,7 +29,7 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, return 0; region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, - (u64)ULONG_MAX); + (u64)EFI_ALLOC_LIMIT); if (region_end < size) return 0; @@ -53,7 +53,8 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, efi_status_t efi_random_alloc(unsigned long size, unsigned long align, unsigned long *addr, - unsigned long random_seed) + unsigned long random_seed, + int memory_type) { unsigned long total_slots = 0, target_slot; unsigned long total_mirrored_slots = 0; @@ -118,7 +119,7 @@ efi_status_t efi_random_alloc(unsigned long size, pages = size / EFI_PAGE_SIZE; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, pages, &target); + memory_type, pages, &target); if (status == EFI_SUCCESS) *addr = target; break; diff --git a/drivers/firmware/efi/libstub/riscv-stub.c b/drivers/firmware/efi/libstub/riscv-stub.c index b450ebf95977..145c9f0ba217 100644 --- a/drivers/firmware/efi/libstub/riscv-stub.c +++ b/drivers/firmware/efi/libstub/riscv-stub.c @@ -4,7 +4,6 @@ */ #include <linux/efi.h> -#include <linux/libfdt.h> #include <asm/efi.h> #include <asm/sections.h> @@ -12,92 +11,16 @@ #include "efistub.h" -/* - * RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and - * 4MB for 32 bit. - */ -#ifdef CONFIG_64BIT -#define MIN_KIMG_ALIGN SZ_2M -#else -#define MIN_KIMG_ALIGN SZ_4M -#endif - -typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long); - -static unsigned long hartid; - -static int get_boot_hartid_from_fdt(void) -{ - const void *fdt; - int chosen_node, len; - const void *prop; - - fdt = get_efi_config_table(DEVICE_TREE_GUID); - if (!fdt) - return -EINVAL; - - chosen_node = fdt_path_offset(fdt, "/chosen"); - if (chosen_node < 0) - return -EINVAL; - - prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); - if (!prop) - return -EINVAL; - - if (len == sizeof(u32)) - hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop); - else if (len == sizeof(u64)) - hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop)); - else - return -EINVAL; - - return 0; -} - -static efi_status_t get_boot_hartid_from_efi(void) +unsigned long stext_offset(void) { - efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; - struct riscv_efi_boot_protocol *boot_protocol; - efi_status_t status; - - status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL, - (void **)&boot_protocol); - if (status != EFI_SUCCESS) - return status; - return efi_call_proto(boot_protocol, get_boot_hartid, &hartid); -} - -efi_status_t check_platform_features(void) -{ - efi_status_t status; - int ret; - - status = get_boot_hartid_from_efi(); - if (status != EFI_SUCCESS) { - ret = get_boot_hartid_from_fdt(); - if (ret) { - efi_err("Failed to get boot hartid!\n"); - return EFI_UNSUPPORTED; - } - } - return EFI_SUCCESS; -} - -void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, - unsigned long fdt_size) -{ - unsigned long stext_offset = _start_kernel - _start; - unsigned long kernel_entry = entrypoint + stext_offset; - jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; - /* - * Jump to real kernel here with following constraints. - * 1. MMU should be disabled. - * 2. a0 should contain hartid - * 3. a1 should DT address + * When built as part of the kernel, the EFI stub cannot branch to the + * kernel proper via the image header, as the PE/COFF header is + * strictly not part of the in-memory presentation of the image, only + * of the file representation. So instead, we need to jump to the + * actual entrypoint in the .text region of the image. */ - csr_write(CSR_SATP, 0); - jump_kernel(hartid, fdt); + return _start_kernel - _start; } efi_status_t handle_kernel_image(unsigned long *image_addr, @@ -125,9 +48,10 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, * lowest possible memory region as long as the address and size meets * the alignment constraints. */ - preferred_addr = MIN_KIMG_ALIGN; + preferred_addr = EFI_KIMG_PREFERRED_ADDRESS; status = efi_relocate_kernel(image_addr, kernel_size, *image_size, - preferred_addr, MIN_KIMG_ALIGN, 0x0); + preferred_addr, efi_get_kimg_min_align(), + 0x0); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); diff --git a/drivers/firmware/efi/libstub/riscv.c b/drivers/firmware/efi/libstub/riscv.c new file mode 100644 index 000000000000..8022b104c3e6 --- /dev/null +++ b/drivers/firmware/efi/libstub/riscv.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Western Digital Corporation or its affiliates. + */ + +#include <linux/efi.h> +#include <linux/libfdt.h> + +#include <asm/efi.h> +#include <asm/unaligned.h> + +#include "efistub.h" + +typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long); + +static unsigned long hartid; + +static int get_boot_hartid_from_fdt(void) +{ + const void *fdt; + int chosen_node, len; + const void *prop; + + fdt = get_efi_config_table(DEVICE_TREE_GUID); + if (!fdt) + return -EINVAL; + + chosen_node = fdt_path_offset(fdt, "/chosen"); + if (chosen_node < 0) + return -EINVAL; + + prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); + if (!prop) + return -EINVAL; + + if (len == sizeof(u32)) + hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop); + else if (len == sizeof(u64)) + hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop)); + else + return -EINVAL; + + return 0; +} + +static efi_status_t get_boot_hartid_from_efi(void) +{ + efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; + struct riscv_efi_boot_protocol *boot_protocol; + efi_status_t status; + + status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL, + (void **)&boot_protocol); + if (status != EFI_SUCCESS) + return status; + return efi_call_proto(boot_protocol, get_boot_hartid, &hartid); +} + +efi_status_t check_platform_features(void) +{ + efi_status_t status; + int ret; + + status = get_boot_hartid_from_efi(); + if (status != EFI_SUCCESS) { + ret = get_boot_hartid_from_fdt(); + if (ret) { + efi_err("Failed to get boot hartid!\n"); + return EFI_UNSUPPORTED; + } + } + return EFI_SUCCESS; +} + +unsigned long __weak stext_offset(void) +{ + /* + * This fallback definition is used by the EFI zboot stub, which loads + * the entire image so it can branch via the image header at offset #0. + */ + return 0; +} + +void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, + unsigned long fdt_size) +{ + unsigned long kernel_entry = entrypoint + stext_offset(); + jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; + + /* + * Jump to real kernel here with following constraints. + * 1. MMU should be disabled. + * 2. a0 should contain hartid + * 3. a1 should DT address + */ + csr_write(CSR_SATP, 0); + jump_kernel(hartid, fdt); +} diff --git a/drivers/firmware/efi/libstub/screen_info.c b/drivers/firmware/efi/libstub/screen_info.c new file mode 100644 index 000000000000..8e76a8b384ba --- /dev/null +++ b/drivers/firmware/efi/libstub/screen_info.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/efi.h> +#include <asm/efi.h> + +#include "efistub.h" + +/* + * There are two ways of populating the core kernel's struct screen_info via the stub: + * - using a configuration table, like below, which relies on the EFI init code + * to locate the table and copy the contents; + * - by linking directly to the core kernel's copy of the global symbol. + * + * The latter is preferred because it makes the EFIFB earlycon available very + * early, but it only works if the EFI stub is part of the core kernel image + * itself. The zboot decompressor can only use the configuration table + * approach. + * + * In order to support both methods from the same build of the EFI stub + * library, provide this dummy global definition of struct screen_info. If it + * is required to satisfy a link dependency, it means we need to override the + * __weak alloc and free methods with the ones below, and those will be pulled + * in as well. + */ +struct screen_info screen_info; + +static efi_guid_t screen_info_guid = LINUX_EFI_SCREEN_INFO_TABLE_GUID; + +struct screen_info *alloc_screen_info(void) +{ + struct screen_info *si; + efi_status_t status; + + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, + sizeof(*si), (void **)&si); + + if (status != EFI_SUCCESS) + return NULL; + + status = efi_bs_call(install_configuration_table, + &screen_info_guid, si); + if (status == EFI_SUCCESS) + return si; + + efi_bs_call(free_pool, si); + return NULL; +} + +void free_screen_info(struct screen_info *si) +{ + if (!si) + return; + + efi_bs_call(install_configuration_table, &screen_info_guid, NULL); + efi_bs_call(free_pool, si); +} diff --git a/drivers/firmware/efi/libstub/smbios.c b/drivers/firmware/efi/libstub/smbios.c new file mode 100644 index 000000000000..460418b7f5f5 --- /dev/null +++ b/drivers/firmware/efi/libstub/smbios.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2022 Google LLC +// Author: Ard Biesheuvel <ardb@google.com> + +#include <linux/efi.h> + +#include "efistub.h" + +typedef struct efi_smbios_protocol efi_smbios_protocol_t; + +struct efi_smbios_protocol { + efi_status_t (__efiapi *add)(efi_smbios_protocol_t *, efi_handle_t, + u16 *, struct efi_smbios_record *); + efi_status_t (__efiapi *update_string)(efi_smbios_protocol_t *, u16 *, + unsigned long *, u8 *); + efi_status_t (__efiapi *remove)(efi_smbios_protocol_t *, u16); + efi_status_t (__efiapi *get_next)(efi_smbios_protocol_t *, u16 *, u8 *, + struct efi_smbios_record **, + efi_handle_t *); + + u8 major_version; + u8 minor_version; +}; + +const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize) +{ + struct efi_smbios_record *record; + efi_smbios_protocol_t *smbios; + efi_status_t status; + u16 handle = 0xfffe; + const u8 *strtable; + + status = efi_bs_call(locate_protocol, &EFI_SMBIOS_PROTOCOL_GUID, NULL, + (void **)&smbios) ?: + efi_call_proto(smbios, get_next, &handle, &type, &record, NULL); + if (status != EFI_SUCCESS) + return NULL; + + strtable = (u8 *)record + recsize; + for (int i = 1; i < ((u8 *)record)[offset]; i++) { + int len = strlen(strtable); + + if (!len) + return NULL; + strtable += len + 1; + } + return strtable; +} diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 5d13e43869ee..168fe8e79abc 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -11,7 +11,37 @@ #include <linux/types.h> #include <linux/string.h> -#ifndef __HAVE_ARCH_STRSTR +#ifndef EFI_HAVE_STRLEN +/** + * strlen - Find the length of a string + * @s: The string to be sized + */ +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +#ifndef EFI_HAVE_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + /** * strstr - Find the first substring in a %NUL terminated string * @s1: The string to be searched @@ -33,9 +63,29 @@ char *strstr(const char *s1, const char *s2) } return NULL; } + +#ifndef EFI_HAVE_STRCMP +/** + * strcmp - Compare two strings + * @cs: One string + * @ct: Another string + */ +int strcmp(const char *cs, const char *ct) +{ + unsigned char c1, c2; + + while (1) { + c1 = *cs++; + c2 = *ct++; + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + } + return 0; +} #endif -#ifndef __HAVE_ARCH_STRNCMP /** * strncmp - Compare two length-limited strings * @cs: One string @@ -57,7 +107,6 @@ int strncmp(const char *cs, const char *ct, size_t count) } return 0; } -#endif /* Works only for digits and letters, but small and fast */ #define TOLOWER(x) ((x) | 0x20) @@ -113,3 +162,43 @@ long simple_strtol(const char *cp, char **endp, unsigned int base) return simple_strtoull(cp, endp, base); } + +#ifdef CONFIG_EFI_PARAMS_FROM_FDT +#ifndef EFI_HAVE_STRRCHR +/** + * strrchr - Find the last occurrence of a character in a string + * @s: The string to be searched + * @c: The character to search for + */ +char *strrchr(const char *s, int c) +{ + const char *last = NULL; + do { + if (*s == (char)c) + last = s; + } while (*s++); + return (char *)last; +} +#endif +#ifndef EFI_HAVE_MEMCHR +/** + * memchr - Find a character in an area of memory. + * @s: The memory area + * @c: The byte to search for + * @n: The size of the area. + * + * returns the address of the first occurrence of @c, or %NULL + * if @c is not found + */ +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *p = s; + while (n-- != 0) { + if ((unsigned char)c == *p++) { + return (void *)(p - 1); + } + } + return NULL; +} +#endif +#endif diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index b9ce6393e353..a0bfd31358ba 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -23,7 +23,7 @@ const efi_system_table_t *efi_system_table; const efi_dxe_services_table_t *efi_dxe_table; -extern u32 image_offset; +u32 image_offset __section(".data"); static efi_loaded_image_t *image = NULL; static efi_status_t @@ -765,9 +765,9 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle) * relocated by efi_relocate_kernel. * On failure, we exit to the firmware via efi_exit instead of returning. */ -unsigned long efi_main(efi_handle_t handle, - efi_system_table_t *sys_table_arg, - struct boot_params *boot_params) +asmlinkage unsigned long efi_main(efi_handle_t handle, + efi_system_table_t *sys_table_arg, + struct boot_params *boot_params) { unsigned long bzimage_addr = (unsigned long)startup_32; unsigned long buffer_start, buffer_end; diff --git a/drivers/firmware/efi/libstub/zboot-header.S b/drivers/firmware/efi/libstub/zboot-header.S index 9e6fe061ab07..ec4525d40e0c 100644 --- a/drivers/firmware/efi/libstub/zboot-header.S +++ b/drivers/firmware/efi/libstub/zboot-header.S @@ -17,10 +17,11 @@ __efistub_efi_zboot_header: .long MZ_MAGIC .ascii "zimg" // image type .long __efistub__gzdata_start - .Ldoshdr // payload offset - .long __efistub__gzdata_size - ZBOOT_SIZE_LEN // payload size + .long __efistub__gzdata_size - 12 // payload size .long 0, 0 // reserved .asciz COMP_TYPE // compression type - .org .Ldoshdr + 0x3c + .org .Ldoshdr + 0x38 + .long LINUX_PE_MAGIC .long .Lpehdr - .Ldoshdr // PE header offset .Lpehdr: diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index ea72c8f27da6..66be5fdc6b58 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -32,271 +32,116 @@ static unsigned long free_mem_ptr, free_mem_end_ptr; extern char efi_zboot_header[]; extern char _gzdata_start[], _gzdata_end[]; -static void log(efi_char16_t str[]) -{ - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, L"EFI decompressor: "); - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, str); - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, L"\n"); -} - static void error(char *x) { - log(L"error() called from decompressor library\n"); -} - -// Local version to avoid pulling in memcmp() -static bool guids_eq(const efi_guid_t *a, const efi_guid_t *b) -{ - const u32 *l = (u32 *)a; - const u32 *r = (u32 *)b; - - return l[0] == r[0] && l[1] == r[1] && l[2] == r[2] && l[3] == r[3]; -} - -static efi_status_t __efiapi -load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem, - bool boot_policy, unsigned long *bufsize, void *buffer) -{ - unsigned long compressed_size = _gzdata_end - _gzdata_start; - struct efi_vendor_dev_path *vendor_dp; - bool decompress = false; - unsigned long size; - int ret; - - if (rem == NULL || bufsize == NULL) - return EFI_INVALID_PARAMETER; - - if (boot_policy) - return EFI_UNSUPPORTED; - - // Look for our vendor media device node in the remaining file path - if (rem->type == EFI_DEV_MEDIA && - rem->sub_type == EFI_DEV_MEDIA_VENDOR) { - vendor_dp = container_of(rem, struct efi_vendor_dev_path, header); - if (!guids_eq(&vendor_dp->vendorguid, &LINUX_EFI_ZBOOT_MEDIA_GUID)) - return EFI_NOT_FOUND; - - decompress = true; - rem = (void *)(vendor_dp + 1); - } - - if (rem->type != EFI_DEV_END_PATH || - rem->sub_type != EFI_DEV_END_ENTIRE) - return EFI_NOT_FOUND; - - // The uncompressed size of the payload is appended to the raw bit - // stream, and may therefore appear misaligned in memory - size = decompress ? get_unaligned_le32(_gzdata_end - 4) - : compressed_size; - if (buffer == NULL || *bufsize < size) { - *bufsize = size; - return EFI_BUFFER_TOO_SMALL; - } - - if (decompress) { - ret = __decompress(_gzdata_start, compressed_size, NULL, NULL, - buffer, size, NULL, error); - if (ret < 0) { - log(L"Decompression failed"); - return EFI_DEVICE_ERROR; - } - } else { - memcpy(buffer, _gzdata_start, compressed_size); - } - - return EFI_SUCCESS; -} - -// Return the length in bytes of the device path up to the first end node. -static int device_path_length(const efi_device_path_protocol_t *dp) -{ - int len = 0; - - while (dp->type != EFI_DEV_END_PATH) { - len += dp->length; - dp = (void *)((u8 *)dp + dp->length); - } - return len; + efi_err("EFI decompressor: %s\n", x); } -static void append_rel_offset_node(efi_device_path_protocol_t **dp, - unsigned long start, unsigned long end) +static unsigned long alloc_preferred_address(unsigned long alloc_size) { - struct efi_rel_offset_dev_path *rodp = (void *)*dp; - - rodp->header.type = EFI_DEV_MEDIA; - rodp->header.sub_type = EFI_DEV_MEDIA_REL_OFFSET; - rodp->header.length = sizeof(struct efi_rel_offset_dev_path); - rodp->reserved = 0; - rodp->starting_offset = start; - rodp->ending_offset = end; +#ifdef EFI_KIMG_PREFERRED_ADDRESS + efi_physical_addr_t efi_addr = EFI_KIMG_PREFERRED_ADDRESS; - *dp = (void *)(rodp + 1); -} - -static void append_ven_media_node(efi_device_path_protocol_t **dp, - efi_guid_t *guid) -{ - struct efi_vendor_dev_path *vmdp = (void *)*dp; - - vmdp->header.type = EFI_DEV_MEDIA; - vmdp->header.sub_type = EFI_DEV_MEDIA_VENDOR; - vmdp->header.length = sizeof(struct efi_vendor_dev_path); - vmdp->vendorguid = *guid; - - *dp = (void *)(vmdp + 1); + if (efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + alloc_size / EFI_PAGE_SIZE, &efi_addr) == EFI_SUCCESS) + return efi_addr; +#endif + return ULONG_MAX; } -static void append_end_node(efi_device_path_protocol_t **dp) +void __weak efi_cache_sync_image(unsigned long image_base, + unsigned long alloc_size, + unsigned long code_size) { - (*dp)->type = EFI_DEV_END_PATH; - (*dp)->sub_type = EFI_DEV_END_ENTIRE; - (*dp)->length = sizeof(struct efi_generic_dev_path); - - ++*dp; + // Provided by the arch to perform the cache maintenance necessary for + // executable code loaded into memory to be safe for execution. } asmlinkage efi_status_t __efiapi efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) { - struct efi_mem_mapped_dev_path mmdp = { - .header.type = EFI_DEV_HW, - .header.sub_type = EFI_DEV_MEM_MAPPED, - .header.length = sizeof(struct efi_mem_mapped_dev_path) - }; - efi_device_path_protocol_t *parent_dp, *dpp, *lf2_dp, *li_dp; - efi_load_file2_protocol_t zboot_load_file2; - efi_loaded_image_t *parent, *child; - unsigned long exit_data_size; - efi_handle_t child_handle; - efi_handle_t zboot_handle; - efi_char16_t *exit_data; + unsigned long compressed_size = _gzdata_end - _gzdata_start; + unsigned long image_base, alloc_size, code_size; + efi_loaded_image_t *image; efi_status_t status; - void *dp_alloc; - int dp_len; + char *cmdline_ptr; + int ret; WRITE_ONCE(efi_system_table, systab); free_mem_ptr = (unsigned long)&zboot_heap; free_mem_end_ptr = free_mem_ptr + sizeof(zboot_heap); - exit_data = NULL; - exit_data_size = 0; - status = efi_bs_call(handle_protocol, handle, - &LOADED_IMAGE_PROTOCOL_GUID, (void **)&parent); + &LOADED_IMAGE_PROTOCOL_GUID, (void **)&image); if (status != EFI_SUCCESS) { - log(L"Failed to locate parent's loaded image protocol"); + error("Failed to locate parent's loaded image protocol"); return status; } - status = efi_bs_call(handle_protocol, handle, - &LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID, - (void **)&parent_dp); - if (status != EFI_SUCCESS || parent_dp == NULL) { - // Create a MemoryMapped() device path node to describe - // the parent image if no device path was provided. - mmdp.memory_type = parent->image_code_type; - mmdp.starting_addr = (unsigned long)parent->image_base; - mmdp.ending_addr = (unsigned long)parent->image_base + - parent->image_size - 1; - parent_dp = &mmdp.header; - dp_len = sizeof(mmdp); - } else { - dp_len = device_path_length(parent_dp); - } - - // Allocate some pool memory for device path protocol data - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, - 2 * (dp_len + sizeof(struct efi_rel_offset_dev_path) + - sizeof(struct efi_generic_dev_path)) + - sizeof(struct efi_vendor_dev_path), - (void **)&dp_alloc); - if (status != EFI_SUCCESS) { - log(L"Failed to allocate device path pool memory"); + status = efi_handle_cmdline(image, &cmdline_ptr); + if (status != EFI_SUCCESS) return status; - } - // Create a device path describing the compressed payload in this image - // <...parent_dp...>/Offset(<start>, <end>) - lf2_dp = memcpy(dp_alloc, parent_dp, dp_len); - dpp = (void *)((u8 *)lf2_dp + dp_len); - append_rel_offset_node(&dpp, - (unsigned long)(_gzdata_start - efi_zboot_header), - (unsigned long)(_gzdata_end - efi_zboot_header - 1)); - append_end_node(&dpp); - - // Create a device path describing the decompressed payload in this image - // <...parent_dp...>/Offset(<start>, <end>)/VenMedia(ZBOOT_MEDIA_GUID) - dp_len += sizeof(struct efi_rel_offset_dev_path); - li_dp = memcpy(dpp, lf2_dp, dp_len); - dpp = (void *)((u8 *)li_dp + dp_len); - append_ven_media_node(&dpp, &LINUX_EFI_ZBOOT_MEDIA_GUID); - append_end_node(&dpp); - - zboot_handle = NULL; - zboot_load_file2.load_file = load_file; - status = efi_bs_call(install_multiple_protocol_interfaces, - &zboot_handle, - &EFI_DEVICE_PATH_PROTOCOL_GUID, lf2_dp, - &EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2, - NULL); - if (status != EFI_SUCCESS) { - log(L"Failed to install LoadFile2 protocol and device path"); - goto free_dpalloc; - } - - status = efi_bs_call(load_image, false, handle, li_dp, NULL, 0, - &child_handle); - if (status != EFI_SUCCESS) { - log(L"Failed to load image"); - goto uninstall_lf2; - } + efi_info("Decompressing Linux Kernel...\n"); + + // SizeOfImage from the compressee's PE/COFF header + alloc_size = round_up(get_unaligned_le32(_gzdata_end - 4), + EFI_ALLOC_ALIGN); + + // SizeOfHeaders and SizeOfCode from the compressee's PE/COFF header + code_size = get_unaligned_le32(_gzdata_end - 8) + + get_unaligned_le32(_gzdata_end - 12); + + // If the architecture has a preferred address for the image, + // try that first. + image_base = alloc_preferred_address(alloc_size); + if (image_base == ULONG_MAX) { + unsigned long min_kimg_align = efi_get_kimg_min_align(); + u32 seed = U32_MAX; + + if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { + // Setting the random seed to 0x0 is the same as + // allocating as low as possible + seed = 0; + } else if (efi_nokaslr) { + efi_info("KASLR disabled on kernel command line\n"); + } else { + status = efi_get_random_bytes(sizeof(seed), (u8 *)&seed); + if (status == EFI_NOT_FOUND) { + efi_info("EFI_RNG_PROTOCOL unavailable\n"); + efi_nokaslr = true; + } else if (status != EFI_SUCCESS) { + efi_err("efi_get_random_bytes() failed (0x%lx)\n", + status); + efi_nokaslr = true; + } + } - status = efi_bs_call(handle_protocol, child_handle, - &LOADED_IMAGE_PROTOCOL_GUID, (void **)&child); - if (status != EFI_SUCCESS) { - log(L"Failed to locate child's loaded image protocol"); - goto unload_image; + status = efi_random_alloc(alloc_size, min_kimg_align, &image_base, + seed, EFI_LOADER_CODE); + if (status != EFI_SUCCESS) { + efi_err("Failed to allocate memory\n"); + goto free_cmdline; + } } - // Copy the kernel command line - child->load_options = parent->load_options; - child->load_options_size = parent->load_options_size; - - status = efi_bs_call(start_image, child_handle, &exit_data_size, - &exit_data); - if (status != EFI_SUCCESS) { - log(L"StartImage() returned with error"); - if (exit_data_size > 0) - log(exit_data); - - // If StartImage() returns EFI_SECURITY_VIOLATION, the image is - // not unloaded so we need to do it by hand. - if (status == EFI_SECURITY_VIOLATION) -unload_image: - efi_bs_call(unload_image, child_handle); + // Decompress the payload into the newly allocated buffer. + ret = __decompress(_gzdata_start, compressed_size, NULL, NULL, + (void *)image_base, alloc_size, NULL, error); + if (ret < 0) { + error("Decompression failed"); + status = EFI_DEVICE_ERROR; + goto free_image; } -uninstall_lf2: - efi_bs_call(uninstall_multiple_protocol_interfaces, - zboot_handle, - &EFI_DEVICE_PATH_PROTOCOL_GUID, lf2_dp, - &EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2, - NULL); - -free_dpalloc: - efi_bs_call(free_pool, dp_alloc); + efi_cache_sync_image(image_base, alloc_size, code_size); - efi_bs_call(exit, handle, status, exit_data_size, exit_data); + status = efi_stub_common(handle, image, image_base, cmdline_ptr); - // Free ExitData in case Exit() returned with a failure code, - // but return the original status code. - log(L"Exit() returned with failure code"); - if (exit_data != NULL) - efi_bs_call(free_pool, exit_data); +free_image: + efi_free(alloc_size, image_base); +free_cmdline: + efi_bs_call(free_pool, cmdline_ptr); return status; } diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index 87a62765bafd..93d33f68333b 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -38,7 +38,8 @@ SECTIONS } } -PROVIDE(__efistub__gzdata_size = ABSOLUTE(. - __efistub__gzdata_start)); +PROVIDE(__efistub__gzdata_size = + ABSOLUTE(__efistub__gzdata_end - __efistub__gzdata_start)); PROVIDE(__data_rawsize = ABSOLUTE(_edata - _etext)); PROVIDE(__data_size = ABSOLUTE(_end - _etext)); diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 6ec7970dbd40..a1180461a445 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -9,82 +9,15 @@ #include <linux/kernel.h> #include <linux/efi.h> #include <linux/io.h> -#include <asm/early_ioremap.h> #include <linux/memblock.h> #include <linux/slab.h> -static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) -{ - return memblock_phys_alloc(size, SMP_CACHE_BYTES); -} - -static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) -{ - unsigned int order = get_order(size); - struct page *p = alloc_pages(GFP_KERNEL, order); - - if (!p) - return 0; - - return PFN_PHYS(page_to_pfn(p)); -} - -void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) -{ - if (flags & EFI_MEMMAP_MEMBLOCK) { - if (slab_is_available()) - memblock_free_late(phys, size); - else - memblock_phys_free(phys, size); - } else if (flags & EFI_MEMMAP_SLAB) { - struct page *p = pfn_to_page(PHYS_PFN(phys)); - unsigned int order = get_order(size); - - free_pages((unsigned long) page_address(p), order); - } -} - -static void __init efi_memmap_free(void) -{ - __efi_memmap_free(efi.memmap.phys_map, - efi.memmap.desc_size * efi.memmap.nr_map, - efi.memmap.flags); -} - -/** - * efi_memmap_alloc - Allocate memory for the EFI memory map - * @num_entries: Number of entries in the allocated map. - * @data: efi memmap installation parameters - * - * Depending on whether mm_init() has already been invoked or not, - * either memblock or "normal" page allocation is used. - * - * Returns zero on success, a negative error code on failure. - */ -int __init efi_memmap_alloc(unsigned int num_entries, - struct efi_memory_map_data *data) -{ - /* Expect allocation parameters are zero initialized */ - WARN_ON(data->phys_map || data->size); - - data->size = num_entries * efi.memmap.desc_size; - data->desc_version = efi.memmap.desc_version; - data->desc_size = efi.memmap.desc_size; - data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK); - data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE; - - if (slab_is_available()) { - data->flags |= EFI_MEMMAP_SLAB; - data->phys_map = __efi_memmap_alloc_late(data->size); - } else { - data->flags |= EFI_MEMMAP_MEMBLOCK; - data->phys_map = __efi_memmap_alloc_early(data->size); - } +#include <asm/early_ioremap.h> +#include <asm/efi.h> - if (!data->phys_map) - return -ENOMEM; - return 0; -} +#ifndef __efi_memmap_free +#define __efi_memmap_free(phys, size, flags) do { } while (0) +#endif /** * __efi_memmap_init - Common code for mapping the EFI memory map @@ -101,14 +34,11 @@ int __init efi_memmap_alloc(unsigned int num_entries, * * Returns zero on success, a negative error code on failure. */ -static int __init __efi_memmap_init(struct efi_memory_map_data *data) +int __init __efi_memmap_init(struct efi_memory_map_data *data) { struct efi_memory_map map; phys_addr_t phys_map; - if (efi_enabled(EFI_PARAVIRT)) - return 0; - phys_map = data->phys_map; if (data->flags & EFI_MEMMAP_LATE) @@ -121,8 +51,10 @@ static int __init __efi_memmap_init(struct efi_memory_map_data *data) return -ENOMEM; } - /* NOP if data->flags & (EFI_MEMMAP_MEMBLOCK | EFI_MEMMAP_SLAB) == 0 */ - efi_memmap_free(); + if (efi.memmap.flags & (EFI_MEMMAP_MEMBLOCK | EFI_MEMMAP_SLAB)) + __efi_memmap_free(efi.memmap.phys_map, + efi.memmap.desc_size * efi.memmap.nr_map, + efi.memmap.flags); map.phys_map = data->phys_map; map.nr_map = data->size / data->desc_size; @@ -220,158 +152,3 @@ int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size) return __efi_memmap_init(&data); } - -/** - * efi_memmap_install - Install a new EFI memory map in efi.memmap - * @ctx: map allocation parameters (address, size, flags) - * - * Unlike efi_memmap_init_*(), this function does not allow the caller - * to switch from early to late mappings. It simply uses the existing - * mapping function and installs the new memmap. - * - * Returns zero on success, a negative error code on failure. - */ -int __init efi_memmap_install(struct efi_memory_map_data *data) -{ - efi_memmap_unmap(); - - return __efi_memmap_init(data); -} - -/** - * efi_memmap_split_count - Count number of additional EFI memmap entries - * @md: EFI memory descriptor to split - * @range: Address range (start, end) to split around - * - * Returns the number of additional EFI memmap entries required to - * accommodate @range. - */ -int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range) -{ - u64 m_start, m_end; - u64 start, end; - int count = 0; - - start = md->phys_addr; - end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; - - /* modifying range */ - m_start = range->start; - m_end = range->end; - - if (m_start <= start) { - /* split into 2 parts */ - if (start < m_end && m_end < end) - count++; - } - - if (start < m_start && m_start < end) { - /* split into 3 parts */ - if (m_end < end) - count += 2; - /* split into 2 parts */ - if (end <= m_end) - count++; - } - - return count; -} - -/** - * efi_memmap_insert - Insert a memory region in an EFI memmap - * @old_memmap: The existing EFI memory map structure - * @buf: Address of buffer to store new map - * @mem: Memory map entry to insert - * - * It is suggested that you call efi_memmap_split_count() first - * to see how large @buf needs to be. - */ -void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, - struct efi_mem_range *mem) -{ - u64 m_start, m_end, m_attr; - efi_memory_desc_t *md; - u64 start, end; - void *old, *new; - - /* modifying range */ - m_start = mem->range.start; - m_end = mem->range.end; - m_attr = mem->attribute; - - /* - * The EFI memory map deals with regions in EFI_PAGE_SIZE - * units. Ensure that the region described by 'mem' is aligned - * correctly. - */ - if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) || - !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) { - WARN_ON(1); - return; - } - - for (old = old_memmap->map, new = buf; - old < old_memmap->map_end; - old += old_memmap->desc_size, new += old_memmap->desc_size) { - - /* copy original EFI memory descriptor */ - memcpy(new, old, old_memmap->desc_size); - md = new; - start = md->phys_addr; - end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; - - if (m_start <= start && end <= m_end) - md->attribute |= m_attr; - - if (m_start <= start && - (start < m_end && m_end < end)) { - /* first part */ - md->attribute |= m_attr; - md->num_pages = (m_end - md->phys_addr + 1) >> - EFI_PAGE_SHIFT; - /* latter part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->phys_addr = m_end + 1; - md->num_pages = (end - md->phys_addr + 1) >> - EFI_PAGE_SHIFT; - } - - if ((start < m_start && m_start < end) && m_end < end) { - /* first part */ - md->num_pages = (m_start - md->phys_addr) >> - EFI_PAGE_SHIFT; - /* middle part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->attribute |= m_attr; - md->phys_addr = m_start; - md->num_pages = (m_end - m_start + 1) >> - EFI_PAGE_SHIFT; - /* last part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->phys_addr = m_end + 1; - md->num_pages = (end - m_end) >> - EFI_PAGE_SHIFT; - } - - if ((start < m_start && m_start < end) && - (end <= m_end)) { - /* first part */ - md->num_pages = (m_start - md->phys_addr) >> - EFI_PAGE_SHIFT; - /* latter part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->phys_addr = m_start; - md->num_pages = (end - md->phys_addr + 1) >> - EFI_PAGE_SHIFT; - md->attribute |= m_attr; - } - } -} diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c index d28e715d2bcc..d0daacd2c903 100644 --- a/drivers/firmware/efi/riscv-runtime.c +++ b/drivers/firmware/efi/riscv-runtime.c @@ -41,7 +41,7 @@ static bool __init efi_virtmap_init(void) if (!(md->attribute & EFI_MEMORY_RUNTIME)) continue; - if (md->virt_addr == 0) + if (md->virt_addr == U64_MAX) return false; ret = efi_create_mapping(&efi_mm, md); diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c deleted file mode 100644 index 92a3d45a795c..000000000000 --- a/drivers/firmware/efi/runtime-map.c +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/drivers/efi/runtime-map.c - * Copyright (C) 2013 Red Hat, Inc., Dave Young <dyoung@redhat.com> - */ - -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/efi.h> -#include <linux/slab.h> - -#include <asm/setup.h> - -struct efi_runtime_map_entry { - efi_memory_desc_t md; - struct kobject kobj; /* kobject for each entry */ -}; - -static struct efi_runtime_map_entry **map_entries; - -struct map_attribute { - struct attribute attr; - ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf); -}; - -static inline struct map_attribute *to_map_attr(struct attribute *attr) -{ - return container_of(attr, struct map_attribute, attr); -} - -static ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type); -} - -#define EFI_RUNTIME_FIELD(var) entry->md.var - -#define EFI_RUNTIME_U64_ATTR_SHOW(name) \ -static ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \ -{ \ - return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \ -} - -EFI_RUNTIME_U64_ATTR_SHOW(phys_addr); -EFI_RUNTIME_U64_ATTR_SHOW(virt_addr); -EFI_RUNTIME_U64_ATTR_SHOW(num_pages); -EFI_RUNTIME_U64_ATTR_SHOW(attribute); - -static inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj) -{ - return container_of(kobj, struct efi_runtime_map_entry, kobj); -} - -static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct efi_runtime_map_entry *entry = to_map_entry(kobj); - struct map_attribute *map_attr = to_map_attr(attr); - - return map_attr->show(entry, buf); -} - -static struct map_attribute map_type_attr = __ATTR_RO_MODE(type, 0400); -static struct map_attribute map_phys_addr_attr = __ATTR_RO_MODE(phys_addr, 0400); -static struct map_attribute map_virt_addr_attr = __ATTR_RO_MODE(virt_addr, 0400); -static struct map_attribute map_num_pages_attr = __ATTR_RO_MODE(num_pages, 0400); -static struct map_attribute map_attribute_attr = __ATTR_RO_MODE(attribute, 0400); - -/* - * These are default attributes that are added for every memmap entry. - */ -static struct attribute *def_attrs[] = { - &map_type_attr.attr, - &map_phys_addr_attr.attr, - &map_virt_addr_attr.attr, - &map_num_pages_attr.attr, - &map_attribute_attr.attr, - NULL -}; -ATTRIBUTE_GROUPS(def); - -static const struct sysfs_ops map_attr_ops = { - .show = map_attr_show, -}; - -static void map_release(struct kobject *kobj) -{ - struct efi_runtime_map_entry *entry; - - entry = to_map_entry(kobj); - kfree(entry); -} - -static struct kobj_type __refdata map_ktype = { - .sysfs_ops = &map_attr_ops, - .default_groups = def_groups, - .release = map_release, -}; - -static struct kset *map_kset; - -static struct efi_runtime_map_entry * -add_sysfs_runtime_map_entry(struct kobject *kobj, int nr, - efi_memory_desc_t *md) -{ - int ret; - struct efi_runtime_map_entry *entry; - - if (!map_kset) { - map_kset = kset_create_and_add("runtime-map", NULL, kobj); - if (!map_kset) - return ERR_PTR(-ENOMEM); - } - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - kset_unregister(map_kset); - map_kset = NULL; - return ERR_PTR(-ENOMEM); - } - - memcpy(&entry->md, md, sizeof(efi_memory_desc_t)); - - kobject_init(&entry->kobj, &map_ktype); - entry->kobj.kset = map_kset; - ret = kobject_add(&entry->kobj, NULL, "%d", nr); - if (ret) { - kobject_put(&entry->kobj); - kset_unregister(map_kset); - map_kset = NULL; - return ERR_PTR(ret); - } - - return entry; -} - -int efi_get_runtime_map_size(void) -{ - return efi.memmap.nr_map * efi.memmap.desc_size; -} - -int efi_get_runtime_map_desc_size(void) -{ - return efi.memmap.desc_size; -} - -int efi_runtime_map_copy(void *buf, size_t bufsz) -{ - size_t sz = efi_get_runtime_map_size(); - - if (sz > bufsz) - sz = bufsz; - - memcpy(buf, efi.memmap.map, sz); - return 0; -} - -int __init efi_runtime_map_init(struct kobject *efi_kobj) -{ - int i, j, ret = 0; - struct efi_runtime_map_entry *entry; - efi_memory_desc_t *md; - - if (!efi_enabled(EFI_MEMMAP)) - return 0; - - map_entries = kcalloc(efi.memmap.nr_map, sizeof(entry), GFP_KERNEL); - if (!map_entries) { - ret = -ENOMEM; - goto out; - } - - i = 0; - for_each_efi_memory_desc(md) { - entry = add_sysfs_runtime_map_entry(efi_kobj, i, md); - if (IS_ERR(entry)) { - ret = PTR_ERR(entry); - goto out_add_entry; - } - *(map_entries + i++) = entry; - } - - return 0; -out_add_entry: - for (j = i - 1; j >= 0; j--) { - entry = *(map_entries + j); - kobject_put(&entry->kobj); - } -out: - return ret; -} diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index f3e54f6616f0..7feee3d9c2bf 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -83,6 +83,7 @@ struct efi_runtime_work efi_rts_work; else \ pr_err("Failed to queue work to efi_rts_wq.\n"); \ \ + WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); \ exit: \ efi_rts_work.efi_rts_id = EFI_NONE; \ efi_rts_work.status; \ diff --git a/drivers/firmware/efi/tpm.c b/drivers/firmware/efi/tpm.c index 8f665678e9e3..e8d69bd548f3 100644 --- a/drivers/firmware/efi/tpm.c +++ b/drivers/firmware/efi/tpm.c @@ -97,7 +97,7 @@ int __init efi_tpm_eventlog_init(void) goto out_calc; } - memblock_reserve((unsigned long)final_tbl, + memblock_reserve(efi.tpm_final_log, tbl_size + sizeof(*final_tbl)); efi_tpm_final_log_size = tbl_size; diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index dd74d2ad3184..0ba9f18312f5 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -7,6 +7,7 @@ */ #include <linux/types.h> +#include <linux/sizes.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/module.h> @@ -20,31 +21,23 @@ static struct efivars *__efivars; static DEFINE_SEMAPHORE(efivars_lock); -efi_status_t check_var_size(u32 attributes, unsigned long size) -{ - const struct efivar_operations *fops; - - fops = __efivars->ops; - - if (!fops->query_variable_store) - return EFI_UNSUPPORTED; - - return fops->query_variable_store(attributes, size, false); -} -EXPORT_SYMBOL_NS_GPL(check_var_size, EFIVAR); - -efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size) +static efi_status_t check_var_size(bool nonblocking, u32 attributes, + unsigned long size) { const struct efivar_operations *fops; + efi_status_t status; fops = __efivars->ops; if (!fops->query_variable_store) - return EFI_UNSUPPORTED; - - return fops->query_variable_store(attributes, size, true); + status = EFI_UNSUPPORTED; + else + status = fops->query_variable_store(attributes, size, + nonblocking); + if (status == EFI_UNSUPPORTED) + return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; + return status; } -EXPORT_SYMBOL_NS_GPL(check_var_size_nonblocking, EFIVAR); /** * efivars_kobject - get the kobject for the registered efivars @@ -196,26 +189,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size, EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR); /* - * efivar_set_variable_blocking() - local helper function for set_variable - * - * Must be called with efivars_lock held. - */ -static efi_status_t -efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, void *data) -{ - efi_status_t status; - - if (data_size > 0) { - status = check_var_size(attr, data_size + - ucs2_strsize(name, 1024)); - if (status != EFI_SUCCESS) - return status; - } - return __efivars->ops->set_variable(name, vendor, attr, data_size, data); -} - -/* * efivar_set_variable_locked() - set a variable identified by name/vendor * * Must be called with efivars_lock held. If @nonblocking is set, it will use @@ -228,23 +201,21 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor, efi_set_variable_t *setvar; efi_status_t status; - if (!nonblocking) - return efivar_set_variable_blocking(name, vendor, attr, - data_size, data); + if (data_size > 0) { + status = check_var_size(nonblocking, attr, + data_size + ucs2_strsize(name, 1024)); + if (status != EFI_SUCCESS) + return status; + } /* * If no _nonblocking variant exists, the ordinary one * is assumed to be non-blocking. */ - setvar = __efivars->ops->set_variable_nonblocking ?: - __efivars->ops->set_variable; + setvar = __efivars->ops->set_variable_nonblocking; + if (!setvar || !nonblocking) + setvar = __efivars->ops->set_variable; - if (data_size > 0) { - status = check_var_size_nonblocking(attr, data_size + - ucs2_strsize(name, 1024)); - if (status != EFI_SUCCESS) - return status; - } return setvar(name, vendor, attr, data_size, data); } EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR); @@ -264,7 +235,8 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor, if (efivar_lock()) return EFI_ABORTED; - status = efivar_set_variable_blocking(name, vendor, attr, data_size, data); + status = efivar_set_variable_locked(name, vendor, attr, data_size, + data, false); efivar_unlock(); return status; } diff --git a/drivers/firmware/efi/x86_fake_mem.c b/drivers/firmware/efi/x86_fake_mem.c deleted file mode 100644 index 0bafcc1bb0f6..000000000000 --- a/drivers/firmware/efi/x86_fake_mem.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2019 Intel Corporation. All rights reserved. */ -#include <linux/efi.h> -#include <asm/e820/api.h> -#include "fake_mem.h" - -void __init efi_fake_memmap_early(void) -{ - int i; - - /* - * The late efi_fake_mem() call can handle all requests if - * EFI_MEMORY_SP support is disabled. - */ - if (!efi_soft_reserve_enabled()) - return; - - if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) - return; - - /* - * Given that efi_fake_memmap() needs to perform memblock - * allocations it needs to run after e820__memblock_setup(). - * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given - * address range that potentially needs to mark the memory as - * reserved prior to e820__memblock_setup(). Update e820 - * directly if EFI_MEMORY_SP is specified for an - * EFI_CONVENTIONAL_MEMORY descriptor. - */ - for (i = 0; i < nr_fake_mem; i++) { - struct efi_mem_range *mem = &efi_fake_mems[i]; - efi_memory_desc_t *md; - u64 m_start, m_end; - - if ((mem->attribute & EFI_MEMORY_SP) == 0) - continue; - - m_start = mem->range.start; - m_end = mem->range.end; - for_each_efi_memory_desc(md) { - u64 start, end, size; - - if (md->type != EFI_CONVENTIONAL_MEMORY) - continue; - - start = md->phys_addr; - end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; - - if (m_start <= end && m_end >= start) - /* fake range overlaps descriptor */; - else - continue; - - /* - * Trim the boundary of the e820 update to the - * descriptor in case the fake range overlaps - * !EFI_CONVENTIONAL_MEMORY - */ - start = max(start, m_start); - end = min(end, m_end); - size = end - start + 1; - - if (end <= start) - continue; - - /* - * Ensure each efi_fake_mem instance results in - * a unique e820 resource - */ - e820__range_remove(start, size, E820_TYPE_RAM, 1); - e820__range_add(start, size, E820_TYPE_SOFT_RESERVED); - e820__update_table(e820_table); - } - } -} diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 983e07dc022e..9f190eab43ed 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -19,6 +19,21 @@ config GOOGLE_SMI driver provides an interface for reading and writing NVRAM variables. +config GOOGLE_CBMEM + tristate "CBMEM entries in sysfs" + depends on GOOGLE_COREBOOT_TABLE + help + CBMEM is a downwards-growing memory region created by the + Coreboot BIOS containing tagged data structures from the + BIOS. These data structures expose things like the verified + boot firmware variables, flash layout, firmware event log, + and more. + + This option enables the cbmem module, which causes the + kernel to search for Coreboot CBMEM entries, and expose the + memory for each entry in sysfs under + /sys/bus/coreboot/devices/cbmem-<id>. + config GOOGLE_COREBOOT_TABLE tristate "Coreboot Table Access" depends on HAS_IOMEM && (ACPI || OF) diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile index d17caded5d88..8151e323cc43 100644 --- a/drivers/firmware/google/Makefile +++ b/drivers/firmware/google/Makefile @@ -7,5 +7,8 @@ obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o +# Must come after coreboot_table.o, as this driver depends on that bus type. +obj-$(CONFIG_GOOGLE_CBMEM) += cbmem.o + vpd-sysfs-y := vpd.o vpd_decode.o obj-$(CONFIG_GOOGLE_VPD) += vpd-sysfs.o diff --git a/drivers/firmware/google/cbmem.c b/drivers/firmware/google/cbmem.c new file mode 100644 index 000000000000..88e587ba1e0d --- /dev/null +++ b/drivers/firmware/google/cbmem.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cbmem.c + * + * Driver for exporting cbmem entries in sysfs. + * + * Copyright 2022 Google LLC + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include "coreboot_table.h" + +struct cbmem_entry { + char *mem_file_buf; + u32 size; +}; + +static struct cbmem_entry *to_cbmem_entry(struct kobject *kobj) +{ + return dev_get_drvdata(kobj_to_dev(kobj)); +} + +static ssize_t mem_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t pos, + size_t count) +{ + struct cbmem_entry *entry = to_cbmem_entry(kobj); + + return memory_read_from_buffer(buf, count, &pos, entry->mem_file_buf, + entry->size); +} + +static ssize_t mem_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t pos, + size_t count) +{ + struct cbmem_entry *entry = to_cbmem_entry(kobj); + + if (pos < 0 || pos >= entry->size) + return -EINVAL; + if (count > entry->size - pos) + count = entry->size - pos; + + memcpy(entry->mem_file_buf + pos, buf, count); + return count; +} +static BIN_ATTR_ADMIN_RW(mem, 0); + +static ssize_t address_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct coreboot_device *cbdev = dev_to_coreboot_device(dev); + + return sysfs_emit(buf, "0x%llx\n", cbdev->cbmem_entry.address); +} +static DEVICE_ATTR_RO(address); + +static ssize_t size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct coreboot_device *cbdev = dev_to_coreboot_device(dev); + + return sysfs_emit(buf, "0x%x\n", cbdev->cbmem_entry.entry_size); +} +static DEVICE_ATTR_RO(size); + +static struct attribute *attrs[] = { + &dev_attr_address.attr, + &dev_attr_size.attr, + NULL, +}; + +static struct bin_attribute *bin_attrs[] = { + &bin_attr_mem, + NULL, +}; + +static const struct attribute_group cbmem_entry_group = { + .attrs = attrs, + .bin_attrs = bin_attrs, +}; + +static const struct attribute_group *dev_groups[] = { + &cbmem_entry_group, + NULL, +}; + +static int cbmem_entry_probe(struct coreboot_device *dev) +{ + struct cbmem_entry *entry; + + entry = devm_kzalloc(&dev->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + dev_set_drvdata(&dev->dev, entry); + entry->mem_file_buf = devm_memremap(&dev->dev, dev->cbmem_entry.address, + dev->cbmem_entry.entry_size, + MEMREMAP_WB); + if (IS_ERR(entry->mem_file_buf)) + return PTR_ERR(entry->mem_file_buf); + + entry->size = dev->cbmem_entry.entry_size; + + return 0; +} + +static struct coreboot_driver cbmem_entry_driver = { + .probe = cbmem_entry_probe, + .drv = { + .name = "cbmem", + .owner = THIS_MODULE, + .dev_groups = dev_groups, + }, + .tag = LB_TAG_CBMEM_ENTRY, +}; +module_coreboot_driver(cbmem_entry_driver); + +MODULE_AUTHOR("Jack Rosenthal <jrosenth@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index c52bcaa9def6..2652c396c423 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -97,12 +97,21 @@ static int coreboot_table_populate(struct device *dev, void *ptr) if (!device) return -ENOMEM; - dev_set_name(&device->dev, "coreboot%d", i); device->dev.parent = dev; device->dev.bus = &coreboot_bus_type; device->dev.release = coreboot_device_release; memcpy(&device->entry, ptr_entry, entry->size); + switch (device->entry.tag) { + case LB_TAG_CBMEM_ENTRY: + dev_set_name(&device->dev, "cbmem-%08x", + device->cbmem_entry.id); + break; + default: + dev_set_name(&device->dev, "coreboot%d", i); + break; + } + ret = device_register(&device->dev); if (ret) { put_device(&device->dev); @@ -149,12 +158,8 @@ static int coreboot_table_probe(struct platform_device *pdev) if (!ptr) return -ENOMEM; - ret = bus_register(&coreboot_bus_type); - if (!ret) { - ret = coreboot_table_populate(dev, ptr); - if (ret) - bus_unregister(&coreboot_bus_type); - } + ret = coreboot_table_populate(dev, ptr); + memunmap(ptr); return ret; @@ -169,7 +174,6 @@ static int __cb_dev_unregister(struct device *dev, void *dummy) static int coreboot_table_remove(struct platform_device *pdev) { bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister); - bus_unregister(&coreboot_bus_type); return 0; } @@ -199,6 +203,32 @@ static struct platform_driver coreboot_table_driver = { .of_match_table = of_match_ptr(coreboot_of_match), }, }; -module_platform_driver(coreboot_table_driver); + +static int __init coreboot_table_driver_init(void) +{ + int ret; + + ret = bus_register(&coreboot_bus_type); + if (ret) + return ret; + + ret = platform_driver_register(&coreboot_table_driver); + if (ret) { + bus_unregister(&coreboot_bus_type); + return ret; + } + + return 0; +} + +static void __exit coreboot_table_driver_exit(void) +{ + platform_driver_unregister(&coreboot_table_driver); + bus_unregister(&coreboot_bus_type); +} + +module_init(coreboot_table_driver_init); +module_exit(coreboot_table_driver_exit); + MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h index beb778674acd..37f4d335a606 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/google/coreboot_table.h @@ -39,6 +39,18 @@ struct lb_cbmem_ref { u64 cbmem_addr; }; +#define LB_TAG_CBMEM_ENTRY 0x31 + +/* Corresponds to LB_TAG_CBMEM_ENTRY */ +struct lb_cbmem_entry { + u32 tag; + u32 size; + + u64 address; + u32 entry_size; + u32 id; +}; + /* Describes framebuffer setup by coreboot */ struct lb_framebuffer { u32 tag; @@ -65,10 +77,16 @@ struct coreboot_device { union { struct coreboot_table_entry entry; struct lb_cbmem_ref cbmem_ref; + struct lb_cbmem_entry cbmem_entry; struct lb_framebuffer framebuffer; }; }; +static inline struct coreboot_device *dev_to_coreboot_device(struct device *dev) +{ + return container_of(dev, struct coreboot_device, dev); +} + /* A driver for handling devices described in coreboot tables. */ struct coreboot_driver { int (*probe)(struct coreboot_device *); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index af3d057e6421..2a4f07423365 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -108,6 +108,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* CONN SS */ { "usb", IMX_SC_R_USB_0, 2, true, 0 }, { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index 4b8978b254f9..c3bc29e0a488 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -228,6 +228,26 @@ static void rpi_register_clk_driver(struct device *dev) -1, NULL, 0); } +unsigned int rpi_firmware_clk_get_max_rate(struct rpi_firmware *fw, unsigned int id) +{ + struct rpi_firmware_clk_rate_request msg = + RPI_FIRMWARE_CLK_RATE_REQUEST(id); + int ret; + + ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, + &msg, sizeof(msg)); + if (ret) + /* + * If our firmware doesn't support that operation, or fails, we + * assume the maximum clock rate is absolute maximum we can + * store over our type. + */ + return UINT_MAX; + + return le32_to_cpu(msg.rate); +} +EXPORT_SYMBOL_GPL(rpi_firmware_clk_get_max_rate); + static void rpi_firmware_delete(struct kref *kref) { struct rpi_firmware *fw = container_of(kref, struct rpi_firmware, @@ -270,9 +290,8 @@ static int rpi_firmware_probe(struct platform_device *pdev) fw->chan = mbox_request_channel(&fw->cl, 0); if (IS_ERR(fw->chan)) { int ret = PTR_ERR(fw->chan); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get mbox channel: %d\n", ret); - return ret; + kfree(fw); + return dev_err_probe(dev, ret, "Failed to get mbox channel\n"); } init_completion(&fw->c); @@ -311,6 +330,18 @@ static int rpi_firmware_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id rpi_firmware_of_match[] = { + { .compatible = "raspberrypi,bcm2835-firmware", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rpi_firmware_of_match); + +struct device_node *rpi_firmware_find_node(void) +{ + return of_find_matching_node(NULL, rpi_firmware_of_match); +} +EXPORT_SYMBOL_GPL(rpi_firmware_find_node); + /** * rpi_firmware_get - Get pointer to rpi_firmware structure. * @firmware_node: Pointer to the firmware Device Tree node. @@ -366,12 +397,6 @@ struct rpi_firmware *devm_rpi_firmware_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_rpi_firmware_get); -static const struct of_device_id rpi_firmware_of_match[] = { - { .compatible = "raspberrypi,bcm2835-firmware", }, - {}, -}; -MODULE_DEVICE_TABLE(of, rpi_firmware_of_match); - static struct platform_driver rpi_firmware_driver = { .driver = { .name = "raspberrypi-firmware", diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig index 1c8ba1f47c7c..cde1ab8bd9d1 100644 --- a/drivers/firmware/tegra/Kconfig +++ b/drivers/firmware/tegra/Kconfig @@ -14,6 +14,7 @@ config TEGRA_IVC config TEGRA_BPMP bool "Tegra BPMP driver" depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC + depends on !CPU_BIG_ENDIAN help BPMP (Boot and Power Management Processor) is designed to off-loading the PM functions which include clock/DVFS/thermal/power from the CPU. diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c index 9d3874cdaaee..3ca2b5d9e66f 100644 --- a/drivers/firmware/tegra/bpmp-debugfs.c +++ b/drivers/firmware/tegra/bpmp-debugfs.c @@ -48,13 +48,9 @@ static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte) return seqbuf_status(seqbuf); } -static int seqbuf_read_u32(struct seqbuf *seqbuf, uint32_t *v) +static int seqbuf_read_u32(struct seqbuf *seqbuf, u32 *v) { - int err; - - err = seqbuf_read(seqbuf, v, 4); - *v = le32_to_cpu(*v); - return err; + return seqbuf_read(seqbuf, v, 4); } static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str) @@ -109,10 +105,10 @@ out: } static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name, - uint32_t *fd, uint32_t *len, bool write) + u32 *fd, u32 *len, bool write) { struct mrq_debug_request req = { - .cmd = cpu_to_le32(write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO), + .cmd = write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO, }; struct mrq_debug_response resp; struct tegra_bpmp_message msg = { @@ -147,10 +143,10 @@ static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name, return 0; } -static int mrq_debug_close(struct tegra_bpmp *bpmp, uint32_t fd) +static int mrq_debug_close(struct tegra_bpmp *bpmp, u32 fd) { struct mrq_debug_request req = { - .cmd = cpu_to_le32(CMD_DEBUG_CLOSE), + .cmd = CMD_DEBUG_CLOSE, .frd = { .fd = fd, }, @@ -179,10 +175,10 @@ static int mrq_debug_close(struct tegra_bpmp *bpmp, uint32_t fd) } static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name, - char *data, size_t sz_data, uint32_t *nbytes) + char *data, size_t sz_data, u32 *nbytes) { struct mrq_debug_request req = { - .cmd = cpu_to_le32(CMD_DEBUG_READ), + .cmd = CMD_DEBUG_READ, }; struct mrq_debug_response resp; struct tegra_bpmp_message msg = { @@ -196,7 +192,7 @@ static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name, .size = sizeof(resp), }, }; - uint32_t fd = 0, len = 0; + u32 fd = 0, len = 0; int remaining, err; mutex_lock(&bpmp_debug_lock); @@ -245,7 +241,7 @@ static int mrq_debug_write(struct tegra_bpmp *bpmp, const char *name, uint8_t *data, size_t sz_data) { struct mrq_debug_request req = { - .cmd = cpu_to_le32(CMD_DEBUG_WRITE) + .cmd = CMD_DEBUG_WRITE }; struct mrq_debug_response resp; struct tegra_bpmp_message msg = { @@ -259,7 +255,7 @@ static int mrq_debug_write(struct tegra_bpmp *bpmp, const char *name, .size = sizeof(resp), }, }; - uint32_t fd = 0, len = 0; + u32 fd = 0, len = 0; size_t remaining; int err; @@ -308,7 +304,7 @@ static int bpmp_debug_show(struct seq_file *m, void *p) char fnamebuf[256]; const char *filename; struct mrq_debug_request req = { - .cmd = cpu_to_le32(CMD_DEBUG_READ), + .cmd = CMD_DEBUG_READ, }; struct mrq_debug_response resp; struct tegra_bpmp_message msg = { @@ -322,7 +318,7 @@ static int bpmp_debug_show(struct seq_file *m, void *p) .size = sizeof(resp), }, }; - uint32_t fd = 0, len = 0; + u32 fd = 0, len = 0; int remaining, err; filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf)); @@ -406,8 +402,8 @@ static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp, { const size_t pathlen = SZ_256; const size_t bufsize = SZ_16K; - uint32_t dsize, attrs = 0; struct dentry *dentry; + u32 dsize, attrs = 0; struct seqbuf seqbuf; char *buf, *pathbuf; const char *name; @@ -487,12 +483,12 @@ static int mrq_debugfs_read(struct tegra_bpmp *bpmp, size_t *nbytes) { struct mrq_debugfs_request req = { - .cmd = cpu_to_le32(CMD_DEBUGFS_READ), + .cmd = CMD_DEBUGFS_READ, .fop = { - .fnameaddr = cpu_to_le32((uint32_t)name), - .fnamelen = cpu_to_le32((uint32_t)sz_name), - .dataaddr = cpu_to_le32((uint32_t)data), - .datalen = cpu_to_le32((uint32_t)sz_data), + .fnameaddr = (u32)name, + .fnamelen = (u32)sz_name, + .dataaddr = (u32)data, + .datalen = (u32)sz_data, }, }; struct mrq_debugfs_response resp; @@ -525,12 +521,12 @@ static int mrq_debugfs_write(struct tegra_bpmp *bpmp, dma_addr_t data, size_t sz_data) { const struct mrq_debugfs_request req = { - .cmd = cpu_to_le32(CMD_DEBUGFS_WRITE), + .cmd = CMD_DEBUGFS_WRITE, .fop = { - .fnameaddr = cpu_to_le32((uint32_t)name), - .fnamelen = cpu_to_le32((uint32_t)sz_name), - .dataaddr = cpu_to_le32((uint32_t)data), - .datalen = cpu_to_le32((uint32_t)sz_data), + .fnameaddr = (u32)name, + .fnamelen = (u32)sz_name, + .dataaddr = (u32)data, + .datalen = (u32)sz_data, }, }; struct tegra_bpmp_message msg = { @@ -548,10 +544,10 @@ static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr, size_t size, size_t *nbytes) { const struct mrq_debugfs_request req = { - .cmd = cpu_to_le32(CMD_DEBUGFS_DUMPDIR), + .cmd = CMD_DEBUGFS_DUMPDIR, .dumpdir = { - .dataaddr = cpu_to_le32((uint32_t)addr), - .datalen = cpu_to_le32((uint32_t)size), + .dataaddr = (u32)addr, + .datalen = (u32)size, }, }; struct mrq_debugfs_response resp; @@ -688,10 +684,10 @@ static const struct file_operations debugfs_fops = { }; static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf, - struct dentry *parent, uint32_t depth) + struct dentry *parent, u32 depth) { int err; - uint32_t d, t; + u32 d, t; const char *name; struct dentry *dentry; diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c index 63ab21d89c2c..2e26199041cd 100644 --- a/drivers/firmware/tegra/bpmp-tegra186.c +++ b/drivers/firmware/tegra/bpmp-tegra186.c @@ -18,8 +18,8 @@ struct tegra186_bpmp { struct { struct gen_pool *pool; + void __iomem *virt; dma_addr_t phys; - void *virt; } tx, rx; struct { @@ -40,31 +40,27 @@ mbox_client_to_bpmp(struct mbox_client *client) static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel) { - void *frame; + int err; - frame = tegra_ivc_read_get_next_frame(channel->ivc); - if (IS_ERR(frame)) { - channel->ib = NULL; + err = tegra_ivc_read_get_next_frame(channel->ivc, &channel->ib); + if (err) { + iosys_map_clear(&channel->ib); return false; } - channel->ib = frame; - return true; } static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel) { - void *frame; + int err; - frame = tegra_ivc_write_get_next_frame(channel->ivc); - if (IS_ERR(frame)) { - channel->ob = NULL; + err = tegra_ivc_write_get_next_frame(channel->ivc, &channel->ob); + if (err) { + iosys_map_clear(&channel->ob); return false; } - channel->ob = frame; - return true; } @@ -109,6 +105,7 @@ static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, { struct tegra186_bpmp *priv = bpmp->priv; size_t message_size, queue_size; + struct iosys_map rx, tx; unsigned int offset; int err; @@ -121,10 +118,11 @@ static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, queue_size = tegra_ivc_total_queue_size(message_size); offset = queue_size * index; - err = tegra_ivc_init(channel->ivc, NULL, - priv->rx.virt + offset, priv->rx.phys + offset, - priv->tx.virt + offset, priv->tx.phys + offset, - 1, message_size, tegra186_bpmp_ivc_notify, + iosys_map_set_vaddr_iomem(&rx, priv->rx.virt + offset); + iosys_map_set_vaddr_iomem(&tx, priv->tx.virt + offset); + + err = tegra_ivc_init(channel->ivc, NULL, &rx, priv->rx.phys + offset, &tx, + priv->tx.phys + offset, 1, message_size, tegra186_bpmp_ivc_notify, bpmp); if (err < 0) { dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", @@ -179,7 +177,7 @@ static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) return -EPROBE_DEFER; } - priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); + priv->tx.virt = (void __iomem *)gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); if (!priv->tx.virt) { dev_err(bpmp->dev, "failed to allocate from TX pool\n"); return -ENOMEM; @@ -192,7 +190,7 @@ static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) goto free_tx; } - priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); + priv->rx.virt = (void __iomem *)gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); if (!priv->rx.virt) { dev_err(bpmp->dev, "failed to allocate from RX pool\n"); err = -ENOMEM; diff --git a/drivers/firmware/tegra/bpmp-tegra210.c b/drivers/firmware/tegra/bpmp-tegra210.c index c9c830f658c3..6295f5640c15 100644 --- a/drivers/firmware/tegra/bpmp-tegra210.c +++ b/drivers/firmware/tegra/bpmp-tegra210.c @@ -137,8 +137,8 @@ static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel, unsigned int index) { struct tegra210_bpmp *priv = bpmp->priv; + void __iomem *p; u32 address; - void *p; /* Retrieve channel base address from BPMP */ writel(index << TRIGGER_ID_SHIFT | TRIGGER_CMD_GET, @@ -149,8 +149,9 @@ static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel, if (!p) return -ENOMEM; - channel->ib = p; - channel->ob = p; + iosys_map_set_vaddr_iomem(&channel->ib, p); + iosys_map_set_vaddr_iomem(&channel->ob, p); + channel->index = index; init_completion(&channel->completion); channel->bpmp = bpmp; @@ -199,10 +200,8 @@ static int tegra210_bpmp_init(struct tegra_bpmp *bpmp) } err = platform_get_irq_byname(pdev, "tx"); - if (err < 0) { - dev_err(&pdev->dev, "failed to get TX IRQ: %d\n", err); + if (err < 0) return err; - } priv->tx_irq_data = irq_get_irq_data(err); if (!priv->tx_irq_data) { @@ -211,10 +210,8 @@ static int tegra210_bpmp_init(struct tegra_bpmp *bpmp) } err = platform_get_irq_byname(pdev, "rx"); - if (err < 0) { - dev_err(&pdev->dev, "failed to get rx IRQ: %d\n", err); + if (err < 0) return err; - } err = devm_request_irq(&pdev->dev, err, rx_irq, IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp); diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 037db21de510..042c2043929d 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -201,13 +201,13 @@ static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, int err; if (data && size > 0) - memcpy_fromio(data, channel->ib->data, size); + tegra_bpmp_mb_read(data, &channel->ib, size); err = tegra_bpmp_ack_response(channel); if (err < 0) return err; - *ret = channel->ib->code; + *ret = tegra_bpmp_mb_read_field(&channel->ib, code); return 0; } @@ -241,11 +241,11 @@ static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, unsigned int mrq, unsigned long flags, const void *data, size_t size) { - channel->ob->code = mrq; - channel->ob->flags = flags; + tegra_bpmp_mb_write_field(&channel->ob, code, mrq); + tegra_bpmp_mb_write_field(&channel->ob, flags, flags); if (data && size > 0) - memcpy_toio(channel->ob->data, data, size); + tegra_bpmp_mb_write(&channel->ob, data, size); return tegra_bpmp_post_request(channel); } @@ -400,7 +400,7 @@ static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp, void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, const void *data, size_t size) { - unsigned long flags = channel->ib->flags; + unsigned long flags = tegra_bpmp_mb_read_field(&channel->ib, flags); struct tegra_bpmp *bpmp = channel->bpmp; int err; @@ -417,10 +417,10 @@ void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, if (WARN_ON(!tegra_bpmp_is_response_channel_free(channel))) return; - channel->ob->code = code; + tegra_bpmp_mb_write_field(&channel->ob, code, code); if (data && size > 0) - memcpy_toio(channel->ob->data, data, size); + tegra_bpmp_mb_write(&channel->ob, data, size); err = tegra_bpmp_post_response(channel); if (WARN_ON(err < 0)) @@ -502,7 +502,7 @@ EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq); bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) { - struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) }; + struct mrq_query_abi_request req = { .mrq = mrq }; struct mrq_query_abi_response resp; struct tegra_bpmp_message msg = { .mrq = MRQ_QUERY_ABI, @@ -529,13 +529,13 @@ static void tegra_bpmp_mrq_handle_ping(unsigned int mrq, struct tegra_bpmp_channel *channel, void *data) { - struct mrq_ping_request *request; + struct mrq_ping_request request; struct mrq_ping_response response; - request = (struct mrq_ping_request *)channel->ib->data; + tegra_bpmp_mb_read(&request, &channel->ib, sizeof(request)); memset(&response, 0, sizeof(response)); - response.reply = request->challenge << 1; + response.reply = request.challenge << 1; tegra_bpmp_mrq_return(channel, 0, &response, sizeof(response)); } @@ -648,7 +648,7 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) { - unsigned long flags = channel->ob->flags; + unsigned long flags = tegra_bpmp_mb_read_field(&channel->ob, flags); if ((flags & MSG_RING) == 0) return; @@ -666,8 +666,11 @@ void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) count = bpmp->soc->channels.thread.count; busy = bpmp->threaded.busy; - if (tegra_bpmp_is_request_ready(channel)) - tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel); + if (tegra_bpmp_is_request_ready(channel)) { + unsigned int mrq = tegra_bpmp_mb_read_field(&channel->ib, code); + + tegra_bpmp_handle_mrq(bpmp, mrq, channel); + } spin_lock(&bpmp->lock); diff --git a/drivers/firmware/tegra/ivc.c b/drivers/firmware/tegra/ivc.c index e2398cd7ca98..8c9aff9804c0 100644 --- a/drivers/firmware/tegra/ivc.c +++ b/drivers/firmware/tegra/ivc.c @@ -68,6 +68,12 @@ struct tegra_ivc_header { } rx; }; +#define tegra_ivc_header_read_field(hdr, field) \ + iosys_map_rd_field(hdr, 0, struct tegra_ivc_header, field) + +#define tegra_ivc_header_write_field(hdr, field, value) \ + iosys_map_wr_field(hdr, 0, struct tegra_ivc_header, field, value) + static inline void tegra_ivc_invalidate(struct tegra_ivc *ivc, dma_addr_t phys) { if (!ivc->peer) @@ -86,16 +92,15 @@ static inline void tegra_ivc_flush(struct tegra_ivc *ivc, dma_addr_t phys) DMA_TO_DEVICE); } -static inline bool tegra_ivc_empty(struct tegra_ivc *ivc, - struct tegra_ivc_header *header) +static inline bool tegra_ivc_empty(struct tegra_ivc *ivc, struct iosys_map *map) { /* * This function performs multiple checks on the same values with * security implications, so create snapshots with READ_ONCE() to * ensure that these checks use the same values. */ - u32 tx = READ_ONCE(header->tx.count); - u32 rx = READ_ONCE(header->rx.count); + u32 tx = tegra_ivc_header_read_field(map, tx.count); + u32 rx = tegra_ivc_header_read_field(map, rx.count); /* * Perform an over-full check to prevent denial of service attacks @@ -113,11 +118,10 @@ static inline bool tegra_ivc_empty(struct tegra_ivc *ivc, return tx == rx; } -static inline bool tegra_ivc_full(struct tegra_ivc *ivc, - struct tegra_ivc_header *header) +static inline bool tegra_ivc_full(struct tegra_ivc *ivc, struct iosys_map *map) { - u32 tx = READ_ONCE(header->tx.count); - u32 rx = READ_ONCE(header->rx.count); + u32 tx = tegra_ivc_header_read_field(map, tx.count); + u32 rx = tegra_ivc_header_read_field(map, rx.count); /* * Invalid cases where the counters indicate that the queue is over @@ -126,11 +130,10 @@ static inline bool tegra_ivc_full(struct tegra_ivc *ivc, return tx - rx >= ivc->num_frames; } -static inline u32 tegra_ivc_available(struct tegra_ivc *ivc, - struct tegra_ivc_header *header) +static inline u32 tegra_ivc_available(struct tegra_ivc *ivc, struct iosys_map *map) { - u32 tx = READ_ONCE(header->tx.count); - u32 rx = READ_ONCE(header->rx.count); + u32 tx = tegra_ivc_header_read_field(map, tx.count); + u32 rx = tegra_ivc_header_read_field(map, rx.count); /* * This function isn't expected to be used in scenarios where an @@ -143,8 +146,9 @@ static inline u32 tegra_ivc_available(struct tegra_ivc *ivc, static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc) { - WRITE_ONCE(ivc->tx.channel->tx.count, - READ_ONCE(ivc->tx.channel->tx.count) + 1); + unsigned int count = tegra_ivc_header_read_field(&ivc->tx.map, tx.count); + + tegra_ivc_header_write_field(&ivc->tx.map, tx.count, count + 1); if (ivc->tx.position == ivc->num_frames - 1) ivc->tx.position = 0; @@ -154,8 +158,9 @@ static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc) static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc) { - WRITE_ONCE(ivc->rx.channel->rx.count, - READ_ONCE(ivc->rx.channel->rx.count) + 1); + unsigned int count = tegra_ivc_header_read_field(&ivc->rx.map, rx.count); + + tegra_ivc_header_write_field(&ivc->rx.map, rx.count, count + 1); if (ivc->rx.position == ivc->num_frames - 1) ivc->rx.position = 0; @@ -166,6 +171,7 @@ static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc) static inline int tegra_ivc_check_read(struct tegra_ivc *ivc) { unsigned int offset = offsetof(struct tegra_ivc_header, tx.count); + unsigned int state; /* * tx.channel->state is set locally, so it is not synchronized with @@ -175,7 +181,8 @@ static inline int tegra_ivc_check_read(struct tegra_ivc *ivc) * asynchronous transition of rx.channel->state to * TEGRA_IVC_STATE_ACK is not allowed. */ - if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED) + state = tegra_ivc_header_read_field(&ivc->tx.map, tx.state); + if (state != TEGRA_IVC_STATE_ESTABLISHED) return -ECONNRESET; /* @@ -185,12 +192,12 @@ static inline int tegra_ivc_check_read(struct tegra_ivc *ivc) * Synchronization is only necessary when these pointers indicate * empty or full. */ - if (!tegra_ivc_empty(ivc, ivc->rx.channel)) + if (!tegra_ivc_empty(ivc, &ivc->rx.map)) return 0; tegra_ivc_invalidate(ivc, ivc->rx.phys + offset); - if (tegra_ivc_empty(ivc, ivc->rx.channel)) + if (tegra_ivc_empty(ivc, &ivc->rx.map)) return -ENOSPC; return 0; @@ -199,29 +206,34 @@ static inline int tegra_ivc_check_read(struct tegra_ivc *ivc) static inline int tegra_ivc_check_write(struct tegra_ivc *ivc) { unsigned int offset = offsetof(struct tegra_ivc_header, rx.count); + unsigned int state; - if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED) + state = tegra_ivc_header_read_field(&ivc->tx.map, tx.state); + if (state != TEGRA_IVC_STATE_ESTABLISHED) return -ECONNRESET; - if (!tegra_ivc_full(ivc, ivc->tx.channel)) + if (!tegra_ivc_full(ivc, &ivc->tx.map)) return 0; tegra_ivc_invalidate(ivc, ivc->tx.phys + offset); - if (tegra_ivc_full(ivc, ivc->tx.channel)) + if (tegra_ivc_full(ivc, &ivc->tx.map)) return -ENOSPC; return 0; } -static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc, - struct tegra_ivc_header *header, - unsigned int frame) +static int tegra_ivc_frame_virt(struct tegra_ivc *ivc, const struct iosys_map *header, + unsigned int frame, struct iosys_map *map) { + size_t offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame; + if (WARN_ON(frame >= ivc->num_frames)) - return ERR_PTR(-EINVAL); + return -EINVAL; - return (void *)(header + 1) + ivc->frame_size * frame; + *map = IOSYS_MAP_INIT_OFFSET(header, offset); + + return 0; } static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc, @@ -264,16 +276,16 @@ static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc, } /* directly peek at the next frame rx'ed */ -void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc) +int tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc, struct iosys_map *map) { int err; if (WARN_ON(ivc == NULL)) - return ERR_PTR(-EINVAL); + return -EINVAL; err = tegra_ivc_check_read(ivc); if (err < 0) - return ERR_PTR(err); + return err; /* * Order observation of ivc->rx.position potentially indicating new @@ -284,7 +296,7 @@ void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc) tegra_ivc_invalidate_frame(ivc, ivc->rx.phys, ivc->rx.position, 0, ivc->frame_size); - return tegra_ivc_frame_virt(ivc, ivc->rx.channel, ivc->rx.position); + return tegra_ivc_frame_virt(ivc, &ivc->rx.map, ivc->rx.position, map); } EXPORT_SYMBOL(tegra_ivc_read_get_next_frame); @@ -320,7 +332,7 @@ int tegra_ivc_read_advance(struct tegra_ivc *ivc) */ tegra_ivc_invalidate(ivc, ivc->rx.phys + tx); - if (tegra_ivc_available(ivc, ivc->rx.channel) == ivc->num_frames - 1) + if (tegra_ivc_available(ivc, &ivc->rx.map) == ivc->num_frames - 1) ivc->notify(ivc, ivc->notify_data); return 0; @@ -328,15 +340,15 @@ int tegra_ivc_read_advance(struct tegra_ivc *ivc) EXPORT_SYMBOL(tegra_ivc_read_advance); /* directly poke at the next frame to be tx'ed */ -void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc) +int tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc, struct iosys_map *map) { int err; err = tegra_ivc_check_write(ivc); if (err < 0) - return ERR_PTR(err); + return err; - return tegra_ivc_frame_virt(ivc, ivc->tx.channel, ivc->tx.position); + return tegra_ivc_frame_virt(ivc, &ivc->tx.map, ivc->tx.position, map); } EXPORT_SYMBOL(tegra_ivc_write_get_next_frame); @@ -376,7 +388,7 @@ int tegra_ivc_write_advance(struct tegra_ivc *ivc) */ tegra_ivc_invalidate(ivc, ivc->tx.phys + rx); - if (tegra_ivc_available(ivc, ivc->tx.channel) == 1) + if (tegra_ivc_available(ivc, &ivc->tx.map) == 1) ivc->notify(ivc, ivc->notify_data); return 0; @@ -387,7 +399,7 @@ void tegra_ivc_reset(struct tegra_ivc *ivc) { unsigned int offset = offsetof(struct tegra_ivc_header, tx.count); - ivc->tx.channel->tx.state = TEGRA_IVC_STATE_SYNC; + tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_SYNC); tegra_ivc_flush(ivc, ivc->tx.phys + offset); ivc->notify(ivc, ivc->notify_data); } @@ -416,13 +428,14 @@ EXPORT_SYMBOL(tegra_ivc_reset); int tegra_ivc_notified(struct tegra_ivc *ivc) { unsigned int offset = offsetof(struct tegra_ivc_header, tx.count); - enum tegra_ivc_state state; + enum tegra_ivc_state rx_state, tx_state; /* Copy the receiver's state out of shared memory. */ tegra_ivc_invalidate(ivc, ivc->rx.phys + offset); - state = READ_ONCE(ivc->rx.channel->tx.state); + rx_state = tegra_ivc_header_read_field(&ivc->rx.map, tx.state); + tx_state = tegra_ivc_header_read_field(&ivc->tx.map, tx.state); - if (state == TEGRA_IVC_STATE_SYNC) { + if (rx_state == TEGRA_IVC_STATE_SYNC) { offset = offsetof(struct tegra_ivc_header, tx.count); /* @@ -436,8 +449,8 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) * state and won't make progress until we change our state, * so the counters are not in use at this time. */ - ivc->tx.channel->tx.count = 0; - ivc->rx.channel->rx.count = 0; + tegra_ivc_header_write_field(&ivc->tx.map, tx.count, 0); + tegra_ivc_header_write_field(&ivc->rx.map, rx.count, 0); ivc->tx.position = 0; ivc->rx.position = 0; @@ -452,7 +465,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) * Move to ACK state. We have just cleared our counters, so it * is now safe for the remote end to start using these values. */ - ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ACK; + tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_ACK); tegra_ivc_flush(ivc, ivc->tx.phys + offset); /* @@ -460,8 +473,8 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) */ ivc->notify(ivc, ivc->notify_data); - } else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_SYNC && - state == TEGRA_IVC_STATE_ACK) { + } else if (tx_state == TEGRA_IVC_STATE_SYNC && + rx_state == TEGRA_IVC_STATE_ACK) { offset = offsetof(struct tegra_ivc_header, tx.count); /* @@ -475,8 +488,8 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) * state and won't make progress until we change our state, * so the counters are not in use at this time. */ - ivc->tx.channel->tx.count = 0; - ivc->rx.channel->rx.count = 0; + tegra_ivc_header_write_field(&ivc->tx.map, tx.count, 0); + tegra_ivc_header_write_field(&ivc->rx.map, rx.count, 0); ivc->tx.position = 0; ivc->rx.position = 0; @@ -492,7 +505,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) * already cleared its counters, so it is safe to start * writing/reading on this channel. */ - ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED; + tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_ESTABLISHED); tegra_ivc_flush(ivc, ivc->tx.phys + offset); /* @@ -500,7 +513,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) */ ivc->notify(ivc, ivc->notify_data); - } else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_ACK) { + } else if (tx_state == TEGRA_IVC_STATE_ACK) { offset = offsetof(struct tegra_ivc_header, tx.count); /* @@ -516,7 +529,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) * cleared its counters, so it is safe to start writing/reading * on this channel. */ - ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED; + tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_ESTABLISHED); tegra_ivc_flush(ivc, ivc->tx.phys + offset); /* @@ -533,7 +546,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc) */ } - if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED) + if (tx_state != TEGRA_IVC_STATE_ESTABLISHED) return -EAGAIN; return 0; @@ -609,8 +622,29 @@ static int tegra_ivc_check_params(unsigned long rx, unsigned long tx, return 0; } -int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx, - dma_addr_t rx_phys, void *tx, dma_addr_t tx_phys, +static inline void iosys_map_copy(struct iosys_map *dst, const struct iosys_map *src) +{ + *dst = *src; +} + +static inline unsigned long iosys_map_get_address(const struct iosys_map *map) +{ + if (map->is_iomem) + return (unsigned long)map->vaddr_iomem; + + return (unsigned long)map->vaddr; +} + +static inline void *iosys_map_get_vaddr(const struct iosys_map *map) +{ + if (WARN_ON(map->is_iomem)) + return NULL; + + return map->vaddr; +} + +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, const struct iosys_map *rx, + dma_addr_t rx_phys, const struct iosys_map *tx, dma_addr_t tx_phys, unsigned int num_frames, size_t frame_size, void (*notify)(struct tegra_ivc *ivc, void *data), void *data) @@ -628,7 +662,7 @@ int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx, if (frame_size > INT_MAX) return -E2BIG; - err = tegra_ivc_check_params((unsigned long)rx, (unsigned long)tx, + err = tegra_ivc_check_params(iosys_map_get_address(rx), iosys_map_get_address(tx), num_frames, frame_size); if (err < 0) return err; @@ -636,12 +670,12 @@ int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx, queue_size = tegra_ivc_total_queue_size(num_frames * frame_size); if (peer) { - ivc->rx.phys = dma_map_single(peer, rx, queue_size, + ivc->rx.phys = dma_map_single(peer, iosys_map_get_vaddr(rx), queue_size, DMA_BIDIRECTIONAL); if (dma_mapping_error(peer, ivc->rx.phys)) return -ENOMEM; - ivc->tx.phys = dma_map_single(peer, tx, queue_size, + ivc->tx.phys = dma_map_single(peer, iosys_map_get_vaddr(tx), queue_size, DMA_BIDIRECTIONAL); if (dma_mapping_error(peer, ivc->tx.phys)) { dma_unmap_single(peer, ivc->rx.phys, queue_size, @@ -653,8 +687,8 @@ int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx, ivc->tx.phys = tx_phys; } - ivc->rx.channel = rx; - ivc->tx.channel = tx; + iosys_map_copy(&ivc->rx.map, rx); + iosys_map_copy(&ivc->tx.map, tx); ivc->peer = peer; ivc->notify = notify; ivc->notify_data = data; diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index ebc32bbd9b83..039d92a595ec 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -429,15 +429,14 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info, * during noirq phase, so we must manually poll the completion. */ ret = read_poll_timeout_atomic(try_wait_for_completion, done_state, - true, 1, + done_state, 1, info->desc->max_rx_timeout_ms * 1000, false, &xfer->done); } - if (ret == -ETIMEDOUT || !done_state) { + if (ret == -ETIMEDOUT) dev_err(dev, "Mbox timedout in resp(caller: %pS)\n", (void *)_RET_IP_); - } /* * NOTE: we might prefer not to need the mailbox ticker to manage the @@ -3096,7 +3095,7 @@ u16 ti_sci_get_free_resource(struct ti_sci_resource *res) free_bit = find_first_zero_bit(desc->res_map, res_count); if (free_bit != res_count) { - set_bit(free_bit, desc->res_map); + __set_bit(free_bit, desc->res_map); raw_spin_unlock_irqrestore(&res->lock, flags); if (desc->num && free_bit < desc->num) @@ -3127,10 +3126,10 @@ void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) if (desc->num && desc->start <= id && (desc->start + desc->num) > id) - clear_bit(id - desc->start, desc->res_map); + __clear_bit(id - desc->start, desc->res_map); else if (desc->num_sec && desc->start_sec <= id && (desc->start_sec + desc->num_sec) > id) - clear_bit(id - desc->start_sec, desc->res_map); + __clear_bit(id - desc->start_sec, desc->res_map); } raw_spin_unlock_irqrestore(&res->lock, flags); } @@ -3201,9 +3200,8 @@ devm_ti_sci_get_resource_sets(const struct ti_sci_handle *handle, valid_set = true; res_count = res->desc[i].num + res->desc[i].num_sec; - res->desc[i].res_map = - devm_kzalloc(dev, BITS_TO_LONGS(res_count) * - sizeof(*res->desc[i].res_map), GFP_KERNEL); + res->desc[i].res_map = devm_bitmap_zalloc(dev, res_count, + GFP_KERNEL); if (!res->desc[i].res_map) return ERR_PTR(-ENOMEM); } @@ -3400,13 +3398,11 @@ static int ti_sci_probe(struct platform_device *pdev) if (!minfo->xfer_block) return -ENOMEM; - minfo->xfer_alloc_table = devm_kcalloc(dev, - BITS_TO_LONGS(desc->max_msgs), - sizeof(unsigned long), - GFP_KERNEL); + minfo->xfer_alloc_table = devm_bitmap_zalloc(dev, + desc->max_msgs, + GFP_KERNEL); if (!minfo->xfer_alloc_table) return -ENOMEM; - bitmap_zero(minfo->xfer_alloc_table, desc->max_msgs); /* Pre-initialize the buffer pointer to pre-allocated buffers */ for (i = 0, xfer = minfo->xfer_block; i < desc->max_msgs; i++, xfer++) { diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c index c2d34dc8ba46..6ea5789a89e2 100644 --- a/drivers/firmware/turris-mox-rwtm.c +++ b/drivers/firmware/turris-mox-rwtm.c @@ -528,7 +528,6 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev) rwtm->hwrng.name = DRIVER_NAME "_hwrng"; rwtm->hwrng.read = mox_hwrng_read; rwtm->hwrng.priv = (unsigned long) rwtm; - rwtm->hwrng.quality = 1024; ret = devm_hwrng_register(dev, &rwtm->hwrng); if (ret < 0) { diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index ff5cabe70a2b..6bc6b6c84241 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -843,6 +843,13 @@ int zynqmp_pm_read_pggs(u32 index, u32 *value) } EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs); +int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value) +{ + return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_TAPDELAY_BYPASS, + index, value, NULL); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass); + /** * zynqmp_pm_set_boot_health_status() - PM API for setting healthy boot status * @value: Status value to be written |