From patchwork Mon Oct 30 13:00:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaydeep Patil X-Patchwork-Id: 78751 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 130E0385609B for ; Mon, 30 Oct 2023 13:01:55 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mx07-00376f01.pphosted.com (mx07-00376f01.pphosted.com [185.132.180.163]) by sourceware.org (Postfix) with ESMTPS id DF9E23856DDE for ; Mon, 30 Oct 2023 13:01:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DF9E23856DDE Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=imgtec.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=imgtec.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DF9E23856DDE Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=185.132.180.163 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698670883; cv=none; b=NwZQ+d8PyBL6FfrKjZfCNdrGimqHTWMsmCYTBTtwM+Bac3MX9zVbS7BcY5Akoh3yvLUTei0y3xTpPfthJCzaJ6ikL3ps6DHw44QqV01WV0KSLF7UrsgaitCrgHMcGaXwghg8N/pJw/TWZZfprlG1+KAC/NF9ToBk6yxEVC0C2ts= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698670883; c=relaxed/simple; bh=BSsYOh2tSh2T2rT/jLXewCeAbqRw8yA1btrBL2FFS/o=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=MMmviM37tkrfVsq4Ap+E4gyKDQ27hIGT4YZqDWhHQa8OrEJifgGVqKbYQsull3X1l4PqnEpUVxC4nHvJHSjLp4X2Bffv2Fc4yCTxM4rLkPecExYa7O7JalDrRYme3rWxb71Iu7PSrXSzng21GNnUVwhMpzzPK5Q0MUEZ3g7q9To= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from pps.filterd (m0168889.ppops.net [127.0.0.1]) by mx07-00376f01.pphosted.com (8.17.1.22/8.17.1.22) with ESMTP id 39U6hUSi026687; Mon, 30 Oct 2023 13:00:56 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=imgtec.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:content-type; s= dk201812; bh=6OLF8NWBGOBvv1mq4O2ISVM5JpXDmib76F2TdSiPtrg=; b=O36 9LvjnkX48Kxta97WRk0vBBEUvlpehi1wNkvyPO0zt0//qPAus/qlpOk0SL4+GPnT y/7DHVCTZK558aOKKY5/ksO12oPGsqsg9yFD0zMCyo1LZC111e743o2kz+6Asm3v ZFXkLGJAOtcvDgVHQXiIdDtZryuJN1h7yjgFuAB0Net+DIYjFmnMiwGyBIY1nyAa GpzdfMg6nNO+ul9PMfqALZmROb+X7jqPycy0o95YVk5dFetH1j4I4jj1mHTcSBhF v9OltCr4YPKL02FmxUxSUb6xUbpwSkhJSuCuiGZY8c9im3tLVq2yGUlGOXiT3S7F hxLKpzEfkW4+8iyhofg== Received: from hhmail04.hh.imgtec.org ([217.156.249.195]) by mx07-00376f01.pphosted.com (PPS) with ESMTPS id 3u0tcrhh37-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT); Mon, 30 Oct 2023 13:00:55 +0000 (GMT) Received: from hhjpatil.hh.imgtec.org (10.100.136.70) by HHMAIL04.hh.imgtec.org (10.100.10.119) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.34; Mon, 30 Oct 2023 13:00:54 +0000 From: To: CC: , , , , Subject: [PATCH v2 1/3] [sim/riscv] Add basic semi-hosting support Date: Mon, 30 Oct 2023 13:00:40 +0000 Message-ID: <20231030130042.1472535-2-jaydeep.patil@imgtec.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231030130042.1472535-1-jaydeep.patil@imgtec.com> References: <20231030130042.1472535-1-jaydeep.patil@imgtec.com> MIME-Version: 1.0 X-Originating-IP: [10.100.136.70] X-ClientProxiedBy: HHMAIL04.hh.imgtec.org (10.100.10.119) To HHMAIL04.hh.imgtec.org (10.100.10.119) X-EXCLAIMER-MD-CONFIG: 15a78312-3e47-46eb-9010-2e54d84a9631 X-Proofpoint-GUID: n7KnMgnJzUlZ4EZ2bxZp9Dxv896Y7XnI X-Proofpoint-ORIG-GUID: n7KnMgnJzUlZ4EZ2bxZp9Dxv896Y7XnI X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org From: Jaydeep Patil 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 | 26 +++ gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp | 27 +++ sim/riscv/riscv-sim.h | 32 +++ sim/riscv/sim-main.c | 230 ++++++++++++++++++- 4 files changed, 310 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..02a97da8643 --- /dev/null +++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c @@ -0,0 +1,26 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2023 Free Software Foundation, Inc. + + 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 . */ + +/* Test basic semi-hosting calls SYS_GET_CMDLINE and SYS_EXIT. */ + +int +main (int argc, char **argv) +{ + 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..7f5670200c2 --- /dev/null +++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp @@ -0,0 +1,27 @@ +# Copyright 2023 Free Software Foundation, Inc. +# +# 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 . + +# Test basic semi-hosting calls SYS_GET_CMDLINE and SYS_EXIT. + +require {istarget "riscv*-*-*"} + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug quiet}] } { + 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..bb296fa6e62 100644 --- a/sim/riscv/riscv-sim.h +++ b/sim/riscv/riscv-sim.h @@ -75,4 +75,36 @@ extern void initialize_env (SIM_DESC, const char * const *argv, #define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu)) +#define APPLICATION_EXIT 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 + +#define SEMI_HOSTING_SLLI 0x01f01013 +#define SEMI_HOSTING_SRAI 0x40705013 + +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 afdfcf50656..8e08eb9fc4e 100644 --- a/sim/riscv/sim-main.c +++ b/sim/riscv/sim-main.c @@ -136,6 +136,176 @@ store_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg, TRACE_REGISTER (cpu, "wrote CSR %s = %#" PRIxTW, name, val); } +/* Read 4 or 8 bytes of data from the core memory. ADDR and (INDEX * XLEN) + form the base address. */ +static uintptr_t +get_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index) +{ + 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; +} + +/* Write string in HOST_BUF at CORE_ADDR. The length of string is LEN. */ +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]); + } +} + +/* Read string of length LEN at address ADDR. */ +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; +} + +/* SYS_OPEN + Register a1 points to a buffer containing: + Index 0: Pointer: Address of the file name string. + Index 1: Integer: File open flags. + Index 2: Integer: Length of the file name string. + File handle is returned through register a0. */ +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); +} + +/* SYS_EXIT. In case of RV32, register a1 holds the application stop reason + and the exit code is decided based on it. Otherwise, register a1 points to + a buffer containing: + Index 0: Integer: Application stop reason (ignored) + Index 1: Integer: Exit code. */ +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; + if (app_code == APPLICATION_EXIT) + exit_code = 0; + else + exit_code = 1; + } + else + exit_code = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1); + riscv_cpu->a0 = exit_code; + sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, exit_code); +} + +/* SYS_GET_CMDLINE. Write command line arguments to a buffer. Arguments + passed to "run" command are stored in STATE_PROG_ARGV member of SIM_DESC. + Register a1 points to a buffer containing: + Index 0: Pointer: Buffer to store command line arguments + Index 1: Integer: Maximum length of the buffer. */ +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. */ + 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. */ +} + +/* Handle semi-hosting calls. Register a0 contains semi-hosting call number + and a1 contains pointer to a buffer containing additional arguments. */ +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 +793,60 @@ 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; + { + /* If ebreak is at PC 0 then do not check for semi-hosting. */ + if (riscv_cpu->pc != 0) + { + /* 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. */ + 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 == SEMI_HOSTING_SLLI) + { + 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); + + if (post_iw == SEMI_HOSTING_SRAI) + { + 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 +1209,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: