Add functions that handle loading the FIT image nodes having the Qclib executable and executing it. Also provides 'weak' hooks that can be overridden by SoC specific implementations.
Signed-off-by: Varadarajan Narayanan <[email protected]> --- arch/arm/mach-snapdragon/Makefile | 1 + arch/arm/mach-snapdragon/include/mach/qclib.h | 82 +++++++++++++ arch/arm/mach-snapdragon/qclib.c | 160 ++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile index 2701f7c6fb1..fc39f7381b3 100644 --- a/arch/arm/mach-snapdragon/Makefile +++ b/arch/arm/mach-snapdragon/Makefile @@ -9,6 +9,7 @@ ifeq ($(CONFIG_SPL_BUILD),y) obj-y += board_spl.o obj-y += fit-handler.o obj-y += pbl-shared-data.o +obj-y += qclib.o else obj-y += board.o obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o diff --git a/arch/arm/mach-snapdragon/include/mach/qclib.h b/arch/arm/mach-snapdragon/include/mach/qclib.h new file mode 100644 index 00000000000..b44874c514e --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/qclib.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QCLIB_H__ +#define __QCLIB_H__ + +#include <linux/bitops.h> +#include <linux/types.h> + +#define MAGIC_KEY "QCLIB_CB" +#define MAX_ENTRIES 0xF +#define IF_TABLE_VERSION 0x1 +#define QCSDI "qcsdi" +#define QCLIB_LOG_BUFFER "qclib_log_buffer" + +/* interface_table.global_attributes bits */ +#define QCLIB_GA_ENABLE_UART_LOGGING BIT(0) + +/** + * struct interface_table_entry - Meta data for blobs in QCLIB interface + * @entry_name: Name of the data blob (e.g., "dcb_settings"). + * @address: Address of the data blob. + * @size: Size of the data blob. + * @attributes: Attributes for the blob (e.g., save to storage). + */ +struct interface_table_entry { + char entry_name[24]; + u64 address; + u32 size; + u32 attributes; +}; + +/** + * struct interface_table - QCLIB Interface table header + * @magic_key: Magic key for validation ("QCLIB_CB"). + * @version: Interface table version. + * @num_entries: Number of valid entries. + * @max_entries: Maximum allowable entries. + * @global_attributes: Flags for global attributes (e.g., SDI path). + * @reserved1: Reserved for future use. + * @reserved2: Reserved for future use. + * @if_table_entries: Array of interface table entries. + */ +struct interface_table { + char magic_key[8]; + u32 version; + u32 num_entries; + u32 max_entries; + u32 global_attributes; + u32 reserved1; + u32 reserved2; + struct interface_table_entry if_table_entries[MAX_ENTRIES]; +}; + +/* Exported by fit-handler.c */ +int qcom_spl_get_fit_img_entry_point(void *fit, int node, u64 *entry_point); +int qcom_spl_get_iftbl_entry_by_name(struct interface_table *if_tbl, + char *name, + struct interface_table_entry *entry); + +/* QCSDI address accessors, populated post-QCLIB, consumed by fit-handler.c */ +u64 qclib_get_qcsdi_address(void); +void qclib_set_qcsdi_address(u64 address); + +/* qclib_log_buffer address accessors, populated post-QCLIB */ +u64 qclib_get_log_buffer_entry(void); +void qclib_set_log_buffer_entry(u64 address); + +/* + * __weak SoC extension points. A board's spl-<soc>.c may provide a strong + * override for any of these; the defaults in qclib.c are all no-ops. + */ +bool qcom_spl_soc_check_dload_mode(void); +int qcom_spl_soc_pre_qclib_routine(void); +int qcom_spl_soc_qclib_override(struct interface_table *table, const void *fit, + int images_node); +int qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl); + +int qclib_populate_interface_table(struct interface_table *if_tbl, + const void *fit, int images_node); +int qcom_spl_invoke_qclib(void); + +#endif /* __QCLIB_H__ */ diff --git a/arch/arm/mach-snapdragon/qclib.c b/arch/arm/mach-snapdragon/qclib.c new file mode 100644 index 00000000000..7f25f37b5d9 --- /dev/null +++ b/arch/arm/mach-snapdragon/qclib.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * QCLIB interface-table protocol: generic, SoC-agnostic orchestration of the + * hand-off to the QCLIB firmware image, plus __weak extension points a + * per-board spl-<soc>.c may override to populate SoC-specific table entries. + */ + +#include <cpu_func.h> +#include <errno.h> +#include <fdt_support.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> +#include <mach/qclib.h> + +static u64 g_qcsdi_address __section(".data"); +static u64 g_log_buffer_entry __section(".data"); + +u64 qclib_get_qcsdi_address(void) +{ + return g_qcsdi_address; +} + +void qclib_set_qcsdi_address(u64 addr) +{ + g_qcsdi_address = addr; +} + +u64 qclib_get_log_buffer_entry(void) +{ + return g_log_buffer_entry; +} + +void qclib_set_log_buffer_entry(u64 address) +{ + g_log_buffer_entry = address; +} + +bool __weak qcom_spl_soc_check_dload_mode(void) +{ + return false; +} + +int __weak qcom_spl_soc_pre_qclib_routine(void) +{ + return 0; +} + +int __weak qcom_spl_soc_qclib_override(struct interface_table *table, + const void *fit, int images_node) +{ + return 0; +} + +int __weak qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl) +{ + return 0; +} + +int __weak qclib_populate_interface_table(struct interface_table *if_tbl, + const void *fit, int images_node) +{ + struct interface_table_entry *log_buffer_entry; + + memset(if_tbl, 0, sizeof(struct interface_table)); + memcpy(if_tbl->magic_key, MAGIC_KEY, strlen(MAGIC_KEY)); + if_tbl->version = IF_TABLE_VERSION; + if_tbl->num_entries = 0; + if_tbl->max_entries = MAX_ENTRIES; + + /* + * Reserve the "qclib_log_buffer" slot — common to every SoC. + * QCLIB fills in the address itself; SPL just allocates the entry. + */ + log_buffer_entry = &if_tbl->if_table_entries[if_tbl->num_entries]; + memcpy(log_buffer_entry->entry_name, QCLIB_LOG_BUFFER, + strlen(QCLIB_LOG_BUFFER)); + log_buffer_entry->address = 0; + log_buffer_entry->size = 0; + log_buffer_entry->attributes = 0; + if_tbl->num_entries++; + + return qcom_spl_soc_qclib_override(if_tbl, fit, images_node); +} + +static int qclib_find_fit_nodes(const void *fit, int *images_node, + int *qclib_node) +{ + *images_node = fdt_subnode_offset(fit, 0, "images"); + if (*images_node < 0) { + pr_err("Failed to find images node in FIT\n"); + return -ENOENT; + } + + *qclib_node = fdt_subnode_offset(fit, *images_node, "qclib_1"); + if (*qclib_node < 0) { + pr_err("Failed to find qclib_1 node in FIT\n"); + return -ENOENT; + } + + return 0; +} + +int qcom_spl_invoke_qclib(void) +{ + int ret; + int images_node; + int qclib_node; + const void *fit; + struct interface_table if_tbl; + u64 entry_point; + + /* Get FIT image from SPL load address */ + fit = (const void *)CONFIG_SPL_LOAD_FIT_ADDRESS; + + pr_debug("QCLIB invoke: fit=%p\n", fit); + + ret = qclib_find_fit_nodes(fit, &images_node, &qclib_node); + if (ret) + return ret; + + ret = qcom_spl_soc_pre_qclib_routine(); + if (ret) + return ret; + + ret = qclib_populate_interface_table(&if_tbl, fit, images_node); + if (ret) + return ret; + + ret = qcom_spl_get_fit_img_entry_point((void *)fit, qclib_node, &entry_point); + if (ret) { + pr_err("Failed to get qcom-lib-1 entry point (%d)\n", ret); + return ret; + } + + pr_info("Jumping to qcom-lib-1 at 0x%llx\n", entry_point); + + /* + * QCLIB is a separately-linked firmware image, not an AAPCS64 + * callee - a plain C function-pointer call risks the compiler + * caching a live value in a callee-saved register across the jump. + * Pin the args/target to fixed registers and clobber everything the + * ABI doesn't guarantee QCLIB will preserve. + */ + { + register void *x0 asm("x0") = &if_tbl; + register void *x1 asm("x1") = NULL; + register u64 x8 asm("x8") = entry_point; + + asm volatile ("blr x8" + : "+r" (x0) + : "r" (x1), "r" (x8) + : "x2", "x3", "x4", "x5", "x6", "x7", "x9", "x10", + "x11", "x12", "x13", "x14", "x15", "x16", "x17", + "x19", "x20", "x21", "x22", "x23", "x24", "x25", + "x26", "x27", "x28", "x30", "cc", "memory"); + } + + return qcom_spl_soc_post_qclib_routine(&if_tbl); +} -- 2.34.1
