From: Matheus Tavares Bernardino <[email protected]>
Bare-metal Hexagon programs use OPENDIR, READDIR, and CLOSEDIR semihosting calls to enumerate host directories. Directory handles are shared by CPUs in a cluster, allowing guest indices to resolve across calls. Use lock_user_string()/unlock_user() to read the OPENDIR path from guest memory instead of copying it into a fixed-size buffer. CLOSEDIR clears its slot in the directory list, so a stale index reports EBADF instead of dereferencing a freed pointer. Also add a functional test for the new semihosting operations. Signed-off-by: Matheus Tavares Bernardino <[email protected]> Signed-off-by: Brian Cain <[email protected]> --- include/hw/hexagon/hexagon.h | 14 ++- hw/hexagon/hex-subsys.c | 32 ++++--- target/hexagon/hexswi.c | 108 ++++++++++++++++++++++ tests/functional/hexagon/test_systests.py | 11 +++ 4 files changed, 153 insertions(+), 12 deletions(-) diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 62398eeb359..58bdcabe87e 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -11,6 +11,7 @@ #include "system/memory.h" #include "hw/core/boards.h" +#include "hw/cpu/cluster.h" struct hexagon_board_boot_info { uint64_t ram_size; @@ -159,6 +160,17 @@ struct hexagon_machine_config { union hexagon_config_table cfgtable; }; +#define TYPE_HEXAGON_CLUSTER_STATE "hexagon-cluster-state" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonClusterState, HEXAGON_CLUSTER_STATE) + +struct HexagonClusterState { + CPUClusterState parent_obj; + + struct { + GList *dir_list; + } semihosting; +}; + #define TYPE_HEXAGON_COMMON_MACHINE "hexagon-common-machine" OBJECT_DECLARE_SIMPLE_TYPE(HexagonCommonMachineState, HEXAGON_COMMON_MACHINE) @@ -168,7 +180,7 @@ struct HexagonCommonMachineState { MemoryRegion ram; MemoryRegion cfgtable_rom; MemoryRegion vtcm; - DeviceState *cluster; + HexagonClusterState cluster; DeviceState *l2vic; DeviceState *qtimer; DeviceState *glob_regs; diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index 4e3a418340e..f76f803dfb0 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -98,14 +98,11 @@ static DeviceState *tlb_create(HexagonCommonMachineState *hms, return tlb; } -static DeviceState *cluster_create(HexagonCommonMachineState *hms) +static void cluster_create(HexagonCommonMachineState *hms) { - DeviceState *cluster = qdev_new(TYPE_CPU_CLUSTER); - - object_property_add_child(OBJECT(hms), "cluster", OBJECT(cluster)); - qdev_prop_set_uint32(cluster, "cluster-id", 0); - - return cluster; + object_initialize_child(OBJECT(hms), "cluster", &hms->cluster, + TYPE_HEXAGON_CLUSTER_STATE); + qdev_prop_set_uint32(DEVICE(&hms->cluster), "cluster-id", 0); } void hex_subsys_create(HexagonCommonMachineState *hms, @@ -135,7 +132,7 @@ void hex_subsys_create(HexagonCommonMachineState *hms, &hms->vtcm); } - hms->cluster = cluster_create(hms); + cluster_create(hms); hms->l2vic = l2vic_create(hms, m_cfg); hms->qtimer = qtimer_create(hms, m_cfg); hms->glob_regs = globalreg_create(hms, m_cfg, rev); @@ -144,7 +141,7 @@ void hex_subsys_create(HexagonCommonMachineState *hms, void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) { - object_property_add_child(OBJECT(hms->cluster), "cpu[*]", OBJECT(cpu)); + object_property_add_child(OBJECT(&hms->cluster), "cpu[*]", OBJECT(cpu)); object_property_set_link(OBJECT(cpu), "global-regs", OBJECT(hms->glob_regs), &error_fatal); object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), @@ -158,10 +155,10 @@ void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) /* * The cluster must be realized after its CPUs have been parented into it * (see hex_subsys_add_cpu()) but before any CPU is itself realized, since - * qdev_realize_and_unref() on a CPU latches cluster_index into the TCG + * qdev_realize() on a CPU latches cluster_index into the TCG * cflags at that point. */ - qdev_realize_and_unref(hms->cluster, NULL, &error_fatal); + qdev_realize(DEVICE(&hms->cluster), NULL, &error_fatal); } void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, @@ -173,3 +170,16 @@ void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, l2vic_connect_cpu(hms->l2vic, cpu); } } + +static const TypeInfo hexagon_cluster_type_info = { + .name = TYPE_HEXAGON_CLUSTER_STATE, + .parent = TYPE_CPU_CLUSTER, + .instance_size = sizeof(HexagonClusterState), +}; + +static void hexagon_cluster_register_types(void) +{ + type_register_static(&hexagon_cluster_type_info); +} + +type_init(hexagon_cluster_register_types) diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c index e56a60f2f06..ddea881de99 100644 --- a/target/hexagon/hexswi.c +++ b/target/hexagon/hexswi.c @@ -19,6 +19,7 @@ #include "hex_mmu.h" #include "hexswi.h" #include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon.h" #ifdef CONFIG_USER_ONLY #error "This file is only used in system emulation" @@ -31,6 +32,18 @@ #include "semihosting/uaccess.h" #include "system/runstate.h" +/* We start from 1 as 0 is used to signal an error from g_dir_open(). */ +static const int DIR_INDEX_OFFSET = 1; + +/* + * GDir does not surface "." and ".." itself, so we track how many of + * those synthetic entries have been served for this handle so far. + */ +typedef struct { + GDir *dir; + unsigned int dot_entries; +} SemihostingDir; + /* non-arm-compatible semihosting calls */ #define HEXAGON_SPECIFIC_SWI_FLAGS \ DEF_SWI_FLAG(OPEN, 0x01) \ @@ -401,6 +414,13 @@ static void coredump(CPUHexagonState *env) qemu_log_unlock(f); } +static GList **hex_semihosting_dir_list(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + HexagonClusterState *cluster = HEXAGON_CLUSTER_STATE(OBJECT(cpu)->parent); + return &cluster->semihosting.dir_list; +} + static void sim_handle_trap0(CPUHexagonState *env) { target_ulong what_swi, swi_info; @@ -666,6 +686,94 @@ static void sim_handle_trap0(CPUHexagonState *env) } break; + case HEX_SYS_OPENDIR: + { + GDir *dir; + SemihostingDir *semidir; + char *buf; + int rc = 0, err = 0; + + buf = lock_user_string(swi_info); + if (!buf) { + common_semi_cb(cs, -1, EFAULT); + break; + } + + GList **dir_list = hex_semihosting_dir_list(env); + dir = g_dir_open(buf, 0, NULL); + if (dir != NULL) { + semidir = g_new(SemihostingDir, 1); + semidir->dir = dir; + semidir->dot_entries = 0; + *dir_list = g_list_append(*dir_list, semidir); + rc = g_list_index(*dir_list, semidir) + DIR_INDEX_OFFSET; + } else { + err = errno; + } + unlock_user(buf, swi_info, 0); + common_semi_cb(cs, rc, rc != 0 ? 0 : err); + break; + } + + case HEX_SYS_READDIR: + { + const char *host_dir_entry = NULL; + int dir_index = swi_info - DIR_INDEX_OFFSET; + GList **dir_list = hex_semihosting_dir_list(env); + SemihostingDir *dir = g_list_nth_data(*dir_list, dir_index); + uint32_t rc = 0, err = 0; + size_t i, name_len; + + if (dir) { + if (dir->dot_entries < 2) { + host_dir_entry = dir->dot_entries++ ? ".." : "."; + } else { + errno = 0; + host_dir_entry = g_dir_read_name(dir->dir); + if (host_dir_entry == NULL) { + err = errno; + } + } + } else { + err = EBADF; + } + + if (host_dir_entry) { + uint32_t guest_dir_entry = env->gpr[HEX_REG_R02]; + /* GDir does not provide a portable inode number. */ + hexagon_write_memory(env, guest_dir_entry, 4, 0, retaddr); + name_len = MIN(strlen(host_dir_entry), 254); + for (i = 0; i <= name_len; i++) { + hexagon_write_memory(env, guest_dir_entry + 4 + i, 1, + host_dir_entry[i], retaddr); + } + rc = guest_dir_entry; + } + common_semi_cb(cs, rc, err); + break; + } + + case HEX_SYS_CLOSEDIR: + { + SemihostingDir *dir; + int ret = -1, err = 0; + int dir_index = swi_info - DIR_INDEX_OFFSET; + GList **dir_list = hex_semihosting_dir_list(env); + GList *node = g_list_nth(*dir_list, dir_index); + + dir = node ? node->data : NULL; + if (dir != NULL) { + g_dir_close(dir->dir); + g_free(dir); + ret = 0; + node->data = NULL; + } else { + err = EBADF; + } + common_semi_cb(cs, ret, ret == 0 ? 0 : err); + break; + } + case HEX_SYS_COREDUMP: coredump(env); break; diff --git a/tests/functional/hexagon/test_systests.py b/tests/functional/hexagon/test_systests.py index 2779efa9172..a1080f7b974 100755 --- a/tests/functional/hexagon/test_systests.py +++ b/tests/functional/hexagon/test_systests.py @@ -7,6 +7,7 @@ import re import time import unittest +from pathlib import Path from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern @@ -99,5 +100,15 @@ def test_mmu_multi_tlb(self): def test_timer_reg(self): self.run_exit_zero("timer_reg") + def test_dirent(self): + testdir = Path(self.scratch_file("_testdir_dirent")) + testdir.mkdir() + files = ["file1", "file2"] + for f in files: + testdir.joinpath(f).touch() + expected = ". .. " + " ".join(files) + self.run_console_pattern("dirent", expected, "-append", + str(testdir)) + if __name__ == "__main__": QemuSystemTest.main() -- 2.34.1
