Baremetal Hexagon programs use semihosting to enumerate host directories via OPENDIR, READDIR, and CLOSEDIR calls. The list of open directory handles are global to all CPUs, so that guest index values map back to host DIR pointers across calls.
Also add functional tests for the new semihosting ops. Signed-off-by: Matheus Tavares Bernardino <[email protected]> Signed-off-by: Brian Cain <[email protected]> --- Note: based on https://lore.kernel.org/qemu-devel/[email protected]/ The added test was verified on Ubuntu x86-64, Debian PowerPC (big-endian) and Windows. hw/hexagon/hexagon_dsp.c | 7 ++ hw/hexagon/virt.c | 7 ++ target/hexagon/cpu.h | 1 + target/hexagon/hexswi.c | 83 +++++++++++++++++++++++ tests/functional/hexagon/test_systests.py | 9 +++ 5 files changed, 107 insertions(+) diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index 2198436a44..4c6aad886e 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -141,6 +141,13 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, for (int i = 0; i < machine->smp.cpus; i++) { hex_subsys_realize_cpu(hms, DEVICE(cpus[i]), (i == 0)); + CPUHexagonState *env = &cpus[i]->env; + if (i == 0) { + env->g_dir_list = g_malloc0(sizeof(GList *)); + } else { + CPUHexagonState *env0 = cpu_env(qemu_get_cpu(0)); + env->g_dir_list = env0->g_dir_list; + } } } diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index 64d8366d27..3b6a072c00 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -344,6 +344,13 @@ static void virt_init(MachineState *ms) for (int i = 0; i < ms->smp.cpus; i++) { hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpus[i]), (i == 0)); + CPUHexagonState *env = &cpus[i]->env; + if (i == 0) { + env->g_dir_list = g_malloc0(sizeof(GList *)); + } else { + CPUHexagonState *env0 = cpu_env(qemu_get_cpu(0)); + env->g_dir_list = env0->g_dir_list; + } } fdt_add_cpu_nodes(vms); diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index c50fbb3f72..fd2cb96cd2 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -145,6 +145,7 @@ typedef struct CPUArchState { uint32_t tlb_lock_count; uint32_t k0_lock_count; uint64_t t_cycle_count; + GList **g_dir_list; #endif uint32_t next_PC; target_ulong new_value_usr; diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c index a50ab22554..cb2a289dd5 100644 --- a/target/hexagon/hexswi.c +++ b/target/hexagon/hexswi.c @@ -30,6 +30,9 @@ #include "semihosting/guestfd.h" #include "system/runstate.h" +/* We start from 1 as 0 is used to signal an error from opendir() */ +static const int DIR_INDEX_OFFSET = 1; + /* non-arm-compatible semihosting calls */ #define HEXAGON_SPECIFIC_SWI_FLAGS \ DEF_SWI_FLAG(OPEN, 0x01) \ @@ -644,6 +647,86 @@ static void sim_handle_trap0(CPUHexagonState *env) } break; + case HEX_SYS_OPENDIR: + { + DIR *dir; + char buf[BUFSIZ]; + int rc = 0, err = 0; + int i = 0; + + do { + hexagon_read_memory(env, swi_info + i, 1, &buf[i], retaddr); + i++; + } while ((i < BUFSIZ) && buf[i - 1]); + + if (buf[i - 1]) { + err = ENAMETOOLONG; + } else { + dir = opendir(buf); + if (dir != NULL) { + *env->g_dir_list = g_list_append(*env->g_dir_list, dir); + rc = g_list_index(*env->g_dir_list, dir) + DIR_INDEX_OFFSET; + } else { + err = errno; + } + } + common_semi_cb(cs, rc, rc != 0 ? 0 : err); + break; + } + + case HEX_SYS_READDIR: + { + struct dirent *host_dir_entry = NULL; + int dir_index = swi_info - DIR_INDEX_OFFSET; + DIR *dir = g_list_nth_data(*env->g_dir_list, dir_index); + uint32_t rc = 0, err = 0; + + if (dir) { + errno = 0; + host_dir_entry = readdir(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]; + hexagon_write_memory(env, guest_dir_entry, 4, host_dir_entry->d_ino, + retaddr); + for (int i = 0; i < sizeof(host_dir_entry->d_name); i++) { + hexagon_write_memory(env, guest_dir_entry + 4 + i, 1, + host_dir_entry->d_name[i], retaddr); + if (!host_dir_entry->d_name[i]) { + break; + } + } + rc = guest_dir_entry; + } + common_semi_cb(cs, rc, err); + break; + } + + case HEX_SYS_CLOSEDIR: + { + DIR *dir; + int ret = -1, err = 0; + int dir_index = swi_info - DIR_INDEX_OFFSET; + + dir = g_list_nth_data(*env->g_dir_list, dir_index); + if (dir != NULL) { + ret = closedir(dir); + if (ret != 0) { + err = errno; + } + } 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 f36e015f50..c354fd20ca 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 @@ -90,5 +91,13 @@ def test_access(self): def test_semihost(self): self.run_console_pattern("semihost", "PASS", "-append", "arg1", "arg2") + def test_dirent(self): + testdir = Path(self.scratch_file("_testdir_dirent")) + testdir.mkdir() + files = [".", "..", "file1", "file2"] + for f in files: + testdir.joinpath(f).touch() + self.run_console_pattern("dirent", " ".join(files), "-append", str(testdir)) + if __name__ == "__main__": QemuSystemTest.main() -- 2.37.2
