text: /*
   * Copyright (c) 2020 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <zephyr.h>
  #include <device.h>
  #include <ztest.h>
  #include <tc_util.h>
  
  #include <drivers/edac.h>
  #include <ibecc.h>
  
  #define DEVICE_NAME		DT_LABEL(DT_NODELABEL(ibecc))
  
  #if defined(CONFIG_EDAC_ERROR_INJECT)
  #define TEST_ADDRESS1		0x1000
  #define TEST_ADDRESS2		0x2000
  #define TEST_DATA		0xface
  #define TEST_ADDRESS_MASK	INJ_ADDR_BASE_MASK_MASK
  #define DURATION		100
  #endif
  
  const struct device *dev;
  
  static void test_ibecc_initialized(void)
  {
  	dev = device_get_binding(DEVICE_NAME);
  	zassert_not_null(dev, "Device not found");
  
  	TC_PRINT("Test ibecc driver is initialized\n");
  
  }
  
  K_APPMEM_PARTITION_DEFINE(default_part);
  
  K_APP_BMEM(default_part) static volatile int interrupt;
  K_APP_BMEM(default_part) static volatile uint32_t error_type;
  K_APP_BMEM(default_part) static volatile uint64_t error_address;
  K_APP_BMEM(default_part) static volatile uint16_t error_syndrome;
  
  static void callback(const struct device *d, void *data)
  {
  	struct ibecc_error *error_data = data;
  
  	interrupt++;
  	error_type = error_data->type;
  	error_address = error_data->address;
  	error_syndrome = error_data->syndrome;
  }
  
  static void test_ibecc_api(void)
  {
  	uint64_t value;
  	int ret;
  
  	/* Error log API */
  
  	ret = edac_ecc_error_log_get(dev, &value);
  	zassert_equal(ret, 0, "edac_ecc_error_log_get failed");
  
  	ret = edac_ecc_error_log_clear(dev);
  	zassert_equal(ret, 0, "edac_ecc_error_log_clear failed");
  
  	ret = edac_parity_error_log_get(dev, &value);
  	zassert_equal(ret, 0, "edac_parity_error_log_get failed");
  
  	ret = edac_parity_error_log_clear(dev);
  	zassert_equal(ret, 0, "edac_parity_error_log_clear failed");
  
  	/* Error stat API */
  
  	ret = edac_errors_cor_get(dev);
  	zassert_equal(ret, 0, "Error correctable count not zero");
  
  	ret = edac_errors_uc_get(dev);
  	zassert_equal(ret, 0, "Error uncorrectable count not zero");
  
  	/* Notification API */
  
  	ret = edac_notify_callback_set(dev, callback);
  	zassert_equal(ret, 0, "Error setting notification callback");
  }
  
  #if defined(CONFIG_EDAC_ERROR_INJECT)
  static void test_ibecc_error_inject_api(void)
  {
  	uint32_t test_value;
  	uint64_t val;
  	int ret;
  
  	/* Verify default parameters */
  
  	ret = edac_inject_get_error_type(dev, &test_value);
  	zassert_equal(ret, 0, "Error getting error_type");
  	zassert_equal(test_value, 0, "Error type not zero");
  
  	ret = edac_inject_get_param1(dev, &val);
  	zassert_equal(ret, 0, "Error getting param1");
  	zassert_equal(val, 0, "Error param1 is not zero");
  
  	ret = edac_inject_get_param2(dev, &val);
  	zassert_equal(ret, 0, "Error getting param2");
  	zassert_equal(val, 0, "Error param2 is not zero");
  
  	/* Verify basic Injection API operations */
  
  	/* Set correct value of param1 */
  	ret = edac_inject_set_param1(dev, TEST_ADDRESS1);
  	zassert_equal(ret, 0, "Error setting inject address");
  
  	/* Try to set incorrect value of param1 with UINT64_MAX */
  	ret = edac_inject_set_param1(dev, UINT64_MAX);
  	zassert_not_equal(ret, 0, "Error setting invalid param1");
  
  	ret = edac_inject_get_param1(dev, &val);
  	zassert_equal(ret, 0, "Error getting param1");
  	zassert_equal(val, TEST_ADDRESS1, "Read back value differs");
  
  	/* Set correct value of param2 */
  	ret = edac_inject_set_param2(dev, TEST_ADDRESS_MASK);
  	zassert_equal(ret, 0, "Error setting inject address mask");
  
  	/* Try to set incorrect value of param2 with UINT64_MAX */
  	ret = edac_inject_set_param2(dev, UINT64_MAX);
  	zassert_not_equal(ret, 0, "Error setting invalid param1");
  
  	ret = edac_inject_get_param2(dev, &val);
  	zassert_equal(ret, 0, "Error getting param2");
  	zassert_equal(val, TEST_ADDRESS_MASK, "Read back value differs");
  
  	/* Clearing parameters */
  
  	ret = edac_inject_set_param1(dev, 0);
  	zassert_equal(ret, 0, "Error setting inject address");
  
  	ret = edac_inject_get_param1(dev, &val);
  	zassert_equal(ret, 0, "Error getting param1");
  	zassert_equal(val, 0, "Read back value differs");
  
  	ret = edac_inject_set_param2(dev, 0);
  	zassert_equal(ret, 0, "Error setting inject address mask");
  
  	ret = edac_inject_get_param2(dev, &val);
  	zassert_equal(ret, 0, "Error getting param2");
  	zassert_equal(val, 0, "Read back value differs");
  }
  #else
  static void test_ibecc_error_inject_api(void)
  {
  	ztest_test_skip();
  }
  #endif
  
  #if defined(CONFIG_EDAC_ERROR_INJECT)
  static void test_inject(uint64_t addr, uint64_t mask, uint8_t type)
  {
  	unsigned int errors_cor, errors_uc;
  	uint64_t test_addr;
  	uint32_t test_value;
  	int ret, num_int;
  
  	interrupt = 0;
  
  	/* Test error_trigger() for unset error type */
  	ret = edac_inject_error_trigger(dev);
  	zassert_equal(ret, 0, "Error setting ctrl");
  
  	errors_cor = edac_errors_cor_get(dev);
  	zassert_not_equal(errors_cor, -ENOSYS, "Not implemented error count");
  
  	errors_uc = edac_errors_uc_get(dev);
  	zassert_not_equal(errors_uc, -ENOSYS, "Not implemented error count");
  
  	ret = edac_inject_set_param1(dev, addr);
  	zassert_equal(ret, 0, "Error setting inject address");
  
  	ret = edac_inject_set_param2(dev, mask);
  	zassert_equal(ret, 0, "Error setting inject address mask");
  
  	/* Test correctable error inject */
  	ret = edac_inject_set_error_type(dev, type);
  	zassert_equal(ret, 0, "Error setting inject error type");
  
  	ret = edac_inject_get_error_type(dev, &test_value);
  	zassert_equal(ret, 0, "Error getting error_type");
  	zassert_equal(test_value, type, "Read back value differs");
  
  	ret = edac_inject_error_trigger(dev);
  	zassert_equal(ret, 0, "Error setting ctrl");
  
  	device_map((mm_reg_t *)&test_addr, addr, 0x100, K_MEM_CACHE_NONE);
  	TC_PRINT("Mapped 0x%llx to 0x%llx\n", addr, test_addr);
  
  	test_value = sys_read32(test_addr);
  	TC_PRINT("Read value 0x%llx: 0x%x\n", test_addr, test_value);
  
  	/* Write to this test address some data */
  	sys_write32(TEST_DATA, test_addr);
  	TC_PRINT("Wrote value 0x%x at 0x%llx\n", TEST_DATA, test_addr);
  
  	/* Read back, triggering interrupt and notification */
  	test_value = sys_read32(test_addr);
  	TC_PRINT("Read value 0x%llx: 0x%x\n", test_addr, test_value);
  
  	/* Wait for interrupt if needed */
  	k_busy_wait(USEC_PER_MSEC * DURATION);
  
  	/* Load to local variable to avoid using volatile in assert */
  	num_int = interrupt;
  
  	zassert_not_equal(num_int, 0, "Interrupt handler did not execute");
  	zassert_equal(num_int, 1,
  		      "Interrupt handler executed more than once! (%d)\n",
  		      num_int);
  
  	TC_PRINT("Interrupt %d\n", num_int);
  	TC_PRINT("Error: type %u, address 0x%llx, syndrome %u\n",
  		 error_type, error_address, error_syndrome);
  
  	/* Check statistic information */
  
  	ret = edac_errors_cor_get(dev);
  	zassert_equal(ret, type == EDAC_ERROR_TYPE_DRAM_COR ?
  		      errors_cor + 1 : errors_cor,
  		      "Incorrect correctable count");
  	TC_PRINT("Correctable error count %d\n", ret);
  
  	ret = edac_errors_uc_get(dev);
  	zassert_equal(ret, type == EDAC_ERROR_TYPE_DRAM_UC ?
  		      errors_uc + 1 : errors_uc,
  		      "Incorrect uncorrectable count");
  	TC_PRINT("Uncorrectable error count %d\n", ret);
  }
  
  static int check_values(void *p1, void *p2, void *p3)
  {
  	intptr_t address = (intptr_t)p1;
  	intptr_t type = (intptr_t)p2;
  	intptr_t addr, errtype;
  
  #if defined(CONFIG_USERSPACE)
  	TC_PRINT("Test communication in user mode thread\n");
  	zassert_true(k_is_user_context(), "thread left in kernel mode");
  #endif
  
  	/* Load to local variables to avoid using volatile in assert */
  	addr = error_address;
  	errtype = error_type;
  
  	/* Verify page address and error type */
  	zassert_equal(addr, address, "Error address wrong");
  	zassert_equal(errtype, type, "Error type wrong");
  
  	return 0;
  }
  
  static void test_ibecc_error_inject_test_cor(void)
  {
  	int ret;
  
  	ret = edac_notify_callback_set(dev, callback);
  	zassert_equal(ret, 0, "Error setting notification callback");
  
  	/* Test injecting correctable error at address TEST_ADDRESS1 */
  	test_inject(TEST_ADDRESS1, TEST_ADDRESS_MASK, EDAC_ERROR_TYPE_DRAM_COR);
  
  #if defined(CONFIG_USERSPACE)
  	k_thread_user_mode_enter((k_thread_entry_t)check_values,
  				 (void *)TEST_ADDRESS1,
  				 (void *)EDAC_ERROR_TYPE_DRAM_COR,
  				 NULL);
  #else
  	check_values((void *)TEST_ADDRESS1, (void *)EDAC_ERROR_TYPE_DRAM_COR,
  		     NULL);
  #endif
  }
  
  static void test_ibecc_error_inject_test_uc(void)
  {
  	int ret;
  
  	ret = edac_notify_callback_set(dev, callback);
  	zassert_equal(ret, 0, "Error setting notification callback");
  
  	/* Test injecting uncorrectable error at address TEST_ADDRESS2 */
  	test_inject(TEST_ADDRESS2, TEST_ADDRESS_MASK, EDAC_ERROR_TYPE_DRAM_UC);
  
  #if defined(CONFIG_USERSPACE)
  	k_thread_user_mode_enter((k_thread_entry_t)check_values,
  				 (void *)TEST_ADDRESS2,
  				 (void *)EDAC_ERROR_TYPE_DRAM_UC,
  				 NULL);
  #else
  	check_values((void *)TEST_ADDRESS2, (void *)EDAC_ERROR_TYPE_DRAM_UC,
  		     NULL);
  #endif
  }
  #else
  static void test_ibecc_error_inject_test_cor(void)
  {
  	ztest_test_skip();
  }
  
  static void test_ibecc_error_inject_test_uc(void)
  {
  	ztest_test_skip();
  }
  #endif
  
  /* Used only for code coverage */
  
  bool z_x86_do_kernel_nmi(const z_arch_esf_t *esf);
  
  static void test_trigger_nmi_handler(void)
  {
  	bool ret;
  
  	ret = z_x86_do_kernel_nmi(NULL);
  	zassert_false(ret, "Test that NMI handling fails");
  }
  
  void test_edac_dummy_api(void);
  
  void test_main(void)
  {
  #if defined(CONFIG_USERSPACE)
  	k_mem_domain_add_partition(&k_mem_domain_default, &default_part);
  #endif
  
  	ztest_test_suite(ibecc,
  			 ztest_unit_test(test_trigger_nmi_handler),
  			 ztest_unit_test(test_ibecc_initialized),
  			 ztest_unit_test(test_ibecc_api),
  			 ztest_unit_test(test_edac_dummy_api),
  			 ztest_unit_test(test_ibecc_error_inject_api),
  			 ztest_unit_test(test_ibecc_error_inject_test_cor),
  			 ztest_unit_test(test_ibecc_error_inject_test_uc)
  			);
  	ztest_run_test_suite(ibecc);
  }
  