1fb57fc78SJithu Joseph // SPDX-License-Identifier: GPL-2.0-only 2fb57fc78SJithu Joseph /* Copyright(c) 2022 Intel Corporation. */ 3fb57fc78SJithu Joseph 4fb57fc78SJithu Joseph #include <linux/firmware.h> 5846e751fSJithu Joseph #include <asm/cpu.h> 682ad097bSAshok Raj #include <asm/microcode.h> 7fb57fc78SJithu Joseph 8fb57fc78SJithu Joseph #include "ifs.h" 9fb57fc78SJithu Joseph 1048c6e7dcSJithu Joseph #define IFS_CHUNK_ALIGNMENT 256 1148c6e7dcSJithu Joseph union meta_data { 1248c6e7dcSJithu Joseph struct { 1348c6e7dcSJithu Joseph u32 meta_type; // metadata type 1448c6e7dcSJithu Joseph u32 meta_size; // size of this entire struct including hdrs. 1548c6e7dcSJithu Joseph u32 test_type; // IFS test type 1648c6e7dcSJithu Joseph u32 fusa_info; // Fusa info 1748c6e7dcSJithu Joseph u32 total_images; // Total number of images 1848c6e7dcSJithu Joseph u32 current_image; // Current Image # 1948c6e7dcSJithu Joseph u32 total_chunks; // Total number of chunks in this image 2048c6e7dcSJithu Joseph u32 starting_chunk; // Starting chunk number in this image 2148c6e7dcSJithu Joseph u32 size_per_chunk; // size of each chunk 2248c6e7dcSJithu Joseph u32 chunks_per_stride; // number of chunks in a stride 2348c6e7dcSJithu Joseph }; 2448c6e7dcSJithu Joseph u8 padding[IFS_CHUNK_ALIGNMENT]; 2548c6e7dcSJithu Joseph }; 2648c6e7dcSJithu Joseph 27aa63e0fdSJithu Joseph #define IFS_HEADER_SIZE (sizeof(struct microcode_header_intel)) 2848c6e7dcSJithu Joseph #define META_TYPE_IFS 1 29aa63e0fdSJithu Joseph static struct microcode_header_intel *ifs_header_ptr; /* pointer to the ifs image header */ 30684ec215SJithu Joseph static u64 ifs_hash_ptr; /* Address of ifs metadata (hash) */ 31684ec215SJithu Joseph static u64 ifs_test_image_ptr; /* 256B aligned address of test pattern */ 32684ec215SJithu Joseph static DECLARE_COMPLETION(ifs_done); 33684ec215SJithu Joseph 34684ec215SJithu Joseph static const char * const scan_hash_status[] = { 35684ec215SJithu Joseph [0] = "No error reported", 36684ec215SJithu Joseph [1] = "Attempt to copy scan hashes when copy already in progress", 37684ec215SJithu Joseph [2] = "Secure Memory not set up correctly", 38684ec215SJithu Joseph [3] = "FuSaInfo.ProgramID does not match or ff-mm-ss does not match", 39684ec215SJithu Joseph [4] = "Reserved", 40684ec215SJithu Joseph [5] = "Integrity check failed", 41684ec215SJithu Joseph [6] = "Scan reload or test is in progress" 42684ec215SJithu Joseph }; 43684ec215SJithu Joseph 44684ec215SJithu Joseph static const char * const scan_authentication_status[] = { 45684ec215SJithu Joseph [0] = "No error reported", 46684ec215SJithu Joseph [1] = "Attempt to authenticate a chunk which is already marked as authentic", 47684ec215SJithu Joseph [2] = "Chunk authentication error. The hash of chunk did not match expected value" 48684ec215SJithu Joseph }; 49684ec215SJithu Joseph 508382fee3SAshok Raj #define MC_HEADER_META_TYPE_END (0) 518382fee3SAshok Raj 528382fee3SAshok Raj struct metadata_header { 538382fee3SAshok Raj unsigned int type; 548382fee3SAshok Raj unsigned int blk_size; 558382fee3SAshok Raj }; 568382fee3SAshok Raj 578382fee3SAshok Raj static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_type) 588382fee3SAshok Raj { 59*b0e67db1SAshok Raj struct microcode_header_intel *hdr = &((struct microcode_intel *)ucode)->hdr; 608382fee3SAshok Raj struct metadata_header *meta_header; 618382fee3SAshok Raj unsigned long data_size, total_meta; 628382fee3SAshok Raj unsigned long meta_size = 0; 638382fee3SAshok Raj 64*b0e67db1SAshok Raj data_size = intel_microcode_get_datasize(hdr); 65*b0e67db1SAshok Raj total_meta = hdr->metasize; 668382fee3SAshok Raj if (!total_meta) 678382fee3SAshok Raj return NULL; 688382fee3SAshok Raj 698382fee3SAshok Raj meta_header = (ucode + MC_HEADER_SIZE + data_size) - total_meta; 708382fee3SAshok Raj 718382fee3SAshok Raj while (meta_header->type != MC_HEADER_META_TYPE_END && 728382fee3SAshok Raj meta_header->blk_size && 738382fee3SAshok Raj meta_size < total_meta) { 748382fee3SAshok Raj meta_size += meta_header->blk_size; 758382fee3SAshok Raj if (meta_header->type == meta_type) 768382fee3SAshok Raj return meta_header; 778382fee3SAshok Raj 788382fee3SAshok Raj meta_header = (void *)meta_header + meta_header->blk_size; 798382fee3SAshok Raj } 808382fee3SAshok Raj return NULL; 818382fee3SAshok Raj } 828382fee3SAshok Raj 83684ec215SJithu Joseph /* 84684ec215SJithu Joseph * To copy scan hashes and authenticate test chunks, the initiating cpu must point 85684ec215SJithu Joseph * to the EDX:EAX to the test image in linear address. 86684ec215SJithu Joseph * Run wrmsr(MSR_COPY_SCAN_HASHES) for scan hash copy and run wrmsr(MSR_AUTHENTICATE_AND_COPY_CHUNK) 87684ec215SJithu Joseph * for scan hash copy and test chunk authentication. 88684ec215SJithu Joseph */ 89684ec215SJithu Joseph static void copy_hashes_authenticate_chunks(struct work_struct *work) 90684ec215SJithu Joseph { 91684ec215SJithu Joseph struct ifs_work *local_work = container_of(work, struct ifs_work, w); 92684ec215SJithu Joseph union ifs_scan_hashes_status hashes_status; 93684ec215SJithu Joseph union ifs_chunks_auth_status chunk_status; 94684ec215SJithu Joseph struct device *dev = local_work->dev; 95684ec215SJithu Joseph int i, num_chunks, chunk_size; 96684ec215SJithu Joseph struct ifs_data *ifsd; 97684ec215SJithu Joseph u64 linear_addr, base; 98684ec215SJithu Joseph u32 err_code; 99684ec215SJithu Joseph 100684ec215SJithu Joseph ifsd = ifs_get_data(dev); 101684ec215SJithu Joseph /* run scan hash copy */ 102684ec215SJithu Joseph wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); 103684ec215SJithu Joseph rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); 104684ec215SJithu Joseph 105684ec215SJithu Joseph /* enumerate the scan image information */ 106684ec215SJithu Joseph num_chunks = hashes_status.num_chunks; 107684ec215SJithu Joseph chunk_size = hashes_status.chunk_size * 1024; 108684ec215SJithu Joseph err_code = hashes_status.error_code; 109684ec215SJithu Joseph 110684ec215SJithu Joseph if (!hashes_status.valid) { 111684ec215SJithu Joseph ifsd->loading_error = true; 112684ec215SJithu Joseph if (err_code >= ARRAY_SIZE(scan_hash_status)) { 113684ec215SJithu Joseph dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); 114684ec215SJithu Joseph goto done; 115684ec215SJithu Joseph } 116684ec215SJithu Joseph dev_err(dev, "Hash copy error : %s", scan_hash_status[err_code]); 117684ec215SJithu Joseph goto done; 118684ec215SJithu Joseph } 119684ec215SJithu Joseph 120684ec215SJithu Joseph /* base linear address to the scan data */ 121684ec215SJithu Joseph base = ifs_test_image_ptr; 122684ec215SJithu Joseph 123684ec215SJithu Joseph /* scan data authentication and copy chunks to secured memory */ 124684ec215SJithu Joseph for (i = 0; i < num_chunks; i++) { 125684ec215SJithu Joseph linear_addr = base + i * chunk_size; 126684ec215SJithu Joseph linear_addr |= i; 127684ec215SJithu Joseph 128684ec215SJithu Joseph wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, linear_addr); 129684ec215SJithu Joseph rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); 130684ec215SJithu Joseph 131684ec215SJithu Joseph ifsd->valid_chunks = chunk_status.valid_chunks; 132684ec215SJithu Joseph err_code = chunk_status.error_code; 133684ec215SJithu Joseph 134684ec215SJithu Joseph if (err_code) { 135684ec215SJithu Joseph ifsd->loading_error = true; 136684ec215SJithu Joseph if (err_code >= ARRAY_SIZE(scan_authentication_status)) { 137684ec215SJithu Joseph dev_err(dev, 138684ec215SJithu Joseph "invalid error code 0x%x for authentication\n", err_code); 139684ec215SJithu Joseph goto done; 140684ec215SJithu Joseph } 141684ec215SJithu Joseph dev_err(dev, "Chunk authentication error %s\n", 142684ec215SJithu Joseph scan_authentication_status[err_code]); 143684ec215SJithu Joseph goto done; 144684ec215SJithu Joseph } 145684ec215SJithu Joseph } 146684ec215SJithu Joseph done: 147684ec215SJithu Joseph complete(&ifs_done); 148684ec215SJithu Joseph } 149684ec215SJithu Joseph 15048c6e7dcSJithu Joseph static int validate_ifs_metadata(struct device *dev) 15148c6e7dcSJithu Joseph { 15248c6e7dcSJithu Joseph struct ifs_data *ifsd = ifs_get_data(dev); 15348c6e7dcSJithu Joseph union meta_data *ifs_meta; 15448c6e7dcSJithu Joseph char test_file[64]; 15548c6e7dcSJithu Joseph int ret = -EINVAL; 15648c6e7dcSJithu Joseph 15748c6e7dcSJithu Joseph snprintf(test_file, sizeof(test_file), "%02x-%02x-%02x-%02x.scan", 15848c6e7dcSJithu Joseph boot_cpu_data.x86, boot_cpu_data.x86_model, 15948c6e7dcSJithu Joseph boot_cpu_data.x86_stepping, ifsd->cur_batch); 16048c6e7dcSJithu Joseph 16148c6e7dcSJithu Joseph ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS); 16248c6e7dcSJithu Joseph if (!ifs_meta) { 16348c6e7dcSJithu Joseph dev_err(dev, "IFS Metadata missing in file %s\n", test_file); 16448c6e7dcSJithu Joseph return ret; 16548c6e7dcSJithu Joseph } 16648c6e7dcSJithu Joseph 16748c6e7dcSJithu Joseph ifs_test_image_ptr = (u64)ifs_meta + sizeof(union meta_data); 16848c6e7dcSJithu Joseph 16948c6e7dcSJithu Joseph /* Scan chunk start must be 256 byte aligned */ 17048c6e7dcSJithu Joseph if (!IS_ALIGNED(ifs_test_image_ptr, IFS_CHUNK_ALIGNMENT)) { 17148c6e7dcSJithu Joseph dev_err(dev, "Scan pattern is not aligned on %d bytes aligned in %s\n", 17248c6e7dcSJithu Joseph IFS_CHUNK_ALIGNMENT, test_file); 17348c6e7dcSJithu Joseph return ret; 17448c6e7dcSJithu Joseph } 17548c6e7dcSJithu Joseph 17648c6e7dcSJithu Joseph if (ifs_meta->current_image != ifsd->cur_batch) { 17748c6e7dcSJithu Joseph dev_warn(dev, "Mismatch between filename %s and batch metadata 0x%02x\n", 17848c6e7dcSJithu Joseph test_file, ifs_meta->current_image); 17948c6e7dcSJithu Joseph return ret; 18048c6e7dcSJithu Joseph } 18148c6e7dcSJithu Joseph 18248c6e7dcSJithu Joseph return 0; 18348c6e7dcSJithu Joseph } 18448c6e7dcSJithu Joseph 185684ec215SJithu Joseph /* 186684ec215SJithu Joseph * IFS requires scan chunks authenticated per each socket in the platform. 187684ec215SJithu Joseph * Once the test chunk is authenticated, it is automatically copied to secured memory 188684ec215SJithu Joseph * and proceed the authentication for the next chunk. 189684ec215SJithu Joseph */ 190684ec215SJithu Joseph static int scan_chunks_sanity_check(struct device *dev) 191684ec215SJithu Joseph { 192684ec215SJithu Joseph struct ifs_data *ifsd = ifs_get_data(dev); 193684ec215SJithu Joseph struct ifs_work local_work; 194aa63e0fdSJithu Joseph int curr_pkg, cpu, ret; 195aa63e0fdSJithu Joseph 19667f88ffaSJithu Joseph memset(ifs_pkg_auth, 0, (topology_max_packages() * sizeof(bool))); 19748c6e7dcSJithu Joseph ret = validate_ifs_metadata(dev); 19848c6e7dcSJithu Joseph if (ret) 19948c6e7dcSJithu Joseph return ret; 20048c6e7dcSJithu Joseph 201684ec215SJithu Joseph ifsd->loading_error = false; 202aa63e0fdSJithu Joseph ifsd->loaded_version = ifs_header_ptr->rev; 203684ec215SJithu Joseph 204684ec215SJithu Joseph /* copy the scan hash and authenticate per package */ 205684ec215SJithu Joseph cpus_read_lock(); 206684ec215SJithu Joseph for_each_online_cpu(cpu) { 207684ec215SJithu Joseph curr_pkg = topology_physical_package_id(cpu); 20867f88ffaSJithu Joseph if (ifs_pkg_auth[curr_pkg]) 209684ec215SJithu Joseph continue; 210684ec215SJithu Joseph reinit_completion(&ifs_done); 211684ec215SJithu Joseph local_work.dev = dev; 2123279decbSDavid Arcari INIT_WORK_ONSTACK(&local_work.w, copy_hashes_authenticate_chunks); 213684ec215SJithu Joseph schedule_work_on(cpu, &local_work.w); 214684ec215SJithu Joseph wait_for_completion(&ifs_done); 215f4e209e9SJithu Joseph if (ifsd->loading_error) { 216f4e209e9SJithu Joseph ret = -EIO; 217684ec215SJithu Joseph goto out; 218f4e209e9SJithu Joseph } 21967f88ffaSJithu Joseph ifs_pkg_auth[curr_pkg] = 1; 220684ec215SJithu Joseph } 221684ec215SJithu Joseph ret = 0; 222684ec215SJithu Joseph out: 223684ec215SJithu Joseph cpus_read_unlock(); 224684ec215SJithu Joseph 225684ec215SJithu Joseph return ret; 226684ec215SJithu Joseph } 227684ec215SJithu Joseph 228aa63e0fdSJithu Joseph static int image_sanity_check(struct device *dev, const struct microcode_header_intel *data) 229846e751fSJithu Joseph { 230aa63e0fdSJithu Joseph struct ucode_cpu_info uci; 231846e751fSJithu Joseph 232aa63e0fdSJithu Joseph /* Provide a specific error message when loading an older/unsupported image */ 233aa63e0fdSJithu Joseph if (data->hdrver != MC_HEADER_TYPE_IFS) { 234aa63e0fdSJithu Joseph dev_err(dev, "Header version %d not supported\n", data->hdrver); 235846e751fSJithu Joseph return -EINVAL; 236846e751fSJithu Joseph } 237846e751fSJithu Joseph 238aa63e0fdSJithu Joseph if (intel_microcode_sanity_check((void *)data, true, MC_HEADER_TYPE_IFS)) { 239aa63e0fdSJithu Joseph dev_err(dev, "sanity check failed\n"); 240846e751fSJithu Joseph return -EINVAL; 241846e751fSJithu Joseph } 242846e751fSJithu Joseph 243aa63e0fdSJithu Joseph intel_cpu_collect_info(&uci); 244846e751fSJithu Joseph 245aa63e0fdSJithu Joseph if (!intel_find_matching_signature((void *)data, 246aa63e0fdSJithu Joseph uci.cpu_sig.sig, 247aa63e0fdSJithu Joseph uci.cpu_sig.pf)) { 248aa63e0fdSJithu Joseph dev_err(dev, "cpu signature, processor flags not matching\n"); 249846e751fSJithu Joseph return -EINVAL; 250846e751fSJithu Joseph } 251846e751fSJithu Joseph 252846e751fSJithu Joseph return 0; 253846e751fSJithu Joseph } 254846e751fSJithu Joseph 255fb57fc78SJithu Joseph /* 256fb57fc78SJithu Joseph * Load ifs image. Before loading ifs module, the ifs image must be located 2574fb858f3SJithu Joseph * in /lib/firmware/intel/ifs_x/ and named as family-model-stepping-02x.{testname}. 258fb57fc78SJithu Joseph */ 2594fb858f3SJithu Joseph int ifs_load_firmware(struct device *dev) 260fb57fc78SJithu Joseph { 26154c9fcd1SJithu Joseph const struct ifs_test_caps *test = ifs_get_test_caps(dev); 262684ec215SJithu Joseph struct ifs_data *ifsd = ifs_get_data(dev); 263fb57fc78SJithu Joseph const struct firmware *fw; 2644fb858f3SJithu Joseph char scan_path[64]; 2654fb858f3SJithu Joseph int ret = -EINVAL; 266fb57fc78SJithu Joseph 2674fb858f3SJithu Joseph snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", 26854c9fcd1SJithu Joseph test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, 2694fb858f3SJithu Joseph boot_cpu_data.x86_stepping, ifsd->cur_batch); 270fb57fc78SJithu Joseph 271fb57fc78SJithu Joseph ret = request_firmware_direct(&fw, scan_path, dev); 272fb57fc78SJithu Joseph if (ret) { 273fb57fc78SJithu Joseph dev_err(dev, "ifs file %s load failed\n", scan_path); 274684ec215SJithu Joseph goto done; 275fb57fc78SJithu Joseph } 276fb57fc78SJithu Joseph 277aa63e0fdSJithu Joseph ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data); 278aa63e0fdSJithu Joseph if (ret) 279684ec215SJithu Joseph goto release; 280846e751fSJithu Joseph 281aa63e0fdSJithu Joseph ifs_header_ptr = (struct microcode_header_intel *)fw->data; 282684ec215SJithu Joseph ifs_hash_ptr = (u64)(ifs_header_ptr + 1); 283684ec215SJithu Joseph 284684ec215SJithu Joseph ret = scan_chunks_sanity_check(dev); 2854fb858f3SJithu Joseph if (ret) 2864fb858f3SJithu Joseph dev_err(dev, "Load failure for batch: %02x\n", ifsd->cur_batch); 2874fb858f3SJithu Joseph 288684ec215SJithu Joseph release: 289fb57fc78SJithu Joseph release_firmware(fw); 290684ec215SJithu Joseph done: 291684ec215SJithu Joseph ifsd->loaded = (ret == 0); 2924fb858f3SJithu Joseph 2934fb858f3SJithu Joseph return ret; 294fb57fc78SJithu Joseph } 295