diff mbox series

[v2] target/hexagon: add OPEN/READ/CLOSE DIR semihosting operations

Message ID 810448a08da00bd2e1debaad949cdea5b1cd0059.1788368635.git.matheus.bernardino@oss.qualcomm.com
State New
Headers show
Series [v2] target/hexagon: add OPEN/READ/CLOSE DIR semihosting operations | expand

Commit Message

Matheus Tavares Bernardino Sept. 2, 2026, 5:04 p.m. UTC
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 <matheus.bernardino@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
---
v1: https://lore.kernel.org/qemu-devel/9823f67f-bfc6-40e2-ba3e-cb02e53a4004@oss.qualcomm.com/

Diff from v1: defined the dir_list inside the new HexagonClusterState,
as suggested by Phil.

 hw/hexagon/hex-subsys.c                   | 32 +++++---
 include/hw/hexagon/hexagon.h              | 14 +++-
 target/hexagon/hexswi.c                   | 94 +++++++++++++++++++++++
 tests/functional/hexagon/test_systests.py |  9 +++
 4 files changed, 137 insertions(+), 12 deletions(-)

Comments

Philippe Mathieu-Daudé Sept. 2, 2026, 9:05 p.m. UTC | #1
On 2/9/26 19:04, Matheus Tavares Bernardino wrote:
> 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 <matheus.bernardino@oss.qualcomm.com>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
> ---
> v1: https://lore.kernel.org/qemu-devel/9823f67f-bfc6-40e2-ba3e-cb02e53a4004@oss.qualcomm.com/
> 
> Diff from v1: defined the dir_list inside the new HexagonClusterState,
> as suggested by Phil.

Thank you Matheus! QOM/QDev:

Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>

> 
>   hw/hexagon/hex-subsys.c                   | 32 +++++---
>   include/hw/hexagon/hexagon.h              | 14 +++-
>   target/hexagon/hexswi.c                   | 94 +++++++++++++++++++++++
>   tests/functional/hexagon/test_systests.py |  9 +++
>   4 files changed, 137 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c
index 4e3a418340..f76f803dfb 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/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h
index 3d7b3cb12d..bdb95b1819 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;
@@ -148,6 +149,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)
 
@@ -157,7 +169,7 @@  struct HexagonCommonMachineState {
     MemoryRegion ram;
     MemoryRegion cfgtable_rom;
     MemoryRegion vtcm;
-    DeviceState *cluster;
+    HexagonClusterState cluster;
     DeviceState *l2vic;
     DeviceState *qtimer;
     DeviceState *glob_regs;
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index a50ab22554..f24ebe24c4 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"
@@ -30,6 +31,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) \
@@ -375,6 +379,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;
@@ -644,6 +655,89 @@  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 {
+            GList **dir_list = hex_semihosting_dir_list(env);
+            dir = opendir(buf);
+            if (dir != NULL) {
+                *dir_list = g_list_append(*dir_list, dir);
+                rc = g_list_index(*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;
+        GList **dir_list = hex_semihosting_dir_list(env);
+        DIR *dir = g_list_nth_data(*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;
+        GList **dir_list = hex_semihosting_dir_list(env);
+
+        dir = g_list_nth_data(*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()