From: Andrew Burgess <aburgess@redhat.com>
To: Jaydeep Patil <Jaydeep.Patil@imgtec.com>,
"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Cc: "vapier@gentoo.org" <vapier@gentoo.org>,
Joseph Faulls <Joseph.Faulls@imgtec.com>,
Bhushan Attarde <Bhushan.Attarde@imgtec.com>
Subject: Re: [PATCH 1/4] [sim/riscv] Add basic semi-hosting support
Date: Wed, 18 Oct 2023 13:37:55 +0100 [thread overview]
Message-ID: <87lec03y3g.fsf@redhat.com> (raw)
In-Reply-To: <CWXP265MB5321DC38C4B49638D24C92FE8CD6A@CWXP265MB5321.GBRP265.PROD.OUTLOOK.COM>
Jaydeep Patil <Jaydeep.Patil@imgtec.com> writes:
> Added support for basic semi-hosting calls OPEN, EXIT and GET_CMDLINE.
> Added gdb.arch/riscv-exit-getcmd.c to test it.
> ---
> gdb/testsuite/gdb.arch/riscv-exit-getcmd.c | 6 +
> gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp | 31 +++
> sim/riscv/riscv-sim.h | 29 +++
> sim/riscv/sim-main.c | 205 ++++++++++++++++++-
> 4 files changed, 266 insertions(+), 5 deletions(-)
> create mode 100644 gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
> create mode 100644 gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
>
> diff --git a/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
> new file mode 100644
> index 00000000000..2d396e530f6
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
> @@ -0,0 +1,6 @@
New test source files should include a copyright header -- all but the
oldest files will have one you can copy.
> +
> +int main (int argc, char **argv) {
You should follow the GDB coding standard wherever possible when writing
tests, so function name should start a new line, and '{' should be on
its own line.
> + if (argc != 4)
> + return 1;
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
> new file mode 100644
> index 00000000000..672b3c4aa10
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
> @@ -0,0 +1,31 @@
> +# Copyright 2020-2022 Free Software Foundation, Inc.
Unless this patch was posted to the list back in 2020, the date here
should be just '2023'.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see http://www.gnu.org/licenses/.
> +
> +# Test basic semi-hosting calls SYS_GET_CMDLINE and SYS_EXIT
Comments should be complete sentences, so end with a period.
> +
> +if {![istarget "riscv*-*-*"]} {
> + verbose "Skipping ${gdb_test_file_name}."
> + return
> +}
This should be replaced with:
require {istarget "riscv*-*-*"}
> +
> +standard_testfile
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + {debug quiet}] } {
> + unsupported "failed to compile"
I believe this unsupported is not required. prepare_for_testing should
already report if this test fails to prepare.
> + return -1
> +}
> +
> +gdb_test "run 1 2 3" ".*Inferior.*process.*exited normally.*"
> diff --git a/sim/riscv/riscv-sim.h b/sim/riscv/riscv-sim.h
> index 1bc9aa12156..c9e78a756c4 100644
> --- a/sim/riscv/riscv-sim.h
> +++ b/sim/riscv/riscv-sim.h
> @@ -75,4 +75,33 @@ extern void initialize_env (SIM_DESC, const char * const *argv,
>
> #define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu))
>
> +#define ApplicationExit 0x20026
> +
> +#define SYS_OPEN 0x01
> +#define SYS_GET_CMDLINE 0x15
> +#define SYS_EXIT 0x18
> +
> +#define GDB_O_RDONLY 0x000
> +#define GDB_O_WRONLY 0x001
> +#define GDB_O_RDWR 0x002
> +#define GDB_O_APPEND 0x008
> +#define GDB_O_CREAT 0x200
> +#define GDB_O_TRUNC 0x400
> +#define GDB_O_BINARY 0
> +
> +static int gdb_open_modeflags[12] = {
> + GDB_O_RDONLY,
> + GDB_O_RDONLY | GDB_O_BINARY,
> + GDB_O_RDWR,
> + GDB_O_RDWR | GDB_O_BINARY,
> + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
> + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
> + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
> + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
> + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
> + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
> + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
> + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
> +};
> +
> #endif
> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
> index 250791634a1..2b184aea554 100644
> --- a/sim/riscv/sim-main.c
> +++ b/sim/riscv/sim-main.c
> @@ -136,6 +136,155 @@ store_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg,
> TRACE_REGISTER (cpu, "wrote CSR %s = %#" PRIxTW, name, val);
> }
>
> +static uintptr_t
> +get_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index)
All these new static functions should ideally have a header comment
explaining what they do, and what the parameters are for.
> +{
> + uintptr_t param;
> + int xlen = RISCV_XLEN (cpu);
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> +
> + if (xlen == 64)
> + param = sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
> + addr + (index * 8));
> + else
> + param = sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
> + addr + (index * 4));
> +
> + return param;
> +}
> +
> +static void
> +set_core_string (SIM_CPU *cpu, unsigned_word core_addr, char *host_buf,
> + int len)
> +{
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> + for (int i = 0; i < len; i++)
> + {
> + sim_core_write_unaligned_1 (cpu, riscv_cpu->pc, write_map,
> + core_addr + i, host_buf[i]);
> + }
> +}
> +
> +static char *
> +get_core_string_with_len (SIM_CPU *cpu, unsigned_word addr,
> + unsigned_word len)
> +{
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> + char * str;
> + str = (char *) malloc (len + 1);
> +
> + for (int i = 0; i < len; i++)
> + {
> + str[i] = sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map,
> + addr + i);
> + }
> + str[len] = 0;
> +
> + return str;
> +}
> +
> +static void
> +semihosting_open (SIM_CPU *cpu)
> +{
> + uintptr_t fname_addr;
> + uintptr_t flags;
> + uintptr_t fname_len;
> + char *name;
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> +
> + fname_addr = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
> + flags = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
> + fname_len = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 2);
> +
> + if (fname_len <= 0)
> + {
> + riscv_cpu->a0 = -1;
> + return;
> + }
> +
> + name = get_core_string_with_len (cpu, fname_addr, fname_len);
> + riscv_cpu->a0 = sim_io_open (CPU_STATE (cpu), name,
> + gdb_open_modeflags[flags]);
> + free (name);
> +}
> +
> +static void
> +semihosting_exit (SIM_CPU *cpu)
> +{
> + uintptr_t app_code, exit_code;
> + SIM_DESC sd = CPU_STATE (cpu);
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> + if (RISCV_XLEN (cpu) == 32)
> + app_code = riscv_cpu->a1;
> + else
> + {
> + app_code = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
> + exit_code = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
> + }
> + if (app_code == ApplicationExit)
GNU style doesn't use camel case. Read-only constants can be
APPLICATION_EXIT, or just 'application_exit'.
> + exit_code = 0;
> + else
> + exit_code = 1;
> + riscv_cpu->a0 = exit_code;
> + sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, exit_code);
> +}
> +
> +static void
> +semihosting_get_cmdline (SIM_CPU *cpu)
> +{
> + int i = 0, len = 0, total_len = 0;
> + uintptr_t buf_addr, max_buf_len;
> + SIM_DESC sd = CPU_STATE (cpu);
> + char *space = " ";
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> +
> + char **prog_argv = STATE_PROG_ARGV (sd);
> + if (prog_argv == NULL)
> + {
> + riscv_cpu->a0 = 1; // return non-zero to indicate error
GNU style is for /* ... */ comments, not // style.
> + return;
> + }
> +
> + buf_addr = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
> + max_buf_len = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
> +
> + while (prog_argv[i])
> + {
> + len = strlen (prog_argv[i]);
> + if ((total_len + len) > max_buf_len)
> + break;
> + set_core_string (cpu, buf_addr, prog_argv[i], len);
> + set_core_string (cpu, buf_addr + len, space, 1);
> + len++; // terminate it with space
> + buf_addr += len;
> + total_len += len;
> + i++;
> + }
> + riscv_cpu->a0 = 0; // no error
> +}
> +
> +static int
> +do_semihosting (SIM_CPU *cpu)
> +{
> + struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
> + switch (riscv_cpu->a0)
> + {
> + case SYS_OPEN:
> + semihosting_open (cpu);
> + break;
> + case SYS_GET_CMDLINE:
> + semihosting_get_cmdline (cpu);
> + break;
> + case SYS_EXIT:
> + semihosting_exit (cpu);
> + break;
> + default:
> + return -1; // semi-hosting call not supported
> + }
> +
> + return 0;
> +}
> +
> static inline unsigned_word
> ashiftrt (unsigned_word val, unsigned_word shift)
> {
> @@ -623,11 +772,56 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
> TRACE_INSN (cpu, "fence.i;");
> break;
> case MATCH_EBREAK:
> - TRACE_INSN (cpu, "ebreak;");
> - /* GDB expects us to step over EBREAK. */
> - sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc + 4, sim_stopped,
> - SIM_SIGTRAP);
> - break;
> + {
> + /* RISC-V semi-hosting call is flagged using these three
> + * instructions
> + * slli zero, zero, 0x1f 0x01f01013
> + * ebreak 0x00100073
> + * srai zero, zero, 0x7 0x40705013
> + * Register a0 holds the system call number and a1 holds the
> + * pointer to parameter buffer. Do not read 4 bytes in one go
> + * as we might read malformed 4 byte instruction. */
Comments should follow GNU style; so drop all the extra '*'.
> + int iw_len;
> + sim_cia pre_pc = riscv_cpu->pc - 4;
> + unsigned_word pre_iw;
> + pre_iw = sim_core_read_aligned_2 (cpu, pre_pc, exec_map, pre_pc);
> + iw_len = riscv_insn_length (pre_iw);
> + if (iw_len == 4)
> + pre_iw |= ((unsigned_word) sim_core_read_aligned_2 (
> + cpu, pre_pc, exec_map, pre_pc + 2) << 16);
> +
> + if (pre_iw == 0x01f01013)
At a minimum, these magic numbers needs a good comment. But I wonder if
we can do better, maybe we can make use of MASK_* and MATCH_* macros
from include/opcode/riscv-opc.h?
> + {
> + sim_cia post_pc = riscv_cpu->pc + 4;
> + unsigned_word post_iw;
> + post_iw = sim_core_read_aligned_2 (cpu, post_pc, exec_map,
> + post_pc);
> + iw_len = riscv_insn_length (post_iw);
> + if (iw_len == 4)
> + post_iw |= ((unsigned_word) sim_core_read_aligned_2 (
> + cpu, post_pc, exec_map, post_pc + 2) << 16);
GNU style places the opening '(' onto the new line, rather than trailing
at the end of the previous line.
> +
> + if (post_iw == 0x40705013)
> + {
The indentation looks wrong here, but I'm not sure if that's from your
original patch, or just a result of how the email was sent.
Thanks,
Andrew
> + TRACE_INSN (cpu, "semi-hosting a0=%lx,a1=%lx;",
> + riscv_cpu->a0, riscv_cpu->a1);
> + if (do_semihosting (cpu))
> + {
> + /* Invalid semi-hosting call. */
> + TRACE_INSN (cpu, "ebreak;");
> + sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc,
> + sim_stopped, SIM_SIGTRAP);
> + }
> + else
> + pc = pc + 4; /* post srai. */
> + break;
> + }
> + }
> + TRACE_INSN (cpu, "ebreak;");
> + sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped,
> + SIM_SIGTRAP);
> + break;
> + }
> case MATCH_ECALL:
> TRACE_INSN (cpu, "ecall;");
> riscv_cpu->a0 = sim_syscall (cpu, riscv_cpu->a7, riscv_cpu->a0,
> @@ -990,6 +1184,7 @@ execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
> case INSN_CLASS_A:
> return execute_a (cpu, iw, op);
> case INSN_CLASS_I:
> + case INSN_CLASS_ZICSR:
> return execute_i (cpu, iw, op);
> case INSN_CLASS_M:
> case INSN_CLASS_ZMMUL:
> --
> 2.25.1
prev parent reply other threads:[~2023-10-18 12:38 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-17 5:53 Jaydeep Patil
2023-10-18 12:37 ` Andrew Burgess [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87lec03y3g.fsf@redhat.com \
--to=aburgess@redhat.com \
--cc=Bhushan.Attarde@imgtec.com \
--cc=Jaydeep.Patil@imgtec.com \
--cc=Joseph.Faulls@imgtec.com \
--cc=gdb-patches@sourceware.org \
--cc=vapier@gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).