162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * imr_selftest.c -- Intel Isolated Memory Region self-test driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(c) 2013 Intel Corporation. 662306a36Sopenharmony_ci * Copyright(c) 2015 Bryan O'Donoghue <pure.logic@nexus-software.ie> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * IMR self test. The purpose of this module is to run a set of tests on the 962306a36Sopenharmony_ci * IMR API to validate it's sanity. We check for overlapping, reserved 1062306a36Sopenharmony_ci * addresses and setup/teardown sanity. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm-generic/sections.h> 1562306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 1662306a36Sopenharmony_ci#include <asm/imr.h> 1762306a36Sopenharmony_ci#include <asm/io.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/mm.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define SELFTEST KBUILD_MODNAME ": " 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * imr_self_test_result - Print result string for self test. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * @res: result code - true if test passed false otherwise. 2862306a36Sopenharmony_ci * @fmt: format string. 2962306a36Sopenharmony_ci * ... variadic argument list. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic __printf(2, 3) 3262306a36Sopenharmony_civoid __init imr_self_test_result(int res, const char *fmt, ...) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci va_list vlist; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* Print pass/fail. */ 3762306a36Sopenharmony_ci if (res) 3862306a36Sopenharmony_ci pr_info(SELFTEST "pass "); 3962306a36Sopenharmony_ci else 4062306a36Sopenharmony_ci pr_info(SELFTEST "fail "); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Print variable string. */ 4362306a36Sopenharmony_ci va_start(vlist, fmt); 4462306a36Sopenharmony_ci vprintk(fmt, vlist); 4562306a36Sopenharmony_ci va_end(vlist); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Optional warning. */ 4862306a36Sopenharmony_ci WARN(res == 0, "test failed"); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci#undef SELFTEST 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * imr_self_test 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Verify IMR self_test with some simple tests to verify overlap, 5662306a36Sopenharmony_ci * zero sized allocations and 1 KiB sized areas. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic void __init imr_self_test(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci phys_addr_t base = virt_to_phys(&_text); 6262306a36Sopenharmony_ci size_t size = virt_to_phys(&__end_rodata) - base; 6362306a36Sopenharmony_ci const char *fmt_over = "overlapped IMR @ (0x%08lx - 0x%08lx)\n"; 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Test zero zero. */ 6762306a36Sopenharmony_ci ret = imr_add_range(0, 0, 0, 0); 6862306a36Sopenharmony_ci imr_self_test_result(ret < 0, "zero sized IMR\n"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Test exact overlap. */ 7162306a36Sopenharmony_ci ret = imr_add_range(base, size, IMR_CPU, IMR_CPU); 7262306a36Sopenharmony_ci imr_self_test_result(ret < 0, fmt_over, __va(base), __va(base + size)); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Test overlap with base inside of existing. */ 7562306a36Sopenharmony_ci base += size - IMR_ALIGN; 7662306a36Sopenharmony_ci ret = imr_add_range(base, size, IMR_CPU, IMR_CPU); 7762306a36Sopenharmony_ci imr_self_test_result(ret < 0, fmt_over, __va(base), __va(base + size)); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Test overlap with end inside of existing. */ 8062306a36Sopenharmony_ci base -= size + IMR_ALIGN * 2; 8162306a36Sopenharmony_ci ret = imr_add_range(base, size, IMR_CPU, IMR_CPU); 8262306a36Sopenharmony_ci imr_self_test_result(ret < 0, fmt_over, __va(base), __va(base + size)); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Test that a 1 KiB IMR @ zero with read/write all will bomb out. */ 8562306a36Sopenharmony_ci ret = imr_add_range(0, IMR_ALIGN, IMR_READ_ACCESS_ALL, 8662306a36Sopenharmony_ci IMR_WRITE_ACCESS_ALL); 8762306a36Sopenharmony_ci imr_self_test_result(ret < 0, "1KiB IMR @ 0x00000000 - access-all\n"); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Test that a 1 KiB IMR @ zero with CPU only will work. */ 9062306a36Sopenharmony_ci ret = imr_add_range(0, IMR_ALIGN, IMR_CPU, IMR_CPU); 9162306a36Sopenharmony_ci imr_self_test_result(ret >= 0, "1KiB IMR @ 0x00000000 - cpu-access\n"); 9262306a36Sopenharmony_ci if (ret >= 0) { 9362306a36Sopenharmony_ci ret = imr_remove_range(0, IMR_ALIGN); 9462306a36Sopenharmony_ci imr_self_test_result(ret == 0, "teardown - cpu-access\n"); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Test 2 KiB works. */ 9862306a36Sopenharmony_ci size = IMR_ALIGN * 2; 9962306a36Sopenharmony_ci ret = imr_add_range(0, size, IMR_READ_ACCESS_ALL, IMR_WRITE_ACCESS_ALL); 10062306a36Sopenharmony_ci imr_self_test_result(ret >= 0, "2KiB IMR @ 0x00000000\n"); 10162306a36Sopenharmony_ci if (ret >= 0) { 10262306a36Sopenharmony_ci ret = imr_remove_range(0, size); 10362306a36Sopenharmony_ci imr_self_test_result(ret == 0, "teardown 2KiB\n"); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic const struct x86_cpu_id imr_ids[] __initconst = { 10862306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL), 10962306a36Sopenharmony_ci {} 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * imr_self_test_init - entry point for IMR driver. 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * return: -ENODEV for no IMR support 0 if good to go. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_cistatic int __init imr_self_test_init(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci if (x86_match_cpu(imr_ids)) 12062306a36Sopenharmony_ci imr_self_test(); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * imr_self_test_exit - exit point for IMR code. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * return: 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cidevice_initcall(imr_self_test_init); 130