1 /* 2 * skl-sst-utils.c - SKL sst utils functions 3 * 4 * Copyright (C) 2016 Intel Corp 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as version 2, as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 */ 15 16 #include <linux/device.h> 17 #include <linux/slab.h> 18 #include <linux/uuid.h> 19 #include "skl-sst-dsp.h" 20 #include "../common/sst-dsp.h" 21 #include "../common/sst-dsp-priv.h" 22 #include "skl-sst-ipc.h" 23 24 25 #define UUID_STR_SIZE 37 26 #define DEFAULT_HASH_SHA256_LEN 32 27 28 /* FW Extended Manifest Header id = $AE1 */ 29 #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 30 31 struct skl_dfw_module_mod { 32 char name[100]; 33 struct skl_dfw_module skl_dfw_mod; 34 }; 35 36 struct UUID { 37 u8 id[16]; 38 }; 39 40 union seg_flags { 41 u32 ul; 42 struct { 43 u32 contents : 1; 44 u32 alloc : 1; 45 u32 load : 1; 46 u32 read_only : 1; 47 u32 code : 1; 48 u32 data : 1; 49 u32 _rsvd0 : 2; 50 u32 type : 4; 51 u32 _rsvd1 : 4; 52 u32 length : 16; 53 } r; 54 } __packed; 55 56 struct segment_desc { 57 union seg_flags flags; 58 u32 v_base_addr; 59 u32 file_offset; 60 }; 61 62 struct module_type { 63 u32 load_type : 4; 64 u32 auto_start : 1; 65 u32 domain_ll : 1; 66 u32 domain_dp : 1; 67 u32 rsvd : 25; 68 } __packed; 69 70 struct adsp_module_entry { 71 u32 struct_id; 72 u8 name[8]; 73 struct UUID uuid; 74 struct module_type type; 75 u8 hash1[DEFAULT_HASH_SHA256_LEN]; 76 u32 entry_point; 77 u16 cfg_offset; 78 u16 cfg_count; 79 u32 affinity_mask; 80 u16 instance_max_count; 81 u16 instance_bss_size; 82 struct segment_desc segments[3]; 83 } __packed; 84 85 struct adsp_fw_hdr { 86 u32 id; 87 u32 len; 88 u8 name[8]; 89 u32 preload_page_count; 90 u32 fw_image_flags; 91 u32 feature_mask; 92 u16 major; 93 u16 minor; 94 u16 hotfix; 95 u16 build; 96 u32 num_modules; 97 u32 hw_buf_base; 98 u32 hw_buf_length; 99 u32 load_offset; 100 } __packed; 101 102 struct uuid_module { 103 uuid_le uuid; 104 int id; 105 int is_loadable; 106 107 struct list_head list; 108 }; 109 110 struct skl_ext_manifest_hdr { 111 u32 id; 112 u32 len; 113 u16 version_major; 114 u16 version_minor; 115 u32 entries; 116 }; 117 118 int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, 119 struct skl_dfw_module *dfw_config) 120 { 121 struct uuid_module *module; 122 uuid_le *uuid_mod; 123 124 uuid_mod = (uuid_le *)uuid; 125 126 if (list_empty(&ctx->uuid_list)) { 127 dev_err(ctx->dev, "Module list is empty\n"); 128 return -EINVAL; 129 } 130 131 list_for_each_entry(module, &ctx->uuid_list, list) { 132 if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { 133 dfw_config->module_id = module->id; 134 dfw_config->is_loadable = module->is_loadable; 135 136 return 0; 137 } 138 } 139 140 return -EINVAL; 141 } 142 EXPORT_SYMBOL_GPL(snd_skl_get_module_info); 143 144 /* 145 * Parse the firmware binary to get the UUID, module id 146 * and loadable flags 147 */ 148 int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) 149 { 150 struct adsp_fw_hdr *adsp_hdr; 151 struct adsp_module_entry *mod_entry; 152 int i, num_entry; 153 uuid_le *uuid_bin; 154 const char *buf; 155 struct skl_sst *skl = ctx->thread_context; 156 struct uuid_module *module; 157 struct firmware stripped_fw; 158 unsigned int safe_file; 159 160 /* Get the FW pointer to derive ADSP header */ 161 stripped_fw.data = ctx->fw->data; 162 stripped_fw.size = ctx->fw->size; 163 164 skl_dsp_strip_extended_manifest(&stripped_fw); 165 166 buf = stripped_fw.data; 167 168 /* check if we have enough space in file to move to header */ 169 safe_file = sizeof(*adsp_hdr) + offset; 170 if (stripped_fw.size <= safe_file) { 171 dev_err(ctx->dev, "Small fw file size, No space for hdr\n"); 172 return -EINVAL; 173 } 174 175 adsp_hdr = (struct adsp_fw_hdr *)(buf + offset); 176 177 /* check 1st module entry is in file */ 178 safe_file += adsp_hdr->len + sizeof(*mod_entry); 179 if (stripped_fw.size <= safe_file) { 180 dev_err(ctx->dev, "Small fw file size, No module entry\n"); 181 return -EINVAL; 182 } 183 184 mod_entry = (struct adsp_module_entry *) 185 (buf + offset + adsp_hdr->len); 186 187 num_entry = adsp_hdr->num_modules; 188 189 /* check all entries are in file */ 190 safe_file += num_entry * sizeof(*mod_entry); 191 if (stripped_fw.size <= safe_file) { 192 dev_err(ctx->dev, "Small fw file size, No modules\n"); 193 return -EINVAL; 194 } 195 196 197 /* 198 * Read the UUID(GUID) from FW Manifest. 199 * 200 * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX 201 * Populate the UUID table to store module_id and loadable flags 202 * for the module. 203 */ 204 205 for (i = 0; i < num_entry; i++, mod_entry++) { 206 module = kzalloc(sizeof(*module), GFP_KERNEL); 207 if (!module) 208 return -ENOMEM; 209 210 uuid_bin = (uuid_le *)mod_entry->uuid.id; 211 memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); 212 213 module->id = i; 214 module->is_loadable = mod_entry->type.load_type; 215 216 list_add_tail(&module->list, &skl->uuid_list); 217 218 dev_dbg(ctx->dev, 219 "Adding uuid :%pUL mod id: %d Loadable: %d\n", 220 &module->uuid, module->id, module->is_loadable); 221 } 222 223 return 0; 224 } 225 226 void skl_freeup_uuid_list(struct skl_sst *ctx) 227 { 228 struct uuid_module *uuid, *_uuid; 229 230 list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) { 231 list_del(&uuid->list); 232 kfree(uuid); 233 } 234 } 235 236 /* 237 * some firmware binary contains some extended manifest. This needs 238 * to be stripped in that case before we load and use that image. 239 * 240 * Get the module id for the module by checking 241 * the table for the UUID for the module 242 */ 243 int skl_dsp_strip_extended_manifest(struct firmware *fw) 244 { 245 struct skl_ext_manifest_hdr *hdr; 246 247 /* check if fw file is greater than header we are looking */ 248 if (fw->size < sizeof(hdr)) { 249 pr_err("%s: Firmware file small, no hdr\n", __func__); 250 return -EINVAL; 251 } 252 253 hdr = (struct skl_ext_manifest_hdr *)fw->data; 254 255 if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) { 256 fw->size -= hdr->len; 257 fw->data += hdr->len; 258 } 259 260 return 0; 261 } 262