/* * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvgpu_mem.h" /* * MEM_ADDRESS represents arbitrary memory start address. Init function will * allocate MEM_PAGES number of pages in memory. */ #define MEM_ADDRESS 0x00040000 #define MEM_PAGES 4U #define MEM_SIZE (MEM_PAGES * SZ_4K) /* Amount of test data should be less than or equal to MEM_SIZE */ #define TEST_SIZE (2U * SZ_4K) #if TEST_SIZE > MEM_SIZE #error "TEST_SIZE should be less than or equal to MEM_SIZE" #endif static struct nvgpu_mem *test_mem; #ifdef CONFIG_NVGPU_DGPU /* * Pramin write callback (for all nvgpu_writel calls). * No-op as callbacks/functions are already tested in pramin module. */ static void writel_access_reg_fn(struct gk20a *g, struct nvgpu_reg_access *access) { /* No-op */ } /* * Pramin read callback, similar to the write callback above. * Dummy return as callbacks/functions are already tested in pramin module. */ static void readl_access_reg_fn(struct gk20a *g, struct nvgpu_reg_access *access) { access->value = 0; } /* * Define pramin callbacks to be used during the test. Typically all * write operations use the same callback, likewise for all read operations. */ static struct nvgpu_posix_io_callbacks pramin_callbacks = { /* Write APIs all can use the same accessor. */ .writel = writel_access_reg_fn, .writel_check = writel_access_reg_fn, .bar1_writel = writel_access_reg_fn, .usermode_writel = writel_access_reg_fn, /* Likewise for the read APIs. */ .__readl = readl_access_reg_fn, .readl = readl_access_reg_fn, .bar1_readl = readl_access_reg_fn, }; /* * Populate vidmem allocations. * These are required for testing APERTURE_VIDMEM branches. */ static int init_vidmem_env(struct unit_module *m, struct gk20a *g) { int err; nvgpu_init_pramin(&g->mm); nvgpu_posix_register_io(g, &pramin_callbacks); nvgpu_posix_io_init_reg_space(g); /* Minimum HAL init for PRAMIN */ g->ops.bus.set_bar0_window = gk20a_bus_set_bar0_window; g->ops.pramin.data032_r = pram_data032_r; err = nvgpu_dma_alloc_vid_at(g, TEST_SIZE, test_mem, 0); if (err != 0) { return err; } return 0; } /* Free vidmem allocations */ static void free_vidmem_env(struct unit_module *m, struct gk20a *g) { nvgpu_dma_free(g, test_mem); nvgpu_posix_io_delete_reg_space(g, bus_bar0_window_r()); } /* * Test APERTURE_VIDMEM branch of nvgpu_mem read and write functions */ int test_nvgpu_mem_vidmem(struct unit_module *m, struct gk20a *g, void *args) { int err; u32 memset_pattern = 0x0000005A; u32 data_size = (16U * sizeof(u32)); u32 *data_src = (u32 *) nvgpu_kmalloc(g, data_size); if (data_src == NULL) { free_vidmem_env(m, g); unit_return_fail(m, "Could not allocate data_src\n"); } (void) memset(data_src, memset_pattern, (data_size)); /* Reset aperture to invalid, so that init doesn't complain */ test_mem->aperture = APERTURE_INVALID; err = init_vidmem_env(m, g); if (err != 0) { nvgpu_kfree(g, data_src); unit_return_fail(m, "Vidmem init failed with err=%d\n", err); } nvgpu_memset(g, test_mem, 0, memset_pattern, TEST_SIZE); nvgpu_mem_wr(g, test_mem, 0, memset_pattern); nvgpu_mem_rd(g, test_mem, 0); nvgpu_mem_wr_n(g, test_mem, 0, data_src, data_size); nvgpu_mem_rd_n(g, test_mem, 0, (void *)data_src, data_size); nvgpu_kfree(g, data_src); free_vidmem_env(m, g); /* Reset attributes */ test_mem->aperture = APERTURE_SYSMEM; return UNIT_SUCCESS; } #endif int test_nvgpu_aperture_mask(struct unit_module *m, struct gk20a *g, void *args) { u32 sysmem_mask = 1; u32 sysmem_coh_mask = 3; u32 vidmem_mask = 4; u32 ret_ap_mask; #ifdef CONFIG_NVGPU_DGPU /* Case: APERTURE_VIDMEM */ test_mem->aperture = APERTURE_VIDMEM; ret_ap_mask = nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask); if (ret_ap_mask != vidmem_mask) { unit_return_fail(m, "Vidmem mask returned incorrect\n"); } #endif /* * NVGPU_MM_HONORS_APERTURE enabled */ nvgpu_set_enabled(g, NVGPU_MM_HONORS_APERTURE, true); /* Case: APERTURE_SYSMEM */ test_mem->aperture = APERTURE_SYSMEM; ret_ap_mask = nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask); if (ret_ap_mask != sysmem_mask) { unit_return_fail(m, "MM_HONORS enabled: " "Incorrect mask returned for sysmem\n"); } /* Case: APERTURE_SYSMEM_COH */ test_mem->aperture = APERTURE_SYSMEM_COH; ret_ap_mask = nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask); if (ret_ap_mask != sysmem_coh_mask) { unit_return_fail(m, "MM_HONORS enabled: " "Incorrect mask returned for sysmem_coh\n"); } /* Case: APERTURE_INVALID */ test_mem->aperture = APERTURE_INVALID; if (!EXPECT_BUG(nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask))) { unit_return_fail(m, "MM_HONORS enabled: Aperture_mask " "did not BUG() for APERTURE_INVALID as expected\n"); } /* Case: Bad aperture value. This will cover default return value */ test_mem->aperture = 10; if (!EXPECT_BUG(nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask))) { unit_return_fail(m, "MM_HONORS enabled: Aperture_mask" "did not BUG() for junk aperture as expected\n"); } /* * NVGPU_MM_HONORS_APERTURE disabled */ nvgpu_set_enabled(g, NVGPU_MM_HONORS_APERTURE, false); #ifdef CONFIG_NVGPU_DGPU /* Case: APERTURE_SYSMEM */ test_mem->aperture = APERTURE_SYSMEM; ret_ap_mask = nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask); if (ret_ap_mask != vidmem_mask) { unit_return_fail(m, "MM_HONORS disabled: " "Incorrect mask returned for sysmem\n"); } /* Case: APERTURE_SYSMEM_COH */ test_mem->aperture = APERTURE_SYSMEM_COH; ret_ap_mask = nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask); if (ret_ap_mask != vidmem_mask) { unit_return_fail(m, "MM_HONORS disabled: " "Incorrect mask returned for sysmem_coh\n"); } #endif /* Case: APERTURE_INVALID */ test_mem->aperture = APERTURE_INVALID; if (!EXPECT_BUG(nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask))) { unit_return_fail(m, "MM_HONORS disabled: Aperture_mask " "did not BUG() for APERTURE_INVALID as expected\n"); } /* Case: Bad aperture value. This will cover default return value */ test_mem->aperture = 10; if (!EXPECT_BUG(nvgpu_aperture_mask(g, test_mem, sysmem_mask, sysmem_coh_mask, vidmem_mask))) { unit_return_fail(m, "MM_HONORS disabled: Aperture_mask" "did not BUG() for junk aperture as expected\n"); } /* Reset attributes */ test_mem->aperture = APERTURE_SYSMEM; return UNIT_SUCCESS; } /* * Test for iommu translate */ int test_nvgpu_mem_iommu_translate(struct unit_module *m, struct gk20a *g, void *args) { u64 temp_phys; struct nvgpu_mem_sgl *test_sgl = (struct nvgpu_mem_sgl *) test_mem->phys_sgt->sgl; struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g); /* * Case: mm is not iommuable * This is specified in nvgpu_os_posix structure. */ temp_phys = nvgpu_mem_iommu_translate(g, test_sgl->phys); if (temp_phys != test_sgl->phys) { unit_return_fail(m, "iommu_translate did not return " "same phys as expected\n"); } /* * Case: mm is not iommuable * But, mm_is_iommuable = true. */ p->mm_is_iommuable = true; temp_phys = nvgpu_mem_iommu_translate(g, test_sgl->phys); if (temp_phys != test_sgl->phys) { unit_return_fail(m, "iommu_translate: mm_is_iommuable=true: " "did not return same phys as expected\n"); } /* * Case: mm is iommuable * Set HAL to enable iommu_translate */ g->ops.mm.gmmu.get_iommu_bit = gp10b_mm_get_iommu_bit; temp_phys = nvgpu_mem_iommu_translate(g, test_sgl->phys); if (temp_phys == test_sgl->phys) { unit_return_fail(m, "iommu_translate did not translate address\n"); } /* Reset iommuable settings */ p->mm_is_iommuable = false; return UNIT_SUCCESS; } /* * Test nvgpu_memset() * * Testing function in APERTURE_SYSMEM and APERTURE_INVALID case. * */ int test_nvgpu_memset_sysmem(struct unit_module *m, struct gk20a *g, void *args) { u32 i; u32 memset_pattern = 0x0000005A; u32 memset_pattern_word = (memset_pattern << 24) | (memset_pattern << 16) | (memset_pattern << 8) | (memset_pattern); u32 memset_words = TEST_SIZE / sizeof(u32); u32 *test_cpu_va = (u32 *)test_mem->cpu_va; /* Case: APERTURE_SYSMEM */ test_mem->aperture = APERTURE_SYSMEM; nvgpu_memset(g, test_mem, 0, memset_pattern, TEST_SIZE); for (i = 0; i < memset_words; i++) { if (test_cpu_va[i] != memset_pattern_word) { unit_return_fail(m, "Memset pattern not found at offset %d\n", i); } } /* Case: APERTURE_INVALID */ test_mem->aperture = APERTURE_INVALID; if (!EXPECT_BUG(nvgpu_memset(g, test_mem, 0, memset_pattern, TEST_SIZE))) { unit_return_fail(m, "APERTURE_INVALID: " "nvgpu_memset did not BUG() as expected\n"); } /* Reset attributes */ test_mem->aperture = APERTURE_SYSMEM; return UNIT_SUCCESS; } /* * Test all memory write and read calls. */ int test_nvgpu_mem_wr_rd(struct unit_module *m, struct gk20a *g, void *args) { u32 i; u32 data_rd, data_words = 16U; u32 test_offset = 0x400; u32 data_pattern = 0x5A5A5A5A; u32 *test_cpu_va = (u32 *)test_mem->cpu_va; u32 data_size = (data_words * sizeof(u32)); u32 *data_src, *data_rd_buf; u64 data_rd_pair; /* Test nvgpu_mem_is_sysmem()*/ /* Case: APERTURE_INVALID */ test_mem->aperture = APERTURE_INVALID; if (nvgpu_mem_is_sysmem(test_mem) != false) { unit_return_fail(m, "nvgpu_mem_is_sysmem " "returns true for APERTURE_INVALID\n"); } if (nvgpu_mem_is_valid(test_mem) != false) { unit_return_fail(m, "nvgpu_mem_is_valid " "returns true for APERTURE_INVALID\n"); } /* Case: APERTURE_SYSMEM_COH */ test_mem->aperture = APERTURE_SYSMEM_COH; /* Confirm nvgpu_mem is set to SYSMEM*/ if (nvgpu_mem_is_sysmem(test_mem) != true) { unit_return_fail(m, "nvgpu_mem_is_sysmem " "returns false for APERTURE_SYSMEM_COH\n"); } /* Case: APERTURE_SYSMEM */ test_mem->aperture = APERTURE_SYSMEM; if (nvgpu_mem_is_sysmem(test_mem) != true) { unit_return_fail(m, "nvgpu_mem_is_sysmem " "returns false for APERTURE_SYSMEM\n"); } /* Confirm nvgpu_mem is allocated*/ if (nvgpu_mem_is_valid(test_mem) != true) { unit_return_fail(m, "nvgpu_mem_is_valid " "returns false for APERTURE_SYSMEM\n"); } /* Test read and write functions */ /* Case: APERTURE_SYSMEM */ nvgpu_mem_wr(g, test_mem, test_offset, data_pattern); if (test_cpu_va[(test_offset / (u32)sizeof(u32))] != data_pattern) { unit_err(m, "mem_wr incorrect write at offset %d\n", test_offset); goto return_fail; } data_rd = nvgpu_mem_rd(g, test_mem, test_offset); if (data_rd != data_pattern) { unit_err(m, "mem_rd data at offset %d incorrect\n", test_offset); goto return_fail; } data_src = (u32 *) nvgpu_kmalloc(g, data_size); if (data_src == NULL) { unit_err(m, "Could not allocate data_src\n"); goto return_fail; } (void) memset(data_src, data_pattern, (data_size)); nvgpu_mem_wr_n(g, test_mem, 0, data_src, data_size); for (i = 0; i < data_words; i++) { if (test_cpu_va[i] != data_src[i]) { unit_err(m, "mem_wr_n incorrect write at offset %d\n", i); goto free_data_src; } } data_rd_buf = (u32 *) nvgpu_kzalloc(g, data_size); if (data_rd_buf == NULL) { unit_err(m, "Could not allocate data_rd_buf\n"); goto free_data_src; } nvgpu_mem_rd_n(g, test_mem, 0, (void *)data_rd_buf, data_size); for (i = 0; i < data_words; i++) { if (data_rd_buf[i] != data_src[i]) { unit_err(m, "mem_rd_n data at offset %d incorrect\n", i); goto free_buffers; } } data_rd_pair = nvgpu_mem_rd32_pair(g, test_mem, 0, 1); if (data_rd_pair != ((u64)data_pattern | ((u64)data_pattern << 32ULL))) { unit_err(m, "nvgpu_mem_rd32_pair pattern incorrect\n"); goto free_buffers; } /* Case: APERTURE_INVALID */ test_mem->aperture = APERTURE_INVALID; if (!EXPECT_BUG(nvgpu_mem_wr(g, test_mem, test_offset, data_pattern))) { unit_err(m, "APERTURE_INVALID: mem_wr did not BUG() as expected\n"); goto free_buffers; } if (!EXPECT_BUG(nvgpu_mem_rd(g, test_mem, test_offset))) { unit_err(m, "APERTURE_INVALID: " "mem_rd did not BUG() as expected\n"); goto free_buffers; } if (!EXPECT_BUG(nvgpu_mem_wr_n(g, test_mem, 0, data_src, data_size))) { unit_err(m, "APERTURE_INVALID: " "mem_wr_n did not BUG() as expected\n"); goto free_buffers; } if (!EXPECT_BUG(nvgpu_mem_rd_n(g, test_mem, 0, (void *)data_rd_buf, data_size))) { unit_err(m, "APERTURE_INVALID: " "mem_rd_n did not BUG() as expected\n"); goto free_buffers; } nvgpu_kfree(g, data_src); data_src = NULL; nvgpu_kfree(g, data_rd_buf); data_rd_buf = NULL; /* Reset attribute */ test_mem->aperture = APERTURE_SYSMEM; return UNIT_SUCCESS; free_buffers: nvgpu_kfree(g, data_rd_buf); free_data_src: nvgpu_kfree(g, data_src); return_fail: return UNIT_FAIL; } /* * Test: test_nvgpu_mem_phys_ops */ int test_nvgpu_mem_phys_ops(struct unit_module *m, struct gk20a *g, void *args) { u64 ret; struct nvgpu_gmmu_attrs *attrs = NULL; struct nvgpu_sgt *test_sgt = test_mem->phys_sgt; struct nvgpu_sgl *test_sgl = test_sgt->sgl; struct nvgpu_sgl *temp_sgl = test_sgt->ops->sgl_next(test_sgl); if (temp_sgl != NULL) { unit_return_fail(m, "nvgpu_mem_phys_sgl_next not NULL as expected\n"); } ret = test_sgt->ops->sgl_dma(test_sgl); if (ret != MEM_ADDRESS) { unit_return_fail(m, "nvgpu_mem_phys_sgl_dma not equal to phys as expected\n"); } ret = test_sgt->ops->sgl_phys(g, test_sgl); if (ret != MEM_ADDRESS) { unit_return_fail(m, "nvgpu_mem_phys_sgl_phys not equal to phys as expected\n"); } ret = test_sgt->ops->sgl_ipa(g, test_sgl); if (ret != MEM_ADDRESS) { unit_return_fail(m, "nvgpu_mem_phys_sgl_ipa incorrect\n"); } ret = test_sgt->ops->sgl_ipa_to_pa(g, test_sgl, 0ULL, NULL); if (ret != 0ULL) { unit_return_fail(m, "nvgpu_mem_phys_sgl_ipa_to_pa not zero as expected\n"); } ret = test_sgt->ops->sgl_length(test_sgl); if (ret != MEM_SIZE) { unit_return_fail(m, "nvgpu_mem_phys_sgl_length incorrect\n"); } ret = test_sgt->ops->sgl_gpu_addr(g, test_sgl, attrs); if (ret != MEM_ADDRESS) { unit_return_fail(m, "nvgpu_mem_phys_sgl_gpu_addr incorrect\n"); } if (test_sgt->ops->sgt_iommuable != NULL) { unit_return_fail(m, "physical nvgpu_mems is not IOMMU'able\n"); } /* * Test nvgpu_mem_phys_sgt_free - No-op */ test_sgt->ops->sgt_free(g, test_sgt); return UNIT_SUCCESS; } int test_nvgpu_mem_create_from_phys(struct unit_module *m, struct gk20a *g, void *args) { int err; struct nvgpu_posix_fault_inj *kmem_fi = nvgpu_kmem_get_fault_injection(); test_mem = (struct nvgpu_mem *) nvgpu_kzalloc(g, sizeof(struct nvgpu_mem)); if (test_mem == NULL) { unit_return_fail(m, "Couldn't allocate memory for nvgpu_mem\n"); } /* * Test 1 - Enable SW fault injection and check that init function * fails with -ENOMEM. */ nvgpu_posix_enable_fault_injection(kmem_fi, true, 0); err = nvgpu_mem_create_from_phys(g, test_mem, MEM_ADDRESS, MEM_PAGES); if (err != -ENOMEM) { unit_return_fail(m, "nvgpu_mem_create_from_phys didn't fail as expected\n"); } nvgpu_posix_enable_fault_injection(kmem_fi, false, 0); /* * Test 2 - Enable SW fault injection for second allocation and * check that init function fails with -ENOMEM. */ nvgpu_posix_enable_fault_injection(kmem_fi, true, 1); err = nvgpu_mem_create_from_phys(g, test_mem, MEM_ADDRESS, MEM_PAGES); if (err != -ENOMEM) { unit_return_fail(m, "nvgpu_mem_create_from_phys didn't fail as expected\n"); } nvgpu_posix_enable_fault_injection(kmem_fi, false, 0); /* * Test 3 - Check that physical memory is inited successfully * Use this allocated memory for next tests in module. */ err = nvgpu_mem_create_from_phys(g, test_mem, MEM_ADDRESS, MEM_PAGES); if (err != 0) { unit_return_fail(m, "nvgpu_mem_create_from_phys init failed\n"); } /* Allocate cpu_va for later tests */ test_mem->cpu_va = nvgpu_kzalloc(g, MEM_SIZE); if (test_mem->cpu_va == NULL) { nvgpu_kfree(g, test_mem); unit_return_fail(m, "Could not allocate memory for cpu_va\n"); } return UNIT_SUCCESS; } int test_free_nvgpu_mem(struct unit_module *m, struct gk20a *g, void *args) { test_mem->aperture = APERTURE_SYSMEM; nvgpu_dma_free(g, test_mem); nvgpu_kfree(g, test_mem); return UNIT_SUCCESS; } struct unit_module_test nvgpu_mem_tests[] = { /* * Init test should run first in order to use allocated memory. */ UNIT_TEST(mem_create_from_phys, test_nvgpu_mem_create_from_phys, NULL, 0), /* * Tests for SYSMEM */ UNIT_TEST(nvgpu_mem_phys_ops, test_nvgpu_mem_phys_ops, NULL, 0), UNIT_TEST(nvgpu_memset_sysmem, test_nvgpu_memset_sysmem, NULL, 0), UNIT_TEST(nvgpu_mem_wr_rd, test_nvgpu_mem_wr_rd, NULL, 0), UNIT_TEST(mem_iommu_translate, test_nvgpu_mem_iommu_translate, NULL, 0), /* * Tests covering VIDMEM branches */ UNIT_TEST(nvgpu_aperture_mask, test_nvgpu_aperture_mask, NULL, 0), #ifdef CONFIG_NVGPU_DGPU UNIT_TEST(nvgpu_mem_vidmem, test_nvgpu_mem_vidmem, NULL, 0), #endif /* * Free test should be executed at the end to free allocated memory. * As nvgpu_mem doesn't not have an explicit free function for sysmem, * this test doesn't cover any nvgpu_mem code. */ UNIT_TEST(test_free_nvgpu_mem, test_free_nvgpu_mem, NULL, 0), }; UNIT_MODULE(nvgpu_mem, nvgpu_mem_tests, UNIT_PRIO_NVGPU_TEST);