summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Iacobucci <alexiacobucci@google.com>2023-05-11 22:24:36 +0000
committerAlex Iacobucci <alexiacobucci@google.com>2023-07-14 14:13:01 +0000
commit509a9573ada096ff90812243669896902ed35523 (patch)
treeb41ce96c8b1db193754a175acee041d46b92b1b4
parent997a693de9062e9302aa50bf7430dc5de157f9d7 (diff)
downloadaoc-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.c119
-rw-r--r--aoc_firmware.c117
-rw-r--r--aoc_firmware.h21
3 files changed, 170 insertions, 87 deletions
diff --git a/aoc.c b/aoc.c
index 155ad5b..c2f2003 100644
--- a/aoc.c
+++ b/aoc.c
@@ -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);