diff options
author | Alex Iacobucci <alexiacobucci@google.com> | 2023-05-11 22:24:36 +0000 |
---|---|---|
committer | Alex Iacobucci <alexiacobucci@google.com> | 2023-07-14 14:13:01 +0000 |
commit | 509a9573ada096ff90812243669896902ed35523 (patch) | |
tree | b41ce96c8b1db193754a175acee041d46b92b1b4 | |
parent | 997a693de9062e9302aa50bf7430dc5de157f9d7 (diff) | |
download | aoc-509a9573ada096ff90812243669896902ed35523.tar.gz |
aoc: configure iommu from fw headers
Bug: 282063178
Test: tested on device
Change-Id: I4c867c878683d1aa42d7493c74eda085502c5bf2
Signed-off-by: Alex Iacobucci <alexiacobucci@google.com>
-rw-r--r-- | aoc.c | 119 | ||||
-rw-r--r-- | aoc_firmware.c | 117 | ||||
-rw-r--r-- | aoc_firmware.h | 21 |
3 files changed, 170 insertions, 87 deletions
@@ -267,6 +267,9 @@ static int aoc_bus_match(struct device *dev, struct device_driver *drv); static int aoc_bus_probe(struct device *dev); static int aoc_bus_remove(struct device *dev); +static void aoc_configure_sysmmu(struct aoc_prvdata *p, const struct firmware *fw); +static void aoc_configure_sysmmu_manual(struct aoc_prvdata *p); + static struct bus_type aoc_bus_type = { .name = "aoc", .match = aoc_bus_match, @@ -749,7 +752,7 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx) phys_addr_t playback_heap = aoc_dram_translate_to_aoc(prvdata, prvdata->audio_playback_heap_base); phys_addr_t capture_heap = aoc_dram_translate_to_aoc(prvdata, prvdata->audio_capture_heap_base); unsigned int i; - bool fw_signed; + bool fw_signed, gsa_enabled; struct aoc_fw_data fw_data[] = { { .key = kAOCBoardID, .value = board_id }, @@ -841,13 +844,22 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx) _aoc_fw_commit(fw, aoc_dram_virt_mapping + AOC_BINARY_DRAM_OFFSET); + gsa_enabled = of_property_read_bool(prvdata->dev->of_node, "gsa-enabled"); + if (fw_signed) { - int rc = aoc_fw_authenticate(prvdata, fw); - if (rc) { - dev_err(dev, "GSA: FW authentication failed: %d\n", rc); - goto free_fw; + if (gsa_enabled) { + int rc = aoc_fw_authenticate(prvdata, fw); + + if (rc) { + dev_err(dev, "GSA: FW authentication failed: %d\n", rc); + goto free_fw; + } + } else { + aoc_configure_sysmmu(prvdata, fw); + write_reset_trampoline(AOC_BINARY_LOAD_ADDRESS + bootloader_offset); } } else { + aoc_configure_sysmmu_manual(prvdata); write_reset_trampoline(AOC_BINARY_LOAD_ADDRESS + bootloader_offset); } @@ -861,7 +873,7 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx) prvdata->ipc_base = aoc_dram_translate(prvdata, ipc_offset); /* start AOC */ - if (fw_signed) { + if (fw_signed && gsa_enabled) { int rc = gsa_send_aoc_cmd(prvdata->gsa_dev, GSA_AOC_START); if (rc < 0) { dev_err(dev, "GSA: Failed to start AOC: %d\n", rc); @@ -2096,83 +2108,46 @@ static inline void aoc_configure_ssmt( struct platform_device *pdev __attribute__((unused))) { } #endif -static void aoc_configure_sysmmu(struct aoc_prvdata *p) +static void aoc_configure_sysmmu(struct aoc_prvdata *p, const struct firmware *fw) { #ifndef AOC_JUNO + int rc; + size_t i, cnt; + struct sysmmu_entry *sysmmu; struct iommu_domain *domain = p->domain; struct device *dev = p->dev; - int rc; + u16 sysmmu_offset, sysmmu_size; rc = iommu_register_device_fault_handler(dev, aoc_iommu_fault_handler, dev); if (rc) dev_err(dev, "iommu_register_device_fault_handler failed: rc = %d\n", rc); - /* Map in the AoC carveout */ - if (iommu_map(domain, 0x98000000, p->dram_resource.start, p->dram_size, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping carveout failed\n"); - -#if IS_ENABLED(CONFIG_SOC_GS201) - /* Use a 1MB mapping instead of individual mailboxes for now */ - /* TODO: Turn the mailbox address ranges into dtb entries */ - if (iommu_map(domain, 0x9E000000, 0x18200000, SZ_2M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping mailboxes failed\n"); - - /* Map in GSA mailbox */ - if (iommu_map(domain, 0x9E200000, 0x17C00000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping gsa mailbox failed\n"); - - /* Map in modem registers */ - if (iommu_map(domain, 0x9E300000, 0x40000000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping modem failed\n"); - - /* Map in BLK_TPU */ - /* if (iommu_map(domain, 0x9E600000, 0x1CE00000, SZ_2M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping mailboxes failed\n"); */ - - /* Map in the xhci_dma carveout */ - if (iommu_map(domain, 0x9B000000, 0x97000000, SZ_4M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping xhci_dma carveout failed\n"); - - /* Map in USB for low power audio */ - if (iommu_map(domain, 0x9E500000, 0x11200000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping usb failed\n"); -#else - /* Map in the xhci_dma carveout */ - if (iommu_map(domain, 0x9B000000, 0x97000000, SZ_4M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping xhci_dma carveout failed\n"); - - /* Use a 1MB mapping instead of individual mailboxes for now */ - /* TODO: Turn the mailbox address ranges into dtb entries */ - if (iommu_map(domain, 0x9E000000, 0x17600000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping mailboxes failed\n"); - - /* Map in GSA mailbox */ - if (iommu_map(domain, 0x9E100000, 0x17C00000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping gsa mailbox failed\n"); - - /* Map in USB for low power audio */ - if (iommu_map(domain, 0x9E200000, 0x11100000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping usb failed\n"); - - /* Map in modem registers */ - if (iommu_map(domain, 0x9E300000, 0x40000000, SZ_1M, - IOMMU_READ | IOMMU_WRITE)) - dev_err(dev, "mapping modem failed\n"); -#endif + sysmmu_offset = _aoc_fw_sysmmu_offset(fw); + sysmmu_size = _aoc_fw_sysmmu_size(fw); + if (!_aoc_fw_is_valid_sysmmu_size(fw)) { + dev_info(dev, "Invalid sysmmu table (%u @ %u)\n", sysmmu_size, sysmmu_offset); + return; + } + cnt = sysmmu_size / sizeof(struct sysmmu_entry); + sysmmu = _aoc_fw_sysmmu_entry(fw); + for (i = 0; i < cnt; i++, sysmmu++) { + dev_info(dev, "Configuring sysmmu entry = 0x%llx 0x%llx 0x%llx\n", + SYSMMU_VADDR(sysmmu->value), SYSMMU_PADDR(sysmmu->value), + SYSMMU_SIZE(sysmmu->value)); + rc = iommu_map(domain, SYSMMU_VADDR(sysmmu->value), + SYSMMU_PADDR(sysmmu->value), + SYSMMU_SIZE(sysmmu->value), + IOMMU_READ | IOMMU_WRITE); + if (rc < 0) { + dev_err(dev, "sysmmu mapping failed: %d\n", rc); + return; + } + } #endif } +static void aoc_configure_sysmmu_manual(struct aoc_prvdata *p) {} + static void aoc_clear_sysmmu(struct aoc_prvdata *p) { #ifndef AOC_JUNO @@ -3385,8 +3360,6 @@ static int aoc_platform_probe(struct platform_device *pdev) aoc_configure_ssmt(pdev); - aoc_configure_sysmmu(prvdata); - if (!aoc_create_dma_buf_heaps(prvdata)) { pr_err("Unable to create dma_buf heaps\n"); aoc_cleanup_resources(pdev); diff --git a/aoc_firmware.c b/aoc_firmware.c index e883baf..5ae96aa 100644 --- a/aoc_firmware.c +++ b/aoc_firmware.c @@ -10,6 +10,8 @@ */ #define pr_fmt(fmt) "aoc-fw: " fmt +#define AOC_AUTH_HEADER_MAGIC_VALUE 0x00434F41 + #include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> @@ -22,17 +24,30 @@ struct aoc_auth_header { u8 signature[512]; u8 key[512]; - struct { - u32 magic; - u32 generation; - u32 rollback_info; - u32 length; - u8 flags [16]; - u8 hash [32]; - u8 chip_id [32]; - u8 auth_config [256]; - u8 image_config [256]; - } header; + union { + struct { + u32 magic; + u32 generation; + u32 rollback_info; + u32 length; + u8 flags [16]; + u8 hash [32]; + u8 chip_id [32]; + u8 auth_config [256]; + u8 image_config [256]; + } header_v1; + struct { + u32 magic; + u32 generation; + u32 rollback_info; + u32 length; + u8 flags [16]; + u8 hash [64]; + u8 chip_id [32]; + u8 auth_config [256]; + u8 image_config [256]; + } header_v2; + }; } __packed; struct aoc_superbin_header { @@ -62,6 +77,24 @@ struct aoc_superbin_header { u32 crc32; } __packed; +struct aoc_image_config { + union { + struct { + uint32_t version; + struct { + uint32_t fw_start; + uint32_t fw_size; + uint32_t privilege_level; + uint16_t bl_offset; + uint16_t bl_size; + uint16_t sysmmu_offset; + uint16_t sysmmu_size; + }; + }; + uint8_t raw_bytes[256]; + }; +}; + static u32 aoc_img_header_size(const struct firmware *fw) { if(_aoc_fw_is_signed(fw)) @@ -127,14 +160,70 @@ bool _aoc_fw_is_release(const struct firmware *fw) return (le32_to_cpu(header->release_type) == 1); } +u32 _aoc_fw_get_header_version(const struct firmware *fw) +{ + const struct aoc_auth_header *header = (const struct aoc_auth_header *)(fw->data); + if ((le32_to_cpu(header->header_v1.generation) == 1) && + le32_to_cpu(header->header_v1.magic) == AOC_AUTH_HEADER_MAGIC_VALUE) { + return 1; + } + + if ((le32_to_cpu(header->header_v2.generation) == 2) && + le32_to_cpu(header->header_v2.magic) == AOC_AUTH_HEADER_MAGIC_VALUE) { + return 2; + } + + return 0; +} + bool _aoc_fw_is_signed(const struct firmware *fw) { + return _aoc_fw_get_header_version(fw) != 0; +} + +struct aoc_image_config *_aoc_fw_image_config(const struct firmware *fw) +{ const struct aoc_auth_header *header = (const struct aoc_auth_header *)(fw->data); - if (le32_to_cpu(header->header.magic) != 0x00434F41) { - return false; + u32 header_version = _aoc_fw_get_header_version(fw); + + switch (header_version) { + case 1: + return (struct aoc_image_config *)&header->header_v1.image_config; + case 2: + return (struct aoc_image_config *)&header->header_v2.image_config; + default: + return (struct aoc_image_config *)&header->header_v1.image_config; } +} - return true; +uint16_t _aoc_fw_sysmmu_offset(const struct firmware *fw) +{ + struct aoc_image_config *cfg = _aoc_fw_image_config(fw); + + return cfg->sysmmu_offset; +} + +uint16_t _aoc_fw_sysmmu_size(const struct firmware *fw) +{ + struct aoc_image_config *cfg = _aoc_fw_image_config(fw); + + return cfg->sysmmu_size; +} + +bool _aoc_fw_is_valid_sysmmu_size(const struct firmware *fw) +{ + struct aoc_image_config *cfg = _aoc_fw_image_config(fw); + + return cfg->sysmmu_offset < sizeof(*cfg) && + (cfg->sysmmu_offset + cfg->sysmmu_size) <= sizeof(*cfg) && + (cfg->sysmmu_size % sizeof(struct sysmmu_entry) == 0); +} + +struct sysmmu_entry *_aoc_fw_sysmmu_entry(const struct firmware *fw) +{ + struct aoc_image_config *cfg = _aoc_fw_image_config(fw); + + return (struct sysmmu_entry *)((uint8_t *)cfg + cfg->sysmmu_offset); } bool _aoc_fw_is_compatible(const struct firmware *fw) diff --git a/aoc_firmware.h b/aoc_firmware.h index b481087..2b5834c 100644 --- a/aoc_firmware.h +++ b/aoc_firmware.h @@ -15,6 +15,15 @@ #define AOC_AUTH_HEADER_SIZE 4096 +struct sysmmu_entry { + uint64_t value; +}; + +/* Access macros to decode SYSMMU entry */ +#define SYSMMU_VADDR(v) ((((v) >> 0) & 0x000FFFFF) << 12) +#define SYSMMU_PADDR(v) ((((v) >> 20) & 0x00FFFFFF) << 12) +#define SYSMMU_SIZE(v) ((((v) >> 44) & 0x000FFFFF) << 12) + /* Dev builds bypass the UUID check on load */ bool _aoc_fw_is_release(const struct firmware *fw); @@ -32,3 +41,15 @@ u32 _aoc_fw_ipc_offset(const struct firmware *fw); const char *_aoc_fw_version(const struct firmware *fw); bool _aoc_fw_commit(const struct firmware *fw, void *dest); + +uint16_t _aoc_fw_sysmmu_offset(const struct firmware *fw); + +uint16_t _aoc_fw_sysmmu_size(const struct firmware *fw); + +bool _aoc_fw_is_valid_sysmmu_size(const struct firmware *fw); + +struct sysmmu_entry *_aoc_fw_sysmmu_entry(const struct firmware *fw); + +struct aoc_image_config *_aoc_fw_image_config(const struct firmware *fw); + +u32 _aoc_fw_get_header_version(const struct firmware *fw); |