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


Reply via email to