From 92d5c53c59955f10fecf9a3f88eef549eed779c2 Mon Sep 17 00:00:00 2001 From: Nicolas Benech Date: Tue, 10 Dec 2019 18:12:57 -0500 Subject: [PATCH] gpu: nvgpu: unit: add fb HAL unit tests Unit tests covering the FB related HALs. JIRA NVGPU-932 Change-Id: I46de25ea2a495e22ca6485d1fae1778261a804bd Signed-off-by: Nicolas Benech Reviewed-on: https://git-master.nvidia.com/r/2259666 Reviewed-by: mobile promotions Tested-by: mobile promotions --- Makefile.umbrella.tmk | 1 + drivers/gpu/nvgpu/libnvgpu-drv_safe.export | 38 +- userspace/Makefile.sources | 3 +- userspace/SWUTS.h | 1 + userspace/SWUTS.sources | 1 + userspace/units/fb/Makefile | 27 ++ userspace/units/fb/Makefile.interface.tmk | 23 ++ userspace/units/fb/Makefile.tmk | 25 ++ userspace/units/fb/fb_fusa.c | 158 ++++++++ userspace/units/fb/fb_fusa.h | 389 +++++++++++++++++++ userspace/units/fb/fb_gm20b_fusa.c | 184 +++++++++ userspace/units/fb/fb_gv11b_fusa.c | 112 ++++++ userspace/units/fb/fb_intr_gv11b_fusa.c | 211 ++++++++++ userspace/units/fb/fb_mmu_fault_gv11b_fusa.c | 301 ++++++++++++++ 14 files changed, 1467 insertions(+), 7 deletions(-) create mode 100644 userspace/units/fb/Makefile create mode 100644 userspace/units/fb/Makefile.interface.tmk create mode 100644 userspace/units/fb/Makefile.tmk create mode 100644 userspace/units/fb/fb_fusa.c create mode 100644 userspace/units/fb/fb_fusa.h create mode 100644 userspace/units/fb/fb_gm20b_fusa.c create mode 100644 userspace/units/fb/fb_gv11b_fusa.c create mode 100644 userspace/units/fb/fb_intr_gv11b_fusa.c create mode 100644 userspace/units/fb/fb_mmu_fault_gv11b_fusa.c diff --git a/Makefile.umbrella.tmk b/Makefile.umbrella.tmk index 51437f2ed..e9869bd49 100644 --- a/Makefile.umbrella.tmk +++ b/Makefile.umbrella.tmk @@ -70,6 +70,7 @@ NV_REPOSITORY_COMPONENTS += userspace/units/mm/mm NV_REPOSITORY_COMPONENTS += userspace/units/mm/page_table_faults NV_REPOSITORY_COMPONENTS += userspace/units/mm/vm NV_REPOSITORY_COMPONENTS += userspace/units/netlist +NV_REPOSITORY_COMPONENTS += userspace/units/fb NV_REPOSITORY_COMPONENTS += userspace/units/fbp NV_REPOSITORY_COMPONENTS += userspace/units/fifo NV_REPOSITORY_COMPONENTS += userspace/units/fifo/channel diff --git a/drivers/gpu/nvgpu/libnvgpu-drv_safe.export b/drivers/gpu/nvgpu/libnvgpu-drv_safe.export index a7c8c2354..9d5af62bf 100644 --- a/drivers/gpu/nvgpu/libnvgpu-drv_safe.export +++ b/drivers/gpu/nvgpu/libnvgpu-drv_safe.export @@ -3,10 +3,7 @@ bitmap_find_next_zero_area_off bug_handler_cancel bug_handler_register -gv11b_fb_read_mmu_fault_buffer_size -gv11b_fb_read_mmu_fault_status -gv11b_fb_write_mmu_fault_buffer_lo_hi -gv11b_fb_write_mmu_fault_buffer_size +fb_gv11b_write_mmu_fault_buffer_get find_first_bit find_first_zero_bit find_next_bit @@ -39,7 +36,15 @@ gk20a_vm_release_share gm20b_channel_bind gm20b_channel_force_ctx_reload gm20b_device_info_parse_enum +gm20b_fb_dump_vpr_info +gm20b_fb_dump_wpr_info +gm20b_fb_mmu_ctrl +gm20b_fb_mmu_debug_ctrl +gm20b_fb_mmu_debug_rd +gm20b_fb_mmu_debug_wr +gm20b_fb_read_wpr_info gm20b_fb_tlb_invalidate +gm20b_fb_vpr_info_fetch gm20b_gr_falcon_get_fecs_ctx_state_store_major_rev_id gm20b_is_engine_gr gm20b_mm_get_big_page_sizes @@ -100,13 +105,33 @@ gv11b_channel_debug_dump gv11b_channel_read_state gv11b_channel_reset_faulted gv11b_channel_unbind -gv11b_fb_intr_enable +gv11b_fb_ecc_free +gv11b_fb_ecc_init gv11b_fb_fault_buf_configure_hw gv11b_fb_fault_buf_set_state_hw +gv11b_fb_fault_buffer_get_ptr_update +gv11b_fb_fault_buffer_size_val +gv11b_fb_handle_bar2_fault +gv11b_fb_handle_mmu_fault +gv11b_fb_init_fs_state gv11b_fb_init_hw -gv11b_fb_is_fault_buf_enabled gv11b_fb_intr_disable +gv11b_fb_intr_enable gv11b_fb_intr_is_mmu_fault_pending +gv11b_fb_intr_isr +gv11b_fb_is_fault_buf_enabled +gv11b_fb_is_fault_buffer_empty +gv11b_fb_mmu_fault_info_dump +gv11b_fb_read_mmu_fault_addr_lo_hi +gv11b_fb_read_mmu_fault_buffer_get +gv11b_fb_read_mmu_fault_buffer_put +gv11b_fb_read_mmu_fault_buffer_size +gv11b_fb_read_mmu_fault_info +gv11b_fb_read_mmu_fault_inst_lo_hi +gv11b_fb_read_mmu_fault_status +gv11b_fb_write_mmu_fault_buffer_lo_hi +gv11b_fb_write_mmu_fault_buffer_size +gv11b_fb_write_mmu_fault_status gv11b_fifo_handle_sched_error gv11b_fifo_intr_0_enable gv11b_fifo_intr_0_isr @@ -119,6 +144,7 @@ gv11b_init_fifo_reset_enable_hw gv11b_init_fifo_setup_hw gv11b_init_hal gv11b_is_fault_engine_subid_gpc +gv11b_mm_copy_from_fault_snap_reg gv11b_mm_is_bar1_supported gv11b_mm_init_inst_block gv11b_mm_l2_flush diff --git a/userspace/Makefile.sources b/userspace/Makefile.sources index 7827a971d..02fc47e94 100644 --- a/userspace/Makefile.sources +++ b/userspace/Makefile.sources @@ -78,7 +78,8 @@ UNITS := \ $(UNIT_SRC)/mm/nvgpu_mem \ $(UNIT_SRC)/mm/vm \ $(UNIT_SRC)/netlist \ - $(UNIT_SRC)/fbp \ + $(UNIT_SRC)/fb \ + $(UNIT_SRC)/fbp \ $(UNIT_SRC)/fifo \ $(UNIT_SRC)/fifo/fifo/gk20a \ $(UNIT_SRC)/fifo/fifo/gv11b \ diff --git a/userspace/SWUTS.h b/userspace/SWUTS.h index c78cd5b9d..cb3fb7100 100644 --- a/userspace/SWUTS.h +++ b/userspace/SWUTS.h @@ -74,6 +74,7 @@ * - @ref SWUTS-mm-page_table_faults * - @ref SWUTS-mm-mm * - @ref SWUTS-mm-vm + * - @ref SWUTS-fb * - @ref SWUTS-fbp * - @ref SWUTS-fuse * - @ref SWUTS-posix-bitops diff --git a/userspace/SWUTS.sources b/userspace/SWUTS.sources index 5cf32faa8..d00fd9fcf 100644 --- a/userspace/SWUTS.sources +++ b/userspace/SWUTS.sources @@ -7,6 +7,7 @@ INPUT += ../../../userspace/units/interface/rbtree/rbtree.h INPUT += ../../../userspace/units/falcon/falcon_tests/nvgpu-falcon.h INPUT += ../../../userspace/units/netlist/nvgpu-netlist.h INPUT += ../../../userspace/units/fbp/nvgpu-fbp.h +INPUT += ../../../userspace/units/fb/fb_fusa.h INPUT += ../../../userspace/units/fifo/channel/nvgpu-channel.h INPUT += ../../../userspace/units/fifo/channel/gk20a/nvgpu-channel-gk20a.h INPUT += ../../../userspace/units/fifo/channel/gm20b/nvgpu-channel-gm20b.h diff --git a/userspace/units/fb/Makefile b/userspace/units/fb/Makefile new file mode 100644 index 000000000..9ab0bb348 --- /dev/null +++ b/userspace/units/fb/Makefile @@ -0,0 +1,27 @@ +# 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. + +.SUFFIXES: + +OBJS = fb_fusa.o fb_gv11b_fusa.o fb_gm20b_fusa.o fb_mmu_fault_gv11b_fusa.o \ + fb_intr_gv11b_fusa.o +MODULE = fb + +include ../Makefile.units diff --git a/userspace/units/fb/Makefile.interface.tmk b/userspace/units/fb/Makefile.interface.tmk new file mode 100644 index 000000000..4ed9f3fa3 --- /dev/null +++ b/userspace/units/fb/Makefile.interface.tmk @@ -0,0 +1,23 @@ +################################### tell Emacs this is a -*- makefile-gmake -*- +# +# Copyright (c) 2019, NVIDIA CORPORATION. All Rights Reserved. +# +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. +# +# tmake for SW Mobile component makefile +# +############################################################################### + +NVGPU_UNIT_NAME=fb + +include $(NV_COMPONENT_DIR)/../Makefile.units.common.interface.tmk + +# Local Variables: +# indent-tabs-mode: t +# tab-width: 8 +# End: +# vi: set tabstop=8 noexpandtab: diff --git a/userspace/units/fb/Makefile.tmk b/userspace/units/fb/Makefile.tmk new file mode 100644 index 000000000..667895e68 --- /dev/null +++ b/userspace/units/fb/Makefile.tmk @@ -0,0 +1,25 @@ +################################### tell Emacs this is a -*- makefile-gmake -*- +# +# Copyright (c) 2019, NVIDIA CORPORATION. All Rights Reserved. +# +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. +# +# tmake for SW Mobile component makefile +# +############################################################################### + +NVGPU_UNIT_NAME=fb +NVGPU_UNIT_SRCS=fb_fusa.c fb_gv11b_fusa.c fb_gm20b_fusa.c \ + fb_mmu_fault_gv11b_fusa.c fb_intr_gv11b_fusa.c + +include $(NV_COMPONENT_DIR)/../Makefile.units.common.tmk + +# Local Variables: +# indent-tabs-mode: t +# tab-width: 8 +# End: +# vi: set tabstop=8 noexpandtab: diff --git a/userspace/units/fb/fb_fusa.c b/userspace/units/fb/fb_fusa.c new file mode 100644 index 000000000..a76a31a05 --- /dev/null +++ b/userspace/units/fb/fb_fusa.c @@ -0,0 +1,158 @@ +/* + * 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 "hal/fb/intr/fb_intr_gv11b.h" +#include +#include + +#include "fb_fusa.h" + +static bool intercept_mmu_invalidate; +static u32 intercept_fb_mmu_ctrl_r; + +void helper_intercept_mmu_write(u32 val) +{ + intercept_fb_mmu_ctrl_r = val; + intercept_mmu_invalidate = true; +} + +/* + * Write callback (for all nvgpu_writel calls). + */ +static void writel_access_reg_fn(struct gk20a *g, + struct nvgpu_reg_access *access) +{ + if (intercept_mmu_invalidate && + (access->addr == fb_mmu_invalidate_pdb_r())) { + intercept_mmu_invalidate = false; + nvgpu_writel(g, fb_mmu_ctrl_r(), intercept_fb_mmu_ctrl_r); + } + nvgpu_posix_io_writel_reg_space(g, access->addr, access->value); + nvgpu_posix_io_record_access(g, access); +} + +/* + * Read callback, similar to the write callback above. + */ +static void readl_access_reg_fn(struct gk20a *g, + struct nvgpu_reg_access *access) +{ + access->value = nvgpu_posix_io_readl_reg_space(g, access->addr); +} + +/* + * Define all the 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 fb_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, +}; + +static int fb_gv11b_init(struct unit_module *m, struct gk20a *g, void *args) +{ + nvgpu_posix_register_io(g, &fb_callbacks); + nvgpu_posix_io_init_reg_space(g); + + /* Register space: FB */ + if (nvgpu_posix_io_add_reg_space(g, fb_niso_intr_r(), SZ_4K) != 0) { + unit_return_fail(m, "nvgpu_posix_io_add_reg_space failed FB\n"); + } + + /* Register space: MC_INTR */ + if (nvgpu_posix_io_add_reg_space(g, mc_intr_r(0), SZ_1K) != 0) { + unit_return_fail(m, "nvgpu_posix_io_add_reg_space failed MC\n"); + } + + /* Register space: HSHUB */ + if (nvgpu_posix_io_add_reg_space(g, fb_hshub_num_active_ltcs_r(), + SZ_256) != 0) { + + unit_return_fail(m, + "nvgpu_posix_io_add_reg_space failed HSHUB\n"); + } + + /* Register space: FBHUB */ + if (nvgpu_posix_io_add_reg_space(g, fb_fbhub_num_active_ltcs_r(), + SZ_256) != 0) { + + unit_return_fail(m, + "nvgpu_posix_io_add_reg_space failed FBHUB\n"); + } + + return UNIT_SUCCESS; +} + +static int fb_gv11b_cleanup(struct unit_module *m, struct gk20a *g, void *args) +{ + /* Unregister space: FB */ + nvgpu_posix_io_delete_reg_space(g, fb_niso_intr_r()); + nvgpu_posix_io_delete_reg_space(g, mc_intr_r(0)); + nvgpu_posix_io_delete_reg_space(g, fb_hshub_num_active_ltcs_r()); + nvgpu_posix_io_delete_reg_space(g, fb_fbhub_num_active_ltcs_r()); + + return UNIT_SUCCESS; +} + +struct unit_module_test fb_tests[] = { + UNIT_TEST(fb_gv11b_init, fb_gv11b_init, NULL, 0), + UNIT_TEST(fb_gv11b_init_test, fb_gv11b_init_test, NULL, 0), + UNIT_TEST(fb_gm20b_tlb_invalidate_test, fb_gm20b_tlb_invalidate_test, + NULL, 0), + UNIT_TEST(fb_gm20b_mmu_ctrl_test, fb_gm20b_mmu_ctrl_test, NULL, 0), + UNIT_TEST(fb_mmu_fault_gv11b_init_test, fb_mmu_fault_gv11b_init_test, + NULL, 0), + UNIT_TEST(fb_mmu_fault_gv11b_buffer_test, + fb_mmu_fault_gv11b_buffer_test, NULL, 0), + UNIT_TEST(fb_mmu_fault_gv11b_snap_reg, fb_mmu_fault_gv11b_snap_reg, + NULL, 0), + UNIT_TEST(fb_mmu_fault_gv11b_handle_fault, + fb_mmu_fault_gv11b_handle_fault, NULL, 0), + UNIT_TEST(fb_mmu_fault_gv11b_handle_bar2_fault, + fb_mmu_fault_gv11b_handle_bar2_fault, NULL, 0), + UNIT_TEST(fb_intr_gv11b_init_test, fb_intr_gv11b_init_test, NULL, 0), + UNIT_TEST(fb_intr_gv11b_isr_test, fb_intr_gv11b_isr_test, NULL, 0), + UNIT_TEST(fb_intr_gv11b_ecc_test_L2TLB, fb_intr_gv11b_ecc_test, + (void *) TEST_ECC_L2TLB, 0), + UNIT_TEST(fb_intr_gv11b_ecc_test_HUBTLB, fb_intr_gv11b_ecc_test, + (void *) TEST_ECC_HUBTLB, 0), + UNIT_TEST(fb_intr_gv11b_ecc_test_FILLUNIT, fb_intr_gv11b_ecc_test, + (void *) TEST_ECC_FILLUNIT, 0), + UNIT_TEST(fb_gv11b_cleanup, fb_gv11b_cleanup, NULL, 0), +}; + +UNIT_MODULE(fb, fb_tests, UNIT_PRIO_NVGPU_TEST); diff --git a/userspace/units/fb/fb_fusa.h b/userspace/units/fb/fb_fusa.h new file mode 100644 index 000000000..fc404dd0f --- /dev/null +++ b/userspace/units/fb/fb_fusa.h @@ -0,0 +1,389 @@ +/* + * 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. + */ + +#ifndef UNIT_NVGPU_FB_H +#define UNIT_NVGPU_FB_H + +struct gk20a; +struct unit_module; + +/** @addtogroup SWUTS-fb + * @{ + * + * Software Unit Test Specification for nvgpu.hal.fb + */ + +/** + * Test specification for: fb_gv11b_init_test + * + * Description: Tests the init HALs for GV11B. + * + * Targets: nvgpu_ecc_init_support, gv11b_fb_init_hw, gv11b_fb_init_fs_state, + * gv11b_fb_ecc_init, gv11b_fb_ecc_free + * + * Test Type: Feature based, Init, Error injection + * + * Input: None + * + * Steps: + * - Set up the ops function pointer for all the HALs under test. + * - Initialize the g->mm structure with arbitrary addresses. + * - Call the ecc_init_support HAL to initialize ECC support. + * - Call the init_hw HAL and ensure the FB_NISO mask was set. + * - Call the init_fs_state HAL and ensure atomic mode was set in the MMU + * control register. + * - Perform dynamic memory error injection on the fb_ecc_init HAL to ensure + * it fails as expected. + * - Call the fb_ecc_init HAL and ensure it succeeds. + * - Call the fb_ecc_free HAL to free dynamic memory. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_gv11b_init_test(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: fb_gm20b_tlb_invalidate_test + * + * Description: . + * + * Targets: gm20b_fb_tlb_invalidate + * + * Test Type: Feature based, Error injection + * + * Input: None + * + * Steps: + * - Initialize ops.fb.tlb_invalidate pointer to gm20b_fb_tlb_invalidate HAL. + * - Create a test nvgpu_mem PDB with SYSMEM aperture. + * - While the NVGPU is powered off, call gm20b_fb_tlb_invalidate and ensure + * it returned success. + * - The power on state of NVGPU. + * - Enable timer error injection (1st occurnce), call gm20b_fb_tlb_invalidate + * and ensure it failed. + * - Call gm20b_fb_tlb_invalidate again and check that it still failed (because + * the fb_mmu_ctrl_r register is not set properly) + * - Set the fb_mmu_ctrl_pri_fifo_space_v bit in fb_mmu_ctrl_r register. + * - Enable timer error injection (2nd occurnce), call gm20b_fb_tlb_invalidate + * and ensure it failed. + * - Using an helper during register writes, intercept writes to fb_mmu_ctrl_r + * to cause a timeout after the MMU invalidate. Ensure that + * gm20b_fb_tlb_invalidate returns a failure. + * - Set the fb_mmu_ctrl_pri_fifo_space_v bit again, and set the intercept + * helper to write the fb_mmu_ctrl_pri_fifo_empty_v bit upon a write to + * fb_mmu_ctrl_r. Ensure that gm20b_fb_tlb_invalidate succeeds. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_gm20b_tlb_invalidate_test(struct unit_module *m, struct gk20a *g, + void *args); + +/** + * Test specification for: fb_gm20b_mmu_ctrl_test + * + * Description: Test GM20B HALs targeting MMU features. + * + * Targets: gm20b_fb_mmu_ctrl, gm20b_fb_mmu_debug_ctrl, gm20b_fb_mmu_debug_wr, + * gm20b_fb_mmu_debug_rd, gm20b_fb_vpr_info_fetch, gm20b_fb_dump_vpr_info, + * gm20b_fb_dump_wpr_info, gm20b_fb_read_wpr_info + * + * Test Type: Feature based, Error injection + * + * Input: None + * + * Steps: + * - Set up the ops function pointer for all the HALs under test. + * - Program an arbitrary value in the fb_mmu_ctrl_r register and ensure the + * gm20b_fb_mmu_ctrl HAL returns the same value. + * - Program an arbitrary value in the fb_mmu_debug_ctrl_r register and ensure + * the gm20b_fb_mmu_debug_ctrl HAL returns the same value. + * - Program an arbitrary value in the fb_mmu_debug_wr_r register and ensure the + * gm20b_fb_mmu_debug_wr HAL returns the same value. + * - Program an arbitrary value in the fb_mmu_debug_rd_r register and ensure the + * gm20b_fb_mmu_debug_rd HAL returns the same value. + * - Call the VPR/WPR dump operations for code coverage. Ensure that none of + * those operations cause a crash. + * - Enable timer error injection (1st occurnce), call gm20b_fb_vpr_info_fetch + * and ensure it failed. + * - Write in the fb_mmu_vpr_info register so that calling + * gm20b_fb_vpr_info_fetch triggers timeout in the + * gm20b_fb_vpr_info_fetch_wait function. Ensure the return values reflects + * a timeout. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_gm20b_mmu_ctrl_test(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: fb_mmu_fault_gv11b_init_test + * + * Description: Init test to setup HAL pointers for FB_MMU fault testing. + * + * Targets: None + * + * Test Type: Init + * + * Input: None + * + * Steps: + * - Set up the ops function pointer for all the HALs under test. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_mmu_fault_gv11b_init_test(struct unit_module *m, struct gk20a *g, + void *args); + +/** + * Test specification for: fb_mmu_fault_gv11b_buffer_test + * + * Description: Ensure all HAL functions work without causing an ABORT. + * + * Targets: gv11b_fb_is_fault_buf_enabled, gv11b_fb_fault_buffer_get_ptr_update, + * gv11b_fb_write_mmu_fault_buffer_size, gv11b_fb_fault_buf_set_state_hw, + * gv11b_fb_read_mmu_fault_status, gv11b_fb_fault_buf_configure_hw + * + * Test Type: Feature based + * + * Input: fb_mmu_fault_gv11b_init_test + * + * Steps: + * - Call gv11b_fb_fault_buffer_get_ptr_update. + * - Set the overflow bit in the fb_mmu_fault_buffer_get_r(0) register, and call + * gv11b_fb_fault_buffer_get_ptr_update. + * - Call gv11b_fb_fault_buffer_size_val and check that the fault buffer is + * empty. + * - Call the gv11b_fb_fault_buf_configure_hw HAL and enable fault buffer. + * - Enable fault buffer again which shouldn't cause any crash. + * - Disable the fault buffer. + * - Enable fault buffer, set the busy bit in fb_mmu_fault_status_r register, + * disable the fault buffer which should cause an internal timeout. Ensure + * that the fault buffer is disabled anyway. + * - Write test values in the fb_mmu_fault_addr_lo_r / fb_mmu_fault_addr_hi_r + * registers, call gv11b_fb_read_mmu_fault_addr_lo_hi and ensure the + * returned values match the test values. + * - Write test values in the fb_mmu_fault_inst_lo_r / fb_mmu_fault_inst_hi_r + * registers, call gv11b_fb_read_mmu_fault_inst_lo_hi and ensure the + * returned values match the test values. + * - Call the gv11b_fb_read_mmu_fault_info HAL and ensure it returns the same + * value as in the fb_mmu_fault_info_r register. + * - Call the gv11b_fb_write_mmu_fault_status HAL to write a test value, then + * read the fb_mmu_fault_status_r register to ensure it is the same value. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_mmu_fault_gv11b_buffer_test(struct unit_module *m, struct gk20a *g, + void *args); + +/** + * Test specification for: fb_mmu_fault_gv11b_snap_reg + * + * Description: Test that gv11b_mm_copy_from_fault_snap_reg behaves correctly + * if the reported fault is valid/invalid. + * + * Targets: gv11b_mm_copy_from_fault_snap_reg + * + * Test Type: Feature based + * + * Input: fb_mmu_fault_gv11b_init_test + * + * Steps: + * - Create a test mmu_fault_info instance. + * - Call gv11b_mm_copy_from_fault_snap_reg with an invalid fault bit and + * ensure the chid of the mmu_fault_info was just set to a default value of 0. + * - Call gv11b_mm_copy_from_fault_snap_reg again with a valid fault bit and + * ensure the chid of the mmu_fault_info is now set to + * NVGPU_INVALID_CHANNEL_ID. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_mmu_fault_gv11b_snap_reg(struct unit_module *m, struct gk20a *g, + void *args); + +/** + * Test specification for: fb_mmu_fault_gv11b_handle_fault + * + * Description: Test the gv11b_fb_handle_mmu_fault HAL for all supported + * interrupt statuses. + * + * Targets: gv11b_fb_handle_mmu_fault, gv11b_fb_fault_buf_set_state_hw + * + * Test Type: Feature based + * + * Input: fb_mmu_fault_gv11b_init_test + * + * Steps: + * - Call gv11b_fb_handle_mmu_fault with an interrupt source set to "other" + * and ensure it was handled by checking the "valid_clear" bit of the + * fb_mmu_fault_status_r register. + * - Enable the fault buffer. + * - Set interrupt source as dropped and ensure it is handled by + * gv11b_fb_handle_mmu_fault. + * - Repeat with a source as non-replayable. + * - Repeat with a source as non-replayable and overflow. + * - Repeat with a source as overflow and corrupted getptr. + * - Disable the fault buffer. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_mmu_fault_gv11b_handle_fault(struct unit_module *m, struct gk20a *g, + void *args); + +/** + * Test specification for: fb_mmu_fault_gv11b_handle_bar2_fault + * + * Description: Test the gv11b_fb_handle_bar2_fault HAL for all supported + * interrupt statuses. + * + * Targets: gv11b_fb_handle_bar2_fault, gv11b_fb_mmu_fault_info_dump, + * gv11b_fb_fault_buf_set_state_hw + * + * Test Type: Feature based + * + * Input: fb_mmu_fault_gv11b_init_test + * + * Steps: + * - Create zero'ed test instances of mmu_fault_info and nvgpu_channel. + * - Call gv11b_fb_handle_bar2_fault with a fault_status of 0. + * - Ensure the gv11b_fb_mmu_fault_info_dump HAL does not cause a crash when + * called with a NULL pointer or a zero'ed out mmu_fault_info structure. + * - Set the minimum set of properties in the mmu_fault_info structure (valid + * and a pointer to the channel) + * - Call the gv11b_fb_mmu_fault_info_dump and ensure it doesn't cause a crash. + * - Set the fault_status to non-replayable and call gv11b_fb_handle_bar2_fault. + * - Repeat with the fault buffer disabled. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_mmu_fault_gv11b_handle_bar2_fault(struct unit_module *m, struct gk20a *g, + void *args); + +/** + * Test specification for: fb_intr_gv11b_init_test + * + * Description: Init test to setup HAL pointers for FB_INTR testing. + * + * Targets: None + * + * Test Type: Init + * + * Input: None + * + * Steps: + * - Set up the ops function pointer for all the HALs under test. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_intr_gv11b_init_test(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: fb_intr_gv11b_isr_test + * + * Description: Test ISR handling with all supported types of interrupts. + * + * Targets: gv11b_fb_intr_enable, gv11b_fb_intr_disable, gv11b_fb_intr_isr + * + * Test Type: Feature based + * + * Input: fb_intr_gv11b_init_test + * + * Steps: + * - Mask all interrupts in the fb_niso_intr_en_set_r register. + * - Call the gv11b_fb_intr_enable HAL and ensure several interrupts are + * unmasked. + * - Set the fb_niso_intr_r register to 0 (no interrupt), and ensure that + * gv11b_fb_intr_is_mmu_fault_pending indicates that no fault is pending. + * - Call the gv11b_fb_intr_isr HAL. + * - Set interrupt source as "access counter notify/error" and call the + * gv11b_fb_intr_isr HAL (this will only cause a nvgpu_info call) + * - Set interrupt source as "MMU fault" and ensure that + * gv11b_fb_intr_is_mmu_fault_pending indicates that a fault is pending. + * - Set interrupt source as "ECC fault" and call the gv11b_fb_intr_isr HAL + * (further ECC testing is done in other tests). + * - Use the gv11b_fb_intr_disable HAL to disable interrupts. + * - Ensure that what was written in the clear register matches the interrupts + * that were enabled at the beginning of this test. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_intr_gv11b_isr_test(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: fb_intr_gv11b_ecc_test + * + * Description: Tests handling of ECC errors. + * + * Targets: gv11b_fb_ecc_init, gv11b_fb_intr_isr, gv11b_fb_intr_handle_ecc, + * gv11b_fb_ecc_free + * + * Test Type: Feature based + * + * Input: fb_intr_gv11b_init_test, args as a subcase with one of these values: + * - TEST_ECC_L2TLB + * - TEST_ECC_HUBTLB + * - TEST_ECC_FILLUNIT + * + * Steps: + * - Based on the subcase passed as an argument to this test, select the + * appropriate values for each HW unit: + * - Address of the status register + * - Address of the corrected error count register + * - Address of the uncorrected error count register + * - Expected status mask for corrected errors + * - Expected status mask for uncorrected errors + * - Expected status mask for corrected errors overflow + * - Expected status mask for uncorrected errors overflow + * - Call the gv11b_fb_ecc_init HAL. + * - Test the hanlding of ISRs in the following cases: + * - Corrected error + * - Uncorrected error + * - Corrected error and overflow (with >0 number of errors) + * - Uncorrected error and overflow (with >0 number of errors) + * - Corrected and uncorrected with overflow and 0 errors. + * - In the case of FILLUNIT, also test the case of corrected and uncorrected + * PDE0 errors. + * - Clear the interrupt status register. + * - Call the gv11b_fb_ecc_free HAL. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int fb_intr_gv11b_ecc_test(struct unit_module *m, struct gk20a *g, void *args); + +/* Values below are used by the fb_intr_gv11b_ecc_test test. */ +#define TEST_ECC_L2TLB 1U +#define TEST_ECC_HUBTLB 2U +#define TEST_ECC_FILLUNIT 3U + +/* Helper function to intercept writes to the MMU status register. */ +void helper_intercept_mmu_write(u32 val); + +/** @} */ +#endif /* UNIT_NVGPU_FB_H */ diff --git a/userspace/units/fb/fb_gm20b_fusa.c b/userspace/units/fb/fb_gm20b_fusa.c new file mode 100644 index 000000000..a5d76f0b7 --- /dev/null +++ b/userspace/units/fb/fb_gm20b_fusa.c @@ -0,0 +1,184 @@ +/* + * 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 "hal/mc/mc_gp10b.h" +#include "hal/fb/fb_gm20b.h" +#include "hal/fb/fb_gv11b.h" +#include "hal/fb/intr/fb_intr_gv11b.h" +#include + +#include "fb_fusa.h" + +#define TEST_REG_VALUE 0x8080A0A0 + +int fb_gm20b_tlb_invalidate_test(struct unit_module *m, struct gk20a *g, + void *args) +{ + int err; + struct nvgpu_mem pdb; + struct nvgpu_posix_fault_inj *timer_fi = + nvgpu_timers_get_fault_injection(); + + /* Define the operations being tested in this unit test */ + g->ops.fb.tlb_invalidate = gm20b_fb_tlb_invalidate; + + /* Setup PDB */ + pdb.aperture = APERTURE_SYSMEM; + + /* First NVGPU is powered off */ + err = g->ops.fb.tlb_invalidate(g, &pdb); + if (err != 0) { + unit_return_fail(m, "tlb_invalidate failed (1)\n"); + } + + /* Set NVGPU as powered on */ + g->power_on_state = NVGPU_STATE_POWERED_ON; + + /* Timeout init fault injection (MMU FIFO space) */ + nvgpu_posix_enable_fault_injection(timer_fi, true, 0); + err = g->ops.fb.tlb_invalidate(g, &pdb); + if (err != -ETIMEDOUT) { + unit_return_fail(m, + "tlb_invalidate did not fail as expected (1)\n"); + } + nvgpu_posix_enable_fault_injection(timer_fi, false, 0); + + /* Timeout fail on fb_mmu_ctrl_r() read */ + err = g->ops.fb.tlb_invalidate(g, &pdb); + if (err != -ETIMEDOUT) { + unit_return_fail(m, + "tlb_invalidate did not fail as expected (2)\n"); + } + + /* + * Prevent timeout on fb_mmu_ctrl_r() by setting a non-zero value in + * the fb_mmu_ctrl_pri_fifo_space_v field. + */ + nvgpu_writel(g, fb_mmu_ctrl_r(), 1 << 16U); + + /* Timeout init fault injection (MMU invalidate) */ + nvgpu_posix_enable_fault_injection(timer_fi, true, 1); + err = g->ops.fb.tlb_invalidate(g, &pdb); + if (err != -ETIMEDOUT) { + unit_return_fail(m, + "tlb_invalidate did not fail as expected (3)\n"); + } + nvgpu_posix_enable_fault_injection(timer_fi, false, 0); + + /* + * Timeout on fb_mmu_ctrl_r read after MMU invalidate (does not return + * a failure) + */ + helper_intercept_mmu_write(0); + err = g->ops.fb.tlb_invalidate(g, &pdb); + if (err != 0) { + unit_return_fail(m, "tlb_invalidate failed (2)\n"); + } + + /* Success */ + nvgpu_writel(g, fb_mmu_ctrl_r(), 1 << 16U); + helper_intercept_mmu_write(1 << 15U); + err = g->ops.fb.tlb_invalidate(g, &pdb); + if (err != 0) { + unit_return_fail(m, "tlb_invalidate failed (3)\n"); + } + + return UNIT_SUCCESS; +} + +int fb_gm20b_mmu_ctrl_test(struct unit_module *m, struct gk20a *g, void *args) +{ + int err; + u64 wpr_base, wpr_size; + struct nvgpu_posix_fault_inj *timer_fi = + nvgpu_timers_get_fault_injection(); + + /* Define the operations being tested in this unit test */ + g->ops.fb.mmu_ctrl = gm20b_fb_mmu_ctrl; + g->ops.fb.mmu_debug_ctrl = gm20b_fb_mmu_debug_ctrl; + g->ops.fb.mmu_debug_wr = gm20b_fb_mmu_debug_wr; + g->ops.fb.mmu_debug_rd = gm20b_fb_mmu_debug_rd; + g->ops.fb.vpr_info_fetch = gm20b_fb_vpr_info_fetch; + g->ops.fb.dump_vpr_info = gm20b_fb_dump_vpr_info; + g->ops.fb.dump_wpr_info = gm20b_fb_dump_wpr_info; + g->ops.fb.read_wpr_info = gm20b_fb_read_wpr_info; + + /* g->ops.mmu_ctrl must return the value in fb_mmu_ctrl_r */ + nvgpu_writel(g, fb_mmu_ctrl_r(), TEST_REG_VALUE); + if (g->ops.fb.mmu_ctrl(g) != TEST_REG_VALUE) { + unit_return_fail(m, "ops.mmu_ctrl: incorrect value\n"); + } + + /* g->ops.mmu_debug_ctrl must return the value in fb_mmu_debug_ctrl_r */ + nvgpu_writel(g, fb_mmu_debug_ctrl_r(), TEST_REG_VALUE); + if (g->ops.fb.mmu_debug_ctrl(g) != TEST_REG_VALUE) { + unit_return_fail(m, "ops.mmu_debug_ctrl: incorrect value\n"); + } + + /* g->ops.mmu_debug_wr must return the value in fb_mmu_debug_wr_r */ + nvgpu_writel(g, fb_mmu_debug_wr_r(), TEST_REG_VALUE); + if (g->ops.fb.mmu_debug_wr(g) != TEST_REG_VALUE) { + unit_return_fail(m, "ops.mmu_debug_wr: incorrect value\n"); + } + + /* g->ops.mmu_debug_rd must return the value in fb_mmu_debug_rd_r */ + nvgpu_writel(g, fb_mmu_debug_rd_r(), TEST_REG_VALUE); + if (g->ops.fb.mmu_debug_rd(g) != TEST_REG_VALUE) { + unit_return_fail(m, "ops.mmu_debug_rd: incorrect value\n"); + } + + /* For code coverage, run the VPR/WPR dump ops */ + g->ops.fb.dump_vpr_info(g); + g->ops.fb.dump_wpr_info(g); + g->ops.fb.read_wpr_info(g, &wpr_base, &wpr_size); + g->ops.fb.vpr_info_fetch(g); + + /* Error injection for g->ops.fb.vpr_info_fetch */ + nvgpu_posix_enable_fault_injection(timer_fi, true, 0); + err = g->ops.fb.vpr_info_fetch(g); + nvgpu_posix_enable_fault_injection(timer_fi, false, 0); + if (err != -ETIMEDOUT) { + unit_return_fail(m, + "vpr_info_fetch did not fail as expected (1)\n"); + } + + /* + * Trigger timeout in the gm20b_fb_vpr_info_fetch_wait function on + * fb_mmu_vpr_info_fetch_v(val) == fb_mmu_vpr_info_fetch_false_v() + */ + nvgpu_writel(g, fb_mmu_vpr_info_r(), 1 << 2U); + err = g->ops.fb.vpr_info_fetch(g); + if (err != -ETIMEDOUT) { + unit_return_fail(m, + "vpr_info_fetch did not fail as expected (2)\n"); + } + + return UNIT_SUCCESS; +} diff --git a/userspace/units/fb/fb_gv11b_fusa.c b/userspace/units/fb/fb_gv11b_fusa.c new file mode 100644 index 000000000..dd11e4553 --- /dev/null +++ b/userspace/units/fb/fb_gv11b_fusa.c @@ -0,0 +1,112 @@ +/* + * 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 "hal/mc/mc_gp10b.h" +#include "hal/fb/fb_gm20b.h" +#include "hal/fb/fb_gv11b.h" +#include "hal/fb/intr/fb_intr_gv11b.h" +#include + +#include "fb_fusa.h" + +int fb_gv11b_init_test(struct unit_module *m, struct gk20a *g, void *args) +{ + int err; + struct nvgpu_posix_fault_inj *kmem_fi = + nvgpu_kmem_get_fault_injection(); + + /* Define the operations being targeted in this unit test */ + g->ops.ecc.ecc_init_support = nvgpu_ecc_init_support; + g->ops.fb.init_hw = gv11b_fb_init_hw; + g->ops.fb.init_fs_state = gv11b_fb_init_fs_state; + g->ops.fb.fb_ecc_init = gv11b_fb_ecc_init; + g->ops.fb.fb_ecc_free = gv11b_fb_ecc_free; + + /* Other HALs */ + g->ops.mc.intr_stall_unit_config = mc_gp10b_intr_stall_unit_config; + g->ops.mc.intr_nonstall_unit_config = + mc_gp10b_intr_nonstall_unit_config; + g->ops.fb.intr.enable = gv11b_fb_intr_enable; + + /* + * Define some arbitrary addresses for test purposes. + * Note: no need to malloc any memory as this unit only needs to trigger + * MMU faults via register mocking. No other memory accesses are done. + */ + g->mm.sysmem_flush.cpu_va = (void *) 0x10000000; + g->mm.mmu_wr_mem.cpu_va = (void *) 0x20000000; + g->mm.mmu_wr_mem.aperture = APERTURE_SYSMEM; + g->mm.mmu_rd_mem.cpu_va = (void *) 0x30000000; + g->mm.mmu_rd_mem.aperture = APERTURE_SYSMEM; + + g->ops.ecc.ecc_init_support(g); + + nvgpu_writel(g, fb_niso_intr_en_set_r(0), 0); + g->ops.fb.init_hw(g); + /* Ensure that g->ops.fb.intr.enable set up a mask */ + if (nvgpu_readl(g, fb_niso_intr_en_set_r(0)) == 0) { + unit_return_fail(m, "FB_NISO mask not set\n"); + } + + g->ops.fb.init_fs_state(g); + /* Ensure atomic mode was enabled */ + if ((nvgpu_readl(g, fb_mmu_ctrl_r()) & + fb_mmu_ctrl_atomic_capability_mode_m()) == 0) { + + unit_return_fail(m, "Atomic mode not set\n"); + } + + /* For branch coverage */ + nvgpu_set_enabled(g, NVGPU_SEC_PRIVSECURITY, true); + g->ops.fb.init_fs_state(g); + nvgpu_set_enabled(g, NVGPU_SEC_PRIVSECURITY, false); + + /* + * gv11b_fb_ecc_init initializes 5 structures via kmem. Test the failure + * of all of them. + */ + for (int i = 0; i < 5; i++) { + nvgpu_posix_enable_fault_injection(kmem_fi, true, i); + err = g->ops.fb.fb_ecc_init(g); + nvgpu_posix_enable_fault_injection(kmem_fi, false, 0); + if (err != -ENOMEM) { + unit_return_fail(m, "gv11b_fb_ecc_init did not fail as expected (%d)\n", i); + } + } + + err = g->ops.fb.fb_ecc_init(g); + if (err != 0) { + unit_return_fail(m, "gv11b_fb_ecc_init failed\n"); + } + + g->ops.fb.fb_ecc_free(g); + + return UNIT_SUCCESS; +} diff --git a/userspace/units/fb/fb_intr_gv11b_fusa.c b/userspace/units/fb/fb_intr_gv11b_fusa.c new file mode 100644 index 000000000..828dc167f --- /dev/null +++ b/userspace/units/fb/fb_intr_gv11b_fusa.c @@ -0,0 +1,211 @@ +/* + * 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 "hal/mc/mc_gp10b.h" +#include "hal/fb/fb_gm20b.h" +#include "hal/fb/fb_gv11b.h" +#include "hal/fb/fb_mmu_fault_gv11b.h" +#include "hal/fb/intr/fb_intr_gv11b.h" +#include +#include + +#include "fb_fusa.h" + +/* Arbitrary number of errors */ +#define ECC_ERRORS 15U + +int fb_intr_gv11b_init_test(struct unit_module *m, struct gk20a *g, void *args) +{ + /* HALs under test */ + g->ops.fb.fb_ecc_init = gv11b_fb_ecc_init; + g->ops.fb.fb_ecc_free = gv11b_fb_ecc_free; + + return UNIT_SUCCESS; +} + +int fb_intr_gv11b_isr_test(struct unit_module *m, struct gk20a *g, void *args) +{ + /* Mask all interrupts */ + nvgpu_writel(g, fb_niso_intr_en_set_r(0), 0); + /* Enable interrupts */ + gv11b_fb_intr_enable(g); + if (nvgpu_readl(g, fb_niso_intr_en_set_r(0)) == 0) { + unit_return_fail(m, "FB_INTR not unmasked\n"); + } + + /* Set INTR status register to 0, i.e. no interrupt */ + nvgpu_writel(g, fb_niso_intr_r(), 0); + if (gv11b_fb_intr_is_mmu_fault_pending(g)) { + unit_return_fail(m, "MMU fault should NOT be pending\n"); + } + gv11b_fb_intr_isr(g); + + /* Hub access counter notify/error: just causes a nvgpu_info call */ + nvgpu_writel(g, fb_niso_intr_r(), + fb_niso_intr_hub_access_counter_notify_m()); + gv11b_fb_intr_isr(g); + + /* MMU fault: testing of MMU fault handling is done in other tests */ + nvgpu_writel(g, fb_niso_intr_r(), + fb_niso_intr_mmu_other_fault_notify_m()); + if (!gv11b_fb_intr_is_mmu_fault_pending(g)) { + unit_return_fail(m, "MMU fault should be pending\n"); + } + gv11b_fb_intr_isr(g); + + /* ECC fault: testing of ECC fault handling is done in other tests */ + nvgpu_writel(g, fb_niso_intr_r(), + fb_niso_intr_mmu_ecc_uncorrected_error_notify_pending_f()); + gv11b_fb_intr_isr(g); + + /* Disable interrupts */ + gv11b_fb_intr_disable(g); + /* + * In real HW it may not be possible to read the set/clear registers but + * here we can, and what was programmed in the set register should be + * the same as what was programmed in the clear register. + */ + if (nvgpu_readl(g, fb_niso_intr_en_set_r(0)) != + nvgpu_readl(g, fb_niso_intr_en_clr_r(0))) { + + unit_return_fail(m, "FB_INTR set/clear mismatch\n"); + } + + return UNIT_SUCCESS; +} + +struct gv11b_ecc_test_parameters { + u32 status_reg; + u32 corrected_err_reg; + u32 uncorrected_err_reg; + u32 corrected_status, uncorrected_status; + u32 corrected_overflow, uncorrected_overflow; +}; + +static struct gv11b_ecc_test_parameters l2tlb_parameters = { + .status_reg = fb_mmu_l2tlb_ecc_status_r(), + .corrected_err_reg = fb_mmu_l2tlb_ecc_corrected_err_count_r(), + .uncorrected_err_reg = fb_mmu_l2tlb_ecc_uncorrected_err_count_r(), + .corrected_status = fb_mmu_l2tlb_ecc_status_corrected_err_l2tlb_sa_data_m(), + .uncorrected_status = fb_mmu_l2tlb_ecc_status_uncorrected_err_l2tlb_sa_data_m(), + .corrected_overflow = fb_mmu_l2tlb_ecc_status_corrected_err_total_counter_overflow_m(), + .uncorrected_overflow = fb_mmu_l2tlb_ecc_status_uncorrected_err_total_counter_overflow_m(), +}; + +static struct gv11b_ecc_test_parameters hubtlb_parameters = { + .status_reg = fb_mmu_hubtlb_ecc_status_r(), + .corrected_err_reg = fb_mmu_hubtlb_ecc_corrected_err_count_r(), + .uncorrected_err_reg = fb_mmu_hubtlb_ecc_uncorrected_err_count_r(), + .corrected_status = fb_mmu_hubtlb_ecc_status_corrected_err_sa_data_m(), + .uncorrected_status = fb_mmu_hubtlb_ecc_status_uncorrected_err_sa_data_m(), + .corrected_overflow = fb_mmu_hubtlb_ecc_status_corrected_err_total_counter_overflow_m(), + .uncorrected_overflow = fb_mmu_hubtlb_ecc_status_uncorrected_err_total_counter_overflow_m(), +}; + +static struct gv11b_ecc_test_parameters fillunit_parameters = { + .status_reg = fb_mmu_fillunit_ecc_status_r(), + .corrected_err_reg = fb_mmu_fillunit_ecc_corrected_err_count_r(), + .uncorrected_err_reg = fb_mmu_fillunit_ecc_uncorrected_err_count_r(), + .corrected_status = fb_mmu_fillunit_ecc_status_corrected_err_pte_data_m(), + .uncorrected_status = fb_mmu_fillunit_ecc_status_uncorrected_err_pte_data_m(), + .corrected_overflow = fb_mmu_fillunit_ecc_status_corrected_err_total_counter_overflow_m(), + .uncorrected_overflow = fb_mmu_fillunit_ecc_status_uncorrected_err_total_counter_overflow_m(), +}; + +int fb_intr_gv11b_ecc_test(struct unit_module *m, struct gk20a *g, void *args) +{ + struct gv11b_ecc_test_parameters *p; + u64 subcase = (u64) args; + + switch (subcase) { + case TEST_ECC_L2TLB: + p = &l2tlb_parameters; + break; + case TEST_ECC_HUBTLB: + p = &hubtlb_parameters; + break; + case TEST_ECC_FILLUNIT: + p = &fillunit_parameters; + break; + default: + unit_return_fail(m, "Invalid subcase\n"); + } + + g->ops.fb.fb_ecc_init(g); + + /* Set the interrupt status as corrected */ + nvgpu_writel(g, p->status_reg, p->corrected_status); + gv11b_fb_intr_isr(g); + + /* Set the interrupt status as uncorrected */ + nvgpu_writel(g, p->status_reg, p->uncorrected_status); + gv11b_fb_intr_isr(g); + + /* Set arbitrary number of corrected and uncorrected errors */ + nvgpu_writel(g, p->corrected_err_reg, ECC_ERRORS); + nvgpu_writel(g, p->uncorrected_err_reg, ECC_ERRORS); + gv11b_fb_intr_isr(g); + + /* Same but with corrected overflow bit set */ + nvgpu_writel(g, p->status_reg, 1 | p->corrected_overflow); + nvgpu_writel(g, p->corrected_err_reg, ECC_ERRORS); + nvgpu_writel(g, p->uncorrected_err_reg, ECC_ERRORS); + gv11b_fb_intr_isr(g); + + /* Same but with uncorrected overflow bit set */ + nvgpu_writel(g, p->status_reg, 1 | p->uncorrected_overflow); + nvgpu_writel(g, p->corrected_err_reg, ECC_ERRORS); + nvgpu_writel(g, p->uncorrected_err_reg, ECC_ERRORS); + gv11b_fb_intr_isr(g); + + /* Both overflow but error counts at 0 */ + nvgpu_writel(g, p->status_reg, 1 | p->corrected_overflow | + p->uncorrected_overflow); + nvgpu_writel(g, p->corrected_err_reg, 0); + nvgpu_writel(g, p->uncorrected_err_reg, 0); + gv11b_fb_intr_isr(g); + + /* Extra case for fillunit */ + if (subcase == TEST_ECC_FILLUNIT) { + /* PDE0 */ + nvgpu_writel(g, p->status_reg, + fb_mmu_fillunit_ecc_status_corrected_err_pde0_data_m() | + fb_mmu_fillunit_ecc_status_uncorrected_err_pde0_data_m()); + gv11b_fb_intr_isr(g); + } + + /* Clear interrupt status */ + nvgpu_writel(g, p->status_reg, 0); + + g->ops.fb.fb_ecc_free(g); + + return UNIT_SUCCESS; +} diff --git a/userspace/units/fb/fb_mmu_fault_gv11b_fusa.c b/userspace/units/fb/fb_mmu_fault_gv11b_fusa.c new file mode 100644 index 000000000..f3cf0f17a --- /dev/null +++ b/userspace/units/fb/fb_mmu_fault_gv11b_fusa.c @@ -0,0 +1,301 @@ +/* + * 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 "hal/mc/mc_gp10b.h" +#include "hal/fb/fb_gm20b.h" +#include "hal/fb/fb_gv11b.h" +#include "hal/fb/fb_mmu_fault_gv11b.h" +#include "hal/fb/intr/fb_intr_gv11b.h" +#include + +#include "fb_fusa.h" + +#define FAULT_STATUS_TEST_VAL 0x101U +#define TEST_VALUE_1 0x80801234 +#define TEST_VALUE_2 0xABCD4567 + +static u32 hal_channel_count(struct gk20a *g) +{ + /* Reasonable channel count for the purpose of this test */ + return 0x00000200U; +} + +static void hal_bar2_fault_nop(struct gk20a *g) +{ + /* no-op */ +} + +static int hal_bar2_bind_nop(struct gk20a *g, struct nvgpu_mem *bar2_inst) +{ + /* no-op */ + return 0; +} + +static u32 hal_fifo_mmu_fault_id_to_pbdma_id(struct gk20a *g, u32 mmu_fault_id) +{ + return INVAL_ID; +} + +int fb_mmu_fault_gv11b_init_test(struct unit_module *m, struct gk20a *g, + void *args) +{ + /* HALs under test */ + g->ops.fb.read_mmu_fault_buffer_size = + gv11b_fb_read_mmu_fault_buffer_size; + g->ops.fb.read_mmu_fault_buffer_get = + gv11b_fb_read_mmu_fault_buffer_get; + g->ops.fb.read_mmu_fault_buffer_put = + gv11b_fb_read_mmu_fault_buffer_put; + g->ops.fb.write_mmu_fault_buffer_get = + fb_gv11b_write_mmu_fault_buffer_get; + g->ops.fb.is_fault_buf_enabled = gv11b_fb_is_fault_buf_enabled; + g->ops.fb.fault_buf_set_state_hw = gv11b_fb_fault_buf_set_state_hw; + g->ops.fb.write_mmu_fault_buffer_size = + gv11b_fb_write_mmu_fault_buffer_size; + g->ops.fb.read_mmu_fault_status = gv11b_fb_read_mmu_fault_status; + g->ops.fb.fault_buf_configure_hw = gv11b_fb_fault_buf_configure_hw; + g->ops.fb.write_mmu_fault_buffer_lo_hi = + gv11b_fb_write_mmu_fault_buffer_lo_hi; + g->ops.fb.read_mmu_fault_addr_lo_hi = + gv11b_fb_read_mmu_fault_addr_lo_hi; + g->ops.fb.read_mmu_fault_inst_lo_hi = + gv11b_fb_read_mmu_fault_inst_lo_hi; + g->ops.fb.read_mmu_fault_info = gv11b_fb_read_mmu_fault_info; + g->ops.fb.write_mmu_fault_status = gv11b_fb_write_mmu_fault_status; + + /* Other HALs that are needed */ + g->ops.channel.count = hal_channel_count; + g->ops.ce.mthd_buffer_fault_in_bar2_fault = hal_bar2_fault_nop; + g->ops.bus.bar2_bind = hal_bar2_bind_nop; + g->ops.fifo.mmu_fault_id_to_pbdma_id = + hal_fifo_mmu_fault_id_to_pbdma_id; + + return UNIT_SUCCESS; +} + +int fb_mmu_fault_gv11b_buffer_test(struct unit_module *m, struct gk20a *g, + void *args) +{ + u32 get_idx; + u32 val; + u32 lo, hi; + + if (g->ops.fb.is_fault_buf_enabled(g, 0)) { + unit_return_fail(m, "fault buffer not disabled as expected\n"); + } + + /* Standard case */ + gv11b_fb_fault_buffer_get_ptr_update(g, 0, 0); + + /* Overflow situation */ + nvgpu_writel(g, fb_mmu_fault_buffer_get_r(0), + fb_mmu_fault_buffer_get_overflow_m()); + gv11b_fb_fault_buffer_get_ptr_update(g, 0, 0); + + gv11b_fb_fault_buffer_size_val(g, 0); + if (!gv11b_fb_is_fault_buffer_empty(g, 0, &get_idx)) { + unit_return_fail(m, "fault buffer not empty as expected\n"); + } + + /* Fault buffer hw setup */ + g->ops.fb.fault_buf_configure_hw(g, 0); + + /* Enable fault buffer */ + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_ENABLED); + + /* Enabling again shouldn't cause an issue */ + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_ENABLED); + + /* Disable */ + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_DISABLED); + + /* Try to disable again, but cause a timeout as fault status is set */ + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_ENABLED); + nvgpu_writel(g, fb_mmu_fault_status_r(), + fb_mmu_fault_status_busy_true_f()); + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_DISABLED); + if (g->ops.fb.is_fault_buf_enabled(g, 0)) { + unit_return_fail(m, "fault buffer not disabled as expected\n"); + } + + nvgpu_writel(g, fb_mmu_fault_addr_lo_r(), TEST_VALUE_1); + nvgpu_writel(g, fb_mmu_fault_addr_hi_r(), TEST_VALUE_2); + g->ops.fb.read_mmu_fault_addr_lo_hi(g, &lo, &hi); + if ((lo != TEST_VALUE_1) || (hi != TEST_VALUE_2)) { + unit_return_fail(m, "Invalid MMU fault address\n"); + } + + nvgpu_writel(g, fb_mmu_fault_inst_lo_r(), TEST_VALUE_1); + nvgpu_writel(g, fb_mmu_fault_inst_hi_r(), TEST_VALUE_2); + g->ops.fb.read_mmu_fault_inst_lo_hi(g, &lo, &hi); + if ((lo != TEST_VALUE_1) || (hi != TEST_VALUE_2)) { + unit_return_fail(m, "Invalid MMU fault inst\n"); + } + + val = g->ops.fb.read_mmu_fault_info(g); + if (val != nvgpu_readl(g, fb_mmu_fault_info_r())) { + unit_return_fail(m, "invalid fb_mmu_fault_info_r value\n"); + } + + g->ops.fb.write_mmu_fault_status(g, FAULT_STATUS_TEST_VAL); + if (nvgpu_readl(g, fb_mmu_fault_status_r()) != FAULT_STATUS_TEST_VAL) { + unit_return_fail(m, "invalid fb_mmu_fault_status_r value\n"); + } + + return UNIT_SUCCESS; +} + +int fb_mmu_fault_gv11b_snap_reg(struct unit_module *m, struct gk20a *g, + void *args) +{ + struct mmu_fault_info mmufault; + + /* Not a valid fault, chid should just be zero'ed out by memset */ + gv11b_mm_copy_from_fault_snap_reg(g, 0, &mmufault); + if (mmufault.chid != 0) { + unit_return_fail(m, "chid updated for invalid fault\n"); + } + + /* Valid fault */ + gv11b_mm_copy_from_fault_snap_reg(g, fb_mmu_fault_status_valid_set_f(), + &mmufault); + if (mmufault.chid != NVGPU_INVALID_CHANNEL_ID) { + unit_return_fail(m, "chid NOT updated for valid fault\n"); + } + + return UNIT_SUCCESS; +} + +static bool helper_is_intr_cleared(struct gk20a *g) +{ + return (nvgpu_readl(g, fb_mmu_fault_status_r()) == + fb_mmu_fault_status_valid_clear_f()); +} + +int fb_mmu_fault_gv11b_handle_fault(struct unit_module *m, struct gk20a *g, + void *args) +{ + u32 niso_intr; + + /* Set interrupt source as "other" and handle it */ + niso_intr = fb_niso_intr_mmu_other_fault_notify_m(); + nvgpu_writel(g, fb_mmu_fault_status_r(), 0); + gv11b_fb_handle_mmu_fault(g, niso_intr); + if (!helper_is_intr_cleared(g)) { + unit_return_fail(m, "unhandled interrupt (1)\n"); + } + + /* Enable fault buffer */ + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_ENABLED); + + /* Handle again for branch coverage */ + gv11b_fb_handle_mmu_fault(g, niso_intr); + + /* Set a valid dropped status and handle again */ + nvgpu_writel(g, fb_mmu_fault_status_r(), + fb_mmu_fault_status_dropped_bar1_phys_set_f()); + gv11b_fb_handle_mmu_fault(g, niso_intr); + if (!helper_is_intr_cleared(g)) { + unit_return_fail(m, "unhandled interrupt (2)\n"); + } + + /* Now set interrupt source as a non-replayable fault and handle it */ + niso_intr = fb_niso_intr_mmu_nonreplayable_fault_notify_m(); + nvgpu_writel(g, fb_mmu_fault_status_r(), 0); + gv11b_fb_handle_mmu_fault(g, niso_intr); + if (!helper_is_intr_cleared(g)) { + unit_return_fail(m, "unhandled interrupt (3)\n"); + } + + /* Now set source as non-replayable and overflow then handle it */ + niso_intr = fb_niso_intr_mmu_nonreplayable_fault_notify_m() | + fb_niso_intr_mmu_nonreplayable_fault_overflow_m(); + nvgpu_writel(g, fb_mmu_fault_status_r(), 0); + gv11b_fb_handle_mmu_fault(g, niso_intr); + if (!helper_is_intr_cleared(g)) { + unit_return_fail(m, "unhandled interrupt (4)\n"); + } + + /* Same case but ensure fault status register is also set properly */ + nvgpu_writel(g, fb_mmu_fault_status_r(), + fb_mmu_fault_status_non_replayable_overflow_m()); + nvgpu_writel(g, fb_mmu_fault_status_r(), 0); + gv11b_fb_handle_mmu_fault(g, niso_intr); + if (!helper_is_intr_cleared(g)) { + unit_return_fail(m, "unhandled interrupt (5)\n"); + } + + /* Case where getptr is reported as corrupted */ + nvgpu_writel(g, fb_mmu_fault_status_r(), + fb_mmu_fault_status_non_replayable_overflow_m() | + fb_mmu_fault_status_non_replayable_getptr_corrupted_m()); + nvgpu_writel(g, fb_mmu_fault_status_r(), 0); + gv11b_fb_handle_mmu_fault(g, niso_intr); + if (!helper_is_intr_cleared(g)) { + unit_return_fail(m, "unhandled interrupt (6)\n"); + } + + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_DISABLED); + + return UNIT_SUCCESS; +} + +int fb_mmu_fault_gv11b_handle_bar2_fault(struct unit_module *m, struct gk20a *g, + void *args) +{ + struct mmu_fault_info mmufault; + struct nvgpu_channel refch; + u32 fault_status = 0; + + (void) memset(&mmufault, 0, sizeof(mmufault)); + (void) memset(&refch, 0, sizeof(refch)); + + gv11b_fb_handle_bar2_fault(g, &mmufault, fault_status); + + /* Set the minimum mmufault struct to handle the fault */ + /* First cover some error cases */ + gv11b_fb_mmu_fault_info_dump(g, NULL); + gv11b_fb_mmu_fault_info_dump(g, &mmufault); + + /* Now set it up properly */ + mmufault.valid = true; + mmufault.refch = &refch; + gv11b_fb_mmu_fault_info_dump(g, &mmufault); + fault_status = fb_mmu_fault_status_non_replayable_error_m(); + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_ENABLED); + gv11b_fb_handle_bar2_fault(g, &mmufault, fault_status); + + /* Case where fault buffer is not enabled */ + g->ops.fb.fault_buf_set_state_hw(g, 0, NVGPU_MMU_FAULT_BUF_DISABLED); + gv11b_fb_handle_bar2_fault(g, &mmufault, fault_status); + + return UNIT_SUCCESS; +}