MIPS patches queue
- Cavium Octeon MIPS extension and CPU model (Pavel Dovgalyuk) - Semihosting cleanup (Richard Henderson) -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmLN3yQACgkQ4+MsLN6t wN4RcBAAglgmqoFSHerA/oyK6AfsiMar8doL+hzr0YLdZcGpSYXOAhOHoTkNXg4u +DeXBOZUE5E36XonTEhBPY1bNHs07HfESqOYPqp/z1q1Z6sXV1FvJmhqbfmkYCJp rM18n7tK2vtB8XO/HwcUPQMN3HwKQsaikoA254bKVLPAMseBrTD+TBOTlA6dWIOg Vwk5EdhqSTmJn/MhetI7xBwTt5H1gs6ZDXAi+URs4bbw55+Z4tAmLaAX/pw+HSls aDT17RR1QcMlBx6kA48r2CsGMA0MZtPiVT92Sl7KynZQ3Q6pQcrqs5GV+IYXe9QV RUkBLYHww5znwY8KwLX8hEGyS4LkHihzHlHTtCQS6X2iRsnBEWwTi+ODBDwAsOTf xU39Wtno/Tq+UEnIerGGrzFidCLxbw68Dwq+60+JqAmk58soAP+vE4FTeN5ddjpB kQY6W85lhwshI00WQ/dk7Xw1w2BDBEXPJ6YHiQjcTLep2Gvp8FvudVW6metvrf4r uIOtouYwy3JGNWD0lmssUBSB7nb1VrLviufU26bDCnME1NRRID25ryiu//x1p9en u6Jp8zfMU4d4VpYv9jTbeMRYUM1gl/tPOMcR+sLFbEJ3R9xi1ngIO5Zu6OMgWn5T fuHUp6z4O++n4Xb5MhVi10+wye++eQJhM/n4OiQAFM8aJtz3KZw= =O4Yo -----END PGP SIGNATURE----- Merge tag 'mips-20220712' of https://github.com/philmd/qemu into staging MIPS patches queue - Cavium Octeon MIPS extension and CPU model (Pavel Dovgalyuk) - Semihosting cleanup (Richard Henderson) # gpg: Signature made Tue 12 Jul 2022 21:52:52 BST # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * tag 'mips-20220712' of https://github.com/philmd/qemu: target/mips: Remove GET_TARGET_STRING and FREE_TARGET_STRING target/mips: Simplify UHI_argnlen and UHI_argn semihosting: Remove qemu_semihosting_log_out target/mips: Use error_report for UHI_assert target/mips: Avoid qemu_semihosting_log_out for UHI_plog target/mips: Use semihosting/syscalls.h target/mips: Drop link syscall from semihosting target/mips: Create report_fault for semihosting target/mips: introduce Cavium Octeon CPU model target/mips: implement Octeon-specific arithmetic instructions target/mips: implement Octeon-specific BBIT instructions target/mips: introduce decodetree structure for Cavium Octeon extension Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
455c62d85f
@ -40,19 +40,6 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len);
|
||||
*/
|
||||
int qemu_semihosting_console_write(void *buf, int len);
|
||||
|
||||
/**
|
||||
* qemu_semihosting_log_out:
|
||||
* @s: pointer to string
|
||||
* @len: length of string
|
||||
*
|
||||
* Send a string to the debug output. Unlike console_out these strings
|
||||
* can't be sent to a remote gdb instance as they don't exist in guest
|
||||
* memory.
|
||||
*
|
||||
* Returns: number of bytes written
|
||||
*/
|
||||
int qemu_semihosting_log_out(const char *s, int len);
|
||||
|
||||
/*
|
||||
* qemu_semihosting_console_block_until_ready:
|
||||
* @cs: CPUState
|
||||
|
@ -38,15 +38,6 @@ typedef struct SemihostingConsole {
|
||||
|
||||
static SemihostingConsole console;
|
||||
|
||||
int qemu_semihosting_log_out(const char *s, int len)
|
||||
{
|
||||
if (console.chr) {
|
||||
return qemu_chr_write_all(console.chr, (uint8_t *) s, len);
|
||||
} else {
|
||||
return write(STDERR_FILENO, s, len);
|
||||
}
|
||||
}
|
||||
|
||||
#define FIFO_SIZE 1024
|
||||
|
||||
static int console_can_read(void *opaque)
|
||||
|
@ -921,6 +921,34 @@ const mips_def_t mips_defs[] =
|
||||
.insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSP_R2,
|
||||
.mmu_type = MMU_TYPE_R4000,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Octeon 68xx with MIPS64 Cavium Octeon features.
|
||||
*/
|
||||
.name = "Octeon68XX",
|
||||
.CP0_PRid = 0x000D9100,
|
||||
.CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) |
|
||||
(MMU_TYPE_R4000 << CP0C0_MT),
|
||||
.CP0_Config1 = MIPS_CONFIG1 | (0x3F << CP0C1_MMU) |
|
||||
(1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) |
|
||||
(1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) |
|
||||
(1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
|
||||
.CP0_Config2 = MIPS_CONFIG2,
|
||||
.CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA) | (1 << CP0C3_DSPP) ,
|
||||
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) |
|
||||
(0x3c << CP0C4_KScrExist) | (1U << CP0C4_MMUExtDef) |
|
||||
(3U << CP0C4_MMUSizeExt),
|
||||
.CP0_LLAddr_rw_bitmask = 0,
|
||||
.CP0_LLAddr_shift = 4,
|
||||
.CP0_PageGrain = (1 << CP0PG_ELPA),
|
||||
.SYNCI_Step = 32,
|
||||
.CCRes = 2,
|
||||
.CP0_Status_rw_bitmask = 0x12F8FFFF,
|
||||
.SEGBITS = 42,
|
||||
.PABITS = 49,
|
||||
.insn_flags = CPU_MIPS64R2 | INSN_OCTEON | ASE_DSP,
|
||||
.mmu_type = MMU_TYPE_R4000,
|
||||
},
|
||||
|
||||
#endif
|
||||
};
|
||||
|
@ -42,6 +42,7 @@
|
||||
#define INSN_LOONGSON2E 0x0000040000000000ULL
|
||||
#define INSN_LOONGSON2F 0x0000080000000000ULL
|
||||
#define INSN_LOONGSON3A 0x0000100000000000ULL
|
||||
#define INSN_OCTEON 0x0000200000000000ULL
|
||||
/*
|
||||
* bits 52-63: vendor-specific ASEs
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ gen = [
|
||||
decodetree.process('msa.decode', extra_args: '--decode=decode_ase_msa'),
|
||||
decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'),
|
||||
decodetree.process('vr54xx.decode', extra_args: '--decode=decode_ext_vr54xx'),
|
||||
decodetree.process('octeon.decode', extra_args: '--decode=decode_ext_octeon'),
|
||||
]
|
||||
|
||||
mips_ss.add(gen)
|
||||
@ -24,6 +25,7 @@ mips_ss.add(files(
|
||||
))
|
||||
mips_ss.add(when: 'TARGET_MIPS64', if_true: files(
|
||||
'tx79_translate.c',
|
||||
'octeon_translate.c',
|
||||
), if_false: files(
|
||||
'mxu_translate.c',
|
||||
))
|
||||
|
41
target/mips/tcg/octeon.decode
Normal file
41
target/mips/tcg/octeon.decode
Normal file
@ -0,0 +1,41 @@
|
||||
# Octeon Architecture Module instruction set
|
||||
#
|
||||
# Copyright (C) 2022 Pavel Dovgalyuk
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
|
||||
# Branch on bit set or clear
|
||||
# BBIT0 110010 ..... ..... ................
|
||||
# BBIT032 110110 ..... ..... ................
|
||||
# BBIT1 111010 ..... ..... ................
|
||||
# BBIT132 111110 ..... ..... ................
|
||||
|
||||
%bbit_p 28:1 16:5
|
||||
BBIT 11 set:1 . 10 rs:5 ..... offset:16 p=%bbit_p
|
||||
|
||||
# Arithmetic
|
||||
# BADDU rd, rs, rt
|
||||
# DMUL rd, rs, rt
|
||||
# EXTS rt, rs, p, lenm1
|
||||
# EXTS32 rt, rs, p, lenm1
|
||||
# CINS rt, rs, p, lenm1
|
||||
# CINS32 rt, rs, p, lenm1
|
||||
# DPOP rd, rs
|
||||
# POP rd, rs
|
||||
# SEQ rd, rs, rt
|
||||
# SEQI rt, rs, immediate
|
||||
# SNE rd, rs, rt
|
||||
# SNEI rt, rs, immediate
|
||||
|
||||
@r3 ...... rs:5 rt:5 rd:5 ..... ......
|
||||
%bitfield_p 0:1 6:5
|
||||
@bitfield ...... rs:5 rt:5 lenm1:5 ..... ..... . p=%bitfield_p
|
||||
|
||||
BADDU 011100 ..... ..... ..... 00000 101000 @r3
|
||||
DMUL 011100 ..... ..... ..... 00000 000011 @r3
|
||||
EXTS 011100 ..... ..... ..... ..... 11101 . @bitfield
|
||||
CINS 011100 ..... ..... ..... ..... 11001 . @bitfield
|
||||
POP 011100 rs:5 00000 rd:5 00000 10110 dw:1
|
||||
SEQNE 011100 rs:5 rt:5 rd:5 00000 10101 ne:1
|
||||
SEQNEI 011100 rs:5 rt:5 imm:s10 10111 ne:1
|
201
target/mips/tcg/octeon_translate.c
Normal file
201
target/mips/tcg/octeon_translate.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Octeon-specific instructions translation routines
|
||||
*
|
||||
* Copyright (c) 2022 Pavel Dovgalyuk
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "tcg/tcg-op-gvec.h"
|
||||
#include "exec/helper-gen.h"
|
||||
#include "translate.h"
|
||||
|
||||
/* Include the auto-generated decoder. */
|
||||
#include "decode-octeon.c.inc"
|
||||
|
||||
static bool trans_BBIT(DisasContext *ctx, arg_BBIT *a)
|
||||
{
|
||||
TCGv p;
|
||||
|
||||
if (ctx->hflags & MIPS_HFLAG_BMASK) {
|
||||
LOG_DISAS("Branch in delay / forbidden slot at PC 0x"
|
||||
TARGET_FMT_lx "\n", ctx->base.pc_next);
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Load needed operands */
|
||||
TCGv t0 = tcg_temp_new();
|
||||
gen_load_gpr(t0, a->rs);
|
||||
|
||||
p = tcg_constant_tl(1ULL << a->p);
|
||||
if (a->set) {
|
||||
tcg_gen_and_tl(bcond, p, t0);
|
||||
} else {
|
||||
tcg_gen_andc_tl(bcond, p, t0);
|
||||
}
|
||||
|
||||
ctx->hflags |= MIPS_HFLAG_BC;
|
||||
ctx->btarget = ctx->base.pc_next + 4 + a->offset * 4;
|
||||
ctx->hflags |= MIPS_HFLAG_BDS32;
|
||||
|
||||
tcg_temp_free(t0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_BADDU(DisasContext *ctx, arg_BADDU *a)
|
||||
{
|
||||
TCGv t0, t1;
|
||||
|
||||
if (a->rt == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
t1 = tcg_temp_new();
|
||||
gen_load_gpr(t0, a->rs);
|
||||
gen_load_gpr(t1, a->rt);
|
||||
|
||||
tcg_gen_add_tl(t0, t0, t1);
|
||||
tcg_gen_andi_i64(cpu_gpr[a->rd], t0, 0xff);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_DMUL(DisasContext *ctx, arg_DMUL *a)
|
||||
{
|
||||
TCGv t0, t1;
|
||||
|
||||
if (a->rt == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
t1 = tcg_temp_new();
|
||||
gen_load_gpr(t0, a->rs);
|
||||
gen_load_gpr(t1, a->rt);
|
||||
|
||||
tcg_gen_mul_i64(cpu_gpr[a->rd], t0, t1);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_EXTS(DisasContext *ctx, arg_EXTS *a)
|
||||
{
|
||||
TCGv t0;
|
||||
|
||||
if (a->rt == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
gen_load_gpr(t0, a->rs);
|
||||
tcg_gen_sextract_tl(t0, t0, a->p, a->lenm1 + 1);
|
||||
gen_store_gpr(t0, a->rt);
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_CINS(DisasContext *ctx, arg_CINS *a)
|
||||
{
|
||||
TCGv t0;
|
||||
|
||||
if (a->rt == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
gen_load_gpr(t0, a->rs);
|
||||
tcg_gen_deposit_z_tl(t0, t0, a->p, a->lenm1 + 1);
|
||||
gen_store_gpr(t0, a->rt);
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_POP(DisasContext *ctx, arg_POP *a)
|
||||
{
|
||||
TCGv t0;
|
||||
|
||||
if (a->rd == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
gen_load_gpr(t0, a->rs);
|
||||
if (!a->dw) {
|
||||
tcg_gen_andi_i64(t0, t0, 0xffffffff);
|
||||
}
|
||||
tcg_gen_ctpop_tl(t0, t0);
|
||||
gen_store_gpr(t0, a->rd);
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_SEQNE(DisasContext *ctx, arg_SEQNE *a)
|
||||
{
|
||||
TCGv t0, t1;
|
||||
|
||||
if (a->rd == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
t1 = tcg_temp_new();
|
||||
|
||||
gen_load_gpr(t0, a->rs);
|
||||
gen_load_gpr(t1, a->rt);
|
||||
|
||||
if (a->ne) {
|
||||
tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr[a->rd], t1, t0);
|
||||
} else {
|
||||
tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr[a->rd], t1, t0);
|
||||
}
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_SEQNEI(DisasContext *ctx, arg_SEQNEI *a)
|
||||
{
|
||||
TCGv t0;
|
||||
|
||||
if (a->rt == 0) {
|
||||
/* nop */
|
||||
return true;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
|
||||
gen_load_gpr(t0, a->rs);
|
||||
|
||||
/* Sign-extend to 64 bit value */
|
||||
target_ulong imm = a->imm;
|
||||
if (a->ne) {
|
||||
tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr[a->rt], t0, imm);
|
||||
} else {
|
||||
tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], t0, imm);
|
||||
}
|
||||
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
@ -20,9 +20,11 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "semihosting/softmmu-uaccess.h"
|
||||
#include "semihosting/semihost.h"
|
||||
#include "semihosting/console.h"
|
||||
#include "semihosting/syscalls.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef enum UHIOp {
|
||||
@ -114,268 +116,241 @@ enum UHIErrno {
|
||||
UHI_EXDEV = 18,
|
||||
};
|
||||
|
||||
static int errno_mips(int host_errno)
|
||||
static void report_fault(CPUMIPSState *env)
|
||||
{
|
||||
/* Errno values taken from asm-mips/errno.h */
|
||||
switch (host_errno) {
|
||||
case 0: return 0;
|
||||
case ENAMETOOLONG: return 78;
|
||||
#ifdef EOVERFLOW
|
||||
case EOVERFLOW: return 79;
|
||||
#endif
|
||||
#ifdef ELOOP
|
||||
case ELOOP: return 90;
|
||||
#endif
|
||||
default: return EINVAL;
|
||||
}
|
||||
int op = env->active_tc.gpr[25];
|
||||
error_report("Fault during UHI operation %d", op);
|
||||
abort();
|
||||
}
|
||||
|
||||
static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
|
||||
target_ulong vaddr)
|
||||
static void uhi_cb(CPUState *cs, uint64_t ret, int err)
|
||||
{
|
||||
hwaddr len = sizeof(struct UHIStat);
|
||||
UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
|
||||
if (!dst) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
CPUMIPSState *env = cs->env_ptr;
|
||||
|
||||
#define E(N) case E##N: err = UHI_E##N; break
|
||||
|
||||
switch (err) {
|
||||
case 0:
|
||||
break;
|
||||
E(PERM);
|
||||
E(NOENT);
|
||||
E(INTR);
|
||||
E(BADF);
|
||||
E(BUSY);
|
||||
E(EXIST);
|
||||
E(NOTDIR);
|
||||
E(ISDIR);
|
||||
E(INVAL);
|
||||
E(NFILE);
|
||||
E(MFILE);
|
||||
E(FBIG);
|
||||
E(NOSPC);
|
||||
E(SPIPE);
|
||||
E(ROFS);
|
||||
E(NAMETOOLONG);
|
||||
default:
|
||||
err = UHI_EINVAL;
|
||||
break;
|
||||
case EFAULT:
|
||||
report_fault(env);
|
||||
}
|
||||
|
||||
dst->uhi_st_dev = tswap16(src->st_dev);
|
||||
dst->uhi_st_ino = tswap16(src->st_ino);
|
||||
dst->uhi_st_mode = tswap32(src->st_mode);
|
||||
dst->uhi_st_nlink = tswap16(src->st_nlink);
|
||||
dst->uhi_st_uid = tswap16(src->st_uid);
|
||||
dst->uhi_st_gid = tswap16(src->st_gid);
|
||||
dst->uhi_st_rdev = tswap16(src->st_rdev);
|
||||
dst->uhi_st_size = tswap64(src->st_size);
|
||||
dst->uhi_st_atime = tswap64(src->st_atime);
|
||||
dst->uhi_st_mtime = tswap64(src->st_mtime);
|
||||
dst->uhi_st_ctime = tswap64(src->st_ctime);
|
||||
#ifdef _WIN32
|
||||
dst->uhi_st_blksize = 0;
|
||||
dst->uhi_st_blocks = 0;
|
||||
#else
|
||||
dst->uhi_st_blksize = tswap64(src->st_blksize);
|
||||
dst->uhi_st_blocks = tswap64(src->st_blocks);
|
||||
#endif
|
||||
unlock_user(dst, vaddr, len);
|
||||
return 0;
|
||||
#undef E
|
||||
|
||||
env->active_tc.gpr[2] = ret;
|
||||
env->active_tc.gpr[3] = err;
|
||||
}
|
||||
|
||||
static int get_open_flags(target_ulong target_flags)
|
||||
static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err)
|
||||
{
|
||||
int open_flags = 0;
|
||||
QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat));
|
||||
|
||||
if (target_flags & UHIOpen_RDWR) {
|
||||
open_flags |= O_RDWR;
|
||||
} else if (target_flags & UHIOpen_WRONLY) {
|
||||
open_flags |= O_WRONLY;
|
||||
} else {
|
||||
open_flags |= O_RDONLY;
|
||||
if (!err) {
|
||||
CPUMIPSState *env = cs->env_ptr;
|
||||
target_ulong addr = env->active_tc.gpr[5];
|
||||
UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1);
|
||||
struct gdb_stat s;
|
||||
|
||||
if (!dst) {
|
||||
report_fault(env);
|
||||
}
|
||||
|
||||
memcpy(&s, dst, sizeof(struct gdb_stat));
|
||||
memset(dst, 0, sizeof(UHIStat));
|
||||
|
||||
dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev));
|
||||
dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino));
|
||||
dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode));
|
||||
dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink));
|
||||
dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid));
|
||||
dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid));
|
||||
dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev));
|
||||
dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size));
|
||||
dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime));
|
||||
dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime));
|
||||
dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime));
|
||||
dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize));
|
||||
dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks));
|
||||
|
||||
unlock_user(dst, addr, sizeof(UHIStat));
|
||||
}
|
||||
|
||||
open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
|
||||
open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0;
|
||||
open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0;
|
||||
open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0;
|
||||
|
||||
return open_flags;
|
||||
uhi_cb(cs, ret, err);
|
||||
}
|
||||
|
||||
static int write_to_file(CPUMIPSState *env, target_ulong fd,
|
||||
target_ulong vaddr, target_ulong len)
|
||||
{
|
||||
int num_of_bytes;
|
||||
void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
|
||||
if (!dst) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_of_bytes = write(fd, dst, len);
|
||||
|
||||
unlock_user(dst, vaddr, 0);
|
||||
return num_of_bytes;
|
||||
}
|
||||
|
||||
static int read_from_file(CPUMIPSState *env, target_ulong fd,
|
||||
target_ulong vaddr, target_ulong len)
|
||||
{
|
||||
int num_of_bytes;
|
||||
void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
|
||||
if (!dst) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_of_bytes = read(fd, dst, len);
|
||||
|
||||
unlock_user(dst, vaddr, len);
|
||||
return num_of_bytes;
|
||||
}
|
||||
|
||||
static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
|
||||
target_ulong vaddr)
|
||||
{
|
||||
int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
|
||||
char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
|
||||
if (!dst) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(dst, semihosting_get_arg(arg_num));
|
||||
|
||||
unlock_user(dst, vaddr, strsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GET_TARGET_STRING(p, addr) \
|
||||
do { \
|
||||
p = lock_user_string(addr); \
|
||||
if (!p) { \
|
||||
gpr[2] = -1; \
|
||||
gpr[3] = EFAULT; \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \
|
||||
do { \
|
||||
p = lock_user_string(addr); \
|
||||
if (!p) { \
|
||||
gpr[2] = -1; \
|
||||
gpr[3] = EFAULT; \
|
||||
return; \
|
||||
} \
|
||||
p2 = lock_user_string(addr2); \
|
||||
if (!p2) { \
|
||||
unlock_user(p, addr, 0); \
|
||||
gpr[2] = -1; \
|
||||
gpr[3] = EFAULT; \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FREE_TARGET_STRING(p, gpr) \
|
||||
do { \
|
||||
unlock_user(p, gpr, 0); \
|
||||
} while (0)
|
||||
|
||||
void mips_semihosting(CPUMIPSState *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
target_ulong *gpr = env->active_tc.gpr;
|
||||
const UHIOp op = gpr[25];
|
||||
char *p, *p2;
|
||||
char *p;
|
||||
|
||||
switch (op) {
|
||||
case UHI_exit:
|
||||
qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
|
||||
gdb_exit(gpr[4]);
|
||||
exit(gpr[4]);
|
||||
|
||||
case UHI_open:
|
||||
GET_TARGET_STRING(p, gpr[4]);
|
||||
if (!strcmp("/dev/stdin", p)) {
|
||||
gpr[2] = 0;
|
||||
} else if (!strcmp("/dev/stdout", p)) {
|
||||
gpr[2] = 1;
|
||||
} else if (!strcmp("/dev/stderr", p)) {
|
||||
gpr[2] = 2;
|
||||
} else {
|
||||
gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
|
||||
gpr[3] = errno_mips(errno);
|
||||
{
|
||||
target_ulong fname = gpr[4];
|
||||
int ret = -1;
|
||||
|
||||
p = lock_user_string(fname);
|
||||
if (!p) {
|
||||
report_fault(env);
|
||||
}
|
||||
if (!strcmp("/dev/stdin", p)) {
|
||||
ret = 0;
|
||||
} else if (!strcmp("/dev/stdout", p)) {
|
||||
ret = 1;
|
||||
} else if (!strcmp("/dev/stderr", p)) {
|
||||
ret = 2;
|
||||
}
|
||||
unlock_user(p, fname, 0);
|
||||
|
||||
/* FIXME: reusing a guest fd doesn't seem correct. */
|
||||
if (ret >= 0) {
|
||||
gpr[2] = ret;
|
||||
break;
|
||||
}
|
||||
|
||||
semihost_sys_open(cs, uhi_cb, fname, 0, gpr[5], gpr[6]);
|
||||
}
|
||||
FREE_TARGET_STRING(p, gpr[4]);
|
||||
break;
|
||||
|
||||
case UHI_close:
|
||||
if (gpr[4] < 3) {
|
||||
/* ignore closing stdin/stdout/stderr */
|
||||
gpr[2] = 0;
|
||||
return;
|
||||
}
|
||||
gpr[2] = close(gpr[4]);
|
||||
gpr[3] = errno_mips(errno);
|
||||
semihost_sys_close(cs, uhi_cb, gpr[4]);
|
||||
break;
|
||||
case UHI_read:
|
||||
gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6]);
|
||||
gpr[3] = errno_mips(errno);
|
||||
semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
|
||||
break;
|
||||
case UHI_write:
|
||||
gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6]);
|
||||
gpr[3] = errno_mips(errno);
|
||||
semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
|
||||
break;
|
||||
case UHI_lseek:
|
||||
gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
|
||||
gpr[3] = errno_mips(errno);
|
||||
semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
|
||||
break;
|
||||
case UHI_unlink:
|
||||
GET_TARGET_STRING(p, gpr[4]);
|
||||
gpr[2] = remove(p);
|
||||
gpr[3] = errno_mips(errno);
|
||||
FREE_TARGET_STRING(p, gpr[4]);
|
||||
semihost_sys_remove(cs, uhi_cb, gpr[4], 0);
|
||||
break;
|
||||
case UHI_fstat:
|
||||
{
|
||||
struct stat sbuf;
|
||||
memset(&sbuf, 0, sizeof(sbuf));
|
||||
gpr[2] = fstat(gpr[4], &sbuf);
|
||||
gpr[3] = errno_mips(errno);
|
||||
if (gpr[2]) {
|
||||
return;
|
||||
}
|
||||
gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
|
||||
gpr[3] = errno_mips(errno);
|
||||
}
|
||||
semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]);
|
||||
break;
|
||||
|
||||
case UHI_argc:
|
||||
gpr[2] = semihosting_get_argc();
|
||||
break;
|
||||
case UHI_argnlen:
|
||||
if (gpr[4] >= semihosting_get_argc()) {
|
||||
gpr[2] = -1;
|
||||
return;
|
||||
{
|
||||
const char *s = semihosting_get_arg(gpr[4]);
|
||||
gpr[2] = s ? strlen(s) : -1;
|
||||
}
|
||||
gpr[2] = strlen(semihosting_get_arg(gpr[4]));
|
||||
break;
|
||||
case UHI_argn:
|
||||
if (gpr[4] >= semihosting_get_argc()) {
|
||||
gpr[2] = -1;
|
||||
return;
|
||||
{
|
||||
const char *s = semihosting_get_arg(gpr[4]);
|
||||
target_ulong addr;
|
||||
size_t len;
|
||||
|
||||
if (!s) {
|
||||
gpr[2] = -1;
|
||||
break;
|
||||
}
|
||||
len = strlen(s) + 1;
|
||||
addr = gpr[5];
|
||||
p = lock_user(VERIFY_WRITE, addr, len, 0);
|
||||
if (!p) {
|
||||
report_fault(env);
|
||||
}
|
||||
memcpy(p, s, len);
|
||||
unlock_user(p, addr, len);
|
||||
gpr[2] = 0;
|
||||
}
|
||||
gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
|
||||
break;
|
||||
|
||||
case UHI_plog:
|
||||
GET_TARGET_STRING(p, gpr[4]);
|
||||
p2 = strstr(p, "%d");
|
||||
if (p2) {
|
||||
int char_num = p2 - p;
|
||||
GString *s = g_string_new_len(p, char_num);
|
||||
g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
|
||||
gpr[2] = qemu_semihosting_log_out(s->str, s->len);
|
||||
g_string_free(s, true);
|
||||
} else {
|
||||
gpr[2] = qemu_semihosting_log_out(p, strlen(p));
|
||||
{
|
||||
target_ulong addr = gpr[4];
|
||||
ssize_t len = target_strlen(addr);
|
||||
GString *str;
|
||||
char *pct_d;
|
||||
|
||||
if (len < 0) {
|
||||
report_fault(env);
|
||||
}
|
||||
p = lock_user(VERIFY_READ, addr, len, 1);
|
||||
if (!p) {
|
||||
report_fault(env);
|
||||
}
|
||||
|
||||
pct_d = strstr(p, "%d");
|
||||
if (!pct_d) {
|
||||
unlock_user(p, addr, 0);
|
||||
semihost_sys_write(cs, uhi_cb, 2, addr, len);
|
||||
break;
|
||||
}
|
||||
|
||||
str = g_string_new_len(p, pct_d - p);
|
||||
g_string_append_printf(str, "%d%s", (int)gpr[5], pct_d + 2);
|
||||
unlock_user(p, addr, 0);
|
||||
|
||||
/*
|
||||
* When we're using gdb, we need a guest address, so
|
||||
* drop the string onto the stack below the stack pointer.
|
||||
*/
|
||||
if (use_gdb_syscalls()) {
|
||||
addr = gpr[29] - str->len;
|
||||
p = lock_user(VERIFY_WRITE, addr, str->len, 0);
|
||||
memcpy(p, str->str, str->len);
|
||||
unlock_user(p, addr, str->len);
|
||||
semihost_sys_write(cs, uhi_cb, 2, addr, str->len);
|
||||
} else {
|
||||
gpr[2] = qemu_semihosting_console_write(str->str, str->len);
|
||||
}
|
||||
g_string_free(str, true);
|
||||
}
|
||||
FREE_TARGET_STRING(p, gpr[4]);
|
||||
break;
|
||||
|
||||
case UHI_assert:
|
||||
GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
|
||||
printf("assertion '");
|
||||
printf("\"%s\"", p);
|
||||
printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
|
||||
FREE_TARGET_STRING(p2, gpr[5]);
|
||||
FREE_TARGET_STRING(p, gpr[4]);
|
||||
abort();
|
||||
break;
|
||||
#ifndef _WIN32
|
||||
case UHI_link:
|
||||
GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
|
||||
gpr[2] = link(p, p2);
|
||||
gpr[3] = errno_mips(errno);
|
||||
FREE_TARGET_STRING(p2, gpr[5]);
|
||||
FREE_TARGET_STRING(p, gpr[4]);
|
||||
break;
|
||||
#endif
|
||||
{
|
||||
const char *msg, *file;
|
||||
|
||||
msg = lock_user_string(gpr[4]);
|
||||
if (!msg) {
|
||||
msg = "<EFAULT>";
|
||||
}
|
||||
file = lock_user_string(gpr[5]);
|
||||
if (!file) {
|
||||
file = "<EFAULT>";
|
||||
}
|
||||
|
||||
error_report("UHI assertion \"%s\": file \"%s\", line %d",
|
||||
msg, file, (int)gpr[6]);
|
||||
abort();
|
||||
}
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown UHI operation %d\n", op);
|
||||
error_report("Unknown UHI operation %d", op);
|
||||
abort();
|
||||
}
|
||||
return;
|
||||
|
@ -15955,6 +15955,11 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
|
||||
if (cpu_supports_isa(env, INSN_VR54XX) && decode_ext_vr54xx(ctx, ctx->opcode)) {
|
||||
return;
|
||||
}
|
||||
#if defined(TARGET_MIPS64)
|
||||
if (cpu_supports_isa(env, INSN_OCTEON) && decode_ext_octeon(ctx, ctx->opcode)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ISA extensions */
|
||||
if (ase_msa_available(env) && decode_ase_msa(ctx, ctx->opcode)) {
|
||||
|
@ -215,6 +215,7 @@ bool decode_ase_msa(DisasContext *ctx, uint32_t insn);
|
||||
bool decode_ext_txx9(DisasContext *ctx, uint32_t insn);
|
||||
#if defined(TARGET_MIPS64)
|
||||
bool decode_ext_tx79(DisasContext *ctx, uint32_t insn);
|
||||
bool decode_ext_octeon(DisasContext *ctx, uint32_t insn);
|
||||
#endif
|
||||
bool decode_ext_vr54xx(DisasContext *ctx, uint32_t insn);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user