diff --git a/Makefile.umbrella.tmk b/Makefile.umbrella.tmk index 305734f78..e8468581f 100644 --- a/Makefile.umbrella.tmk +++ b/Makefile.umbrella.tmk @@ -62,6 +62,7 @@ NV_REPOSITORY_COMPONENTS += userspace/units/fuse endif NV_REPOSITORY_COMPONENTS += userspace/units/list NV_REPOSITORY_COMPONENTS += userspace/units/enabled +NV_REPOSITORY_COMPONENTS += userspace/units/falcon/falcon endif # Local Variables: diff --git a/drivers/gpu/nvgpu/libnvgpu-drv_safe.export b/drivers/gpu/nvgpu/libnvgpu-drv_safe.export index bbf85f819..2c5353f0f 100644 --- a/drivers/gpu/nvgpu/libnvgpu-drv_safe.export +++ b/drivers/gpu/nvgpu/libnvgpu-drv_safe.export @@ -74,6 +74,10 @@ nvgpu_channel_sync_create nvgpu_dma_alloc nvgpu_dma_alloc_get_fault_injection nvgpu_dma_free +nvgpu_falcon_copy_from_dmem +nvgpu_falcon_copy_from_imem +nvgpu_falcon_copy_to_dmem +nvgpu_falcon_copy_to_imem nvgpu_falcon_get_instance nvgpu_falcon_sw_free nvgpu_falcon_sw_init diff --git a/userspace/Makefile.sources b/userspace/Makefile.sources index 93db280db..dc3f56353 100644 --- a/userspace/Makefile.sources +++ b/userspace/Makefile.sources @@ -66,7 +66,8 @@ UNITS := \ $(UNIT_SRC)/fifo/runlist \ $(UNIT_SRC)/fifo/tsg \ $(UNIT_SRC)/list \ - $(UNIT_SRC)/enabled + $(UNIT_SRC)/enabled \ + $(UNIT_SRC)/falcon/falcon ifeq ($(CONFIG_NVGPU_HAL_NON_FUSA),1) UNITS += $(UNIT_SRC)/fuse diff --git a/userspace/required_tests.json b/userspace/required_tests.json index 7b1409bf1..e7eea579d 100644 --- a/userspace/required_tests.json +++ b/userspace/required_tests.json @@ -1252,5 +1252,30 @@ "test": "map_buf_gpu_va", "test_level": 0, "unit": "vm" + }, + { + "test": "falcon_mem_rw_init", + "test_level": 0, + "unit": "falcon" + }, + { + "test": "falcon_mem_rw_range", + "test_level": 0, + "unit": "falcon" + }, + { + "test": "falcon_mem_rw_aligned", + "test_level": 0, + "unit": "falcon" + }, + { + "test": "falcon_mem_rw_zero", + "test_level": 0, + "unit": "falcon" + }, + { + "test": "falcon_free_test_env", + "test_level": 0, + "unit": "falcon" } ] diff --git a/userspace/units/falcon/falcon/Makefile b/userspace/units/falcon/falcon/Makefile new file mode 100644 index 000000000..1ff5dea90 --- /dev/null +++ b/userspace/units/falcon/falcon/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 = ../falcon_utf.o \ + falcon.o +MODULE = falcon + +include ../../Makefile.units diff --git a/userspace/units/falcon/falcon/Makefile.interface.tmk b/userspace/units/falcon/falcon/Makefile.interface.tmk new file mode 100644 index 000000000..2bcaeb33b --- /dev/null +++ b/userspace/units/falcon/falcon/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=falcon + +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/falcon/falcon/Makefile.tmk b/userspace/units/falcon/falcon/Makefile.tmk new file mode 100644 index 000000000..ad164d582 --- /dev/null +++ b/userspace/units/falcon/falcon/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=falcon +NVGPU_UNIT_SRCS=../falcon_utf.c \ + falcon.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/falcon/falcon/falcon.c b/userspace/units/falcon/falcon/falcon.c new file mode 100644 index 000000000..eab3c1869 --- /dev/null +++ b/userspace/units/falcon/falcon/falcon.c @@ -0,0 +1,436 @@ +/* + * 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 "../falcon_utf.h" + +static struct nvgpu_falcon *pmu_flcn; +static struct nvgpu_falcon *uninit_flcn; +static u32 *rand_test_data; + +#define NV_PMC_BOOT_0_ARCHITECTURE_GV110 (0x00000015 << \ + NVGPU_GPU_ARCHITECTURE_SHIFT) +#define NV_PMC_BOOT_0_IMPLEMENTATION_B 0xB +#define MAX_MEM_TYPE (MEM_IMEM + 1) + +#define RAND_DATA_SIZE (SZ_4K) + +static void init_rand_buffer(void) +{ + u32 i; + + /* + * Fill the test buffer with random data. Always use the same seed to + * make the test deterministic. + */ + srand(0); + for (i = 0; i < RAND_DATA_SIZE/sizeof(u32); i++) { + rand_test_data[i] = (u32) rand(); + } +} + +static int init_falcon_test_env(struct unit_module *m, struct gk20a *g) +{ + int err = 0; + + nvgpu_posix_io_init_reg_space(g); + nvgpu_utf_falcon_register_io(g); + + /* + * Fuse register fuse_opt_priv_sec_en_r() is read during init_hal hence + * add it to reg space + */ + if (nvgpu_posix_io_add_reg_space(g, + fuse_opt_priv_sec_en_r(), 0x4) != 0) { + unit_err(m, "Add reg space failed!\n"); + return -ENOMEM; + } + + /* HAL init parameters for gv11b */ + g->params.gpu_arch = NV_PMC_BOOT_0_ARCHITECTURE_GV110; + g->params.gpu_impl = NV_PMC_BOOT_0_IMPLEMENTATION_B; + + /* HAL init required for getting the falcon ops initialized. */ + err = nvgpu_init_hal(g); + if (err != 0) { + return -ENODEV; + } + + /* Initialize utf & nvgpu falcon for test usage */ + err = nvgpu_utf_falcon_init(m, g, FALCON_ID_PMU); + if (err) { + return err; + } + + /* Set falcons for test usage */ + pmu_flcn = &g->pmu_flcn; + uninit_flcn = &g->fecs_flcn; + + /* Create a test buffer to be filled with random data */ + rand_test_data = (u32 *) nvgpu_kzalloc(g, RAND_DATA_SIZE); + if (rand_test_data == NULL) { + return -ENOMEM; + } + + init_rand_buffer(); + return 0; +} + +static int free_falcon_test_env(struct unit_module *m, struct gk20a *g, + void *__args) +{ + if (pmu_flcn == NULL || !pmu_flcn->is_falcon_supported) { + unit_return_fail(m, "test environment not initialized."); + } + + nvgpu_kfree(g, rand_test_data); + nvgpu_utf_falcon_free(g, FALCON_ID_PMU); + return UNIT_SUCCESS; +} + +/* + * This function will compare rand_test_data with falcon flcn's imem/dmem + * based on type from offset src of size. It returns 0 on match else + * non-zero value. + */ +static int falcon_read_compare(struct unit_module *m, struct gk20a *g, + enum falcon_mem_type type, + u32 src, u32 size) +{ + u8 *dest = NULL, *destp; + u32 total_block_read = 0; + u32 byte_read_count = 0; + u32 byte_cnt = size; + int err; + + dest = (u8 *) nvgpu_kzalloc(g, byte_cnt); + if (dest == NULL) { + unit_err(m, "Memory allocation failed\n"); + return -ENOMEM; + } + + destp = dest; + + total_block_read = byte_cnt >> 8; + do { + byte_read_count = + total_block_read ? FALCON_BLOCK_SIZE : byte_cnt; + if (!byte_read_count) { + break; + } + + if (type == MEM_IMEM) { + err = nvgpu_falcon_copy_from_imem(pmu_flcn, src, + destp, byte_read_count, 0); + } else if (type == MEM_DMEM) { + err = nvgpu_falcon_copy_from_dmem(pmu_flcn, src, + destp, byte_read_count, 0); + } else { + unit_err(m, "Invalid read type\n"); + err = -EINVAL; + goto free_dest; + } + + if (err) { + unit_err(m, "Failed to copy from falcon memory\n"); + goto free_dest; + } + + destp += byte_read_count; + src += byte_read_count; + byte_cnt -= byte_read_count; + } while (total_block_read--); + + if (memcmp((void *) dest, (void *) rand_test_data, size) != 0) { + unit_err(m, "Mismatch comparing copied data\n"); + err = -EINVAL; + } + +free_dest: + nvgpu_kfree(g, dest); + return err; +} + +/* + * This function will check that falcon memory read/write functions with + * specified parameters complete with return value exp_err. + */ +static int falcon_check_read_write(struct gk20a *g, + struct unit_module *m, + struct nvgpu_falcon *flcn, + enum falcon_mem_type type, + u32 dst, u32 byte_cnt, int exp_err) +{ + u8 *dest = NULL; + int ret = -1; + int err; + + dest = (u8 *) nvgpu_kzalloc(g, byte_cnt); + if (dest == NULL) { + unit_err(m, "Memory allocation failed\n"); + goto out; + } + + + if (type == MEM_IMEM) { + err = nvgpu_falcon_copy_to_imem(flcn, dst, + (u8 *) rand_test_data, + byte_cnt, 0, false, 0); + if (err != exp_err) { + unit_err(m, "Copy to IMEM should %s\n", + exp_err ? "fail" : "pass"); + goto free_dest; + } + + err = nvgpu_falcon_copy_from_imem(flcn, dst, + dest, byte_cnt, 0); + if (err != exp_err) { + unit_err(m, "Copy from IMEM should %s\n", + exp_err ? "fail" : "pass"); + goto free_dest; + } + } else if (type == MEM_DMEM) { + err = nvgpu_falcon_copy_to_dmem(flcn, dst, + (u8 *) rand_test_data, + byte_cnt, 0); + if (err != exp_err) { + unit_err(m, "Copy to DMEM should %s\n", + exp_err ? "fail" : "pass"); + goto free_dest; + } + + err = nvgpu_falcon_copy_from_dmem(flcn, dst, + dest, byte_cnt, 0); + if (err != exp_err) { + unit_err(m, "Copy from DMEM should %s\n", + exp_err ? "fail" : "pass"); + goto free_dest; + } + } + + ret = 0; + +free_dest: + nvgpu_kfree(g, dest); +out: + return ret; +} + +/* + * Valid/Invalid: Status of read and write from Falcon + * Valid: Read and write from initialized Falcon succeeds. + * Invalid: Read and write for uninitialized Falcon fails + * with error -EINVAL. + */ +static int test_falcon_mem_rw_init(struct unit_module *m, struct gk20a *g, + void *__args) +{ + u32 dst = 0; + int err = 0, i; + + /* initialize falcons */ + if (init_falcon_test_env(m, g) != 0) { + unit_return_fail(m, "Module init failed\n"); + } + + /* write/read to/from uninitialized falcon */ + for (i = 0; i < MAX_MEM_TYPE; i++) { + err = falcon_check_read_write(g, m, uninit_flcn, i, dst, + RAND_DATA_SIZE, -EINVAL); + if (err) { + return UNIT_FAIL; + } + } + + /* write/read to/from initialized falcon */ + for (i = 0; i < MAX_MEM_TYPE; i++) { + err = falcon_check_read_write(g, m, pmu_flcn, i, dst, + RAND_DATA_SIZE, 0); + if (err) { + return UNIT_FAIL; + } + } + + return UNIT_SUCCESS; +} + +/* + * Valid/Invalid: Reading and writing data in accessible range should work + * and fail otherwise. + * Valid: Data read from or written to Falcon memory in bounds is valid + * operation and should return success. + * Invalid: Reading and writing data out of Falcon memory bounds should + * return error -EINVAL. + */ +static int test_falcon_mem_rw_range(struct unit_module *m, struct gk20a *g, + void *__args) +{ + u32 byte_cnt = RAND_DATA_SIZE; + u32 dst = 0; + int err = UNIT_FAIL; + + if (pmu_flcn == NULL || !pmu_flcn->is_falcon_supported) { + unit_return_fail(m, "test environment not initialized."); + } + + /* write data to valid range in imem */ + unit_info(m, "Writing %d bytes to imem\n", byte_cnt); + err = nvgpu_falcon_copy_to_imem(pmu_flcn, dst, (u8 *) rand_test_data, + byte_cnt, 0, false, 0); + if (err) { + unit_return_fail(m, "Failed to copy to IMEM\n"); + } + + /* verify data written to imem matches */ + unit_info(m, "Reading %d bytes from imem\n", byte_cnt); + err = falcon_read_compare(m, g, MEM_IMEM, dst, byte_cnt); + if (err) { + unit_err(m, "IMEM read data does not match %d\n", err); + return UNIT_FAIL; + } + + /* write data to valid range in dmem */ + unit_info(m, "Writing %d bytes to dmem\n", byte_cnt); + err = nvgpu_falcon_copy_to_dmem(pmu_flcn, dst, (u8 *) rand_test_data, + byte_cnt, 0); + if (err) { + unit_return_fail(m, "Failed to copy to DMEM\n"); + } + + /* verify data written to dmem matches */ + unit_info(m, "Reading %d bytes from dmem\n", byte_cnt); + err = falcon_read_compare(m, g, MEM_DMEM, dst, byte_cnt); + if (err) { + unit_err(m, "DMEM read data does not match %d\n", err); + return UNIT_FAIL; + } + + dst = UTF_FALCON_IMEM_DMEM_SIZE - RAND_DATA_SIZE; + byte_cnt *= 2; + + /* write/read data to/from invalid range in imem */ + err = falcon_check_read_write(g, m, pmu_flcn, MEM_IMEM, dst, + byte_cnt, -EINVAL); + if (err) { + return UNIT_FAIL; + } + + /* write/read data to/from invalid range in dmem */ + err = falcon_check_read_write(g, m, pmu_flcn, MEM_DMEM, dst, + byte_cnt, -EINVAL); + if (err) { + return UNIT_FAIL; + } + + return UNIT_SUCCESS; +} + +/* + * Valid/Invalid: Reading and writing data at offset that is word (4-byte) + * aligned data should work and fail otherwise. + * Valid: Data read/written from/to Falcon memory from word (4-byte) aligned + * offset is valid operation and should return success. + * Invalid: Reading and writing data out of non-word-aligned offset in Falcon + * memory should return error -EINVAL. + */ +static int test_falcon_mem_rw_aligned(struct unit_module *m, struct gk20a *g, + void *__args) +{ + u32 byte_cnt = RAND_DATA_SIZE; + u32 dst = 0, i; + int err = 0; + + if (pmu_flcn == NULL || !pmu_flcn->is_falcon_supported) { + unit_return_fail(m, "test environment not initialized."); + } + + for (i = 0; i < MAX_MEM_TYPE; i++) { + /* + * Copy to/from offset dst = 3 that is not word aligned should + * fail. + */ + dst = 0x3; + err = falcon_check_read_write(g, m, pmu_flcn, i, dst, + byte_cnt, -EINVAL); + if (err) { + return UNIT_FAIL; + } + + /* + * Copy to/from offset dst = 4 that is word aligned should + * succeed. + */ + dst = 0x4; + err = falcon_check_read_write(g, m, pmu_flcn, i, dst, + byte_cnt, 0); + if (err) { + return UNIT_FAIL; + } + } + + return UNIT_SUCCESS; +} + +/* + * Reading/writing zero bytes should return error -EINVAL. + */ +static int test_falcon_mem_rw_zero(struct unit_module *m, struct gk20a *g, + void *__args) +{ + u32 byte_cnt = 0; + u32 dst = 0, i; + int err = 0; + + if (pmu_flcn == NULL || !pmu_flcn->is_falcon_supported) { + unit_return_fail(m, "test environment not initialized."); + } + + for (i = 0; i < MAX_MEM_TYPE; i++) { + /* write/read zero bytes should fail*/ + err = falcon_check_read_write(g, m, pmu_flcn, MEM_IMEM, dst, + byte_cnt, -EINVAL); + if (err) { + return UNIT_FAIL; + } + } + + return UNIT_SUCCESS; +} + +struct unit_module_test falcon_tests[] = { + UNIT_TEST(falcon_mem_rw_init, test_falcon_mem_rw_init, NULL, 0), + UNIT_TEST(falcon_mem_rw_range, test_falcon_mem_rw_range, NULL, 0), + UNIT_TEST(falcon_mem_rw_aligned, test_falcon_mem_rw_aligned, NULL, 0), + UNIT_TEST(falcon_mem_rw_zero, test_falcon_mem_rw_zero, NULL, 0), + + /* Cleanup */ + UNIT_TEST(falcon_free_test_env, free_falcon_test_env, NULL, 0), +}; + +UNIT_MODULE(falcon, falcon_tests, UNIT_PRIO_NVGPU_TEST);