All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
@ 2018-03-02 13:51 Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 01/23] RISC-V Maintainers Michael Clark
                   ` (24 more replies)
  0 siblings, 25 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

QEMU RISC-V Emulation Support (RV64GC, RV32GC)

This release renames the SiFive machines to sifive_e and sifive_u
to represent the SiFive Everywhere and SiFive Unleashed platforms.
SiFive has configurable soft-core IP, so it is intended that these
machines will be extended to enable a variety of SiFive IP blocks.
The CPU definition infrastructure has been improved and there are
now vendor CPU modules including the SiFiVe E31, E51, U34 and U54
cores. The emulation accuracy for the E series has been improved
by disabling the MMU for the E series. S mode has been disabled on
cores that only support M mode and U mode. The two Spike machines
that support two privileged ISA versions have been coalesced into
one file. This series has Signed-off-by from the core contributors.

The git tree for the v8 patch series tree (squashed and rebased):

- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v8

The git tree for the v1-v7 patch series with review commit history:

- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v7
- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v6
- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v5
- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v4
- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v3
- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v2
- https://github.com/riscv/riscv-qemu/tree/qemu-upstream-v1

*** Known Issues ***

- Disassembler has some checkpatch warnings for the sake of code brevity
- scripts/qemu-binfmt-conf.sh has checkpatch warnings due to line length
- PMP (Physical Memory Protection) is as-of-yet unused and needs testing

*** Changelog ***

v8

- Added linux-user/riscv/target_elf.h during rebase
- Make resetvec configurable and clear mpp and mie on reset
- Use SiFive E31, E51, U34 and U54 cores in SiFive machines
- Define SiFive E31, E51, U34 and U54 cores
- Refactor CPU core definition in preparation for vendor cores
- Prevent S or U mode unless S or U extensions are present
- SiFive E Series cores have no MMU
- SiFive E Series cores have U mode
- Make privileged ISA v1.10 implicit in CPU types
- Remove DRAM_BASE and EXT_IO_BASE as they vary by machine
- Correctly handle mtvec and stvec alignment with respect to RVC
- Print more machine mode state in riscv_cpu_dump_state
- Make riscv_isa_string use compact extension order method
- Fix bug introduced in v6 RISCV_CPU_TYPE_NAME macro change
- Parameterize spike v1.9.1 config string
- Coalesce spike_v1.9.1 and spike_v1.10 machines
- Rename sifive_e300 to sifive_e, and sifive_u500 to sifive_u

v7

- Make spike_v1.10 the default machine
- Rename spike_v1.9 to spike_v1.9.1 to match privileged spec version
- Remove empty target/riscv/trace-events file
- Monitor ROM 32-bit reset code needs to be target endian
- Add TARGET_TIOCGPTPEER to linux-user/riscv/termbits.h
- Add -initrd support to the virt board
- Fix naming in spike machine interface header
- Update copyright notice on RISC-V Spike machines
- Update copyright notice on RISC-V HTIF Console device
- Change CPU Core and translator to GPLv2+
- Change RISC-V Disassembler to GPLv2+
- Change SiFive Test Finisher to GPLv2+
- Change SiFive CLINT to GPLv2+
- Change SiFive PRCI to GPLv2+
- Change SiFive PLIC to GPLv2+
- Change RISC-V spike machines to GPLv2+
- Change RISC-V virt machine to GPLv2+
- Change SiFive E300 machine to GPLv2+
- Change SiFive U500 machine to GPLv2+
- Change RISC-V Hart Array to GPLv2+
- Change RISC-V HTIF device to GPLv2+
- Change SiFiveUART device to GPLv2+

v6

- Drop IEEE 754-201x minimumNumber/maximumNumber for fmin/fmax
- Remove some unnecessary commented debug statements
- Change RISCV_CPU_TYPE_NAME to use riscv-cpu suffix
- Define all CPU variants for linux-user
- qemu_log calls require trailing \n
- Replace PLIC printfs with qemu_log
- Tear out unused HTIF code and eliminate shouting debug messages
- Fix illegal instruction when sfence.vma is passed (rs2) arguments
- Make updates to PTE accessed and dirty bits atomic
- Only require atomic PTE updates on MTTCG enabled guests
- Page fault if accessed or dirty bits can't be updated
- Fix get_physical_address PTE reads and writes on riscv32
- Remove erroneous comments from the PLIC
- Default enable MTTCG
- Make WFI less conservative
- Unify local interrupt handling
- Expunge HTIF interrupts
- Always access mstatus.mip under a lock
- Don't implement rdtime/rdtimeh in system mode (bbl emulates them)
- Implement insreth/cycleh for rv32 and always enable user-mode counters
- Add GDB stub support for reading and writing CSRs
- Rename ENABLE_CHARDEV #ifdef from HTIF code
- Replace bad HTIF ELF code with load_elf symbol callback
- Convert chained if else fault handlers to switch statements
- Use RISCV exception codes for linux-user page faults

v5

- Implement NaN-boxing for flw, set high order bits to 1
- Use float_muladd_negate_* flags to floatXX_muladd
- Use IEEE 754-201x minimumNumber/maximumNumber for fmin/fmax
- Fix TARGET_NR_syscalls
- Update linux-user/riscv/syscall_nr.h
- Fix FENCE.I, needs to terminate translation block
- Adjust unusual convention for interruptno >= 0

v4

- Add @riscv: since 2.12 to CpuInfoArch
- Remove misleading little-endian comment from load_kernel
- Rename cpu-model property to cpu-type
- Drop some unnecessary inline function attributes
- Don't allow GDB to set value of x0 register
- Remove unnecessary empty property lists
- Add Test Finisher device to implement poweroff in virt machine
- Implement priv ISA v1.10 trap and sret/mret xPIE/xIE behavior
- Store fflags data in fp_status
- Purge runtime users of helper_raise_exception
- Fix validate_csr
- Tidy gen_jalr
- Tidy immediate shifts
- Add gen_exception_inst_addr_mis
- Add gen_exception_debug
- Add gen_exception_illegal
- Tidy helper_fclass_*
- Split rounding mode setting to a new function
- Enforce MSTATUS_FS via TB flags
- Implement acquire/release barrier semantics
- Use atomic operations as required
- Fix FENCE and FENCE_I
- Remove commented code from spike machines
- PAGE_WRITE permissions can be set on loads if page is already dirty
- The result of format conversion on an NaN must be a quiet NaN
- Add missing process_queued_cpu_work to riscv linux-user
- Remove float(32|64)_classify from cpu.h
- Removed nonsensical unions aliasing the same type
- Use uintN_t instead of uintN_fast_t in fpu_helper.c
- Use macros for FPU exception values in softfloat_flags_to_riscv
- Move code to set round mode into set_fp_round_mode function
- Convert set_fp_exceptions from a macro to an inline function
- Convert round mode helper into an inline function
- Make fpu_helper ieee_rm array static const
- Include cpu_mmu_index in cpu_get_tb_cpu_state flags
- Eliminate MPRV influence on mmu_index
- Remove unrecoverable do_unassigned_access function
- Only update PTE accessed and dirty bits if necessary
- Remove unnecessary tlb_flush in set_mode as mode is in mmu_idx
- Remove buggy support for misa writes. misa writes are optional
  and are not implemented in any known hardware
- Always set PTE read or execute permissions during page walk
- Reorder helper function declarations to match order in helper.c
- Remove redundant variable declaration in get_physical_address
- Remove duplicated code from get_physical_address
- Use mmu_idx instead of mem_idx in riscv_cpu_get_phys_page_debug

v3

- Fix indentation in PMP and HTIF debug macros
- Fix disassembler checkpatch open brace '{' on next line errors
- Fix trailing statements on next line in decode_inst_decompress
- NOTE: the other checkpatch issues have been reviewed previously

v2

- Remove redundant NULL terminators from disassembler register arrays
- Change disassembler register name arrays to const
- Refine disassembler internal function names
- Update dates in disassembler copyright message
- Remove #ifdef CONFIG_USER_ONLY version of cpu_has_work
- Use ULL suffix on 64-bit constants
- Move riscv_cpu_mmu_index from cpu.h to helper.c
- Move riscv_cpu_hw_interrupts_pending from cpu.h to helper.c
- Remove redundant TARGET_HAS_ICE from cpu.h
- Use qemu_irq instead of void* for irq definition in cpu.h
- Remove duplicate typedef from struct CPURISCVState
- Remove redundant g_strdup from cpu_register
- Remove redundant tlb_flush from riscv_cpu_reset
- Remove redundant mode calculation from get_physical_address
- Remove redundant debug mode printf and dcsr comment
- Remove redundant clearing of MSB for bare physical addresses
- Use g_assert_not_reached for invalid mode in get_physical_address
- Use g_assert_not_reached for unreachable checks in get_physical_address
- Use g_assert_not_reached for unreachable type in raise_mmu_exception
- Return exception instead of aborting for misaligned fetches
- Move exception defines from cpu.h to cpu_bits.h
- Remove redundant breakpoint control definitions from cpu_bits.h
- Implement riscv_cpu_unassigned_access exception handling
- Log and raise exceptions for unimplemented CSRs
- Match Spike HTIF exit behavior - don’t print TEST-PASSED
- Make frm,fflags,fcsr writes trap when mstatus.FS is clear
- Use g_assert_not_reached for unreachable invalid mode
- Make hret,uret,dret generate illegal instructions
- Move riscv_cpu_dump_state and int/fpr regnames to cpu.c
- Lift interrupt flag and mask into constants in cpu_bits.h
- Change trap debugging to use qemu_log_mask LOG_TRACE 
- Change CSR debugging to use qemu_log_mask LOG_TRACE
- Change PMP debugging to use qemu_log_mask LOG_TRACE
- Remove commented code from pmp.c
- Change CpuInfoRISCV qapi schema docs to Since 2.12
- Change RV feature macro to use target_ulong cast
- Remove riscv_feature and instead use misa extension flags
- Make riscv_flush_icache_syscall a no-op
- Undo checkpatch whitespace fixes in unrelated linux-user code
- Remove redudant constants and tidy up cpu_bits.h
- Make helper_fence_i a no-op
- Move include "exec/cpu-all" to end of cpu.h
- Rename set_privilege to riscv_set_mode
- Move redundant forward declaration for cpu_riscv_translate_address
- Remove TCGV_UNUSED from riscv_translate_init
- Add comment to pmp.c stating the code is untested and currently unused
- Use ctz to simplify decoding of PMP NAPOT address ranges
- Change pmp_is_in_range to use than equal for end addresses
- Fix off by one error in pmp_update_rule
- Rearrange PMP_DEBUG so that formatting is compile-time checked
- Rearrange trap debugging so that formatting is compile-time checked
- Rearrange PLIC debugging so that formatting is compile-time checked
- Use qemu_log/qemu_log_mask for HTIF logging and debugging
- Move exception and interrupt names into cpu.c
- Add Palmer Dabbelt as a RISC-V Maintainer
- Rebase against current qemu master branch

v1

- initial version based on forward port from riscv-qemu repository

*** Background ***

"RISC-V is an open, free ISA enabling a new era of processor innovation
through open standard collaboration. Born in academia and research,
RISC-V ISA delivers a new level of free, extensible software and
hardware freedom on architecture, paving the way for the next 50 years
of computing design and innovation."

The QEMU RISC-V port has been developed and maintained out-of-tree for
several years by Sagar Karandikar and Bastian Koppelmann. The RISC-V
Privileged specification has evolved substantially over this period but
has recently been solidifying. The RISC-V Base ISA has been frozon for
some time and the Privileged ISA, GCC toolchain and Linux ABI are now
quite stable. I have recently joined Sagar and Bastian as a RISC-V QEMU
Maintainer and hope to support upstreaming the port. 

There are multiple vendors taping out, preparing to ship, or shipping
silicon that implements the RISC-V Privileged ISA Version 1.10. There
are also several RISC-V Soft-IP cores implementing Privileged ISA
Version 1.10 that run on FPGA such as SiFive's Freedom U500 Platform
and the U54‑MC RISC-V Core IP, among many more implementations from a
variety of vendors. See https://riscv.org/ for more details.

RISC-V support was upstreamed in binutils 2.28 and GCC 7.1 in the first
half of 2016. RISC-V support is now available in LLVM top-of-tree and
the RISC-V Linux port was accepted into Linux 4.15-rc1 late last year
and is available in the Linux 4.15 release. GLIBC 2.27 added support
for the RISC-V ISA running on Linux (requires at least binutils-2.30,
gcc-7.3.0, and linux-4.15). We believe it is timely to submit the
RISC-V QEMU port for upstream review with the goal of incorporating
RISC-V support into the upcoming QEMU 2.12 release.

The RISC-V QEMU port is still under active development, mostly with
respect to device emulation, the addition of Hypervisor support as
specified in the RISC-V Draft Privileged ISA Version 1.11, and Vector
support once the first draft is finalized later this year. We believe
now is the appropriate time for RISC-V QEMU development to be carried
out in the main QEMU repository as the code will benefit from more
rigorous review. The RISC-V QEMU port currently supports all the ISA
extensions that have been finalized and frozen in the Base ISA.

Blog post about recent additions to RISC-V QEMU: https://goo.gl/fJ4zgk

The RISC-V QEMU wiki: https://github.com/riscv/riscv-qemu/wiki

Instructions for building a busybox+dropbear root image, BBL (Berkeley
Boot Loader) and linux kernel image for use with the RISC-V QEMU
'virt' machine: https://github.com/michaeljclark/busybear-linux

*** Overview ***

The RISC-V QEMU port implements the following specifications:

- RISC-V Instruction Set Manual Volume I: User-Level ISA Version 2.2
- RISC-V Instruction Set Manual Volume II: Privileged ISA Version 1.9.1
- RISC-V Instruction Set Manual Volume II: Privileged ISA Version 1.10

The RISC-V QEMU port supports the following instruction set extensions:

- RV32GC with Supervisor-mode and User-mode (RV32IMAFDCSU)
- RV64GC with Supervisor-mode and User-mode (RV64IMAFDCSU)

The RISC-V QEMU port adds the following targets to QEMU:

- riscv32-softmmu
- riscv64-softmmu
- riscv32-linux-user
- riscv64-linux-user

The RISC-V QEMU port supports the following hardware:

- HTIF Console (Host Target Interface)
- SiFive CLINT (Core Local Interruptor) for Timer interrupts and IPIs
- SiFive PLIC (Platform Level Interrupt Controller)
- SiFive Test (Test Finisher) for exiting simulation
- SiFive UART, PRCI, AON, PWM, QSPI support is partially implemented
- VirtIO MMIO (GPEX PCI support will be added in a future patch)
- Generic 16550A UART emulation using 'hw/char/serial.c'
- MTTCG and SMP support (PLIC and CLINT) on the 'virt' machine

The RISC-V QEMU full system emulator supports 5 machines:

- 'spike_v1.9.1', CLINT, PLIC, HTIF console, config-string, Priv v1.9.1
- 'spike_v1.10', CLINT, PLIC, HTIF console, device-tree, Priv v1.10
- 'sifive_e', CLINT, PLIC, SiFive UART, HiFive1 compat, Priv v1.10
- 'sifive_u', CLINT, PLIC, SiFive UART, device-tree, Priv v1.10
- 'virt', CLINT, PLIC, 16550A UART, VirtIO, device-tree, Priv v1.10

This is a list of RISC-V QEMU Port Contributors:

- Alex Suykov
- Andreas Schwab
- Antony Pavlov
- Bastian Koppelmann
- Bruce Hoult
- Chih-Min Chao
- Daire McNamara
- Darius Rad
- David Abdurachmanov
- Hesham Almatary
- Ivan Griffin
- Jim Wilson
- Kito Cheng
- Michael Clark
- Palmer Dabbelt
- Richard Henderson
- Sagar Karandikar
- Shea Levy
- Stefan O'Rear

Notes:

- contributor email addresses available off-list on request.
- checkpatch has been run on all 23 patches.
- checkpatch exceptions are noted in patches that have errors.
- passes "make check" on full build for all targets
- tested riscv-linux-4.6.2 on 'spike_v1.9.1' machine
- tested riscv-linux-4.15 on 'spike_v1.10' and 'virt' machines
- tested SiFive HiFive1 binaries in 'sifive_e' machine
- tested RV64 on 32-bit i386

This patch series includes the following patches:

Michael Clark (23):
  RISC-V Maintainers
  RISC-V ELF Machine Definition
  RISC-V CPU Core Definition
  RISC-V Disassembler
  RISC-V CPU Helpers
  RISC-V FPU Support
  RISC-V GDB Stub
  RISC-V TCG Code Generation
  RISC-V Physical Memory Protection
  RISC-V Linux User Emulation
  Add symbol table callback interface to load_elf
  RISC-V HTIF Console
  RISC-V HART Array
  SiFive RISC-V CLINT Block
  SiFive RISC-V PLIC Block
  RISC-V Spike Machines
  SiFive RISC-V Test Finisher
  RISC-V VirtIO Machine
  SiFive RISC-V UART Device
  SiFive RISC-V PRCI Block
  SiFive Freedom E Series RISC-V Machine
  SiFive Freedom U Series RISC-V Machine
  RISC-V Build Infrastructure

 MAINTAINERS                            |   11 +
 arch_init.c                            |    2 +
 configure                              |   13 +
 cpus.c                                 |    6 +
 default-configs/riscv32-linux-user.mak |    1 +
 default-configs/riscv32-softmmu.mak    |    4 +
 default-configs/riscv64-linux-user.mak |    1 +
 default-configs/riscv64-softmmu.mak    |    4 +
 disas.c                                |    2 +
 disas/Makefile.objs                    |    1 +
 disas/riscv.c                          | 3048 ++++++++++++++++++++++++++++++++
 fpu/softfloat-specialize.h             |    7 +-
 hw/core/loader.c                       |   18 +-
 hw/riscv/Makefile.objs                 |   11 +
 hw/riscv/riscv_hart.c                  |   89 +
 hw/riscv/riscv_htif.c                  |  258 +++
 hw/riscv/sifive_clint.c                |  254 +++
 hw/riscv/sifive_e.c                    |  234 +++
 hw/riscv/sifive_plic.c                 |  505 ++++++
 hw/riscv/sifive_prci.c                 |   89 +
 hw/riscv/sifive_test.c                 |   93 +
 hw/riscv/sifive_u.c                    |  339 ++++
 hw/riscv/sifive_uart.c                 |  176 ++
 hw/riscv/spike.c                       |  376 ++++
 hw/riscv/virt.c                        |  420 +++++
 include/disas/bfd.h                    |    2 +
 include/elf.h                          |    2 +
 include/hw/elf_ops.h                   |   34 +-
 include/hw/loader.h                    |   17 +-
 include/hw/riscv/riscv_hart.h          |   39 +
 include/hw/riscv/riscv_htif.h          |   61 +
 include/hw/riscv/sifive_clint.h        |   50 +
 include/hw/riscv/sifive_e.h            |   79 +
 include/hw/riscv/sifive_plic.h         |   85 +
 include/hw/riscv/sifive_prci.h         |   37 +
 include/hw/riscv/sifive_test.h         |   42 +
 include/hw/riscv/sifive_u.h            |   69 +
 include/hw/riscv/sifive_uart.h         |   71 +
 include/hw/riscv/spike.h               |   53 +
 include/hw/riscv/virt.h                |   74 +
 include/sysemu/arch_init.h             |    1 +
 linux-user/elfload.c                   |   22 +
 linux-user/main.c                      |   99 ++
 linux-user/riscv/syscall_nr.h          |  287 +++
 linux-user/riscv/target_cpu.h          |   18 +
 linux-user/riscv/target_elf.h          |   14 +
 linux-user/riscv/target_signal.h       |   23 +
 linux-user/riscv/target_structs.h      |   46 +
 linux-user/riscv/target_syscall.h      |   56 +
 linux-user/riscv/termbits.h            |  222 +++
 linux-user/signal.c                    |  203 ++-
 linux-user/syscall.c                   |    2 +
 linux-user/syscall_defs.h              |   13 +-
 qapi-schema.json                       |   17 +-
 scripts/qemu-binfmt-conf.sh            |   13 +-
 target/riscv/Makefile.objs             |    1 +
 target/riscv/cpu.c                     |  432 +++++
 target/riscv/cpu.h                     |  296 ++++
 target/riscv/cpu_bits.h                |  411 +++++
 target/riscv/cpu_user.h                |   13 +
 target/riscv/fpu_helper.c              |  373 ++++
 target/riscv/gdbstub.c                 |   62 +
 target/riscv/helper.c                  |  503 ++++++
 target/riscv/helper.h                  |   78 +
 target/riscv/instmap.h                 |  364 ++++
 target/riscv/op_helper.c               |  669 +++++++
 target/riscv/pmp.c                     |  380 ++++
 target/riscv/pmp.h                     |   64 +
 target/riscv/translate.c               | 1978 +++++++++++++++++++++
 69 files changed, 13310 insertions(+), 27 deletions(-)
 create mode 100644 default-configs/riscv32-linux-user.mak
 create mode 100644 default-configs/riscv32-softmmu.mak
 create mode 100644 default-configs/riscv64-linux-user.mak
 create mode 100644 default-configs/riscv64-softmmu.mak
 create mode 100644 disas/riscv.c
 create mode 100644 hw/riscv/Makefile.objs
 create mode 100644 hw/riscv/riscv_hart.c
 create mode 100644 hw/riscv/riscv_htif.c
 create mode 100644 hw/riscv/sifive_clint.c
 create mode 100644 hw/riscv/sifive_e.c
 create mode 100644 hw/riscv/sifive_plic.c
 create mode 100644 hw/riscv/sifive_prci.c
 create mode 100644 hw/riscv/sifive_test.c
 create mode 100644 hw/riscv/sifive_u.c
 create mode 100644 hw/riscv/sifive_uart.c
 create mode 100644 hw/riscv/spike.c
 create mode 100644 hw/riscv/virt.c
 create mode 100644 include/hw/riscv/riscv_hart.h
 create mode 100644 include/hw/riscv/riscv_htif.h
 create mode 100644 include/hw/riscv/sifive_clint.h
 create mode 100644 include/hw/riscv/sifive_e.h
 create mode 100644 include/hw/riscv/sifive_plic.h
 create mode 100644 include/hw/riscv/sifive_prci.h
 create mode 100644 include/hw/riscv/sifive_test.h
 create mode 100644 include/hw/riscv/sifive_u.h
 create mode 100644 include/hw/riscv/sifive_uart.h
 create mode 100644 include/hw/riscv/spike.h
 create mode 100644 include/hw/riscv/virt.h
 create mode 100644 linux-user/riscv/syscall_nr.h
 create mode 100644 linux-user/riscv/target_cpu.h
 create mode 100644 linux-user/riscv/target_elf.h
 create mode 100644 linux-user/riscv/target_signal.h
 create mode 100644 linux-user/riscv/target_structs.h
 create mode 100644 linux-user/riscv/target_syscall.h
 create mode 100644 linux-user/riscv/termbits.h
 create mode 100644 target/riscv/Makefile.objs
 create mode 100644 target/riscv/cpu.c
 create mode 100644 target/riscv/cpu.h
 create mode 100644 target/riscv/cpu_bits.h
 create mode 100644 target/riscv/cpu_user.h
 create mode 100644 target/riscv/fpu_helper.c
 create mode 100644 target/riscv/gdbstub.c
 create mode 100644 target/riscv/helper.c
 create mode 100644 target/riscv/helper.h
 create mode 100644 target/riscv/instmap.h
 create mode 100644 target/riscv/op_helper.c
 create mode 100644 target/riscv/pmp.c
 create mode 100644 target/riscv/pmp.h
 create mode 100644 target/riscv/translate.c

-- 
2.7.0

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 01/23] RISC-V Maintainers
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 02/23] RISC-V ELF Machine Definition Michael Clark
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Add Michael Clark, Palmer Dabbelt, Sagar Karandikar and Bastian
Koppelmann as RISC-V Maintainers.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
Signed-off-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 MAINTAINERS | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b7c4130..f409f3b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -233,6 +233,17 @@ F: hw/ppc/
 F: include/hw/ppc/
 F: disas/ppc.c
 
+RISC-V
+M: Michael Clark <mjc@sifive.com>
+M: Palmer Dabbelt <palmer@sifive.com>
+M: Sagar Karandikar <sagark@eecs.berkeley.edu>
+M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
+S: Maintained
+F: target/riscv/
+F: hw/riscv/
+F: include/hw/riscv/
+F: disas/riscv.c
+
 S390
 M: Richard Henderson <rth@twiddle.net>
 M: Alexander Graf <agraf@suse.de>
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 02/23] RISC-V ELF Machine Definition
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 01/23] RISC-V Maintainers Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 13:05   ` Philippe Mathieu-Daudé
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition Michael Clark
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Define RISC-V ELF machine EM_RISCV 243

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 include/elf.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/elf.h b/include/elf.h
index 943ee21..c0dc9bb 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -119,6 +119,8 @@ typedef int64_t  Elf64_Sxword;
 
 #define EM_UNICORE32    110     /* UniCore32 */
 
+#define EM_RISCV        243     /* RISC-V */
+
 /*
  * This is an interim value that we will use until the committee comes
  * up with a final number.
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 01/23] RISC-V Maintainers Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 02/23] RISC-V ELF Machine Definition Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-03  2:23   ` Michael Clark
  2018-03-05  9:44   ` Igor Mammedov
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler Michael Clark
                   ` (21 subsequent siblings)
  24 siblings, 2 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Add CPU state header, CPU definitions and initialization routines

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++++++++++++++++++++
 target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
 target/riscv/cpu_bits.h | 411 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1139 insertions(+)
 create mode 100644 target/riscv/cpu.c
 create mode 100644 target/riscv/cpu.h
 create mode 100644 target/riscv/cpu_bits.h

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
new file mode 100644
index 0000000..4851890
--- /dev/null
+++ b/target/riscv/cpu.c
@@ -0,0 +1,432 @@
+/*
+ * QEMU RISC-V CPU
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+
+/* RISC-V CPU definitions */
+
+static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG";
+
+const char * const riscv_int_regnames[] = {
+  "zero", "ra  ", "sp  ", "gp  ", "tp  ", "t0  ", "t1  ", "t2  ",
+  "s0  ", "s1  ", "a0  ", "a1  ", "a2  ", "a3  ", "a4  ", "a5  ",
+  "a6  ", "a7  ", "s2  ", "s3  ", "s4  ", "s5  ", "s6  ", "s7  ",
+  "s8  ", "s9  ", "s10 ", "s11 ", "t3  ", "t4  ", "t5  ", "t6  "
+};
+
+const char * const riscv_fpr_regnames[] = {
+  "ft0 ", "ft1 ", "ft2 ", "ft3 ", "ft4 ", "ft5 ", "ft6 ",  "ft7 ",
+  "fs0 ", "fs1 ", "fa0 ", "fa1 ", "fa2 ", "fa3 ", "fa4 ",  "fa5 ",
+  "fa6 ", "fa7 ", "fs2 ", "fs3 ", "fs4 ", "fs5 ", "fs6 ",  "fs7 ",
+  "fs8 ", "fs9 ", "fs10", "fs11", "ft8 ", "ft9 ", "ft10",  "ft11"
+};
+
+const char * const riscv_excp_names[] = {
+    "misaligned_fetch",
+    "fault_fetch",
+    "illegal_instruction",
+    "breakpoint",
+    "misaligned_load",
+    "fault_load",
+    "misaligned_store",
+    "fault_store",
+    "user_ecall",
+    "supervisor_ecall",
+    "hypervisor_ecall",
+    "machine_ecall",
+    "exec_page_fault",
+    "load_page_fault",
+    "reserved",
+    "store_page_fault"
+};
+
+const char * const riscv_intr_names[] = {
+    "u_software",
+    "s_software",
+    "h_software",
+    "m_software",
+    "u_timer",
+    "s_timer",
+    "h_timer",
+    "m_timer",
+    "u_external",
+    "s_external",
+    "h_external",
+    "m_external",
+    "coprocessor",
+    "host"
+};
+
+typedef struct RISCVCPUInfo {
+    const int bit_widths;
+    const char *name;
+    void (*initfn)(Object *obj);
+} RISCVCPUInfo;
+
+static void set_misa(CPURISCVState *env, target_ulong misa)
+{
+    env->misa = misa;
+}
+
+static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
+{
+    env->user_ver = user_ver;
+    env->priv_ver = priv_ver;
+}
+
+static void set_feature(CPURISCVState *env, int feature)
+{
+    env->features |= (1ULL << feature);
+}
+
+static void set_resetvec(CPURISCVState *env, int resetvec)
+{
+#ifndef CONFIG_USER_ONLY
+    env->resetvec = resetvec;
+#endif
+}
+
+static void riscv_any_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+}
+
+static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv32imacu_nommu_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+}
+
+static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv64imacu_nommu_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+}
+
+static const RISCVCPUInfo riscv_cpus[] = {
+    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
+    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init },
+    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init },
+    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
+    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
+    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init },
+    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init },
+    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init },
+    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
+    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
+    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init },
+    { 0, NULL, NULL }
+};
+
+static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+    char *typename;
+    char **cpuname;
+
+    cpuname = g_strsplit(cpu_model, ",", 1);
+    typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]);
+    oc = object_class_by_name(typename);
+    g_strfreev(cpuname);
+    g_free(typename);
+    if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) ||
+        object_class_is_abstract(oc)) {
+        return NULL;
+    }
+    return oc;
+}
+
+static void riscv_cpu_dump_state(CPUState *cs, FILE *f,
+    fprintf_function cpu_fprintf, int flags)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    int i;
+
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc      ", env->pc);
+#ifndef CONFIG_USER_ONLY
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip     ",
+        (target_ulong)atomic_read(&env->mip));
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie     ", env->mie);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec   ", env->mtvec);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc    ", env->mepc);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause  ", env->mcause);
+#endif
+
+    for (i = 0; i < 32; i++) {
+        cpu_fprintf(f, " %s " TARGET_FMT_lx,
+            riscv_int_regnames[i], env->gpr[i]);
+        if ((i & 3) == 3) {
+            cpu_fprintf(f, "\n");
+        }
+    }
+    for (i = 0; i < 32; i++) {
+        cpu_fprintf(f, " %s %016" PRIx64,
+            riscv_fpr_regnames[i], env->fpr[i]);
+        if ((i & 3) == 3) {
+            cpu_fprintf(f, "\n");
+        }
+    }
+}
+
+static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    env->pc = value;
+}
+
+static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    env->pc = tb->pc;
+}
+
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+#ifndef CONFIG_USER_ONLY
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    /*
+     * Definition of the WFI instruction requires it to ignore the privilege
+     * mode and delegation registers, but respect individual enables
+     */
+    return (atomic_read(&env->mip) & env->mie) != 0;
+#else
+    return true;
+#endif
+}
+
+void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->pc = data[0];
+}
+
+static void riscv_cpu_reset(CPUState *cs)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
+    CPURISCVState *env = &cpu->env;
+
+    mcc->parent_reset(cs);
+#ifndef CONFIG_USER_ONLY
+    env->priv = PRV_M;
+    env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
+    env->mcause = 0;
+    env->pc = env->resetvec;
+#endif
+    cs->exception_index = EXCP_NONE;
+    set_default_nan_mode(1, &env->fp_status);
+}
+
+static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
+{
+#if defined(TARGET_RISCV32)
+    info->print_insn = print_insn_riscv32;
+#elif defined(TARGET_RISCV64)
+    info->print_insn = print_insn_riscv64;
+#endif
+}
+
+static void riscv_cpu_realize(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    qemu_init_vcpu(cs);
+    cpu_reset(cs);
+
+    mcc->parent_realize(dev, errp);
+}
+
+static void riscv_cpu_init(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    RISCVCPU *cpu = RISCV_CPU(obj);
+
+    cs->env_ptr = &cpu->env;
+}
+
+static const VMStateDescription vmstate_riscv_cpu = {
+    .name = "cpu",
+    .unmigratable = 1,
+};
+
+static void riscv_cpu_class_init(ObjectClass *c, void *data)
+{
+    RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
+    CPUClass *cc = CPU_CLASS(c);
+    DeviceClass *dc = DEVICE_CLASS(c);
+
+    mcc->parent_realize = dc->realize;
+    dc->realize = riscv_cpu_realize;
+
+    mcc->parent_reset = cc->reset;
+    cc->reset = riscv_cpu_reset;
+
+    cc->class_by_name = riscv_cpu_class_by_name;
+    cc->has_work = riscv_cpu_has_work;
+    cc->do_interrupt = riscv_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
+    cc->dump_state = riscv_cpu_dump_state;
+    cc->set_pc = riscv_cpu_set_pc;
+    cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb;
+    cc->gdb_read_register = riscv_cpu_gdb_read_register;
+    cc->gdb_write_register = riscv_cpu_gdb_write_register;
+    cc->gdb_num_core_regs = 65;
+    cc->gdb_stop_before_watchpoint = true;
+    cc->disas_set_info = riscv_cpu_disas_set_info;
+#ifdef CONFIG_USER_ONLY
+    cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault;
+#else
+    cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
+    cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
+#endif
+#ifdef CONFIG_TCG
+    cc->tcg_initialize = riscv_translate_init;
+#endif
+    /* For now, mark unmigratable: */
+    cc->vmsd = &vmstate_riscv_cpu;
+}
+
+static void cpu_register(const RISCVCPUInfo *info)
+{
+    TypeInfo type_info = {
+        .name = info->name,
+        .parent = TYPE_RISCV_CPU,
+        .instance_size = sizeof(RISCVCPU),
+        .instance_init = info->initfn,
+    };
+
+    type_register(&type_info);
+}
+
+static const TypeInfo riscv_cpu_type_info = {
+    .name = TYPE_RISCV_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(RISCVCPU),
+    .instance_init = riscv_cpu_init,
+    .abstract = false,
+    .class_size = sizeof(RISCVCPUClass),
+    .class_init = riscv_cpu_class_init,
+};
+
+char *riscv_isa_string(RISCVCPU *cpu)
+{
+    int i;
+    size_t maxlen = 5 + ctz32(cpu->env.misa);
+    char *isa_string = g_new0(char, maxlen);
+    snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS);
+    for (i = 0; i < sizeof(riscv_exts); i++) {
+        if (cpu->env.misa & RV(riscv_exts[i])) {
+            isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a';
+
+        }
+    }
+    return isa_string;
+}
+
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+    const RISCVCPUInfo *info = riscv_cpus;
+
+    while (info->name) {
+        if (info->bit_widths & TARGET_LONG_BITS) {
+            (*cpu_fprintf)(f, "%s\n", info->name);
+        }
+        info++;
+    }
+}
+
+static void riscv_cpu_register_types(void)
+{
+    const RISCVCPUInfo *info = riscv_cpus;
+
+    type_register_static(&riscv_cpu_type_info);
+
+    while (info->name) {
+        if (info->bit_widths & TARGET_LONG_BITS) {
+            cpu_register(info);
+        }
+        info++;
+    }
+}
+
+type_init(riscv_cpu_register_types)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
new file mode 100644
index 0000000..cff02a2
--- /dev/null
+++ b/target/riscv/cpu.h
@@ -0,0 +1,296 @@
+/*
+ * QEMU RISC-V CPU
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef RISCV_CPU_H
+#define RISCV_CPU_H
+
+/* QEMU addressing/paging config */
+#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
+#if defined(TARGET_RISCV64)
+#define TARGET_LONG_BITS 64
+#define TARGET_PHYS_ADDR_SPACE_BITS 50
+#define TARGET_VIRT_ADDR_SPACE_BITS 39
+#elif defined(TARGET_RISCV32)
+#define TARGET_LONG_BITS 32
+#define TARGET_PHYS_ADDR_SPACE_BITS 34
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#endif
+
+#define TCG_GUEST_DEFAULT_MO 0
+
+#define ELF_MACHINE EM_RISCV
+#define CPUArchState struct CPURISCVState
+
+#include "qemu-common.h"
+#include "qom/cpu.h"
+#include "exec/cpu-defs.h"
+#include "fpu/softfloat.h"
+
+#define TYPE_RISCV_CPU "riscv-cpu"
+
+#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
+#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
+
+#define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
+#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1")
+#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0")
+#define TYPE_RISCV_CPU_RV32IMACU_NOMMU  RISCV_CPU_TYPE_NAME("rv32imacu-nommu")
+#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1")
+#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0")
+#define TYPE_RISCV_CPU_RV64IMACU_NOMMU  RISCV_CPU_TYPE_NAME("rv64imacu-nommu")
+#define TYPE_RISCV_CPU_SIFIVE_E31       RISCV_CPU_TYPE_NAME("sifive-e31")
+#define TYPE_RISCV_CPU_SIFIVE_E51       RISCV_CPU_TYPE_NAME("sifive-e51")
+#define TYPE_RISCV_CPU_SIFIVE_U34       RISCV_CPU_TYPE_NAME("sifive-u34")
+#define TYPE_RISCV_CPU_SIFIVE_U54       RISCV_CPU_TYPE_NAME("sifive-u54")
+
+#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2))
+#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2))
+
+#if defined(TARGET_RISCV32)
+#define RVXLEN RV32
+#elif defined(TARGET_RISCV64)
+#define RVXLEN RV64
+#endif
+
+#define RV(x) ((target_ulong)1 << (x - 'A'))
+
+#define RVI RV('I')
+#define RVM RV('M')
+#define RVA RV('A')
+#define RVF RV('F')
+#define RVD RV('D')
+#define RVC RV('C')
+#define RVS RV('S')
+#define RVU RV('U')
+
+/* S extension denotes that Supervisor mode exists, however it is possible
+   to have a core that support S mode but does not have an MMU and there
+   is currently no bit in misa to indicate whether an MMU exists or not
+   so a cpu features bitfield is required */
+enum {
+    RISCV_FEATURE_MMU
+};
+
+#define USER_VERSION_2_02_0 0x00020200
+#define PRIV_VERSION_1_09_1 0x00010901
+#define PRIV_VERSION_1_10_0 0x00011000
+
+#define TRANSLATE_FAIL 1
+#define TRANSLATE_SUCCESS 0
+#define NB_MMU_MODES 4
+#define MMU_USER_IDX 3
+
+#define MAX_RISCV_PMPS (16)
+
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+struct CPURISCVState {
+    target_ulong gpr[32];
+    uint64_t fpr[32]; /* assume both F and D extensions */
+    target_ulong pc;
+    target_ulong load_res;
+    target_ulong load_val;
+
+    target_ulong frm;
+
+    target_ulong badaddr;
+
+    target_ulong user_ver;
+    target_ulong priv_ver;
+    target_ulong misa;
+
+    uint32_t features;
+
+#ifndef CONFIG_USER_ONLY
+    target_ulong priv;
+    target_ulong resetvec;
+
+    target_ulong mhartid;
+    target_ulong mstatus;
+    /*
+     * CAUTION! Unlike the rest of this struct, mip is accessed asynchonously
+     * by I/O threads and other vCPUs, so hold the iothread mutex before
+     * operating on it.  CPU_INTERRUPT_HARD should be in effect iff this is
+     * non-zero.  Use riscv_cpu_set_local_interrupt.
+     */
+    uint32_t mip;        /* allow atomic_read for >= 32-bit hosts */
+    target_ulong mie;
+    target_ulong mideleg;
+
+    target_ulong sptbr;  /* until: priv-1.9.1 */
+    target_ulong satp;   /* since: priv-1.10.0 */
+    target_ulong sbadaddr;
+    target_ulong mbadaddr;
+    target_ulong medeleg;
+
+    target_ulong stvec;
+    target_ulong sepc;
+    target_ulong scause;
+
+    target_ulong mtvec;
+    target_ulong mepc;
+    target_ulong mcause;
+    target_ulong mtval;  /* since: priv-1.10.0 */
+
+    uint32_t mucounteren;
+    uint32_t mscounteren;
+    target_ulong scounteren; /* since: priv-1.10.0 */
+    target_ulong mcounteren; /* since: priv-1.10.0 */
+
+    target_ulong sscratch;
+    target_ulong mscratch;
+
+    /* temporary htif regs */
+    uint64_t mfromhost;
+    uint64_t mtohost;
+    uint64_t timecmp;
+
+    /* physical memory protection */
+    pmp_table_t pmp_state;
+#endif
+
+    float_status fp_status;
+
+    /* QEMU */
+    CPU_COMMON
+
+    /* Fields from here on are preserved across CPU reset. */
+    QEMUTimer *timer; /* Internal timer */
+};
+
+#define RISCV_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RISCVCPUClass, (klass), TYPE_RISCV_CPU)
+#define RISCV_CPU(obj) \
+    OBJECT_CHECK(RISCVCPU, (obj), TYPE_RISCV_CPU)
+#define RISCV_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(RISCVCPUClass, (obj), TYPE_RISCV_CPU)
+
+/**
+ * RISCVCPUClass:
+ * @parent_realize: The parent class' realize handler.
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A RISCV CPU model.
+ */
+typedef struct RISCVCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+} RISCVCPUClass;
+
+/**
+ * RISCVCPU:
+ * @env: #CPURISCVState
+ *
+ * A RISCV CPU.
+ */
+typedef struct RISCVCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+    CPURISCVState env;
+} RISCVCPU;
+
+static inline RISCVCPU *riscv_env_get_cpu(CPURISCVState *env)
+{
+    return container_of(env, RISCVCPU, env);
+}
+
+static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext)
+{
+    return (env->misa & ext) != 0;
+}
+
+static inline bool riscv_feature(CPURISCVState *env, int feature)
+{
+    return env->features & (1ULL << feature);
+}
+
+#include "cpu_user.h"
+#include "cpu_bits.h"
+
+extern const char * const riscv_int_regnames[];
+extern const char * const riscv_fpr_regnames[];
+extern const char * const riscv_excp_names[];
+extern const char * const riscv_intr_names[];
+
+#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
+#define ENV_OFFSET offsetof(RISCVCPU, env)
+
+void riscv_cpu_do_interrupt(CPUState *cpu);
+int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
+int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
+int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+                                    MMUAccessType access_type, int mmu_idx,
+                                    uintptr_t retaddr);
+int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
+                              int rw, int mmu_idx);
+
+char *riscv_isa_string(RISCVCPU *cpu);
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+
+#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model)
+#define cpu_signal_handler cpu_riscv_signal_handler
+#define cpu_list riscv_cpu_list
+#define cpu_mmu_index riscv_cpu_mmu_index
+
+void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
+
+void riscv_translate_init(void);
+RISCVCPU *cpu_riscv_init(const char *cpu_model);
+int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc);
+void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
+                                          uint32_t exception, uintptr_t pc);
+
+target_ulong cpu_riscv_get_fflags(CPURISCVState *env);
+void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong);
+
+#define TB_FLAGS_MMU_MASK  3
+#define TB_FLAGS_FP_ENABLE MSTATUS_FS
+
+static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->pc;
+    *cs_base = 0;
+#ifdef CONFIG_USER_ONLY
+    *flags = TB_FLAGS_FP_ENABLE;
+#else
+    *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
+#endif
+}
+
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+        target_ulong csrno);
+target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
+
+#ifndef CONFIG_USER_ONLY
+void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value);
+#endif
+
+#include "exec/cpu-all.h"
+
+#endif /* RISCV_CPU_H */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
new file mode 100644
index 0000000..64aa097
--- /dev/null
+++ b/target/riscv/cpu_bits.h
@@ -0,0 +1,411 @@
+/* RISC-V ISA constants */
+
+#define get_field(reg, mask) (((reg) & \
+                 (target_ulong)(mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \
+                 (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \
+                 (target_ulong)(mask)))
+
+#define PGSHIFT 12
+
+#define FSR_RD_SHIFT 5
+#define FSR_RD   (0x7 << FSR_RD_SHIFT)
+
+#define FPEXC_NX 0x01
+#define FPEXC_UF 0x02
+#define FPEXC_OF 0x04
+#define FPEXC_DZ 0x08
+#define FPEXC_NV 0x10
+
+#define FSR_AEXC_SHIFT 0
+#define FSR_NVA  (FPEXC_NV << FSR_AEXC_SHIFT)
+#define FSR_OFA  (FPEXC_OF << FSR_AEXC_SHIFT)
+#define FSR_UFA  (FPEXC_UF << FSR_AEXC_SHIFT)
+#define FSR_DZA  (FPEXC_DZ << FSR_AEXC_SHIFT)
+#define FSR_NXA  (FPEXC_NX << FSR_AEXC_SHIFT)
+#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
+
+/* CSR numbers */
+#define CSR_FFLAGS 0x1
+#define CSR_FRM 0x2
+#define CSR_FCSR 0x3
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_HPMCOUNTER3 0xc03
+#define CSR_HPMCOUNTER4 0xc04
+#define CSR_HPMCOUNTER5 0xc05
+#define CSR_HPMCOUNTER6 0xc06
+#define CSR_HPMCOUNTER7 0xc07
+#define CSR_HPMCOUNTER8 0xc08
+#define CSR_HPMCOUNTER9 0xc09
+#define CSR_HPMCOUNTER10 0xc0a
+#define CSR_HPMCOUNTER11 0xc0b
+#define CSR_HPMCOUNTER12 0xc0c
+#define CSR_HPMCOUNTER13 0xc0d
+#define CSR_HPMCOUNTER14 0xc0e
+#define CSR_HPMCOUNTER15 0xc0f
+#define CSR_HPMCOUNTER16 0xc10
+#define CSR_HPMCOUNTER17 0xc11
+#define CSR_HPMCOUNTER18 0xc12
+#define CSR_HPMCOUNTER19 0xc13
+#define CSR_HPMCOUNTER20 0xc14
+#define CSR_HPMCOUNTER21 0xc15
+#define CSR_HPMCOUNTER22 0xc16
+#define CSR_HPMCOUNTER23 0xc17
+#define CSR_HPMCOUNTER24 0xc18
+#define CSR_HPMCOUNTER25 0xc19
+#define CSR_HPMCOUNTER26 0xc1a
+#define CSR_HPMCOUNTER27 0xc1b
+#define CSR_HPMCOUNTER28 0xc1c
+#define CSR_HPMCOUNTER29 0xc1d
+#define CSR_HPMCOUNTER30 0xc1e
+#define CSR_HPMCOUNTER31 0xc1f
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SCOUNTEREN 0x106
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_SBADADDR 0x143
+#define CSR_SIP 0x144
+#define CSR_SPTBR 0x180
+#define CSR_SATP 0x180
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MEDELEG 0x302
+#define CSR_MIDELEG 0x303
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#define CSR_MCOUNTEREN 0x306
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MBADADDR 0x343
+#define CSR_MIP 0x344
+#define CSR_PMPCFG0 0x3a0
+#define CSR_PMPCFG1 0x3a1
+#define CSR_PMPCFG2 0x3a2
+#define CSR_PMPCFG3 0x3a3
+#define CSR_PMPADDR0 0x3b0
+#define CSR_PMPADDR1 0x3b1
+#define CSR_PMPADDR2 0x3b2
+#define CSR_PMPADDR3 0x3b3
+#define CSR_PMPADDR4 0x3b4
+#define CSR_PMPADDR5 0x3b5
+#define CSR_PMPADDR6 0x3b6
+#define CSR_PMPADDR7 0x3b7
+#define CSR_PMPADDR8 0x3b8
+#define CSR_PMPADDR9 0x3b9
+#define CSR_PMPADDR10 0x3ba
+#define CSR_PMPADDR11 0x3bb
+#define CSR_PMPADDR12 0x3bc
+#define CSR_PMPADDR13 0x3bd
+#define CSR_PMPADDR14 0x3be
+#define CSR_PMPADDR15 0x3bf
+#define CSR_TSELECT 0x7a0
+#define CSR_TDATA1 0x7a1
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA3 0x7a3
+#define CSR_DCSR 0x7b0
+#define CSR_DPC 0x7b1
+#define CSR_DSCRATCH 0x7b2
+#define CSR_MCYCLE 0xb00
+#define CSR_MINSTRET 0xb02
+#define CSR_MHPMCOUNTER3 0xb03
+#define CSR_MHPMCOUNTER4 0xb04
+#define CSR_MHPMCOUNTER5 0xb05
+#define CSR_MHPMCOUNTER6 0xb06
+#define CSR_MHPMCOUNTER7 0xb07
+#define CSR_MHPMCOUNTER8 0xb08
+#define CSR_MHPMCOUNTER9 0xb09
+#define CSR_MHPMCOUNTER10 0xb0a
+#define CSR_MHPMCOUNTER11 0xb0b
+#define CSR_MHPMCOUNTER12 0xb0c
+#define CSR_MHPMCOUNTER13 0xb0d
+#define CSR_MHPMCOUNTER14 0xb0e
+#define CSR_MHPMCOUNTER15 0xb0f
+#define CSR_MHPMCOUNTER16 0xb10
+#define CSR_MHPMCOUNTER17 0xb11
+#define CSR_MHPMCOUNTER18 0xb12
+#define CSR_MHPMCOUNTER19 0xb13
+#define CSR_MHPMCOUNTER20 0xb14
+#define CSR_MHPMCOUNTER21 0xb15
+#define CSR_MHPMCOUNTER22 0xb16
+#define CSR_MHPMCOUNTER23 0xb17
+#define CSR_MHPMCOUNTER24 0xb18
+#define CSR_MHPMCOUNTER25 0xb19
+#define CSR_MHPMCOUNTER26 0xb1a
+#define CSR_MHPMCOUNTER27 0xb1b
+#define CSR_MHPMCOUNTER28 0xb1c
+#define CSR_MHPMCOUNTER29 0xb1d
+#define CSR_MHPMCOUNTER30 0xb1e
+#define CSR_MHPMCOUNTER31 0xb1f
+#define CSR_MUCOUNTEREN 0x320
+#define CSR_MSCOUNTEREN 0x321
+#define CSR_MHPMEVENT3 0x323
+#define CSR_MHPMEVENT4 0x324
+#define CSR_MHPMEVENT5 0x325
+#define CSR_MHPMEVENT6 0x326
+#define CSR_MHPMEVENT7 0x327
+#define CSR_MHPMEVENT8 0x328
+#define CSR_MHPMEVENT9 0x329
+#define CSR_MHPMEVENT10 0x32a
+#define CSR_MHPMEVENT11 0x32b
+#define CSR_MHPMEVENT12 0x32c
+#define CSR_MHPMEVENT13 0x32d
+#define CSR_MHPMEVENT14 0x32e
+#define CSR_MHPMEVENT15 0x32f
+#define CSR_MHPMEVENT16 0x330
+#define CSR_MHPMEVENT17 0x331
+#define CSR_MHPMEVENT18 0x332
+#define CSR_MHPMEVENT19 0x333
+#define CSR_MHPMEVENT20 0x334
+#define CSR_MHPMEVENT21 0x335
+#define CSR_MHPMEVENT22 0x336
+#define CSR_MHPMEVENT23 0x337
+#define CSR_MHPMEVENT24 0x338
+#define CSR_MHPMEVENT25 0x339
+#define CSR_MHPMEVENT26 0x33a
+#define CSR_MHPMEVENT27 0x33b
+#define CSR_MHPMEVENT28 0x33c
+#define CSR_MHPMEVENT29 0x33d
+#define CSR_MHPMEVENT30 0x33e
+#define CSR_MHPMEVENT31 0x33f
+#define CSR_MVENDORID 0xf11
+#define CSR_MARCHID 0xf12
+#define CSR_MIMPID 0xf13
+#define CSR_MHARTID 0xf14
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_HPMCOUNTER3H 0xc83
+#define CSR_HPMCOUNTER4H 0xc84
+#define CSR_HPMCOUNTER5H 0xc85
+#define CSR_HPMCOUNTER6H 0xc86
+#define CSR_HPMCOUNTER7H 0xc87
+#define CSR_HPMCOUNTER8H 0xc88
+#define CSR_HPMCOUNTER9H 0xc89
+#define CSR_HPMCOUNTER10H 0xc8a
+#define CSR_HPMCOUNTER11H 0xc8b
+#define CSR_HPMCOUNTER12H 0xc8c
+#define CSR_HPMCOUNTER13H 0xc8d
+#define CSR_HPMCOUNTER14H 0xc8e
+#define CSR_HPMCOUNTER15H 0xc8f
+#define CSR_HPMCOUNTER16H 0xc90
+#define CSR_HPMCOUNTER17H 0xc91
+#define CSR_HPMCOUNTER18H 0xc92
+#define CSR_HPMCOUNTER19H 0xc93
+#define CSR_HPMCOUNTER20H 0xc94
+#define CSR_HPMCOUNTER21H 0xc95
+#define CSR_HPMCOUNTER22H 0xc96
+#define CSR_HPMCOUNTER23H 0xc97
+#define CSR_HPMCOUNTER24H 0xc98
+#define CSR_HPMCOUNTER25H 0xc99
+#define CSR_HPMCOUNTER26H 0xc9a
+#define CSR_HPMCOUNTER27H 0xc9b
+#define CSR_HPMCOUNTER28H 0xc9c
+#define CSR_HPMCOUNTER29H 0xc9d
+#define CSR_HPMCOUNTER30H 0xc9e
+#define CSR_HPMCOUNTER31H 0xc9f
+#define CSR_MCYCLEH 0xb80
+#define CSR_MINSTRETH 0xb82
+#define CSR_MHPMCOUNTER3H 0xb83
+#define CSR_MHPMCOUNTER4H 0xb84
+#define CSR_MHPMCOUNTER5H 0xb85
+#define CSR_MHPMCOUNTER6H 0xb86
+#define CSR_MHPMCOUNTER7H 0xb87
+#define CSR_MHPMCOUNTER8H 0xb88
+#define CSR_MHPMCOUNTER9H 0xb89
+#define CSR_MHPMCOUNTER10H 0xb8a
+#define CSR_MHPMCOUNTER11H 0xb8b
+#define CSR_MHPMCOUNTER12H 0xb8c
+#define CSR_MHPMCOUNTER13H 0xb8d
+#define CSR_MHPMCOUNTER14H 0xb8e
+#define CSR_MHPMCOUNTER15H 0xb8f
+#define CSR_MHPMCOUNTER16H 0xb90
+#define CSR_MHPMCOUNTER17H 0xb91
+#define CSR_MHPMCOUNTER18H 0xb92
+#define CSR_MHPMCOUNTER19H 0xb93
+#define CSR_MHPMCOUNTER20H 0xb94
+#define CSR_MHPMCOUNTER21H 0xb95
+#define CSR_MHPMCOUNTER22H 0xb96
+#define CSR_MHPMCOUNTER23H 0xb97
+#define CSR_MHPMCOUNTER24H 0xb98
+#define CSR_MHPMCOUNTER25H 0xb99
+#define CSR_MHPMCOUNTER26H 0xb9a
+#define CSR_MHPMCOUNTER27H 0xb9b
+#define CSR_MHPMCOUNTER28H 0xb9c
+#define CSR_MHPMCOUNTER29H 0xb9d
+#define CSR_MHPMCOUNTER30H 0xb9e
+#define CSR_MHPMCOUNTER31H 0xb9f
+
+/* mstatus bits */
+#define MSTATUS_UIE         0x00000001
+#define MSTATUS_SIE         0x00000002
+#define MSTATUS_HIE         0x00000004
+#define MSTATUS_MIE         0x00000008
+#define MSTATUS_UPIE        0x00000010
+#define MSTATUS_SPIE        0x00000020
+#define MSTATUS_HPIE        0x00000040
+#define MSTATUS_MPIE        0x00000080
+#define MSTATUS_SPP         0x00000100
+#define MSTATUS_HPP         0x00000600
+#define MSTATUS_MPP         0x00001800
+#define MSTATUS_FS          0x00006000
+#define MSTATUS_XS          0x00018000
+#define MSTATUS_MPRV        0x00020000
+#define MSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
+#define MSTATUS_SUM         0x00040000 /* since: priv-1.10 */
+#define MSTATUS_MXR         0x00080000
+#define MSTATUS_VM          0x1F000000 /* until: priv-1.9.1 */
+#define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
+#define MSTATUS_TW          0x20000000 /* since: priv-1.10 */
+#define MSTATUS_TSR         0x40000000 /* since: priv-1.10 */
+
+#define MSTATUS64_UXL       0x0000000300000000ULL
+#define MSTATUS64_SXL       0x0000000C00000000ULL
+
+#define MSTATUS32_SD        0x80000000
+#define MSTATUS64_SD        0x8000000000000000ULL
+
+#if defined(TARGET_RISCV32)
+#define MSTATUS_SD MSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define MSTATUS_SD MSTATUS64_SD
+#endif
+
+/* sstatus bits */
+#define SSTATUS_UIE         0x00000001
+#define SSTATUS_SIE         0x00000002
+#define SSTATUS_UPIE        0x00000010
+#define SSTATUS_SPIE        0x00000020
+#define SSTATUS_SPP         0x00000100
+#define SSTATUS_FS          0x00006000
+#define SSTATUS_XS          0x00018000
+#define SSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
+#define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
+#define SSTATUS_MXR         0x00080000
+
+#define SSTATUS32_SD        0x80000000
+#define SSTATUS64_SD        0x8000000000000000ULL
+
+#if defined(TARGET_RISCV32)
+#define SSTATUS_SD SSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define SSTATUS_SD SSTATUS64_SD
+#endif
+
+/* irqs */
+#define MIP_SSIP            (1 << IRQ_S_SOFT)
+#define MIP_HSIP            (1 << IRQ_H_SOFT)
+#define MIP_MSIP            (1 << IRQ_M_SOFT)
+#define MIP_STIP            (1 << IRQ_S_TIMER)
+#define MIP_HTIP            (1 << IRQ_H_TIMER)
+#define MIP_MTIP            (1 << IRQ_M_TIMER)
+#define MIP_SEIP            (1 << IRQ_S_EXT)
+#define MIP_HEIP            (1 << IRQ_H_EXT)
+#define MIP_MEIP            (1 << IRQ_M_EXT)
+
+#define SIP_SSIP            MIP_SSIP
+#define SIP_STIP            MIP_STIP
+#define SIP_SEIP            MIP_SEIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+/* privileged ISA 1.9.1 VM modes (mstatus.vm) */
+#define VM_1_09_MBARE 0
+#define VM_1_09_MBB   1
+#define VM_1_09_MBBID 2
+#define VM_1_09_SV32  8
+#define VM_1_09_SV39  9
+#define VM_1_09_SV48  10
+
+/* privileged ISA 1.10.0 VM modes (satp.mode) */
+#define VM_1_10_MBARE 0
+#define VM_1_10_SV32  1
+#define VM_1_10_SV39  8
+#define VM_1_10_SV48  9
+#define VM_1_10_SV57  10
+#define VM_1_10_SV64  11
+
+/* privileged ISA interrupt causes */
+#define IRQ_U_SOFT      0  /* since: priv-1.10 */
+#define IRQ_S_SOFT      1
+#define IRQ_H_SOFT      2  /* until: priv-1.9.1 */
+#define IRQ_M_SOFT      3  /* until: priv-1.9.1 */
+#define IRQ_U_TIMER     4  /* since: priv-1.10 */
+#define IRQ_S_TIMER     5
+#define IRQ_H_TIMER     6  /* until: priv-1.9.1 */
+#define IRQ_M_TIMER     7  /* until: priv-1.9.1 */
+#define IRQ_U_EXT       8  /* since: priv-1.10 */
+#define IRQ_S_EXT       9
+#define IRQ_H_EXT       10 /* until: priv-1.9.1 */
+#define IRQ_M_EXT       11 /* until: priv-1.9.1 */
+#define IRQ_X_COP       12 /* non-standard */
+
+/* Default addresses */
+#define DEFAULT_RSTVEC     0x00001000
+
+/* RV32 satp field masks */
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7fc00000
+#define SATP32_PPN  0x003fffff
+
+/* RV64 satp field masks */
+#define SATP64_MODE 0xF000000000000000ULL
+#define SATP64_ASID 0x0FFFF00000000000ULL
+#define SATP64_PPN  0x00000FFFFFFFFFFFULL
+
+#if defined(TARGET_RISCV32)
+#define SATP_MODE SATP32_MODE
+#define SATP_ASID SATP32_ASID
+#define SATP_PPN  SATP32_PPN
+#endif
+#if defined(TARGET_RISCV64)
+#define SATP_MODE SATP64_MODE
+#define SATP_ASID SATP64_ASID
+#define SATP_PPN  SATP64_PPN
+#endif
+
+/* RISCV Exception Codes */
+#define EXCP_NONE                       -1 /* not a real RISCV exception code */
+#define RISCV_EXCP_INST_ADDR_MIS           0x0
+#define RISCV_EXCP_INST_ACCESS_FAULT       0x1
+#define RISCV_EXCP_ILLEGAL_INST            0x2
+#define RISCV_EXCP_BREAKPOINT              0x3
+#define RISCV_EXCP_LOAD_ADDR_MIS           0x4
+#define RISCV_EXCP_LOAD_ACCESS_FAULT       0x5
+#define RISCV_EXCP_STORE_AMO_ADDR_MIS      0x6
+#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT  0x7
+#define RISCV_EXCP_U_ECALL                 0x8 /* for convenience, report all
+                                                  ECALLs as this, handler
+                                                  fixes */
+#define RISCV_EXCP_S_ECALL                 0x9
+#define RISCV_EXCP_H_ECALL                 0xa
+#define RISCV_EXCP_M_ECALL                 0xb
+#define RISCV_EXCP_INST_PAGE_FAULT         0xc /* since: priv-1.10.0 */
+#define RISCV_EXCP_LOAD_PAGE_FAULT         0xd /* since: priv-1.10.0 */
+#define RISCV_EXCP_STORE_PAGE_FAULT        0xf /* since: priv-1.10.0 */
+
+#define RISCV_EXCP_INT_FLAG                0x80000000
+#define RISCV_EXCP_INT_MASK                0x7fffffff
+
+/* page table entry (PTE) fields */
+#define PTE_V     0x001 /* Valid */
+#define PTE_R     0x002 /* Read */
+#define PTE_W     0x004 /* Write */
+#define PTE_X     0x008 /* Execute */
+#define PTE_U     0x010 /* User */
+#define PTE_G     0x020 /* Global */
+#define PTE_A     0x040 /* Accessed */
+#define PTE_D     0x080 /* Dirty */
+#define PTE_SOFT  0x300 /* Reserved for Software */
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (2 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-04-27 12:26   ` Peter Maydell
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 05/23] RISC-V CPU Helpers Michael Clark
                   ` (20 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

The RISC-V disassembler has no dependencies outside of the 'disas'
directory so it can be applied independently. The majority of the
disassembler is machine-generated from instruction set metadata:

- https://github.com/michaeljclark/riscv-meta

Expected checkpatch errors for consistency and brevity reasons:

ERROR: line over 90 characters
ERROR: trailing statements should be on next line
ERROR: space prohibited between function name and open parenthesis '('

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 disas.c             |    2 +
 disas/Makefile.objs |    1 +
 disas/riscv.c       | 3048 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/disas/bfd.h |    2 +
 4 files changed, 3053 insertions(+)
 create mode 100644 disas/riscv.c

diff --git a/disas.c b/disas.c
index d4ad108..5325b7e 100644
--- a/disas.c
+++ b/disas.c
@@ -522,6 +522,8 @@ void disas(FILE *out, void *code, unsigned long size)
 # ifdef _ARCH_PPC64
     s.info.cap_mode = CS_MODE_64;
 # endif
+#elif defined(__riscv__)
+    print_insn = print_insn_riscv;
 #elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
     print_insn = print_insn_arm_a64;
     s.info.cap_arch = CS_ARCH_ARM64;
diff --git a/disas/Makefile.objs b/disas/Makefile.objs
index 53556f8..213be2f 100644
--- a/disas/Makefile.objs
+++ b/disas/Makefile.objs
@@ -17,6 +17,7 @@ common-obj-$(CONFIG_MIPS_DIS) += mips.o
 common-obj-$(CONFIG_NIOS2_DIS) += nios2.o
 common-obj-$(CONFIG_MOXIE_DIS) += moxie.o
 common-obj-$(CONFIG_PPC_DIS) += ppc.o
+common-obj-$(CONFIG_RISCV_DIS) += riscv.o
 common-obj-$(CONFIG_S390_DIS) += s390.o
 common-obj-$(CONFIG_SH4_DIS) += sh4.o
 common-obj-$(CONFIG_SPARC_DIS) += sparc.o
diff --git a/disas/riscv.c b/disas/riscv.c
new file mode 100644
index 0000000..3c17501
--- /dev/null
+++ b/disas/riscv.c
@@ -0,0 +1,3048 @@
+/*
+ * QEMU RISC-V Disassembler
+ *
+ * Copyright (c) 2016-2017 Michael Clark <michaeljclark@mac.com>
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "disas/bfd.h"
+
+
+/* types */
+
+typedef uint64_t rv_inst;
+typedef uint16_t rv_opcode;
+
+/* enums */
+
+typedef enum {
+    rv32,
+    rv64,
+    rv128
+} rv_isa;
+
+typedef enum {
+    rv_rm_rne = 0,
+    rv_rm_rtz = 1,
+    rv_rm_rdn = 2,
+    rv_rm_rup = 3,
+    rv_rm_rmm = 4,
+    rv_rm_dyn = 7,
+} rv_rm;
+
+typedef enum {
+    rv_fence_i = 8,
+    rv_fence_o = 4,
+    rv_fence_r = 2,
+    rv_fence_w = 1,
+} rv_fence;
+
+typedef enum {
+    rv_ireg_zero,
+    rv_ireg_ra,
+    rv_ireg_sp,
+    rv_ireg_gp,
+    rv_ireg_tp,
+    rv_ireg_t0,
+    rv_ireg_t1,
+    rv_ireg_t2,
+    rv_ireg_s0,
+    rv_ireg_s1,
+    rv_ireg_a0,
+    rv_ireg_a1,
+    rv_ireg_a2,
+    rv_ireg_a3,
+    rv_ireg_a4,
+    rv_ireg_a5,
+    rv_ireg_a6,
+    rv_ireg_a7,
+    rv_ireg_s2,
+    rv_ireg_s3,
+    rv_ireg_s4,
+    rv_ireg_s5,
+    rv_ireg_s6,
+    rv_ireg_s7,
+    rv_ireg_s8,
+    rv_ireg_s9,
+    rv_ireg_s10,
+    rv_ireg_s11,
+    rv_ireg_t3,
+    rv_ireg_t4,
+    rv_ireg_t5,
+    rv_ireg_t6,
+} rv_ireg;
+
+typedef enum {
+    rvc_end,
+    rvc_simm_6,
+    rvc_imm_6,
+    rvc_imm_7,
+    rvc_imm_8,
+    rvc_imm_9,
+    rvc_imm_10,
+    rvc_imm_12,
+    rvc_imm_18,
+    rvc_imm_nz,
+    rvc_imm_x2,
+    rvc_imm_x4,
+    rvc_imm_x8,
+    rvc_imm_x16,
+    rvc_rd_b3,
+    rvc_rs1_b3,
+    rvc_rs2_b3,
+    rvc_rd_eq_rs1,
+    rvc_rd_eq_ra,
+    rvc_rd_eq_sp,
+    rvc_rd_eq_x0,
+    rvc_rs1_eq_sp,
+    rvc_rs1_eq_x0,
+    rvc_rs2_eq_x0,
+    rvc_rd_ne_x0_x2,
+    rvc_rd_ne_x0,
+    rvc_rs1_ne_x0,
+    rvc_rs2_ne_x0,
+    rvc_rs2_eq_rs1,
+    rvc_rs1_eq_ra,
+    rvc_imm_eq_zero,
+    rvc_imm_eq_n1,
+    rvc_imm_eq_p1,
+    rvc_csr_eq_0x001,
+    rvc_csr_eq_0x002,
+    rvc_csr_eq_0x003,
+    rvc_csr_eq_0xc00,
+    rvc_csr_eq_0xc01,
+    rvc_csr_eq_0xc02,
+    rvc_csr_eq_0xc80,
+    rvc_csr_eq_0xc81,
+    rvc_csr_eq_0xc82,
+} rvc_constraint;
+
+typedef enum {
+    rv_codec_illegal,
+    rv_codec_none,
+    rv_codec_u,
+    rv_codec_uj,
+    rv_codec_i,
+    rv_codec_i_sh5,
+    rv_codec_i_sh6,
+    rv_codec_i_sh7,
+    rv_codec_i_csr,
+    rv_codec_s,
+    rv_codec_sb,
+    rv_codec_r,
+    rv_codec_r_m,
+    rv_codec_r4_m,
+    rv_codec_r_a,
+    rv_codec_r_l,
+    rv_codec_r_f,
+    rv_codec_cb,
+    rv_codec_cb_imm,
+    rv_codec_cb_sh5,
+    rv_codec_cb_sh6,
+    rv_codec_ci,
+    rv_codec_ci_sh5,
+    rv_codec_ci_sh6,
+    rv_codec_ci_16sp,
+    rv_codec_ci_lwsp,
+    rv_codec_ci_ldsp,
+    rv_codec_ci_lqsp,
+    rv_codec_ci_li,
+    rv_codec_ci_lui,
+    rv_codec_ci_none,
+    rv_codec_ciw_4spn,
+    rv_codec_cj,
+    rv_codec_cj_jal,
+    rv_codec_cl_lw,
+    rv_codec_cl_ld,
+    rv_codec_cl_lq,
+    rv_codec_cr,
+    rv_codec_cr_mv,
+    rv_codec_cr_jalr,
+    rv_codec_cr_jr,
+    rv_codec_cs,
+    rv_codec_cs_sw,
+    rv_codec_cs_sd,
+    rv_codec_cs_sq,
+    rv_codec_css_swsp,
+    rv_codec_css_sdsp,
+    rv_codec_css_sqsp,
+} rv_codec;
+
+typedef enum {
+    rv_op_illegal = 0,
+    rv_op_lui = 1,
+    rv_op_auipc = 2,
+    rv_op_jal = 3,
+    rv_op_jalr = 4,
+    rv_op_beq = 5,
+    rv_op_bne = 6,
+    rv_op_blt = 7,
+    rv_op_bge = 8,
+    rv_op_bltu = 9,
+    rv_op_bgeu = 10,
+    rv_op_lb = 11,
+    rv_op_lh = 12,
+    rv_op_lw = 13,
+    rv_op_lbu = 14,
+    rv_op_lhu = 15,
+    rv_op_sb = 16,
+    rv_op_sh = 17,
+    rv_op_sw = 18,
+    rv_op_addi = 19,
+    rv_op_slti = 20,
+    rv_op_sltiu = 21,
+    rv_op_xori = 22,
+    rv_op_ori = 23,
+    rv_op_andi = 24,
+    rv_op_slli = 25,
+    rv_op_srli = 26,
+    rv_op_srai = 27,
+    rv_op_add = 28,
+    rv_op_sub = 29,
+    rv_op_sll = 30,
+    rv_op_slt = 31,
+    rv_op_sltu = 32,
+    rv_op_xor = 33,
+    rv_op_srl = 34,
+    rv_op_sra = 35,
+    rv_op_or = 36,
+    rv_op_and = 37,
+    rv_op_fence = 38,
+    rv_op_fence_i = 39,
+    rv_op_lwu = 40,
+    rv_op_ld = 41,
+    rv_op_sd = 42,
+    rv_op_addiw = 43,
+    rv_op_slliw = 44,
+    rv_op_srliw = 45,
+    rv_op_sraiw = 46,
+    rv_op_addw = 47,
+    rv_op_subw = 48,
+    rv_op_sllw = 49,
+    rv_op_srlw = 50,
+    rv_op_sraw = 51,
+    rv_op_ldu = 52,
+    rv_op_lq = 53,
+    rv_op_sq = 54,
+    rv_op_addid = 55,
+    rv_op_sllid = 56,
+    rv_op_srlid = 57,
+    rv_op_sraid = 58,
+    rv_op_addd = 59,
+    rv_op_subd = 60,
+    rv_op_slld = 61,
+    rv_op_srld = 62,
+    rv_op_srad = 63,
+    rv_op_mul = 64,
+    rv_op_mulh = 65,
+    rv_op_mulhsu = 66,
+    rv_op_mulhu = 67,
+    rv_op_div = 68,
+    rv_op_divu = 69,
+    rv_op_rem = 70,
+    rv_op_remu = 71,
+    rv_op_mulw = 72,
+    rv_op_divw = 73,
+    rv_op_divuw = 74,
+    rv_op_remw = 75,
+    rv_op_remuw = 76,
+    rv_op_muld = 77,
+    rv_op_divd = 78,
+    rv_op_divud = 79,
+    rv_op_remd = 80,
+    rv_op_remud = 81,
+    rv_op_lr_w = 82,
+    rv_op_sc_w = 83,
+    rv_op_amoswap_w = 84,
+    rv_op_amoadd_w = 85,
+    rv_op_amoxor_w = 86,
+    rv_op_amoor_w = 87,
+    rv_op_amoand_w = 88,
+    rv_op_amomin_w = 89,
+    rv_op_amomax_w = 90,
+    rv_op_amominu_w = 91,
+    rv_op_amomaxu_w = 92,
+    rv_op_lr_d = 93,
+    rv_op_sc_d = 94,
+    rv_op_amoswap_d = 95,
+    rv_op_amoadd_d = 96,
+    rv_op_amoxor_d = 97,
+    rv_op_amoor_d = 98,
+    rv_op_amoand_d = 99,
+    rv_op_amomin_d = 100,
+    rv_op_amomax_d = 101,
+    rv_op_amominu_d = 102,
+    rv_op_amomaxu_d = 103,
+    rv_op_lr_q = 104,
+    rv_op_sc_q = 105,
+    rv_op_amoswap_q = 106,
+    rv_op_amoadd_q = 107,
+    rv_op_amoxor_q = 108,
+    rv_op_amoor_q = 109,
+    rv_op_amoand_q = 110,
+    rv_op_amomin_q = 111,
+    rv_op_amomax_q = 112,
+    rv_op_amominu_q = 113,
+    rv_op_amomaxu_q = 114,
+    rv_op_ecall = 115,
+    rv_op_ebreak = 116,
+    rv_op_uret = 117,
+    rv_op_sret = 118,
+    rv_op_hret = 119,
+    rv_op_mret = 120,
+    rv_op_dret = 121,
+    rv_op_sfence_vm = 122,
+    rv_op_sfence_vma = 123,
+    rv_op_wfi = 124,
+    rv_op_csrrw = 125,
+    rv_op_csrrs = 126,
+    rv_op_csrrc = 127,
+    rv_op_csrrwi = 128,
+    rv_op_csrrsi = 129,
+    rv_op_csrrci = 130,
+    rv_op_flw = 131,
+    rv_op_fsw = 132,
+    rv_op_fmadd_s = 133,
+    rv_op_fmsub_s = 134,
+    rv_op_fnmsub_s = 135,
+    rv_op_fnmadd_s = 136,
+    rv_op_fadd_s = 137,
+    rv_op_fsub_s = 138,
+    rv_op_fmul_s = 139,
+    rv_op_fdiv_s = 140,
+    rv_op_fsgnj_s = 141,
+    rv_op_fsgnjn_s = 142,
+    rv_op_fsgnjx_s = 143,
+    rv_op_fmin_s = 144,
+    rv_op_fmax_s = 145,
+    rv_op_fsqrt_s = 146,
+    rv_op_fle_s = 147,
+    rv_op_flt_s = 148,
+    rv_op_feq_s = 149,
+    rv_op_fcvt_w_s = 150,
+    rv_op_fcvt_wu_s = 151,
+    rv_op_fcvt_s_w = 152,
+    rv_op_fcvt_s_wu = 153,
+    rv_op_fmv_x_s = 154,
+    rv_op_fclass_s = 155,
+    rv_op_fmv_s_x = 156,
+    rv_op_fcvt_l_s = 157,
+    rv_op_fcvt_lu_s = 158,
+    rv_op_fcvt_s_l = 159,
+    rv_op_fcvt_s_lu = 160,
+    rv_op_fld = 161,
+    rv_op_fsd = 162,
+    rv_op_fmadd_d = 163,
+    rv_op_fmsub_d = 164,
+    rv_op_fnmsub_d = 165,
+    rv_op_fnmadd_d = 166,
+    rv_op_fadd_d = 167,
+    rv_op_fsub_d = 168,
+    rv_op_fmul_d = 169,
+    rv_op_fdiv_d = 170,
+    rv_op_fsgnj_d = 171,
+    rv_op_fsgnjn_d = 172,
+    rv_op_fsgnjx_d = 173,
+    rv_op_fmin_d = 174,
+    rv_op_fmax_d = 175,
+    rv_op_fcvt_s_d = 176,
+    rv_op_fcvt_d_s = 177,
+    rv_op_fsqrt_d = 178,
+    rv_op_fle_d = 179,
+    rv_op_flt_d = 180,
+    rv_op_feq_d = 181,
+    rv_op_fcvt_w_d = 182,
+    rv_op_fcvt_wu_d = 183,
+    rv_op_fcvt_d_w = 184,
+    rv_op_fcvt_d_wu = 185,
+    rv_op_fclass_d = 186,
+    rv_op_fcvt_l_d = 187,
+    rv_op_fcvt_lu_d = 188,
+    rv_op_fmv_x_d = 189,
+    rv_op_fcvt_d_l = 190,
+    rv_op_fcvt_d_lu = 191,
+    rv_op_fmv_d_x = 192,
+    rv_op_flq = 193,
+    rv_op_fsq = 194,
+    rv_op_fmadd_q = 195,
+    rv_op_fmsub_q = 196,
+    rv_op_fnmsub_q = 197,
+    rv_op_fnmadd_q = 198,
+    rv_op_fadd_q = 199,
+    rv_op_fsub_q = 200,
+    rv_op_fmul_q = 201,
+    rv_op_fdiv_q = 202,
+    rv_op_fsgnj_q = 203,
+    rv_op_fsgnjn_q = 204,
+    rv_op_fsgnjx_q = 205,
+    rv_op_fmin_q = 206,
+    rv_op_fmax_q = 207,
+    rv_op_fcvt_s_q = 208,
+    rv_op_fcvt_q_s = 209,
+    rv_op_fcvt_d_q = 210,
+    rv_op_fcvt_q_d = 211,
+    rv_op_fsqrt_q = 212,
+    rv_op_fle_q = 213,
+    rv_op_flt_q = 214,
+    rv_op_feq_q = 215,
+    rv_op_fcvt_w_q = 216,
+    rv_op_fcvt_wu_q = 217,
+    rv_op_fcvt_q_w = 218,
+    rv_op_fcvt_q_wu = 219,
+    rv_op_fclass_q = 220,
+    rv_op_fcvt_l_q = 221,
+    rv_op_fcvt_lu_q = 222,
+    rv_op_fcvt_q_l = 223,
+    rv_op_fcvt_q_lu = 224,
+    rv_op_fmv_x_q = 225,
+    rv_op_fmv_q_x = 226,
+    rv_op_c_addi4spn = 227,
+    rv_op_c_fld = 228,
+    rv_op_c_lw = 229,
+    rv_op_c_flw = 230,
+    rv_op_c_fsd = 231,
+    rv_op_c_sw = 232,
+    rv_op_c_fsw = 233,
+    rv_op_c_nop = 234,
+    rv_op_c_addi = 235,
+    rv_op_c_jal = 236,
+    rv_op_c_li = 237,
+    rv_op_c_addi16sp = 238,
+    rv_op_c_lui = 239,
+    rv_op_c_srli = 240,
+    rv_op_c_srai = 241,
+    rv_op_c_andi = 242,
+    rv_op_c_sub = 243,
+    rv_op_c_xor = 244,
+    rv_op_c_or = 245,
+    rv_op_c_and = 246,
+    rv_op_c_subw = 247,
+    rv_op_c_addw = 248,
+    rv_op_c_j = 249,
+    rv_op_c_beqz = 250,
+    rv_op_c_bnez = 251,
+    rv_op_c_slli = 252,
+    rv_op_c_fldsp = 253,
+    rv_op_c_lwsp = 254,
+    rv_op_c_flwsp = 255,
+    rv_op_c_jr = 256,
+    rv_op_c_mv = 257,
+    rv_op_c_ebreak = 258,
+    rv_op_c_jalr = 259,
+    rv_op_c_add = 260,
+    rv_op_c_fsdsp = 261,
+    rv_op_c_swsp = 262,
+    rv_op_c_fswsp = 263,
+    rv_op_c_ld = 264,
+    rv_op_c_sd = 265,
+    rv_op_c_addiw = 266,
+    rv_op_c_ldsp = 267,
+    rv_op_c_sdsp = 268,
+    rv_op_c_lq = 269,
+    rv_op_c_sq = 270,
+    rv_op_c_lqsp = 271,
+    rv_op_c_sqsp = 272,
+    rv_op_nop = 273,
+    rv_op_mv = 274,
+    rv_op_not = 275,
+    rv_op_neg = 276,
+    rv_op_negw = 277,
+    rv_op_sext_w = 278,
+    rv_op_seqz = 279,
+    rv_op_snez = 280,
+    rv_op_sltz = 281,
+    rv_op_sgtz = 282,
+    rv_op_fmv_s = 283,
+    rv_op_fabs_s = 284,
+    rv_op_fneg_s = 285,
+    rv_op_fmv_d = 286,
+    rv_op_fabs_d = 287,
+    rv_op_fneg_d = 288,
+    rv_op_fmv_q = 289,
+    rv_op_fabs_q = 290,
+    rv_op_fneg_q = 291,
+    rv_op_beqz = 292,
+    rv_op_bnez = 293,
+    rv_op_blez = 294,
+    rv_op_bgez = 295,
+    rv_op_bltz = 296,
+    rv_op_bgtz = 297,
+    rv_op_ble = 298,
+    rv_op_bleu = 299,
+    rv_op_bgt = 300,
+    rv_op_bgtu = 301,
+    rv_op_j = 302,
+    rv_op_ret = 303,
+    rv_op_jr = 304,
+    rv_op_rdcycle = 305,
+    rv_op_rdtime = 306,
+    rv_op_rdinstret = 307,
+    rv_op_rdcycleh = 308,
+    rv_op_rdtimeh = 309,
+    rv_op_rdinstreth = 310,
+    rv_op_frcsr = 311,
+    rv_op_frrm = 312,
+    rv_op_frflags = 313,
+    rv_op_fscsr = 314,
+    rv_op_fsrm = 315,
+    rv_op_fsflags = 316,
+    rv_op_fsrmi = 317,
+    rv_op_fsflagsi = 318,
+} rv_op;
+
+/* structures */
+
+typedef struct {
+    uint64_t  pc;
+    uint64_t  inst;
+    int32_t   imm;
+    uint16_t  op;
+    uint8_t   codec;
+    uint8_t   rd;
+    uint8_t   rs1;
+    uint8_t   rs2;
+    uint8_t   rs3;
+    uint8_t   rm;
+    uint8_t   pred;
+    uint8_t   succ;
+    uint8_t   aq;
+    uint8_t   rl;
+} rv_decode;
+
+typedef struct {
+    const int op;
+    const rvc_constraint *constraints;
+} rv_comp_data;
+
+typedef struct {
+    const char * const name;
+    const rv_codec codec;
+    const char * const format;
+    const rv_comp_data *pseudo;
+    const int decomp_rv32;
+    const int decomp_rv64;
+    const int decomp_rv128;
+} rv_opcode_data;
+
+/* register names */
+
+static const char rv_ireg_name_sym[32][5] = {
+    "zero", "ra",   "sp",   "gp",   "tp",   "t0",   "t1",   "t2",
+    "s0",   "s1",   "a0",   "a1",   "a2",   "a3",   "a4",   "a5",
+    "a6",   "a7",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
+    "s8",   "s9",   "s10",  "s11",  "t3",   "t4",   "t5",   "t6",
+};
+
+static const char rv_freg_name_sym[32][5] = {
+    "ft0",  "ft1",  "ft2",  "ft3",  "ft4",  "ft5",  "ft6",  "ft7",
+    "fs0",  "fs1",  "fa0",  "fa1",  "fa2",  "fa3",  "fa4",  "fa5",
+    "fa6",  "fa7",  "fs2",  "fs3",  "fs4",  "fs5",  "fs6",  "fs7",
+    "fs8",  "fs9",  "fs10", "fs11", "ft8",  "ft9",  "ft10", "ft11",
+};
+
+/* instruction formats */
+
+#define rv_fmt_none                   "O\t"
+#define rv_fmt_rs1                    "O\t1"
+#define rv_fmt_offset                 "O\to"
+#define rv_fmt_pred_succ              "O\tp,s"
+#define rv_fmt_rs1_rs2                "O\t1,2"
+#define rv_fmt_rd_imm                 "O\t0,i"
+#define rv_fmt_rd_offset              "O\t0,o"
+#define rv_fmt_rd_rs1_rs2             "O\t0,1,2"
+#define rv_fmt_frd_rs1                "O\t3,1"
+#define rv_fmt_rd_frs1                "O\t0,4"
+#define rv_fmt_rd_frs1_frs2           "O\t0,4,5"
+#define rv_fmt_frd_frs1_frs2          "O\t3,4,5"
+#define rv_fmt_rm_frd_frs1            "O\tr,3,4"
+#define rv_fmt_rm_frd_rs1             "O\tr,3,1"
+#define rv_fmt_rm_rd_frs1             "O\tr,0,4"
+#define rv_fmt_rm_frd_frs1_frs2       "O\tr,3,4,5"
+#define rv_fmt_rm_frd_frs1_frs2_frs3  "O\tr,3,4,5,6"
+#define rv_fmt_rd_rs1_imm             "O\t0,1,i"
+#define rv_fmt_rd_rs1_offset          "O\t0,1,i"
+#define rv_fmt_rd_offset_rs1          "O\t0,i(1)"
+#define rv_fmt_frd_offset_rs1         "O\t3,i(1)"
+#define rv_fmt_rd_csr_rs1             "O\t0,c,1"
+#define rv_fmt_rd_csr_zimm            "O\t0,c,7"
+#define rv_fmt_rs2_offset_rs1         "O\t2,i(1)"
+#define rv_fmt_frs2_offset_rs1        "O\t5,i(1)"
+#define rv_fmt_rs1_rs2_offset         "O\t1,2,o"
+#define rv_fmt_rs2_rs1_offset         "O\t2,1,o"
+#define rv_fmt_aqrl_rd_rs2_rs1        "OAR\t0,2,(1)"
+#define rv_fmt_aqrl_rd_rs1            "OAR\t0,(1)"
+#define rv_fmt_rd                     "O\t0"
+#define rv_fmt_rd_zimm                "O\t0,7"
+#define rv_fmt_rd_rs1                 "O\t0,1"
+#define rv_fmt_rd_rs2                 "O\t0,2"
+#define rv_fmt_rs1_offset             "O\t1,o"
+#define rv_fmt_rs2_offset             "O\t2,o"
+
+/* pseudo-instruction constraints */
+
+static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end };
+static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end };
+static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_negw[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sext_w[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_seqz[] = { rvc_imm_eq_p1, rvc_end };
+static const rvc_constraint rvcc_snez[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sltz[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sgtz[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_fmv_s[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fabs_s[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fneg_s[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fmv_d[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fabs_d[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fneg_d[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fmv_q[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fabs_q[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fneg_q[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_beqz[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bnez[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_blez[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bgez[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bltz[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bgtz[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_ble[] = { rvc_end };
+static const rvc_constraint rvcc_bleu[] = { rvc_end };
+static const rvc_constraint rvcc_bgt[] = { rvc_end };
+static const rvc_constraint rvcc_bgtu[] = { rvc_end };
+static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end };
+static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end };
+static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, rvc_end };
+static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, rvc_end };
+static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end };
+static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };
+static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end };
+static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };
+static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end };
+static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end };
+static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end };
+static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end };
+static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end };
+static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end };
+static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end };
+static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end };
+
+/* pseudo-instruction metadata */
+
+static const rv_comp_data rvcp_jal[] = {
+    { rv_op_j, rvcc_j },
+    { rv_op_jal, rvcc_jal },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_jalr[] = {
+    { rv_op_ret, rvcc_ret },
+    { rv_op_jr, rvcc_jr },
+    { rv_op_jalr, rvcc_jalr },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_beq[] = {
+    { rv_op_beqz, rvcc_beqz },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bne[] = {
+    { rv_op_bnez, rvcc_bnez },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_blt[] = {
+    { rv_op_bltz, rvcc_bltz },
+    { rv_op_bgtz, rvcc_bgtz },
+    { rv_op_bgt, rvcc_bgt },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bge[] = {
+    { rv_op_blez, rvcc_blez },
+    { rv_op_bgez, rvcc_bgez },
+    { rv_op_ble, rvcc_ble },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bltu[] = {
+    { rv_op_bgtu, rvcc_bgtu },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bgeu[] = {
+    { rv_op_bleu, rvcc_bleu },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_addi[] = {
+    { rv_op_nop, rvcc_nop },
+    { rv_op_mv, rvcc_mv },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_sltiu[] = {
+    { rv_op_seqz, rvcc_seqz },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_xori[] = {
+    { rv_op_not, rvcc_not },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_sub[] = {
+    { rv_op_neg, rvcc_neg },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_slt[] = {
+    { rv_op_sltz, rvcc_sltz },
+    { rv_op_sgtz, rvcc_sgtz },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_sltu[] = {
+    { rv_op_snez, rvcc_snez },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_addiw[] = {
+    { rv_op_sext_w, rvcc_sext_w },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_subw[] = {
+    { rv_op_negw, rvcc_negw },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_csrrw[] = {
+    { rv_op_fscsr, rvcc_fscsr },
+    { rv_op_fsrm, rvcc_fsrm },
+    { rv_op_fsflags, rvcc_fsflags },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_csrrs[] = {
+    { rv_op_rdcycle, rvcc_rdcycle },
+    { rv_op_rdtime, rvcc_rdtime },
+    { rv_op_rdinstret, rvcc_rdinstret },
+    { rv_op_rdcycleh, rvcc_rdcycleh },
+    { rv_op_rdtimeh, rvcc_rdtimeh },
+    { rv_op_rdinstreth, rvcc_rdinstreth },
+    { rv_op_frcsr, rvcc_frcsr },
+    { rv_op_frrm, rvcc_frrm },
+    { rv_op_frflags, rvcc_frflags },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_csrrwi[] = {
+    { rv_op_fsrmi, rvcc_fsrmi },
+    { rv_op_fsflagsi, rvcc_fsflagsi },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnj_s[] = {
+    { rv_op_fmv_s, rvcc_fmv_s },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjn_s[] = {
+    { rv_op_fneg_s, rvcc_fneg_s },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjx_s[] = {
+    { rv_op_fabs_s, rvcc_fabs_s },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnj_d[] = {
+    { rv_op_fmv_d, rvcc_fmv_d },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjn_d[] = {
+    { rv_op_fneg_d, rvcc_fneg_d },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjx_d[] = {
+    { rv_op_fabs_d, rvcc_fabs_d },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnj_q[] = {
+    { rv_op_fmv_q, rvcc_fmv_q },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjn_q[] = {
+    { rv_op_fneg_q, rvcc_fneg_q },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjx_q[] = {
+    { rv_op_fabs_q, rvcc_fabs_q },
+    { rv_op_illegal, NULL }
+};
+
+/* instruction metadata */
+
+const rv_opcode_data opcode_data[] = {
+    { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
+    { "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 },
+    { "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 },
+    { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal, 0, 0, 0 },
+    { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr, 0, 0, 0 },
+    { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq, 0, 0, 0 },
+    { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne, 0, 0, 0 },
+    { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt, 0, 0, 0 },
+    { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge, 0, 0, 0 },
+    { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu, 0, 0, 0 },
+    { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu, 0, 0, 0 },
+    { "lb", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lh", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lw", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi, 0, 0, 0 },
+    { "slti", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu, 0, 0, 0 },
+    { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori, 0, 0, 0 },
+    { "ori", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "andi", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub, 0, 0, 0 },
+    { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt, 0, 0, 0 },
+    { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu, 0, 0, 0 },
+    { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "or", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "and", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "fence", rv_codec_r_f, rv_fmt_pred_succ, NULL, 0, 0, 0 },
+    { "fence.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "ld", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw, 0, 0, 0 },
+    { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw, 0, 0, 0 },
+    { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lq", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "addid", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "div", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 },
+    { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 },
+    { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 },
+    { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "ecall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "ebreak", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "uret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "sret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "hret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "mret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "dret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "sfence.vm", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+    { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 },
+    { "wfi", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw, 0, 0, 0 },
+    { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs, 0, 0, 0 },
+    { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1, NULL, 0, 0, 0 },
+    { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi, 0, 0, 0 },
+    { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 },
+    { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 },
+    { "flw", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+    { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+    { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s, 0, 0, 0 },
+    { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s, 0, 0, 0 },
+    { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s, 0, 0, 0 },
+    { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fclass.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fld", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+    { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+    { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d, 0, 0, 0 },
+    { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d, 0, 0, 0 },
+    { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d, 0, 0, 0 },
+    { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fclass.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+    { "flq", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+    { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+    { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q, 0, 0, 0 },
+    { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q, 0, 0, 0 },
+    { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q, 0, 0, 0 },
+    { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fclass.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+    { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 },
+    { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },
+    { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },
+    { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, 0 },
+    { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },
+    { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 },
+    { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 },
+    { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui },
+    { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli },
+    { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai },
+    { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi },
+    { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub },
+    { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor },
+    { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or },
+    { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, rv_op_and },
+    { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, rv_op_subw },
+    { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, rv_op_addw },
+    { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal },
+    { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq },
+    { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne },
+    { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli },
+    { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld },
+    { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },
+    { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },
+    { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr },
+    { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, rv_op_ebreak, rv_op_ebreak },
+    { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr },
+    { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, rv_op_add },
+    { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, rv_op_fsd },
+    { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },
+    { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 },
+    { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld },
+    { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd },
+    { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, rv_op_addiw },
+    { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld },
+    { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd },
+    { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq },
+    { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq },
+    { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq },
+    { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq },
+    { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 },
+    { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "neg", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "negw", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "sext.w", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "seqz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "snez", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "sltz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "sgtz", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "fmv.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fabs.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fneg.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fmv.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fabs.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fneg.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fmv.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fabs.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fneg.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "beqz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "bnez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "blez", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 },
+    { "bgez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "bltz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "bgtz", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 },
+    { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "j", rv_codec_uj, rv_fmt_offset, NULL, 0, 0, 0 },
+    { "ret", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 },
+    { "jr", rv_codec_i, rv_fmt_rs1, NULL, 0, 0, 0 },
+    { "rdcycle", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdtime", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdinstret", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdcycleh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdtimeh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdinstreth", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "frcsr", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "frrm", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "frflags", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 },
+    { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 },
+};
+
+/* CSR names */
+
+static const char *csr_name(int csrno)
+{
+    switch (csrno) {
+    case 0x0000: return "ustatus";
+    case 0x0001: return "fflags";
+    case 0x0002: return "frm";
+    case 0x0003: return "fcsr";
+    case 0x0004: return "uie";
+    case 0x0005: return "utvec";
+    case 0x0040: return "uscratch";
+    case 0x0041: return "uepc";
+    case 0x0042: return "ucause";
+    case 0x0043: return "utval";
+    case 0x0044: return "uip";
+    case 0x0100: return "sstatus";
+    case 0x0102: return "sedeleg";
+    case 0x0103: return "sideleg";
+    case 0x0104: return "sie";
+    case 0x0105: return "stvec";
+    case 0x0106: return "scounteren";
+    case 0x0140: return "sscratch";
+    case 0x0141: return "sepc";
+    case 0x0142: return "scause";
+    case 0x0143: return "stval";
+    case 0x0144: return "sip";
+    case 0x0180: return "satp";
+    case 0x0200: return "hstatus";
+    case 0x0202: return "hedeleg";
+    case 0x0203: return "hideleg";
+    case 0x0204: return "hie";
+    case 0x0205: return "htvec";
+    case 0x0240: return "hscratch";
+    case 0x0241: return "hepc";
+    case 0x0242: return "hcause";
+    case 0x0243: return "hbadaddr";
+    case 0x0244: return "hip";
+    case 0x0300: return "mstatus";
+    case 0x0301: return "misa";
+    case 0x0302: return "medeleg";
+    case 0x0303: return "mideleg";
+    case 0x0304: return "mie";
+    case 0x0305: return "mtvec";
+    case 0x0306: return "mcounteren";
+    case 0x0320: return "mucounteren";
+    case 0x0321: return "mscounteren";
+    case 0x0322: return "mhcounteren";
+    case 0x0323: return "mhpmevent3";
+    case 0x0324: return "mhpmevent4";
+    case 0x0325: return "mhpmevent5";
+    case 0x0326: return "mhpmevent6";
+    case 0x0327: return "mhpmevent7";
+    case 0x0328: return "mhpmevent8";
+    case 0x0329: return "mhpmevent9";
+    case 0x032a: return "mhpmevent10";
+    case 0x032b: return "mhpmevent11";
+    case 0x032c: return "mhpmevent12";
+    case 0x032d: return "mhpmevent13";
+    case 0x032e: return "mhpmevent14";
+    case 0x032f: return "mhpmevent15";
+    case 0x0330: return "mhpmevent16";
+    case 0x0331: return "mhpmevent17";
+    case 0x0332: return "mhpmevent18";
+    case 0x0333: return "mhpmevent19";
+    case 0x0334: return "mhpmevent20";
+    case 0x0335: return "mhpmevent21";
+    case 0x0336: return "mhpmevent22";
+    case 0x0337: return "mhpmevent23";
+    case 0x0338: return "mhpmevent24";
+    case 0x0339: return "mhpmevent25";
+    case 0x033a: return "mhpmevent26";
+    case 0x033b: return "mhpmevent27";
+    case 0x033c: return "mhpmevent28";
+    case 0x033d: return "mhpmevent29";
+    case 0x033e: return "mhpmevent30";
+    case 0x033f: return "mhpmevent31";
+    case 0x0340: return "mscratch";
+    case 0x0341: return "mepc";
+    case 0x0342: return "mcause";
+    case 0x0343: return "mtval";
+    case 0x0344: return "mip";
+    case 0x0380: return "mbase";
+    case 0x0381: return "mbound";
+    case 0x0382: return "mibase";
+    case 0x0383: return "mibound";
+    case 0x0384: return "mdbase";
+    case 0x0385: return "mdbound";
+    case 0x03a0: return "pmpcfg3";
+    case 0x03b0: return "pmpaddr0";
+    case 0x03b1: return "pmpaddr1";
+    case 0x03b2: return "pmpaddr2";
+    case 0x03b3: return "pmpaddr3";
+    case 0x03b4: return "pmpaddr4";
+    case 0x03b5: return "pmpaddr5";
+    case 0x03b6: return "pmpaddr6";
+    case 0x03b7: return "pmpaddr7";
+    case 0x03b8: return "pmpaddr8";
+    case 0x03b9: return "pmpaddr9";
+    case 0x03ba: return "pmpaddr10";
+    case 0x03bb: return "pmpaddr11";
+    case 0x03bc: return "pmpaddr12";
+    case 0x03bd: return "pmpaddr14";
+    case 0x03be: return "pmpaddr13";
+    case 0x03bf: return "pmpaddr15";
+    case 0x0780: return "mtohost";
+    case 0x0781: return "mfromhost";
+    case 0x0782: return "mreset";
+    case 0x0783: return "mipi";
+    case 0x0784: return "miobase";
+    case 0x07a0: return "tselect";
+    case 0x07a1: return "tdata1";
+    case 0x07a2: return "tdata2";
+    case 0x07a3: return "tdata3";
+    case 0x07b0: return "dcsr";
+    case 0x07b1: return "dpc";
+    case 0x07b2: return "dscratch";
+    case 0x0b00: return "mcycle";
+    case 0x0b01: return "mtime";
+    case 0x0b02: return "minstret";
+    case 0x0b03: return "mhpmcounter3";
+    case 0x0b04: return "mhpmcounter4";
+    case 0x0b05: return "mhpmcounter5";
+    case 0x0b06: return "mhpmcounter6";
+    case 0x0b07: return "mhpmcounter7";
+    case 0x0b08: return "mhpmcounter8";
+    case 0x0b09: return "mhpmcounter9";
+    case 0x0b0a: return "mhpmcounter10";
+    case 0x0b0b: return "mhpmcounter11";
+    case 0x0b0c: return "mhpmcounter12";
+    case 0x0b0d: return "mhpmcounter13";
+    case 0x0b0e: return "mhpmcounter14";
+    case 0x0b0f: return "mhpmcounter15";
+    case 0x0b10: return "mhpmcounter16";
+    case 0x0b11: return "mhpmcounter17";
+    case 0x0b12: return "mhpmcounter18";
+    case 0x0b13: return "mhpmcounter19";
+    case 0x0b14: return "mhpmcounter20";
+    case 0x0b15: return "mhpmcounter21";
+    case 0x0b16: return "mhpmcounter22";
+    case 0x0b17: return "mhpmcounter23";
+    case 0x0b18: return "mhpmcounter24";
+    case 0x0b19: return "mhpmcounter25";
+    case 0x0b1a: return "mhpmcounter26";
+    case 0x0b1b: return "mhpmcounter27";
+    case 0x0b1c: return "mhpmcounter28";
+    case 0x0b1d: return "mhpmcounter29";
+    case 0x0b1e: return "mhpmcounter30";
+    case 0x0b1f: return "mhpmcounter31";
+    case 0x0b80: return "mcycleh";
+    case 0x0b81: return "mtimeh";
+    case 0x0b82: return "minstreth";
+    case 0x0b83: return "mhpmcounter3h";
+    case 0x0b84: return "mhpmcounter4h";
+    case 0x0b85: return "mhpmcounter5h";
+    case 0x0b86: return "mhpmcounter6h";
+    case 0x0b87: return "mhpmcounter7h";
+    case 0x0b88: return "mhpmcounter8h";
+    case 0x0b89: return "mhpmcounter9h";
+    case 0x0b8a: return "mhpmcounter10h";
+    case 0x0b8b: return "mhpmcounter11h";
+    case 0x0b8c: return "mhpmcounter12h";
+    case 0x0b8d: return "mhpmcounter13h";
+    case 0x0b8e: return "mhpmcounter14h";
+    case 0x0b8f: return "mhpmcounter15h";
+    case 0x0b90: return "mhpmcounter16h";
+    case 0x0b91: return "mhpmcounter17h";
+    case 0x0b92: return "mhpmcounter18h";
+    case 0x0b93: return "mhpmcounter19h";
+    case 0x0b94: return "mhpmcounter20h";
+    case 0x0b95: return "mhpmcounter21h";
+    case 0x0b96: return "mhpmcounter22h";
+    case 0x0b97: return "mhpmcounter23h";
+    case 0x0b98: return "mhpmcounter24h";
+    case 0x0b99: return "mhpmcounter25h";
+    case 0x0b9a: return "mhpmcounter26h";
+    case 0x0b9b: return "mhpmcounter27h";
+    case 0x0b9c: return "mhpmcounter28h";
+    case 0x0b9d: return "mhpmcounter29h";
+    case 0x0b9e: return "mhpmcounter30h";
+    case 0x0b9f: return "mhpmcounter31h";
+    case 0x0c00: return "cycle";
+    case 0x0c01: return "time";
+    case 0x0c02: return "instret";
+    case 0x0c80: return "cycleh";
+    case 0x0c81: return "timeh";
+    case 0x0c82: return "instreth";
+    case 0x0d00: return "scycle";
+    case 0x0d01: return "stime";
+    case 0x0d02: return "sinstret";
+    case 0x0d80: return "scycleh";
+    case 0x0d81: return "stimeh";
+    case 0x0d82: return "sinstreth";
+    case 0x0e00: return "hcycle";
+    case 0x0e01: return "htime";
+    case 0x0e02: return "hinstret";
+    case 0x0e80: return "hcycleh";
+    case 0x0e81: return "htimeh";
+    case 0x0e82: return "hinstreth";
+    case 0x0f11: return "mvendorid";
+    case 0x0f12: return "marchid";
+    case 0x0f13: return "mimpid";
+    case 0x0f14: return "mhartid";
+    default: return NULL;
+    }
+}
+
+/* decode opcode */
+
+static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
+{
+    rv_inst inst = dec->inst;
+    rv_opcode op = rv_op_illegal;
+    switch (((inst >> 0) & 0b11)) {
+    case 0:
+        switch (((inst >> 13) & 0b111)) {
+        case 0: op = rv_op_c_addi4spn; break;
+        case 1:
+            if (isa == rv128) {
+                op = rv_op_c_lq;
+            } else {
+                op = rv_op_c_fld;
+            }
+            break;
+        case 2: op = rv_op_c_lw; break;
+        case 3:
+            if (isa == rv32) {
+                op = rv_op_c_flw;
+            } else {
+                op = rv_op_c_ld;
+            }
+            break;
+        case 5:
+            if (isa == rv128) {
+                op = rv_op_c_sq;
+            } else {
+                op = rv_op_c_fsd;
+            }
+            break;
+        case 6: op = rv_op_c_sw; break;
+        case 7:
+            if (isa == rv32) {
+                op = rv_op_c_fsw;
+            } else {
+                op = rv_op_c_sd;
+            }
+            break;
+        }
+        break;
+    case 1:
+        switch (((inst >> 13) & 0b111)) {
+        case 0:
+            switch (((inst >> 2) & 0b11111111111)) {
+            case 0: op = rv_op_c_nop; break;
+            default: op = rv_op_c_addi; break;
+            }
+            break;
+        case 1:
+            if (isa == rv32) {
+                op = rv_op_c_jal;
+            } else {
+                op = rv_op_c_addiw;
+            }
+            break;
+        case 2: op = rv_op_c_li; break;
+        case 3:
+            switch (((inst >> 7) & 0b11111)) {
+            case 2: op = rv_op_c_addi16sp; break;
+            default: op = rv_op_c_lui; break;
+            }
+            break;
+        case 4:
+            switch (((inst >> 10) & 0b11)) {
+            case 0:
+                op = rv_op_c_srli;
+                break;
+            case 1:
+                op = rv_op_c_srai;
+                break;
+            case 2: op = rv_op_c_andi; break;
+            case 3:
+                switch (((inst >> 10) & 0b100) | ((inst >> 5) & 0b011)) {
+                case 0: op = rv_op_c_sub; break;
+                case 1: op = rv_op_c_xor; break;
+                case 2: op = rv_op_c_or; break;
+                case 3: op = rv_op_c_and; break;
+                case 4: op = rv_op_c_subw; break;
+                case 5: op = rv_op_c_addw; break;
+                }
+                break;
+            }
+            break;
+        case 5: op = rv_op_c_j; break;
+        case 6: op = rv_op_c_beqz; break;
+        case 7: op = rv_op_c_bnez; break;
+        }
+        break;
+    case 2:
+        switch (((inst >> 13) & 0b111)) {
+        case 0:
+            op = rv_op_c_slli;
+            break;
+        case 1:
+            if (isa == rv128) {
+                op = rv_op_c_lqsp;
+            } else {
+                op = rv_op_c_fldsp;
+            }
+            break;
+        case 2: op = rv_op_c_lwsp; break;
+        case 3:
+            if (isa == rv32) {
+                op = rv_op_c_flwsp;
+            } else {
+                op = rv_op_c_ldsp;
+            }
+            break;
+        case 4:
+            switch (((inst >> 12) & 0b1)) {
+            case 0:
+                switch (((inst >> 2) & 0b11111)) {
+                case 0: op = rv_op_c_jr; break;
+                default: op = rv_op_c_mv; break;
+                }
+                break;
+            case 1:
+                switch (((inst >> 2) & 0b11111)) {
+                case 0:
+                    switch (((inst >> 7) & 0b11111)) {
+                    case 0: op = rv_op_c_ebreak; break;
+                    default: op = rv_op_c_jalr; break;
+                    }
+                    break;
+                default: op = rv_op_c_add; break;
+                }
+                break;
+            }
+            break;
+        case 5:
+            if (isa == rv128) {
+                op = rv_op_c_sqsp;
+            } else {
+                op = rv_op_c_fsdsp; break;
+            }
+        case 6: op = rv_op_c_swsp; break;
+        case 7:
+            if (isa == rv32) {
+                op = rv_op_c_fswsp;
+            } else {
+                op = rv_op_c_sdsp;
+            }
+            break;
+        }
+        break;
+    case 3:
+        switch (((inst >> 2) & 0b11111)) {
+        case 0:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_lb; break;
+            case 1: op = rv_op_lh; break;
+            case 2: op = rv_op_lw; break;
+            case 3: op = rv_op_ld; break;
+            case 4: op = rv_op_lbu; break;
+            case 5: op = rv_op_lhu; break;
+            case 6: op = rv_op_lwu; break;
+            case 7: op = rv_op_ldu; break;
+            }
+            break;
+        case 1:
+            switch (((inst >> 12) & 0b111)) {
+            case 2: op = rv_op_flw; break;
+            case 3: op = rv_op_fld; break;
+            case 4: op = rv_op_flq; break;
+            }
+            break;
+        case 3:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_fence; break;
+            case 1: op = rv_op_fence_i; break;
+            case 2: op = rv_op_lq; break;
+            }
+            break;
+        case 4:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_addi; break;
+            case 1:
+                switch (((inst >> 27) & 0b11111)) {
+                case 0: op = rv_op_slli; break;
+                }
+                break;
+            case 2: op = rv_op_slti; break;
+            case 3: op = rv_op_sltiu; break;
+            case 4: op = rv_op_xori; break;
+            case 5:
+                switch (((inst >> 27) & 0b11111)) {
+                case 0: op = rv_op_srli; break;
+                case 8: op = rv_op_srai; break;
+                }
+                break;
+            case 6: op = rv_op_ori; break;
+            case 7: op = rv_op_andi; break;
+            }
+            break;
+        case 5: op = rv_op_auipc; break;
+        case 6:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_addiw; break;
+            case 1:
+                switch (((inst >> 25) & 0b1111111)) {
+                case 0: op = rv_op_slliw; break;
+                }
+                break;
+            case 5:
+                switch (((inst >> 25) & 0b1111111)) {
+                case 0: op = rv_op_srliw; break;
+                case 32: op = rv_op_sraiw; break;
+                }
+                break;
+            }
+            break;
+        case 8:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_sb; break;
+            case 1: op = rv_op_sh; break;
+            case 2: op = rv_op_sw; break;
+            case 3: op = rv_op_sd; break;
+            case 4: op = rv_op_sq; break;
+            }
+            break;
+        case 9:
+            switch (((inst >> 12) & 0b111)) {
+            case 2: op = rv_op_fsw; break;
+            case 3: op = rv_op_fsd; break;
+            case 4: op = rv_op_fsq; break;
+            }
+            break;
+        case 11:
+            switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+            case 2: op = rv_op_amoadd_w; break;
+            case 3: op = rv_op_amoadd_d; break;
+            case 4: op = rv_op_amoadd_q; break;
+            case 10: op = rv_op_amoswap_w; break;
+            case 11: op = rv_op_amoswap_d; break;
+            case 12: op = rv_op_amoswap_q; break;
+            case 18:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_lr_w; break;
+                }
+                break;
+            case 19:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_lr_d; break;
+                }
+                break;
+            case 20:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_lr_q; break;
+                }
+                break;
+            case 26: op = rv_op_sc_w; break;
+            case 27: op = rv_op_sc_d; break;
+            case 28: op = rv_op_sc_q; break;
+            case 34: op = rv_op_amoxor_w; break;
+            case 35: op = rv_op_amoxor_d; break;
+            case 36: op = rv_op_amoxor_q; break;
+            case 66: op = rv_op_amoor_w; break;
+            case 67: op = rv_op_amoor_d; break;
+            case 68: op = rv_op_amoor_q; break;
+            case 98: op = rv_op_amoand_w; break;
+            case 99: op = rv_op_amoand_d; break;
+            case 100: op = rv_op_amoand_q; break;
+            case 130: op = rv_op_amomin_w; break;
+            case 131: op = rv_op_amomin_d; break;
+            case 132: op = rv_op_amomin_q; break;
+            case 162: op = rv_op_amomax_w; break;
+            case 163: op = rv_op_amomax_d; break;
+            case 164: op = rv_op_amomax_q; break;
+            case 194: op = rv_op_amominu_w; break;
+            case 195: op = rv_op_amominu_d; break;
+            case 196: op = rv_op_amominu_q; break;
+            case 226: op = rv_op_amomaxu_w; break;
+            case 227: op = rv_op_amomaxu_d; break;
+            case 228: op = rv_op_amomaxu_q; break;
+            }
+            break;
+        case 12:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+            case 0: op = rv_op_add; break;
+            case 1: op = rv_op_sll; break;
+            case 2: op = rv_op_slt; break;
+            case 3: op = rv_op_sltu; break;
+            case 4: op = rv_op_xor; break;
+            case 5: op = rv_op_srl; break;
+            case 6: op = rv_op_or; break;
+            case 7: op = rv_op_and; break;
+            case 8: op = rv_op_mul; break;
+            case 9: op = rv_op_mulh; break;
+            case 10: op = rv_op_mulhsu; break;
+            case 11: op = rv_op_mulhu; break;
+            case 12: op = rv_op_div; break;
+            case 13: op = rv_op_divu; break;
+            case 14: op = rv_op_rem; break;
+            case 15: op = rv_op_remu; break;
+            case 256: op = rv_op_sub; break;
+            case 261: op = rv_op_sra; break;
+            }
+            break;
+        case 13: op = rv_op_lui; break;
+        case 14:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+            case 0: op = rv_op_addw; break;
+            case 1: op = rv_op_sllw; break;
+            case 5: op = rv_op_srlw; break;
+            case 8: op = rv_op_mulw; break;
+            case 12: op = rv_op_divw; break;
+            case 13: op = rv_op_divuw; break;
+            case 14: op = rv_op_remw; break;
+            case 15: op = rv_op_remuw; break;
+            case 256: op = rv_op_subw; break;
+            case 261: op = rv_op_sraw; break;
+            }
+            break;
+        case 16:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fmadd_s; break;
+            case 1: op = rv_op_fmadd_d; break;
+            case 3: op = rv_op_fmadd_q; break;
+            }
+            break;
+        case 17:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fmsub_s; break;
+            case 1: op = rv_op_fmsub_d; break;
+            case 3: op = rv_op_fmsub_q; break;
+            }
+            break;
+        case 18:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fnmsub_s; break;
+            case 1: op = rv_op_fnmsub_d; break;
+            case 3: op = rv_op_fnmsub_q; break;
+            }
+            break;
+        case 19:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fnmadd_s; break;
+            case 1: op = rv_op_fnmadd_d; break;
+            case 3: op = rv_op_fnmadd_q; break;
+            }
+            break;
+        case 20:
+            switch (((inst >> 25) & 0b1111111)) {
+            case 0: op = rv_op_fadd_s; break;
+            case 1: op = rv_op_fadd_d; break;
+            case 3: op = rv_op_fadd_q; break;
+            case 4: op = rv_op_fsub_s; break;
+            case 5: op = rv_op_fsub_d; break;
+            case 7: op = rv_op_fsub_q; break;
+            case 8: op = rv_op_fmul_s; break;
+            case 9: op = rv_op_fmul_d; break;
+            case 11: op = rv_op_fmul_q; break;
+            case 12: op = rv_op_fdiv_s; break;
+            case 13: op = rv_op_fdiv_d; break;
+            case 15: op = rv_op_fdiv_q; break;
+            case 16:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fsgnj_s; break;
+                case 1: op = rv_op_fsgnjn_s; break;
+                case 2: op = rv_op_fsgnjx_s; break;
+                }
+                break;
+            case 17:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fsgnj_d; break;
+                case 1: op = rv_op_fsgnjn_d; break;
+                case 2: op = rv_op_fsgnjx_d; break;
+                }
+                break;
+            case 19:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fsgnj_q; break;
+                case 1: op = rv_op_fsgnjn_q; break;
+                case 2: op = rv_op_fsgnjx_q; break;
+                }
+                break;
+            case 20:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fmin_s; break;
+                case 1: op = rv_op_fmax_s; break;
+                }
+                break;
+            case 21:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fmin_d; break;
+                case 1: op = rv_op_fmax_d; break;
+                }
+                break;
+            case 23:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fmin_q; break;
+                case 1: op = rv_op_fmax_q; break;
+                }
+                break;
+            case 32:
+                switch (((inst >> 20) & 0b11111)) {
+                case 1: op = rv_op_fcvt_s_d; break;
+                case 3: op = rv_op_fcvt_s_q; break;
+                }
+                break;
+            case 33:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_d_s; break;
+                case 3: op = rv_op_fcvt_d_q; break;
+                }
+                break;
+            case 35:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_q_s; break;
+                case 1: op = rv_op_fcvt_q_d; break;
+                }
+                break;
+            case 44:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fsqrt_s; break;
+                }
+                break;
+            case 45:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fsqrt_d; break;
+                }
+                break;
+            case 47:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fsqrt_q; break;
+                }
+                break;
+            case 80:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fle_s; break;
+                case 1: op = rv_op_flt_s; break;
+                case 2: op = rv_op_feq_s; break;
+                }
+                break;
+            case 81:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fle_d; break;
+                case 1: op = rv_op_flt_d; break;
+                case 2: op = rv_op_feq_d; break;
+                }
+                break;
+            case 83:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fle_q; break;
+                case 1: op = rv_op_flt_q; break;
+                case 2: op = rv_op_feq_q; break;
+                }
+                break;
+            case 96:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_w_s; break;
+                case 1: op = rv_op_fcvt_wu_s; break;
+                case 2: op = rv_op_fcvt_l_s; break;
+                case 3: op = rv_op_fcvt_lu_s; break;
+                }
+                break;
+            case 97:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_w_d; break;
+                case 1: op = rv_op_fcvt_wu_d; break;
+                case 2: op = rv_op_fcvt_l_d; break;
+                case 3: op = rv_op_fcvt_lu_d; break;
+                }
+                break;
+            case 99:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_w_q; break;
+                case 1: op = rv_op_fcvt_wu_q; break;
+                case 2: op = rv_op_fcvt_l_q; break;
+                case 3: op = rv_op_fcvt_lu_q; break;
+                }
+                break;
+            case 104:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_s_w; break;
+                case 1: op = rv_op_fcvt_s_wu; break;
+                case 2: op = rv_op_fcvt_s_l; break;
+                case 3: op = rv_op_fcvt_s_lu; break;
+                }
+                break;
+            case 105:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_d_w; break;
+                case 1: op = rv_op_fcvt_d_wu; break;
+                case 2: op = rv_op_fcvt_d_l; break;
+                case 3: op = rv_op_fcvt_d_lu; break;
+                }
+                break;
+            case 107:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_q_w; break;
+                case 1: op = rv_op_fcvt_q_wu; break;
+                case 2: op = rv_op_fcvt_q_l; break;
+                case 3: op = rv_op_fcvt_q_lu; break;
+                }
+                break;
+            case 112:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_x_s; break;
+                case 1: op = rv_op_fclass_s; break;
+                }
+                break;
+            case 113:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_x_d; break;
+                case 1: op = rv_op_fclass_d; break;
+                }
+                break;
+            case 115:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_x_q; break;
+                case 1: op = rv_op_fclass_q; break;
+                }
+                break;
+            case 120:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_s_x; break;
+                }
+                break;
+            case 121:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_d_x; break;
+                }
+                break;
+            case 123:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_q_x; break;
+                }
+                break;
+            }
+            break;
+        case 22:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_addid; break;
+            case 1:
+                switch (((inst >> 26) & 0b111111)) {
+                case 0: op = rv_op_sllid; break;
+                }
+                break;
+            case 5:
+                switch (((inst >> 26) & 0b111111)) {
+                case 0: op = rv_op_srlid; break;
+                case 16: op = rv_op_sraid; break;
+                }
+                break;
+            }
+            break;
+        case 24:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_beq; break;
+            case 1: op = rv_op_bne; break;
+            case 4: op = rv_op_blt; break;
+            case 5: op = rv_op_bge; break;
+            case 6: op = rv_op_bltu; break;
+            case 7: op = rv_op_bgeu; break;
+            }
+            break;
+        case 25:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_jalr; break;
+            }
+            break;
+        case 27: op = rv_op_jal; break;
+        case 28:
+            switch (((inst >> 12) & 0b111)) {
+            case 0:
+                switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) {
+                case 0:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 0: op = rv_op_ecall; break;
+                    case 32: op = rv_op_ebreak; break;
+                    case 64: op = rv_op_uret; break;
+                    }
+                    break;
+                case 256:
+                    switch (((inst >> 20) & 0b11111)) {
+                    case 2:
+                        switch (((inst >> 15) & 0b11111)) {
+                        case 0: op = rv_op_sret; break;
+                        }
+                        break;
+                    case 4: op = rv_op_sfence_vm; break;
+                    case 5:
+                        switch (((inst >> 15) & 0b11111)) {
+                        case 0: op = rv_op_wfi; break;
+                        }
+                        break;
+                    }
+                    break;
+                case 288: op = rv_op_sfence_vma; break;
+                case 512:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 64: op = rv_op_hret; break;
+                    }
+                    break;
+                case 768:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 64: op = rv_op_mret; break;
+                    }
+                    break;
+                case 1952:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 576: op = rv_op_dret; break;
+                    }
+                    break;
+                }
+                break;
+            case 1: op = rv_op_csrrw; break;
+            case 2: op = rv_op_csrrs; break;
+            case 3: op = rv_op_csrrc; break;
+            case 5: op = rv_op_csrrwi; break;
+            case 6: op = rv_op_csrrsi; break;
+            case 7: op = rv_op_csrrci; break;
+            }
+            break;
+        case 30:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+            case 0: op = rv_op_addd; break;
+            case 1: op = rv_op_slld; break;
+            case 5: op = rv_op_srld; break;
+            case 8: op = rv_op_muld; break;
+            case 12: op = rv_op_divd; break;
+            case 13: op = rv_op_divud; break;
+            case 14: op = rv_op_remd; break;
+            case 15: op = rv_op_remud; break;
+            case 256: op = rv_op_subd; break;
+            case 261: op = rv_op_srad; break;
+            }
+            break;
+        }
+        break;
+    }
+    dec->op = op;
+}
+
+/* operand extractors */
+
+static uint32_t operand_rd(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_rs1(rv_inst inst)
+{
+    return (inst << 44) >> 59;
+}
+
+static uint32_t operand_rs2(rv_inst inst)
+{
+    return (inst << 39) >> 59;
+}
+
+static uint32_t operand_rs3(rv_inst inst)
+{
+    return (inst << 32) >> 59;
+}
+
+static uint32_t operand_aq(rv_inst inst)
+{
+    return (inst << 37) >> 63;
+}
+
+static uint32_t operand_rl(rv_inst inst)
+{
+    return (inst << 38) >> 63;
+}
+
+static uint32_t operand_pred(rv_inst inst)
+{
+    return (inst << 36) >> 60;
+}
+
+static uint32_t operand_succ(rv_inst inst)
+{
+    return (inst << 40) >> 60;
+}
+
+static uint32_t operand_rm(rv_inst inst)
+{
+    return (inst << 49) >> 61;
+}
+
+static uint32_t operand_shamt5(rv_inst inst)
+{
+    return (inst << 39) >> 59;
+}
+
+static uint32_t operand_shamt6(rv_inst inst)
+{
+    return (inst << 38) >> 58;
+}
+
+static uint32_t operand_shamt7(rv_inst inst)
+{
+    return (inst << 37) >> 57;
+}
+
+static uint32_t operand_crdq(rv_inst inst)
+{
+    return (inst << 59) >> 61;
+}
+
+static uint32_t operand_crs1q(rv_inst inst)
+{
+    return (inst << 54) >> 61;
+}
+
+static uint32_t operand_crs1rdq(rv_inst inst)
+{
+    return (inst << 54) >> 61;
+}
+
+static uint32_t operand_crs2q(rv_inst inst)
+{
+    return (inst << 59) >> 61;
+}
+
+static uint32_t operand_crd(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_crs1(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_crs1rd(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_crs2(rv_inst inst)
+{
+    return (inst << 57) >> 59;
+}
+
+static uint32_t operand_cimmsh5(rv_inst inst)
+{
+    return (inst << 57) >> 59;
+}
+
+static uint32_t operand_csr12(rv_inst inst)
+{
+    return (inst << 32) >> 52;
+}
+
+static int32_t operand_imm12(rv_inst inst)
+{
+    return ((int64_t)inst << 32) >> 52;
+}
+
+static int32_t operand_imm20(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 44) << 12;
+}
+
+static int32_t operand_jimm20(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 63) << 20 |
+        ((inst << 33) >> 54) << 1 |
+        ((inst << 43) >> 63) << 11 |
+        ((inst << 44) >> 56) << 12;
+}
+
+static int32_t operand_simm12(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 57) << 5 |
+        (inst << 52) >> 59;
+}
+
+static int32_t operand_sbimm12(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 63) << 12 |
+        ((inst << 33) >> 58) << 5 |
+        ((inst << 52) >> 60) << 1 |
+        ((inst << 56) >> 63) << 11;
+}
+
+static uint32_t operand_cimmsh6(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        (inst << 57) >> 59;
+}
+
+static int32_t operand_cimmi(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 5 |
+        (inst << 57) >> 59;
+}
+
+static int32_t operand_cimmui(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 17 |
+        ((inst << 57) >> 59) << 12;
+}
+
+static uint32_t operand_cimmlwsp(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        ((inst << 57) >> 61) << 2 |
+        ((inst << 60) >> 62) << 6;
+}
+
+static uint32_t operand_cimmldsp(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        ((inst << 57) >> 62) << 3 |
+        ((inst << 59) >> 61) << 6;
+}
+
+static uint32_t operand_cimmlqsp(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        ((inst << 57) >> 63) << 4 |
+        ((inst << 58) >> 60) << 6;
+}
+
+static int32_t operand_cimm16sp(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 9 |
+        ((inst << 57) >> 63) << 4 |
+        ((inst << 58) >> 63) << 6 |
+        ((inst << 59) >> 62) << 7 |
+        ((inst << 61) >> 63) << 5;
+}
+
+static int32_t operand_cimmj(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 11 |
+        ((inst << 52) >> 63) << 4 |
+        ((inst << 53) >> 62) << 8 |
+        ((inst << 55) >> 63) << 10 |
+        ((inst << 56) >> 63) << 6 |
+        ((inst << 57) >> 63) << 7 |
+        ((inst << 58) >> 61) << 1 |
+        ((inst << 61) >> 63) << 5;
+}
+
+static int32_t operand_cimmb(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 8 |
+        ((inst << 52) >> 62) << 3 |
+        ((inst << 57) >> 62) << 6 |
+        ((inst << 59) >> 62) << 1 |
+        ((inst << 61) >> 63) << 5;
+}
+
+static uint32_t operand_cimmswsp(rv_inst inst)
+{
+    return ((inst << 51) >> 60) << 2 |
+        ((inst << 55) >> 62) << 6;
+}
+
+static uint32_t operand_cimmsdsp(rv_inst inst)
+{
+    return ((inst << 51) >> 61) << 3 |
+        ((inst << 54) >> 61) << 6;
+}
+
+static uint32_t operand_cimmsqsp(rv_inst inst)
+{
+    return ((inst << 51) >> 62) << 4 |
+        ((inst << 53) >> 60) << 6;
+}
+
+static uint32_t operand_cimm4spn(rv_inst inst)
+{
+    return ((inst << 51) >> 62) << 4 |
+        ((inst << 53) >> 60) << 6 |
+        ((inst << 57) >> 63) << 2 |
+        ((inst << 58) >> 63) << 3;
+}
+
+static uint32_t operand_cimmw(rv_inst inst)
+{
+    return ((inst << 51) >> 61) << 3 |
+        ((inst << 57) >> 63) << 2 |
+        ((inst << 58) >> 63) << 6;
+}
+
+static uint32_t operand_cimmd(rv_inst inst)
+{
+    return ((inst << 51) >> 61) << 3 |
+        ((inst << 57) >> 62) << 6;
+}
+
+static uint32_t operand_cimmq(rv_inst inst)
+{
+    return ((inst << 51) >> 62) << 4 |
+        ((inst << 53) >> 63) << 8 |
+        ((inst << 57) >> 62) << 6;
+}
+
+/* decode operands */
+
+static void decode_inst_operands(rv_decode *dec)
+{
+    rv_inst inst = dec->inst;
+    dec->codec = opcode_data[dec->op].codec;
+    switch (dec->codec) {
+    case rv_codec_none:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_u:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_imm20(inst);
+        break;
+    case rv_codec_uj:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_jimm20(inst);
+        break;
+    case rv_codec_i:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_imm12(inst);
+        break;
+    case rv_codec_i_sh5:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_shamt5(inst);
+        break;
+    case rv_codec_i_sh6:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_shamt6(inst);
+        break;
+    case rv_codec_i_sh7:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_shamt7(inst);
+        break;
+    case rv_codec_i_csr:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_csr12(inst);
+        break;
+    case rv_codec_s:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = operand_simm12(inst);
+        break;
+    case rv_codec_sb:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = operand_sbimm12(inst);
+        break;
+    case rv_codec_r:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = 0;
+        break;
+    case rv_codec_r_m:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = 0;
+        dec->rm = operand_rm(inst);
+        break;
+    case rv_codec_r4_m:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->rs3 = operand_rs3(inst);
+        dec->imm = 0;
+        dec->rm = operand_rm(inst);
+        break;
+    case rv_codec_r_a:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = 0;
+        dec->aq = operand_aq(inst);
+        dec->rl = operand_rl(inst);
+        break;
+    case rv_codec_r_l:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        dec->aq = operand_aq(inst);
+        dec->rl = operand_rl(inst);
+        break;
+    case rv_codec_r_f:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->pred = operand_pred(inst);
+        dec->succ = operand_succ(inst);
+        dec->imm = 0;
+        break;
+    case rv_codec_cb:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmb(inst);
+        break;
+    case rv_codec_cb_imm:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmi(inst);
+        break;
+    case rv_codec_cb_sh5:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh5(inst);
+        break;
+    case rv_codec_cb_sh6:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh6(inst);
+        break;
+    case rv_codec_ci:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmi(inst);
+        break;
+    case rv_codec_ci_sh5:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh5(inst);
+        break;
+    case rv_codec_ci_sh6:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh6(inst);
+        break;
+    case rv_codec_ci_16sp:
+        dec->rd = rv_ireg_sp;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimm16sp(inst);
+        break;
+    case rv_codec_ci_lwsp:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmlwsp(inst);
+        break;
+    case rv_codec_ci_ldsp:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmldsp(inst);
+        break;
+    case rv_codec_ci_lqsp:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmlqsp(inst);
+        break;
+    case rv_codec_ci_li:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_zero;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmi(inst);
+        break;
+    case rv_codec_ci_lui:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_zero;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmui(inst);
+        break;
+    case rv_codec_ci_none:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_ciw_4spn:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimm4spn(inst);
+        break;
+    case rv_codec_cj:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmj(inst);
+        break;
+    case rv_codec_cj_jal:
+        dec->rd = rv_ireg_ra;
+        dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmj(inst);
+        break;
+    case rv_codec_cl_lw:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmw(inst);
+        break;
+    case rv_codec_cl_ld:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmd(inst);
+        break;
+    case rv_codec_cl_lq:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmq(inst);
+        break;
+    case rv_codec_cr:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = 0;
+        break;
+    case rv_codec_cr_mv:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = operand_crs2(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_cr_jalr:
+        dec->rd = rv_ireg_ra;
+        dec->rs1 = operand_crs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_cr_jr:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_cs:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = 0;
+        break;
+    case rv_codec_cs_sw:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = operand_cimmw(inst);
+        break;
+    case rv_codec_cs_sd:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = operand_cimmd(inst);
+        break;
+    case rv_codec_cs_sq:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = operand_cimmq(inst);
+        break;
+    case rv_codec_css_swsp:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = operand_cimmswsp(inst);
+        break;
+    case rv_codec_css_sdsp:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = operand_cimmsdsp(inst);
+        break;
+    case rv_codec_css_sqsp:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = operand_cimmsqsp(inst);
+        break;
+    };
+}
+
+/* check constraint */
+
+static bool check_constraints(rv_decode *dec, const rvc_constraint *c)
+{
+    int32_t imm = dec->imm;
+    uint8_t rd = dec->rd, rs1 = dec->rs1, rs2 = dec->rs2;
+    while (*c != rvc_end) {
+        switch (*c) {
+        case rvc_simm_6:
+            if (!(imm >= -32 && imm < 32)) {
+                return false;
+            }
+            break;
+        case rvc_imm_6:
+            if (!(imm <= 63)) {
+                return false;
+            }
+            break;
+        case rvc_imm_7:
+            if (!(imm <= 127)) {
+                return false;
+            }
+            break;
+        case rvc_imm_8:
+            if (!(imm <= 255)) {
+                return false;
+            }
+            break;
+        case rvc_imm_9:
+            if (!(imm <= 511)) {
+                return false;
+            }
+            break;
+        case rvc_imm_10:
+            if (!(imm <= 1023)) {
+                return false;
+            }
+            break;
+        case rvc_imm_12:
+            if (!(imm <= 4095)) {
+                return false;
+            }
+            break;
+        case rvc_imm_18:
+            if (!(imm <= 262143)) {
+                return false;
+            }
+            break;
+        case rvc_imm_nz:
+            if (!(imm != 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x2:
+            if (!((imm & 0b1) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x4:
+            if (!((imm & 0b11) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x8:
+            if (!((imm & 0b111) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x16:
+            if (!((imm & 0b1111) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rd_b3:
+            if (!(rd  >= 8 && rd  <= 15)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_b3:
+            if (!(rs1 >= 8 && rs1 <= 15)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_b3:
+            if (!(rs2 >= 8 && rs2 <= 15)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_rs1:
+            if (!(rd == rs1)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_ra:
+            if (!(rd == 1)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_sp:
+            if (!(rd == 2)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_x0:
+            if (!(rd == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_eq_sp:
+            if (!(rs1 == 2)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_eq_x0:
+            if (!(rs1 == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_eq_x0:
+            if (!(rs2 == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rd_ne_x0_x2:
+            if (!(rd != 0 && rd != 2)) {
+                return false;
+            }
+            break;
+        case rvc_rd_ne_x0:
+            if (!(rd != 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_ne_x0:
+            if (!(rs1 != 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_ne_x0:
+            if (!(rs2 != 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_eq_rs1:
+            if (!(rs2 == rs1)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_eq_ra:
+            if (!(rs1 == 1)) {
+                return false;
+            }
+            break;
+        case rvc_imm_eq_zero:
+            if (!(imm == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_eq_n1:
+            if (!(imm == -1)) {
+                return false;
+            }
+            break;
+        case rvc_imm_eq_p1:
+            if (!(imm == 1)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0x001:
+            if (!(imm == 0x001)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0x002:
+            if (!(imm == 0x002)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0x003:
+            if (!(imm == 0x003)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc00:
+            if (!(imm == 0xc00)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc01:
+            if (!(imm == 0xc01)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc02:
+            if (!(imm == 0xc02)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc80:
+            if (!(imm == 0xc80)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc81:
+            if (!(imm == 0xc81)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc82:
+            if (!(imm == 0xc82)) {
+                return false;
+            }
+            break;
+        default: break;
+        }
+        c++;
+    }
+    return true;
+}
+
+/* instruction length */
+
+static size_t inst_length(rv_inst inst)
+{
+    /* NOTE: supports maximum instruction size of 64-bits */
+
+    /* instruction length coding
+     *
+     *      aa - 16 bit aa != 11
+     *   bbb11 - 32 bit bbb != 111
+     *  011111 - 48 bit
+     * 0111111 - 64 bit
+     */
+
+    return (inst &      0b11) != 0b11      ? 2
+         : (inst &   0b11100) != 0b11100   ? 4
+         : (inst &  0b111111) == 0b011111  ? 6
+         : (inst & 0b1111111) == 0b0111111 ? 8
+         : 0;
+}
+
+/* format instruction */
+
+static void append(char *s1, const char *s2, size_t n)
+{
+    size_t l1 = strlen(s1);
+    if (n - l1 - 1 > 0) {
+        strncat(s1, s2, n - l1);
+    }
+}
+
+static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
+{
+    char tmp[64];
+    const char *fmt;
+
+    if (dec->op == rv_op_illegal) {
+        size_t len = inst_length(dec->inst);
+        switch (len) {
+        case 2:
+            snprintf(buf, buflen, "(0x%04" PRIx64 ")", dec->inst);
+            break;
+        case 4:
+            snprintf(buf, buflen, "(0x%08" PRIx64 ")", dec->inst);
+            break;
+        case 6:
+            snprintf(buf, buflen, "(0x%012" PRIx64 ")", dec->inst);
+            break;
+        default:
+            snprintf(buf, buflen, "(0x%016" PRIx64 ")", dec->inst);
+            break;
+        }
+        return;
+    }
+
+    fmt = opcode_data[dec->op].format;
+    while (*fmt) {
+        switch (*fmt) {
+        case 'O':
+            append(buf, opcode_data[dec->op].name, buflen);
+            break;
+        case '(':
+            append(buf, "(", buflen);
+            break;
+        case ',':
+            append(buf, ",", buflen);
+            break;
+        case ')':
+            append(buf, ")", buflen);
+            break;
+        case '0':
+            append(buf, rv_ireg_name_sym[dec->rd], buflen);
+            break;
+        case '1':
+            append(buf, rv_ireg_name_sym[dec->rs1], buflen);
+            break;
+        case '2':
+            append(buf, rv_ireg_name_sym[dec->rs2], buflen);
+            break;
+        case '3':
+            append(buf, rv_freg_name_sym[dec->rd], buflen);
+            break;
+        case '4':
+            append(buf, rv_freg_name_sym[dec->rs1], buflen);
+            break;
+        case '5':
+            append(buf, rv_freg_name_sym[dec->rs2], buflen);
+            break;
+        case '6':
+            append(buf, rv_freg_name_sym[dec->rs3], buflen);
+            break;
+        case '7':
+            snprintf(tmp, sizeof(tmp), "%d", dec->rs1);
+            append(buf, tmp, buflen);
+            break;
+        case 'i':
+            snprintf(tmp, sizeof(tmp), "%d", dec->imm);
+            append(buf, tmp, buflen);
+            break;
+        case 'o':
+            snprintf(tmp, sizeof(tmp), "%d", dec->imm);
+            append(buf, tmp, buflen);
+            while (strlen(buf) < tab * 2) {
+                append(buf, " ", buflen);
+            }
+            snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64,
+                dec->pc + dec->imm);
+            append(buf, tmp, buflen);
+            break;
+        case 'c': {
+            const char *name = csr_name(dec->imm & 0xfff);
+            if (name) {
+                append(buf, name, buflen);
+            } else {
+                snprintf(tmp, sizeof(tmp), "0x%03x", dec->imm & 0xfff);
+                append(buf, tmp, buflen);
+            }
+            break;
+        }
+        case 'r':
+            switch (dec->rm) {
+            case rv_rm_rne:
+                append(buf, "rne", buflen);
+                break;
+            case rv_rm_rtz:
+                append(buf, "rtz", buflen);
+                break;
+            case rv_rm_rdn:
+                append(buf, "rdn", buflen);
+                break;
+            case rv_rm_rup:
+                append(buf, "rup", buflen);
+                break;
+            case rv_rm_rmm:
+                append(buf, "rmm", buflen);
+                break;
+            case rv_rm_dyn:
+                append(buf, "dyn", buflen);
+                break;
+            default:
+                append(buf, "inv", buflen);
+                break;
+            }
+            break;
+        case 'p':
+            if (dec->pred & rv_fence_i) {
+                append(buf, "i", buflen);
+            }
+            if (dec->pred & rv_fence_o) {
+                append(buf, "o", buflen);
+            }
+            if (dec->pred & rv_fence_r) {
+                append(buf, "r", buflen);
+            }
+            if (dec->pred & rv_fence_w) {
+                append(buf, "w", buflen);
+            }
+            break;
+        case 's':
+            if (dec->succ & rv_fence_i) {
+                append(buf, "i", buflen);
+            }
+            if (dec->succ & rv_fence_o) {
+                append(buf, "o", buflen);
+            }
+            if (dec->succ & rv_fence_r) {
+                append(buf, "r", buflen);
+            }
+            if (dec->succ & rv_fence_w) {
+                append(buf, "w", buflen);
+            }
+            break;
+        case '\t':
+            while (strlen(buf) < tab) {
+                append(buf, " ", buflen);
+            }
+            break;
+        case 'A':
+            if (dec->aq) {
+                append(buf, ".aq", buflen);
+            }
+            break;
+        case 'R':
+            if (dec->rl) {
+                append(buf, ".rl", buflen);
+            }
+            break;
+        default:
+            break;
+        }
+        fmt++;
+    }
+}
+
+/* lift instruction to pseudo-instruction */
+
+static void decode_inst_lift_pseudo(rv_decode *dec)
+{
+    const rv_comp_data *comp_data = opcode_data[dec->op].pseudo;
+    if (!comp_data) {
+        return;
+    }
+    while (comp_data->constraints) {
+        if (check_constraints(dec, comp_data->constraints)) {
+            dec->op = comp_data->op;
+            dec->codec = opcode_data[dec->op].codec;
+            return;
+        }
+        comp_data++;
+    }
+}
+
+/* decompress instruction */
+
+static void decode_inst_decompress_rv32(rv_decode *dec)
+{
+    int decomp_op = opcode_data[dec->op].decomp_rv32;
+    if (decomp_op != rv_op_illegal) {
+        dec->op = decomp_op;
+        dec->codec = opcode_data[decomp_op].codec;
+    }
+}
+
+static void decode_inst_decompress_rv64(rv_decode *dec)
+{
+    int decomp_op = opcode_data[dec->op].decomp_rv64;
+    if (decomp_op != rv_op_illegal) {
+        dec->op = decomp_op;
+        dec->codec = opcode_data[decomp_op].codec;
+    }
+}
+
+static void decode_inst_decompress_rv128(rv_decode *dec)
+{
+    int decomp_op = opcode_data[dec->op].decomp_rv128;
+    if (decomp_op != rv_op_illegal) {
+        dec->op = decomp_op;
+        dec->codec = opcode_data[decomp_op].codec;
+    }
+}
+
+static void decode_inst_decompress(rv_decode *dec, rv_isa isa)
+{
+    switch (isa) {
+    case rv32:
+        decode_inst_decompress_rv32(dec);
+        break;
+    case rv64:
+        decode_inst_decompress_rv64(dec);
+        break;
+    case rv128:
+        decode_inst_decompress_rv128(dec);
+        break;
+    }
+}
+
+/* disassemble instruction */
+
+static void
+disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst)
+{
+    rv_decode dec = { 0 };
+    dec.pc = pc;
+    dec.inst = inst;
+    decode_inst_opcode(&dec, isa);
+    decode_inst_operands(&dec);
+    decode_inst_decompress(&dec, isa);
+    decode_inst_lift_pseudo(&dec);
+    format_inst(buf, buflen, 16, &dec);
+}
+
+static int
+print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa)
+{
+    char buf[128] = { 0 };
+    bfd_byte packet[2];
+    rv_inst inst = 0;
+    size_t len = 2;
+    bfd_vma n;
+    int status;
+
+    /* Instructions are made of 2-byte packets in little-endian order */
+    for (n = 0; n < len; n += 2) {
+        status = (*info->read_memory_func)(memaddr + n, packet, 2, info);
+        if (status != 0) {
+            /* Don't fail just because we fell off the end.  */
+            if (n > 0) {
+                break;
+            }
+            (*info->memory_error_func)(status, memaddr, info);
+            return status;
+        }
+        inst |= ((rv_inst) bfd_getl16(packet)) << (8 * n);
+        if (n == 0) {
+            len = inst_length(inst);
+        }
+    }
+
+    disasm_inst(buf, sizeof(buf), isa, memaddr, inst);
+    (*info->fprintf_func)(info->stream, "%s", buf);
+
+    return len;
+}
+
+int print_insn_riscv32(bfd_vma memaddr, struct disassemble_info *info)
+{
+    return print_insn_riscv(memaddr, info, rv32);
+}
+
+int print_insn_riscv64(bfd_vma memaddr, struct disassemble_info *info)
+{
+    return print_insn_riscv(memaddr, info, rv64);
+}
diff --git a/include/disas/bfd.h b/include/disas/bfd.h
index 9324537..1f69a6e 100644
--- a/include/disas/bfd.h
+++ b/include/disas/bfd.h
@@ -429,6 +429,8 @@ int print_insn_lm32             (bfd_vma, disassemble_info*);
 int print_insn_big_nios2        (bfd_vma, disassemble_info*);
 int print_insn_little_nios2     (bfd_vma, disassemble_info*);
 int print_insn_xtensa           (bfd_vma, disassemble_info*);
+int print_insn_riscv32          (bfd_vma, disassemble_info*);
+int print_insn_riscv64          (bfd_vma, disassemble_info*);
 
 #if 0
 /* Fetch the disassembler for a given BFD, if that support is available.  */
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 05/23] RISC-V CPU Helpers
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (3 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 06/23] RISC-V FPU Support Michael Clark
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Privileged control and status register helpers and page fault handling.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 target/riscv/helper.c    | 503 +++++++++++++++++++++++++++++++++++
 target/riscv/helper.h    |  78 ++++++
 target/riscv/op_helper.c | 669 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1250 insertions(+)
 create mode 100644 target/riscv/helper.c
 create mode 100644 target/riscv/helper.h
 create mode 100644 target/riscv/op_helper.c

diff --git a/target/riscv/helper.c b/target/riscv/helper.c
new file mode 100644
index 0000000..02cbcea
--- /dev/null
+++ b/target/riscv/helper.c
@@ -0,0 +1,503 @@
+/*
+ * RISC-V emulation helpers for qemu.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "tcg-op.h"
+
+#define RISCV_DEBUG_INTERRUPT 0
+
+int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+#ifdef CONFIG_USER_ONLY
+    return 0;
+#else
+    return env->priv;
+#endif
+}
+
+#ifndef CONFIG_USER_ONLY
+/*
+ * Return RISC-V IRQ number if an interrupt should be taken, else -1.
+ * Used in cpu-exec.c
+ *
+ * Adapted from Spike's processor_t::take_interrupt()
+ */
+static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env)
+{
+    target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie;
+
+    target_ulong mie = get_field(env->mstatus, MSTATUS_MIE);
+    target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie);
+    target_ulong enabled_interrupts = pending_interrupts &
+                                      ~env->mideleg & -m_enabled;
+
+    target_ulong sie = get_field(env->mstatus, MSTATUS_SIE);
+    target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie);
+    enabled_interrupts |= pending_interrupts & env->mideleg &
+                          -s_enabled;
+
+    if (enabled_interrupts) {
+        return ctz64(enabled_interrupts); /* since non-zero */
+    } else {
+        return EXCP_NONE; /* indicates no pending interrupt */
+    }
+}
+#endif
+
+bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+#if !defined(CONFIG_USER_ONLY)
+    if (interrupt_request & CPU_INTERRUPT_HARD) {
+        RISCVCPU *cpu = RISCV_CPU(cs);
+        CPURISCVState *env = &cpu->env;
+        int interruptno = riscv_cpu_hw_interrupts_pending(env);
+        if (interruptno >= 0) {
+            cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno;
+            riscv_cpu_do_interrupt(cs);
+            return true;
+        }
+    }
+#endif
+    return false;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* get_physical_address - get the physical address for this virtual address
+ *
+ * Do a page table walk to obtain the physical address corresponding to a
+ * virtual address. Returns 0 if the translation was successful
+ *
+ * Adapted from Spike's mmu_t::translate and mmu_t::walk
+ *
+ */
+static int get_physical_address(CPURISCVState *env, hwaddr *physical,
+                                int *prot, target_ulong addr,
+                                int access_type, int mmu_idx)
+{
+    /* NOTE: the env->pc value visible here will not be
+     * correct, but the value visible to the exception handler
+     * (riscv_cpu_do_interrupt) is correct */
+
+    int mode = mmu_idx;
+
+    if (mode == PRV_M && access_type != MMU_INST_FETCH) {
+        if (get_field(env->mstatus, MSTATUS_MPRV)) {
+            mode = get_field(env->mstatus, MSTATUS_MPP);
+        }
+    }
+
+    if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) {
+        *physical = addr;
+        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        return TRANSLATE_SUCCESS;
+    }
+
+    *prot = 0;
+
+    target_ulong base;
+    int levels, ptidxbits, ptesize, vm, sum;
+    int mxr = get_field(env->mstatus, MSTATUS_MXR);
+
+    if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+        base = get_field(env->satp, SATP_PPN) << PGSHIFT;
+        sum = get_field(env->mstatus, MSTATUS_SUM);
+        vm = get_field(env->satp, SATP_MODE);
+        switch (vm) {
+        case VM_1_10_SV32:
+          levels = 2; ptidxbits = 10; ptesize = 4; break;
+        case VM_1_10_SV39:
+          levels = 3; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_10_SV48:
+          levels = 4; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_10_SV57:
+          levels = 5; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_10_MBARE:
+            *physical = addr;
+            *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+            return TRANSLATE_SUCCESS;
+        default:
+          g_assert_not_reached();
+        }
+    } else {
+        base = env->sptbr << PGSHIFT;
+        sum = !get_field(env->mstatus, MSTATUS_PUM);
+        vm = get_field(env->mstatus, MSTATUS_VM);
+        switch (vm) {
+        case VM_1_09_SV32:
+          levels = 2; ptidxbits = 10; ptesize = 4; break;
+        case VM_1_09_SV39:
+          levels = 3; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_09_SV48:
+          levels = 4; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_09_MBARE:
+            *physical = addr;
+            *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+            return TRANSLATE_SUCCESS;
+        default:
+          g_assert_not_reached();
+        }
+    }
+
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    int va_bits = PGSHIFT + levels * ptidxbits;
+    target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1;
+    target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask;
+    if (masked_msbs != 0 && masked_msbs != mask) {
+        return TRANSLATE_FAIL;
+    }
+
+    int ptshift = (levels - 1) * ptidxbits;
+    int i;
+
+#if !TCG_OVERSIZED_GUEST
+restart:
+#endif
+    for (i = 0; i < levels; i++, ptshift -= ptidxbits) {
+        target_ulong idx = (addr >> (PGSHIFT + ptshift)) &
+                           ((1 << ptidxbits) - 1);
+
+        /* check that physical address of PTE is legal */
+        target_ulong pte_addr = base + idx * ptesize;
+#if defined(TARGET_RISCV32)
+        target_ulong pte = ldl_phys(cs->as, pte_addr);
+#elif defined(TARGET_RISCV64)
+        target_ulong pte = ldq_phys(cs->as, pte_addr);
+#endif
+        target_ulong ppn = pte >> PTE_PPN_SHIFT;
+
+        if (PTE_TABLE(pte)) { /* next level of page table */
+            base = ppn << PGSHIFT;
+        } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) {
+            break;
+        } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
+            break;
+        } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) :
+                  access_type == MMU_DATA_LOAD ?  !(pte & PTE_R) &&
+                  !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) {
+            break;
+        } else {
+            /* if necessary, set accessed and dirty bits. */
+            target_ulong updated_pte = pte | PTE_A |
+                (access_type == MMU_DATA_STORE ? PTE_D : 0);
+
+            /* Page table updates need to be atomic with MTTCG enabled */
+            if (updated_pte != pte) {
+                /* if accessed or dirty bits need updating, and the PTE is
+                 * in RAM, then we do so atomically with a compare and swap.
+                 * if the PTE is in IO space, then it can't be updated.
+                 * if the PTE changed, then we must re-walk the page table
+                   as the PTE is no longer valid */
+                MemoryRegion *mr;
+                hwaddr l = sizeof(target_ulong), addr1;
+                mr = address_space_translate(cs->as, pte_addr,
+                    &addr1, &l, false);
+                if (memory_access_is_direct(mr, true)) {
+                    target_ulong *pte_pa =
+                        qemu_map_ram_ptr(mr->ram_block, addr1);
+#if TCG_OVERSIZED_GUEST
+                    /* MTTCG is not enabled on oversized TCG guests so
+                     * page table updates do not need to be atomic */
+                    *pte_pa = pte = updated_pte;
+#else
+                    target_ulong old_pte =
+                        atomic_cmpxchg(pte_pa, pte, updated_pte);
+                    if (old_pte != pte) {
+                        goto restart;
+                    } else {
+                        pte = updated_pte;
+                    }
+#endif
+                } else {
+                    /* misconfigured PTE in ROM (AD bits are not preset) or
+                     * PTE is in IO space and can't be updated atomically */
+                    return TRANSLATE_FAIL;
+                }
+            }
+
+            /* for superpage mappings, make a fake leaf PTE for the TLB's
+               benefit. */
+            target_ulong vpn = addr >> PGSHIFT;
+            *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
+
+            if ((pte & PTE_R)) {
+                *prot |= PAGE_READ;
+            }
+            if ((pte & PTE_X)) {
+                *prot |= PAGE_EXEC;
+            }
+           /* only add write permission on stores or if the page
+              is already dirty, so that we don't miss further
+              page table walks to update the dirty bit */
+            if ((pte & PTE_W) &&
+                    (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
+                *prot |= PAGE_WRITE;
+            }
+            return TRANSLATE_SUCCESS;
+        }
+    }
+    return TRANSLATE_FAIL;
+}
+
+static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
+                                MMUAccessType access_type)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    int page_fault_exceptions =
+        (env->priv_ver >= PRIV_VERSION_1_10_0) &&
+        get_field(env->satp, SATP_MODE) != VM_1_10_MBARE;
+    switch (access_type) {
+    case MMU_INST_FETCH:
+        cs->exception_index = page_fault_exceptions ?
+            RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = page_fault_exceptions ?
+            RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = page_fault_exceptions ?
+            RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    env->badaddr = address;
+}
+
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    hwaddr phys_addr;
+    int prot;
+    int mmu_idx = cpu_mmu_index(&cpu->env, false);
+
+    if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx)) {
+        return -1;
+    }
+    return phys_addr;
+}
+
+void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+                                   MMUAccessType access_type, int mmu_idx,
+                                   uintptr_t retaddr)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    switch (access_type) {
+    case MMU_INST_FETCH:
+        cs->exception_index = RISCV_EXCP_INST_ADDR_MIS;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = RISCV_EXCP_LOAD_ADDR_MIS;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    env->badaddr = addr;
+    do_raise_exception_err(env, cs->exception_index, retaddr);
+}
+
+/* called by qemu's softmmu to fill the qemu tlb */
+void tlb_fill(CPUState *cs, target_ulong addr, int size,
+        MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
+{
+    int ret;
+    ret = riscv_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx);
+    if (ret == TRANSLATE_FAIL) {
+        RISCVCPU *cpu = RISCV_CPU(cs);
+        CPURISCVState *env = &cpu->env;
+        do_raise_exception_err(env, cs->exception_index, retaddr);
+    }
+}
+
+#endif
+
+int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
+        int rw, int mmu_idx)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+#if !defined(CONFIG_USER_ONLY)
+    hwaddr pa = 0;
+    int prot;
+#endif
+    int ret = TRANSLATE_FAIL;
+
+    qemu_log_mask(CPU_LOG_MMU,
+            "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx \
+             %d\n", __func__, env->pc, address, rw, mmu_idx);
+
+#if !defined(CONFIG_USER_ONLY)
+    ret = get_physical_address(env, &pa, &prot, address, rw, mmu_idx);
+    qemu_log_mask(CPU_LOG_MMU,
+            "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
+             " prot %d\n", __func__, address, ret, pa, prot);
+    if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
+        ret = TRANSLATE_FAIL;
+    }
+    if (ret == TRANSLATE_SUCCESS) {
+        tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK,
+                     prot, mmu_idx, TARGET_PAGE_SIZE);
+    } else if (ret == TRANSLATE_FAIL) {
+        raise_mmu_exception(env, address, rw);
+    }
+#else
+    switch (rw) {
+    case MMU_INST_FETCH:
+        cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT;
+        break;
+    }
+#endif
+    return ret;
+}
+
+/*
+ * Handle Traps
+ *
+ * Adapted from Spike's processor_t::take_trap.
+ *
+ */
+void riscv_cpu_do_interrupt(CPUState *cs)
+{
+#if !defined(CONFIG_USER_ONLY)
+
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+
+    if (RISCV_DEBUG_INTERRUPT) {
+        int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK;
+        if (cs->exception_index & RISCV_EXCP_INT_FLAG) {
+            qemu_log_mask(LOG_TRACE, "core   0: trap %s, epc 0x" TARGET_FMT_lx,
+                riscv_intr_names[log_cause], env->pc);
+        } else {
+            qemu_log_mask(LOG_TRACE, "core   0: intr %s, epc 0x" TARGET_FMT_lx,
+                riscv_excp_names[log_cause], env->pc);
+        }
+    }
+
+    target_ulong fixed_cause = 0;
+    if (cs->exception_index & (RISCV_EXCP_INT_FLAG)) {
+        /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception
+           index is only 32 bits wide */
+        fixed_cause = cs->exception_index & RISCV_EXCP_INT_MASK;
+        fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1);
+    } else {
+        /* fixup User ECALL -> correct priv ECALL */
+        if (cs->exception_index == RISCV_EXCP_U_ECALL) {
+            switch (env->priv) {
+            case PRV_U:
+                fixed_cause = RISCV_EXCP_U_ECALL;
+                break;
+            case PRV_S:
+                fixed_cause = RISCV_EXCP_S_ECALL;
+                break;
+            case PRV_H:
+                fixed_cause = RISCV_EXCP_H_ECALL;
+                break;
+            case PRV_M:
+                fixed_cause = RISCV_EXCP_M_ECALL;
+                break;
+            }
+        } else {
+            fixed_cause = cs->exception_index;
+        }
+    }
+
+    target_ulong backup_epc = env->pc;
+
+    target_ulong bit = fixed_cause;
+    target_ulong deleg = env->medeleg;
+
+    int hasbadaddr =
+        (fixed_cause == RISCV_EXCP_INST_ADDR_MIS) ||
+        (fixed_cause == RISCV_EXCP_INST_ACCESS_FAULT) ||
+        (fixed_cause == RISCV_EXCP_LOAD_ADDR_MIS) ||
+        (fixed_cause == RISCV_EXCP_STORE_AMO_ADDR_MIS) ||
+        (fixed_cause == RISCV_EXCP_LOAD_ACCESS_FAULT) ||
+        (fixed_cause == RISCV_EXCP_STORE_AMO_ACCESS_FAULT) ||
+        (fixed_cause == RISCV_EXCP_INST_PAGE_FAULT) ||
+        (fixed_cause == RISCV_EXCP_LOAD_PAGE_FAULT) ||
+        (fixed_cause == RISCV_EXCP_STORE_PAGE_FAULT);
+
+    if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) {
+        deleg = env->mideleg;
+        bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1));
+    }
+
+    if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) {
+        /* handle the trap in S-mode */
+        /* No need to check STVEC for misaligned - lower 2 bits cannot be set */
+        env->pc = env->stvec;
+        env->scause = fixed_cause;
+        env->sepc = backup_epc;
+
+        if (hasbadaddr) {
+            if (RISCV_DEBUG_INTERRUPT) {
+                qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld
+                    ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr);
+            }
+            env->sbadaddr = env->badaddr;
+        }
+
+        target_ulong s = env->mstatus;
+        s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ?
+            get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv));
+        s = set_field(s, MSTATUS_SPP, env->priv);
+        s = set_field(s, MSTATUS_SIE, 0);
+        csr_write_helper(env, s, CSR_MSTATUS);
+        riscv_set_mode(env, PRV_S);
+    } else {
+        /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */
+        env->pc = env->mtvec;
+        env->mepc = backup_epc;
+        env->mcause = fixed_cause;
+
+        if (hasbadaddr) {
+            if (RISCV_DEBUG_INTERRUPT) {
+                qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld
+                    ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr);
+            }
+            env->mbadaddr = env->badaddr;
+        }
+
+        target_ulong s = env->mstatus;
+        s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ?
+            get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv));
+        s = set_field(s, MSTATUS_MPP, env->priv);
+        s = set_field(s, MSTATUS_MIE, 0);
+        csr_write_helper(env, s, CSR_MSTATUS);
+        riscv_set_mode(env, PRV_M);
+    }
+    /* TODO yield load reservation  */
+#endif
+    cs->exception_index = EXCP_NONE; /* mark handled to qemu */
+}
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
new file mode 100644
index 0000000..debb22a
--- /dev/null
+++ b/target/riscv/helper.h
@@ -0,0 +1,78 @@
+/* Exceptions */
+DEF_HELPER_2(raise_exception, noreturn, env, i32)
+
+/* Floating Point - rounding mode */
+DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_WG, void, env, i32)
+
+/* Floating Point - fused */
+DEF_HELPER_FLAGS_4(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+
+/* Floating Point - Single Precision */
+DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_l_s, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_lu_s, TCG_CALL_NO_RWG, tl, env, i64)
+#endif
+DEF_HELPER_FLAGS_2(fcvt_s_w, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl)
+#endif
+DEF_HELPER_FLAGS_1(fclass_s, TCG_CALL_NO_RWG_SE, tl, i64)
+
+/* Floating Point - Double Precision */
+DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64)
+#endif
+DEF_HELPER_FLAGS_2(fcvt_d_w, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl)
+#endif
+DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64)
+
+/* Special functions */
+DEF_HELPER_3(csrrw, tl, env, tl, tl)
+DEF_HELPER_4(csrrs, tl, env, tl, tl, tl)
+DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_2(sret, tl, env, tl)
+DEF_HELPER_2(mret, tl, env, tl)
+DEF_HELPER_1(wfi, void, env)
+DEF_HELPER_1(tlb_flush, void, env)
+#endif
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
new file mode 100644
index 0000000..e34715d
--- /dev/null
+++ b/target/riscv/op_helper.c
@@ -0,0 +1,669 @@
+/*
+ * RISC-V Emulation Helpers for QEMU.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "qemu/main-loop.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+
+#ifndef CONFIG_USER_ONLY
+
+#if defined(TARGET_RISCV32)
+static const char valid_vm_1_09[16] = {
+    [VM_1_09_MBARE] = 1,
+    [VM_1_09_SV32] = 1,
+};
+static const char valid_vm_1_10[16] = {
+    [VM_1_10_MBARE] = 1,
+    [VM_1_10_SV32] = 1
+};
+#elif defined(TARGET_RISCV64)
+static const char valid_vm_1_09[16] = {
+    [VM_1_09_MBARE] = 1,
+    [VM_1_09_SV39] = 1,
+    [VM_1_09_SV48] = 1,
+};
+static const char valid_vm_1_10[16] = {
+    [VM_1_10_MBARE] = 1,
+    [VM_1_10_SV39] = 1,
+    [VM_1_10_SV48] = 1,
+    [VM_1_10_SV57] = 1
+};
+#endif
+
+static int validate_vm(CPURISCVState *env, target_ulong vm)
+{
+    return (env->priv_ver >= PRIV_VERSION_1_10_0) ?
+        valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf];
+}
+
+#endif
+
+/* Exceptions processing helpers */
+void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
+                                          uint32_t exception, uintptr_t pc)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception);
+    cs->exception_index = exception;
+    cpu_loop_exit_restore(cs, pc);
+}
+
+void helper_raise_exception(CPURISCVState *env, uint32_t exception)
+{
+    do_raise_exception_err(env, exception, 0);
+}
+
+static void validate_mstatus_fs(CPURISCVState *env, uintptr_t ra)
+{
+#ifndef CONFIG_USER_ONLY
+    if (!(env->mstatus & MSTATUS_FS)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra);
+    }
+#endif
+}
+
+/*
+ * Handle writes to CSRs and any resulting special behavior
+ *
+ * Adapted from Spike's processor_t::set_csr
+ */
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+        target_ulong csrno)
+{
+#ifndef CONFIG_USER_ONLY
+    uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP);
+    uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP;
+#endif
+
+    switch (csrno) {
+    case CSR_FFLAGS:
+        validate_mstatus_fs(env, GETPC());
+        cpu_riscv_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT));
+        break;
+    case CSR_FRM:
+        validate_mstatus_fs(env, GETPC());
+        env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT);
+        break;
+    case CSR_FCSR:
+        validate_mstatus_fs(env, GETPC());
+        env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT;
+        cpu_riscv_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT);
+        break;
+#ifndef CONFIG_USER_ONLY
+    case CSR_MSTATUS: {
+        target_ulong mstatus = env->mstatus;
+        target_ulong mask = 0;
+        target_ulong mpp = get_field(val_to_write, MSTATUS_MPP);
+
+        /* flush tlb on mstatus fields that affect VM */
+        if (env->priv_ver <= PRIV_VERSION_1_09_1) {
+            if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
+                    MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) {
+                helper_tlb_flush(env);
+            }
+            mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
+                MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
+                MSTATUS_MPP | MSTATUS_MXR |
+                (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ?
+                    MSTATUS_VM : 0);
+        }
+        if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+            if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
+                    MSTATUS_MPRV | MSTATUS_SUM)) {
+                helper_tlb_flush(env);
+            }
+            mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
+                MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
+                MSTATUS_MPP | MSTATUS_MXR;
+        }
+
+        /* silenty discard mstatus.mpp writes for unsupported modes */
+        if (mpp == PRV_H ||
+            (!riscv_has_ext(env, RVS) && mpp == PRV_S) ||
+            (!riscv_has_ext(env, RVU) && mpp == PRV_U)) {
+            mask &= ~MSTATUS_MPP;
+        }
+
+        mstatus = (mstatus & ~mask) | (val_to_write & mask);
+        int dirty = (mstatus & MSTATUS_FS) == MSTATUS_FS;
+        dirty |= (mstatus & MSTATUS_XS) == MSTATUS_XS;
+        mstatus = set_field(mstatus, MSTATUS_SD, dirty);
+        env->mstatus = mstatus;
+        break;
+    }
+    case CSR_MIP: {
+        /*
+         * Since the writeable bits in MIP are not set asynchrously by the
+         * CLINT, no additional locking is needed for read-modifiy-write
+         * CSR operations
+         */
+        qemu_mutex_lock_iothread();
+        RISCVCPU *cpu = riscv_env_get_cpu(env);
+        riscv_set_local_interrupt(cpu, MIP_SSIP,
+                                  (val_to_write & MIP_SSIP) != 0);
+        riscv_set_local_interrupt(cpu, MIP_STIP,
+                                  (val_to_write & MIP_STIP) != 0);
+        /*
+         * csrs, csrc on mip.SEIP is not decomposable into separate read and
+         * write steps, so a different implementation is needed
+         */
+        qemu_mutex_unlock_iothread();
+        break;
+    }
+    case CSR_MIE: {
+        env->mie = (env->mie & ~all_ints) |
+            (val_to_write & all_ints);
+        break;
+    }
+    case CSR_MIDELEG:
+        env->mideleg = (env->mideleg & ~delegable_ints)
+                                | (val_to_write & delegable_ints);
+        break;
+    case CSR_MEDELEG: {
+        target_ulong mask = 0;
+        mask |= 1ULL << (RISCV_EXCP_INST_ADDR_MIS);
+        mask |= 1ULL << (RISCV_EXCP_INST_ACCESS_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_ILLEGAL_INST);
+        mask |= 1ULL << (RISCV_EXCP_BREAKPOINT);
+        mask |= 1ULL << (RISCV_EXCP_LOAD_ADDR_MIS);
+        mask |= 1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS);
+        mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_U_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_S_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_H_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_M_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_INST_PAGE_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_STORE_PAGE_FAULT);
+        env->medeleg = (env->medeleg & ~mask)
+                                | (val_to_write & mask);
+        break;
+    }
+    case CSR_MINSTRET:
+        qemu_log_mask(LOG_UNIMP, "CSR_MINSTRET: write not implemented");
+        goto do_illegal;
+    case CSR_MCYCLE:
+        qemu_log_mask(LOG_UNIMP, "CSR_MCYCLE: write not implemented");
+        goto do_illegal;
+    case CSR_MINSTRETH:
+        qemu_log_mask(LOG_UNIMP, "CSR_MINSTRETH: write not implemented");
+        goto do_illegal;
+    case CSR_MCYCLEH:
+        qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented");
+        goto do_illegal;
+    case CSR_MUCOUNTEREN:
+        env->mucounteren = val_to_write;
+        break;
+    case CSR_MSCOUNTEREN:
+        env->mscounteren = val_to_write;
+        break;
+    case CSR_SSTATUS: {
+        target_ulong ms = env->mstatus;
+        target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE
+            | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS
+            | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD;
+        ms = (ms & ~mask) | (val_to_write & mask);
+        csr_write_helper(env, ms, CSR_MSTATUS);
+        break;
+    }
+    case CSR_SIP: {
+        qemu_mutex_lock_iothread();
+        target_ulong next_mip = (env->mip & ~env->mideleg)
+                                | (val_to_write & env->mideleg);
+        qemu_mutex_unlock_iothread();
+        csr_write_helper(env, next_mip, CSR_MIP);
+        break;
+    }
+    case CSR_SIE: {
+        target_ulong next_mie = (env->mie & ~env->mideleg)
+                                | (val_to_write & env->mideleg);
+        csr_write_helper(env, next_mie, CSR_MIE);
+        break;
+    }
+    case CSR_SATP: /* CSR_SPTBR */ {
+        if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
+            goto do_illegal;
+        }
+        if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr))
+        {
+            helper_tlb_flush(env);
+            env->sptbr = val_to_write & (((target_ulong)
+                1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1);
+        }
+        if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
+            validate_vm(env, get_field(val_to_write, SATP_MODE)) &&
+            ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN)))
+        {
+            helper_tlb_flush(env);
+            env->satp = val_to_write;
+        }
+        break;
+    }
+    case CSR_SEPC:
+        env->sepc = val_to_write;
+        break;
+    case CSR_STVEC:
+        if (val_to_write & 1) {
+            qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported");
+            goto do_illegal;
+        }
+        env->stvec = val_to_write >> 2 << 2;
+        break;
+    case CSR_SCOUNTEREN:
+        env->scounteren = val_to_write;
+        break;
+    case CSR_SSCRATCH:
+        env->sscratch = val_to_write;
+        break;
+    case CSR_SCAUSE:
+        env->scause = val_to_write;
+        break;
+    case CSR_SBADADDR:
+        env->sbadaddr = val_to_write;
+        break;
+    case CSR_MEPC:
+        env->mepc = val_to_write;
+        break;
+    case CSR_MTVEC:
+        if (val_to_write & 1) {
+            qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported");
+            goto do_illegal;
+        }
+        env->mtvec = val_to_write >> 2 << 2;
+        break;
+    case CSR_MCOUNTEREN:
+        env->mcounteren = val_to_write;
+        break;
+    case CSR_MSCRATCH:
+        env->mscratch = val_to_write;
+        break;
+    case CSR_MCAUSE:
+        env->mcause = val_to_write;
+        break;
+    case CSR_MBADADDR:
+        env->mbadaddr = val_to_write;
+        break;
+    case CSR_MISA: {
+        qemu_log_mask(LOG_UNIMP, "CSR_MISA: misa writes not supported");
+        goto do_illegal;
+    }
+    case CSR_PMPCFG0:
+    case CSR_PMPCFG1:
+    case CSR_PMPCFG2:
+    case CSR_PMPCFG3:
+       pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write);
+       break;
+    case CSR_PMPADDR0:
+    case CSR_PMPADDR1:
+    case CSR_PMPADDR2:
+    case CSR_PMPADDR3:
+    case CSR_PMPADDR4:
+    case CSR_PMPADDR5:
+    case CSR_PMPADDR6:
+    case CSR_PMPADDR7:
+    case CSR_PMPADDR8:
+    case CSR_PMPADDR9:
+    case CSR_PMPADDR10:
+    case CSR_PMPADDR11:
+    case CSR_PMPADDR12:
+    case CSR_PMPADDR13:
+    case CSR_PMPADDR14:
+    case CSR_PMPADDR15:
+       pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write);
+       break;
+    do_illegal:
+#endif
+    default:
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+}
+
+/*
+ * Handle reads to CSRs and any resulting special behavior
+ *
+ * Adapted from Spike's processor_t::get_csr
+ */
+target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno)
+{
+#ifndef CONFIG_USER_ONLY
+    target_ulong ctr_en = env->priv == PRV_U ? env->mucounteren :
+                   env->priv == PRV_S ? env->mscounteren : -1U;
+#else
+    target_ulong ctr_en = -1;
+#endif
+    target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1;
+
+    if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) {
+        if (ctr_ok) {
+            return 0;
+        }
+    }
+#if defined(TARGET_RISCV32)
+    if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) {
+        if (ctr_ok) {
+            return 0;
+        }
+    }
+#endif
+    if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) {
+        return 0;
+    }
+#if defined(TARGET_RISCV32)
+    if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) {
+        return 0;
+    }
+#endif
+    if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) {
+        return 0;
+    }
+
+    switch (csrno) {
+    case CSR_FFLAGS:
+        validate_mstatus_fs(env, GETPC());
+        return cpu_riscv_get_fflags(env);
+    case CSR_FRM:
+        validate_mstatus_fs(env, GETPC());
+        return env->frm;
+    case CSR_FCSR:
+        validate_mstatus_fs(env, GETPC());
+        return (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT)
+                | (env->frm << FSR_RD_SHIFT);
+    /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */
+#ifdef CONFIG_USER_ONLY
+    case CSR_TIME:
+        return cpu_get_host_ticks();
+#if defined(TARGET_RISCV32)
+    case CSR_TIMEH:
+        return cpu_get_host_ticks() >> 32;
+#endif
+#endif
+    case CSR_INSTRET:
+    case CSR_CYCLE:
+        if (ctr_ok) {
+            return cpu_get_host_ticks();
+        }
+        break;
+#if defined(TARGET_RISCV32)
+    case CSR_INSTRETH:
+    case CSR_CYCLEH:
+        if (ctr_ok) {
+            return cpu_get_host_ticks() >> 32;
+        }
+        break;
+#endif
+#ifndef CONFIG_USER_ONLY
+    case CSR_MINSTRET:
+    case CSR_MCYCLE:
+        return cpu_get_host_ticks();
+    case CSR_MINSTRETH:
+    case CSR_MCYCLEH:
+#if defined(TARGET_RISCV32)
+        return cpu_get_host_ticks() >> 32;
+#endif
+        break;
+    case CSR_MUCOUNTEREN:
+        return env->mucounteren;
+    case CSR_MSCOUNTEREN:
+        return env->mscounteren;
+    case CSR_SSTATUS: {
+        target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE
+            | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS
+            | SSTATUS_SUM |  SSTATUS_SD;
+        if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+            mask |= SSTATUS_MXR;
+        }
+        return env->mstatus & mask;
+    }
+    case CSR_SIP: {
+        qemu_mutex_lock_iothread();
+        target_ulong tmp = env->mip & env->mideleg;
+        qemu_mutex_unlock_iothread();
+        return tmp;
+    }
+    case CSR_SIE:
+        return env->mie & env->mideleg;
+    case CSR_SEPC:
+        return env->sepc;
+    case CSR_SBADADDR:
+        return env->sbadaddr;
+    case CSR_STVEC:
+        return env->stvec;
+    case CSR_SCOUNTEREN:
+        return env->scounteren;
+    case CSR_SCAUSE:
+        return env->scause;
+    case CSR_SPTBR:
+        if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+            return env->satp;
+        } else {
+            return env->sptbr;
+        }
+    case CSR_SSCRATCH:
+        return env->sscratch;
+    case CSR_MSTATUS:
+        return env->mstatus;
+    case CSR_MIP: {
+        qemu_mutex_lock_iothread();
+        target_ulong tmp = env->mip;
+        qemu_mutex_unlock_iothread();
+        return tmp;
+    }
+    case CSR_MIE:
+        return env->mie;
+    case CSR_MEPC:
+        return env->mepc;
+    case CSR_MSCRATCH:
+        return env->mscratch;
+    case CSR_MCAUSE:
+        return env->mcause;
+    case CSR_MBADADDR:
+        return env->mbadaddr;
+    case CSR_MISA:
+        return env->misa;
+    case CSR_MARCHID:
+        return 0; /* as spike does */
+    case CSR_MIMPID:
+        return 0; /* as spike does */
+    case CSR_MVENDORID:
+        return 0; /* as spike does */
+    case CSR_MHARTID:
+        return env->mhartid;
+    case CSR_MTVEC:
+        return env->mtvec;
+    case CSR_MCOUNTEREN:
+        return env->mcounteren;
+    case CSR_MEDELEG:
+        return env->medeleg;
+    case CSR_MIDELEG:
+        return env->mideleg;
+    case CSR_PMPCFG0:
+    case CSR_PMPCFG1:
+    case CSR_PMPCFG2:
+    case CSR_PMPCFG3:
+       return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0);
+    case CSR_PMPADDR0:
+    case CSR_PMPADDR1:
+    case CSR_PMPADDR2:
+    case CSR_PMPADDR3:
+    case CSR_PMPADDR4:
+    case CSR_PMPADDR5:
+    case CSR_PMPADDR6:
+    case CSR_PMPADDR7:
+    case CSR_PMPADDR8:
+    case CSR_PMPADDR9:
+    case CSR_PMPADDR10:
+    case CSR_PMPADDR11:
+    case CSR_PMPADDR12:
+    case CSR_PMPADDR13:
+    case CSR_PMPADDR14:
+    case CSR_PMPADDR15:
+       return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0);
+#endif
+    }
+    /* used by e.g. MTIME read */
+    do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+}
+
+/*
+ * Check that CSR access is allowed.
+ *
+ * Adapted from Spike's decode.h:validate_csr
+ */
+static void validate_csr(CPURISCVState *env, uint64_t which,
+                         uint64_t write, uintptr_t ra)
+{
+#ifndef CONFIG_USER_ONLY
+    unsigned csr_priv = get_field((which), 0x300);
+    unsigned csr_read_only = get_field((which), 0xC00) == 3;
+    if (((write) && csr_read_only) || (env->priv < csr_priv)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra);
+    }
+#endif
+}
+
+target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
+        target_ulong csr)
+{
+    validate_csr(env, csr, 1, GETPC());
+    uint64_t csr_backup = csr_read_helper(env, csr);
+    csr_write_helper(env, src, csr);
+    return csr_backup;
+}
+
+target_ulong helper_csrrs(CPURISCVState *env, target_ulong src,
+        target_ulong csr, target_ulong rs1_pass)
+{
+    validate_csr(env, csr, rs1_pass != 0, GETPC());
+    uint64_t csr_backup = csr_read_helper(env, csr);
+    if (rs1_pass != 0) {
+        csr_write_helper(env, src | csr_backup, csr);
+    }
+    return csr_backup;
+}
+
+target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
+        target_ulong csr, target_ulong rs1_pass)
+{
+    validate_csr(env, csr, rs1_pass != 0, GETPC());
+    uint64_t csr_backup = csr_read_helper(env, csr);
+    if (rs1_pass != 0) {
+        csr_write_helper(env, (~src) & csr_backup, csr);
+    }
+    return csr_backup;
+}
+
+#ifndef CONFIG_USER_ONLY
+
+/* iothread_mutex must be held */
+void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value)
+{
+    target_ulong old_mip = cpu->env.mip;
+    cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0);
+
+    if (cpu->env.mip && !old_mip) {
+        cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+    } else if (!cpu->env.mip && old_mip) {
+        cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+    }
+}
+
+void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)
+{
+    if (newpriv > PRV_M) {
+        g_assert_not_reached();
+    }
+    if (newpriv == PRV_H) {
+        newpriv = PRV_U;
+    }
+    /* tlb_flush is unnecessary as mode is contained in mmu_idx */
+    env->priv = newpriv;
+}
+
+target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+    if (!(env->priv >= PRV_S)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    target_ulong retpc = env->sepc;
+    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
+        do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    target_ulong mstatus = env->mstatus;
+    target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP);
+    mstatus = set_field(mstatus,
+        env->priv_ver >= PRIV_VERSION_1_10_0 ?
+        MSTATUS_SIE : MSTATUS_UIE << prev_priv,
+        get_field(mstatus, MSTATUS_SPIE));
+    mstatus = set_field(mstatus, MSTATUS_SPIE, 0);
+    mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
+    riscv_set_mode(env, prev_priv);
+    csr_write_helper(env, mstatus, CSR_MSTATUS);
+
+    return retpc;
+}
+
+target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+    if (!(env->priv >= PRV_M)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    target_ulong retpc = env->mepc;
+    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
+        do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    target_ulong mstatus = env->mstatus;
+    target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
+    mstatus = set_field(mstatus,
+        env->priv_ver >= PRIV_VERSION_1_10_0 ?
+        MSTATUS_MIE : MSTATUS_UIE << prev_priv,
+        get_field(mstatus, MSTATUS_MPIE));
+    mstatus = set_field(mstatus, MSTATUS_MPIE, 0);
+    mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
+    riscv_set_mode(env, prev_priv);
+    csr_write_helper(env, mstatus, CSR_MSTATUS);
+
+    return retpc;
+}
+
+
+void helper_wfi(CPURISCVState *env)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+
+    cs->halted = 1;
+    cs->exception_index = EXCP_HLT;
+    cpu_loop_exit(cs);
+}
+
+void helper_tlb_flush(CPURISCVState *env)
+{
+    RISCVCPU *cpu = riscv_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+    tlb_flush(cs);
+}
+
+#endif /* !CONFIG_USER_ONLY */
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 06/23] RISC-V FPU Support
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (4 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 05/23] RISC-V CPU Helpers Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 07/23] RISC-V GDB Stub Michael Clark
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Helper routines for FPU instructions and NaN definitions.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 fpu/softfloat-specialize.h |   7 +-
 target/riscv/fpu_helper.c  | 373 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 377 insertions(+), 3 deletions(-)
 create mode 100644 target/riscv/fpu_helper.c

diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index e81ca00..e2844d0 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -114,7 +114,8 @@ float32 float32_default_nan(float_status *status)
 #if defined(TARGET_SPARC) || defined(TARGET_M68K)
     return const_float32(0x7FFFFFFF);
 #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
-      defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE)
+      defined(TARGET_XTENSA) || defined(TARGET_S390X) || \
+      defined(TARGET_TRICORE) || defined(TARGET_RISCV)
     return const_float32(0x7FC00000);
 #elif defined(TARGET_HPPA)
     return const_float32(0x7FA00000);
@@ -139,7 +140,7 @@ float64 float64_default_nan(float_status *status)
 #if defined(TARGET_SPARC) || defined(TARGET_M68K)
     return const_float64(LIT64(0x7FFFFFFFFFFFFFFF));
 #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
-      defined(TARGET_S390X)
+      defined(TARGET_S390X) || defined(TARGET_RISCV)
     return const_float64(LIT64(0x7FF8000000000000));
 #elif defined(TARGET_HPPA)
     return const_float64(LIT64(0x7FF4000000000000));
@@ -189,7 +190,7 @@ float128 float128_default_nan(float_status *status)
         r.high = LIT64(0x7FFF7FFFFFFFFFFF);
     } else {
         r.low = LIT64(0x0000000000000000);
-#if defined(TARGET_S390X) || defined(TARGET_PPC)
+#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV)
         r.high = LIT64(0x7FFF800000000000);
 #else
         r.high = LIT64(0xFFFF800000000000);
diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
new file mode 100644
index 0000000..abbadea
--- /dev/null
+++ b/target/riscv/fpu_helper.c
@@ -0,0 +1,373 @@
+/*
+ * RISC-V FPU Emulation Helpers for QEMU.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include <stdlib.h>
+#include "cpu.h"
+#include "qemu/host-utils.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+
+target_ulong cpu_riscv_get_fflags(CPURISCVState *env)
+{
+    int soft = get_float_exception_flags(&env->fp_status);
+    target_ulong hard = 0;
+
+    hard |= (soft & float_flag_inexact) ? FPEXC_NX : 0;
+    hard |= (soft & float_flag_underflow) ? FPEXC_UF : 0;
+    hard |= (soft & float_flag_overflow) ? FPEXC_OF : 0;
+    hard |= (soft & float_flag_divbyzero) ? FPEXC_DZ : 0;
+    hard |= (soft & float_flag_invalid) ? FPEXC_NV : 0;
+
+    return hard;
+}
+
+void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard)
+{
+    int soft = 0;
+
+    soft |= (hard & FPEXC_NX) ? float_flag_inexact : 0;
+    soft |= (hard & FPEXC_UF) ? float_flag_underflow : 0;
+    soft |= (hard & FPEXC_OF) ? float_flag_overflow : 0;
+    soft |= (hard & FPEXC_DZ) ? float_flag_divbyzero : 0;
+    soft |= (hard & FPEXC_NV) ? float_flag_invalid : 0;
+
+    set_float_exception_flags(soft, &env->fp_status);
+}
+
+void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm)
+{
+    int softrm;
+
+    if (rm == 7) {
+        rm = env->frm;
+    }
+    switch (rm) {
+    case 0:
+        softrm = float_round_nearest_even;
+        break;
+    case 1:
+        softrm = float_round_to_zero;
+        break;
+    case 2:
+        softrm = float_round_down;
+        break;
+    case 3:
+        softrm = float_round_up;
+        break;
+    case 4:
+        softrm = float_round_ties_away;
+        break;
+    default:
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    set_float_rounding_mode(softrm, &env->fp_status);
+}
+
+uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, 0, &env->fp_status);
+}
+
+uint64_t helper_fmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, 0, &env->fp_status);
+}
+
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c,
+                          &env->fp_status);
+}
+
+uint64_t helper_fmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c,
+                          &env->fp_status);
+}
+
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, float_muladd_negate_product,
+                          &env->fp_status);
+}
+
+uint64_t helper_fnmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, float_muladd_negate_product,
+                          &env->fp_status);
+}
+
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c |
+                          float_muladd_negate_product, &env->fp_status);
+}
+
+uint64_t helper_fnmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c |
+                          float_muladd_negate_product, &env->fp_status);
+}
+
+uint64_t helper_fadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_add(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_sub(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmul_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_mul(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_div(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_minnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmax_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_maxnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_sqrt(frs1, &env->fp_status);
+}
+
+target_ulong helper_fle_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_le(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_flt_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_lt(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_feq_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_eq_quiet(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_to_int32(frs1, &env->fp_status);
+}
+
+target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t frs1)
+{
+    return (int32_t)float32_to_uint32(frs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_l_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_to_int64(frs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_lu_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_to_uint64(frs1, &env->fp_status);
+}
+#endif
+
+uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1)
+{
+    return int32_to_float32((int32_t)rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1)
+{
+    return uint32_to_float32((uint32_t)rs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_s_l(CPURISCVState *env, uint64_t rs1)
+{
+    return int64_to_float32(rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1)
+{
+    return uint64_to_float32(rs1, &env->fp_status);
+}
+#endif
+
+target_ulong helper_fclass_s(uint64_t frs1)
+{
+    float32 f = frs1;
+    bool sign = float32_is_neg(f);
+
+    if (float32_is_infinity(f)) {
+        return sign ? 1 << 0 : 1 << 7;
+    } else if (float32_is_zero(f)) {
+        return sign ? 1 << 3 : 1 << 4;
+    } else if (float32_is_zero_or_denormal(f)) {
+        return sign ? 1 << 2 : 1 << 5;
+    } else if (float32_is_any_nan(f)) {
+        float_status s = { }; /* for snan_bit_is_one */
+        return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8;
+    } else {
+        return sign ? 1 << 1 : 1 << 6;
+    }
+}
+
+uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_add(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_sub(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmul_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_mul(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_div(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_minnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_maxnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1)
+{
+    rs1 = float64_to_float32(rs1, &env->fp_status);
+    return float32_maybe_silence_nan(rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1)
+{
+    rs1 = float32_to_float64(rs1, &env->fp_status);
+    return float64_maybe_silence_nan(rs1, &env->fp_status);
+}
+
+uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_sqrt(frs1, &env->fp_status);
+}
+
+target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_le(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_lt(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_eq_quiet(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_to_int32(frs1, &env->fp_status);
+}
+
+target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1)
+{
+    return (int32_t)float64_to_uint32(frs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_to_int64(frs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_to_uint64(frs1, &env->fp_status);
+}
+#endif
+
+uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1)
+{
+    return int32_to_float64((int32_t)rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_d_wu(CPURISCVState *env, target_ulong rs1)
+{
+    return uint32_to_float64((uint32_t)rs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_d_l(CPURISCVState *env, uint64_t rs1)
+{
+    return int64_to_float64(rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1)
+{
+    return uint64_to_float64(rs1, &env->fp_status);
+}
+#endif
+
+target_ulong helper_fclass_d(uint64_t frs1)
+{
+    float64 f = frs1;
+    bool sign = float64_is_neg(f);
+
+    if (float64_is_infinity(f)) {
+        return sign ? 1 << 0 : 1 << 7;
+    } else if (float64_is_zero(f)) {
+        return sign ? 1 << 3 : 1 << 4;
+    } else if (float64_is_zero_or_denormal(f)) {
+        return sign ? 1 << 2 : 1 << 5;
+    } else if (float64_is_any_nan(f)) {
+        float_status s = { }; /* for snan_bit_is_one */
+        return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8;
+    } else {
+        return sign ? 1 << 1 : 1 << 6;
+    }
+}
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 07/23] RISC-V GDB Stub
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (5 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 06/23] RISC-V FPU Support Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 12:46   ` Philippe Mathieu-Daudé
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 08/23] RISC-V TCG Code Generation Michael Clark
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

GDB Register read and write routines.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 target/riscv/gdbstub.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 target/riscv/gdbstub.c

diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
new file mode 100644
index 0000000..4f919b6
--- /dev/null
+++ b/target/riscv/gdbstub.c
@@ -0,0 +1,62 @@
+/*
+ * RISC-V GDB Server Stub
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "exec/gdbstub.h"
+#include "cpu.h"
+
+int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+
+    if (n < 32) {
+        return gdb_get_regl(mem_buf, env->gpr[n]);
+    } else if (n == 32) {
+        return gdb_get_regl(mem_buf, env->pc);
+    } else if (n < 65) {
+        return gdb_get_reg64(mem_buf, env->fpr[n - 33]);
+    } else if (n < 4096 + 65) {
+        return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65));
+    }
+    return 0;
+}
+
+int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+
+    if (n == 0) {
+        /* discard writes to x0 */
+        return sizeof(target_ulong);
+    } else if (n < 32) {
+        env->gpr[n] = ldtul_p(mem_buf);
+        return sizeof(target_ulong);
+    } else if (n == 32) {
+        env->pc = ldtul_p(mem_buf);
+        return sizeof(target_ulong);
+    } else if (n < 65) {
+        env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */
+        return sizeof(uint64_t);
+    } else if (n < 4096 + 65) {
+        csr_write_helper(env, ldtul_p(mem_buf), n - 65);
+    }
+    return 0;
+}
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 08/23] RISC-V TCG Code Generation
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (6 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 07/23] RISC-V GDB Stub Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 09/23] RISC-V Physical Memory Protection Michael Clark
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

TCG code generation for the RV32IMAFDC and RV64IMAFDC. The QEMU
RISC-V code generator has complete coverage for the Base ISA v2.2,
Privileged ISA v1.9.1 and Privileged ISA v1.10:

- RISC-V Instruction Set Manual Volume I: User-Level ISA Version 2.2
- RISC-V Instruction Set Manual Volume II: Privileged ISA Version 1.9.1
- RISC-V Instruction Set Manual Volume II: Privileged ISA Version 1.10

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 target/riscv/instmap.h   |  364 +++++++++
 target/riscv/translate.c | 1978 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2342 insertions(+)
 create mode 100644 target/riscv/instmap.h
 create mode 100644 target/riscv/translate.c

diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h
new file mode 100644
index 0000000..58baa1b
--- /dev/null
+++ b/target/riscv/instmap.h
@@ -0,0 +1,364 @@
+/*
+ * RISC-V emulation for qemu: Instruction decode helpers
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#define MASK_OP_MAJOR(op)  (op & 0x7F)
+enum {
+    /* rv32i, rv64i, rv32m */
+    OPC_RISC_LUI    = (0x37),
+    OPC_RISC_AUIPC  = (0x17),
+    OPC_RISC_JAL    = (0x6F),
+    OPC_RISC_JALR   = (0x67),
+    OPC_RISC_BRANCH = (0x63),
+    OPC_RISC_LOAD   = (0x03),
+    OPC_RISC_STORE  = (0x23),
+    OPC_RISC_ARITH_IMM  = (0x13),
+    OPC_RISC_ARITH      = (0x33),
+    OPC_RISC_FENCE      = (0x0F),
+    OPC_RISC_SYSTEM     = (0x73),
+
+    /* rv64i, rv64m */
+    OPC_RISC_ARITH_IMM_W = (0x1B),
+    OPC_RISC_ARITH_W = (0x3B),
+
+    /* rv32a, rv64a */
+    OPC_RISC_ATOMIC = (0x2F),
+
+    /* floating point */
+    OPC_RISC_FP_LOAD = (0x7),
+    OPC_RISC_FP_STORE = (0x27),
+
+    OPC_RISC_FMADD = (0x43),
+    OPC_RISC_FMSUB = (0x47),
+    OPC_RISC_FNMSUB = (0x4B),
+    OPC_RISC_FNMADD = (0x4F),
+
+    OPC_RISC_FP_ARITH = (0x53),
+};
+
+#define MASK_OP_ARITH(op)   (MASK_OP_MAJOR(op) | (op & ((0x7 << 12) | \
+                            (0x7F << 25))))
+enum {
+    OPC_RISC_ADD   = OPC_RISC_ARITH | (0x0 << 12) | (0x00 << 25),
+    OPC_RISC_SUB   = OPC_RISC_ARITH | (0x0 << 12) | (0x20 << 25),
+    OPC_RISC_SLL   = OPC_RISC_ARITH | (0x1 << 12) | (0x00 << 25),
+    OPC_RISC_SLT   = OPC_RISC_ARITH | (0x2 << 12) | (0x00 << 25),
+    OPC_RISC_SLTU  = OPC_RISC_ARITH | (0x3 << 12) | (0x00 << 25),
+    OPC_RISC_XOR   = OPC_RISC_ARITH | (0x4 << 12) | (0x00 << 25),
+    OPC_RISC_SRL   = OPC_RISC_ARITH | (0x5 << 12) | (0x00 << 25),
+    OPC_RISC_SRA   = OPC_RISC_ARITH | (0x5 << 12) | (0x20 << 25),
+    OPC_RISC_OR    = OPC_RISC_ARITH | (0x6 << 12) | (0x00 << 25),
+    OPC_RISC_AND   = OPC_RISC_ARITH | (0x7 << 12) | (0x00 << 25),
+
+    /* RV64M */
+    OPC_RISC_MUL    = OPC_RISC_ARITH | (0x0 << 12) | (0x01 << 25),
+    OPC_RISC_MULH   = OPC_RISC_ARITH | (0x1 << 12) | (0x01 << 25),
+    OPC_RISC_MULHSU = OPC_RISC_ARITH | (0x2 << 12) | (0x01 << 25),
+    OPC_RISC_MULHU  = OPC_RISC_ARITH | (0x3 << 12) | (0x01 << 25),
+
+    OPC_RISC_DIV    = OPC_RISC_ARITH | (0x4 << 12) | (0x01 << 25),
+    OPC_RISC_DIVU   = OPC_RISC_ARITH | (0x5 << 12) | (0x01 << 25),
+    OPC_RISC_REM    = OPC_RISC_ARITH | (0x6 << 12) | (0x01 << 25),
+    OPC_RISC_REMU   = OPC_RISC_ARITH | (0x7 << 12) | (0x01 << 25),
+};
+
+
+#define MASK_OP_ARITH_IMM(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_ADDI   = OPC_RISC_ARITH_IMM | (0x0 << 12),
+    OPC_RISC_SLTI   = OPC_RISC_ARITH_IMM | (0x2 << 12),
+    OPC_RISC_SLTIU  = OPC_RISC_ARITH_IMM | (0x3 << 12),
+    OPC_RISC_XORI   = OPC_RISC_ARITH_IMM | (0x4 << 12),
+    OPC_RISC_ORI    = OPC_RISC_ARITH_IMM | (0x6 << 12),
+    OPC_RISC_ANDI   = OPC_RISC_ARITH_IMM | (0x7 << 12),
+    OPC_RISC_SLLI   = OPC_RISC_ARITH_IMM | (0x1 << 12), /* additional part of
+                                                           IMM */
+    OPC_RISC_SHIFT_RIGHT_I = OPC_RISC_ARITH_IMM | (0x5 << 12) /* SRAI, SRLI */
+};
+
+#define MASK_OP_BRANCH(op)     (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_BEQ  = OPC_RISC_BRANCH  | (0x0  << 12),
+    OPC_RISC_BNE  = OPC_RISC_BRANCH  | (0x1  << 12),
+    OPC_RISC_BLT  = OPC_RISC_BRANCH  | (0x4  << 12),
+    OPC_RISC_BGE  = OPC_RISC_BRANCH  | (0x5  << 12),
+    OPC_RISC_BLTU = OPC_RISC_BRANCH  | (0x6  << 12),
+    OPC_RISC_BGEU = OPC_RISC_BRANCH  | (0x7  << 12)
+};
+
+enum {
+    OPC_RISC_ADDIW   = OPC_RISC_ARITH_IMM_W | (0x0 << 12),
+    OPC_RISC_SLLIW   = OPC_RISC_ARITH_IMM_W | (0x1 << 12), /* additional part of
+                                                              IMM */
+    OPC_RISC_SHIFT_RIGHT_IW = OPC_RISC_ARITH_IMM_W | (0x5 << 12) /* SRAI, SRLI
+                                                                  */
+};
+
+enum {
+    OPC_RISC_ADDW   = OPC_RISC_ARITH_W | (0x0 << 12) | (0x00 << 25),
+    OPC_RISC_SUBW   = OPC_RISC_ARITH_W | (0x0 << 12) | (0x20 << 25),
+    OPC_RISC_SLLW   = OPC_RISC_ARITH_W | (0x1 << 12) | (0x00 << 25),
+    OPC_RISC_SRLW   = OPC_RISC_ARITH_W | (0x5 << 12) | (0x00 << 25),
+    OPC_RISC_SRAW   = OPC_RISC_ARITH_W | (0x5 << 12) | (0x20 << 25),
+
+    /* RV64M */
+    OPC_RISC_MULW   = OPC_RISC_ARITH_W | (0x0 << 12) | (0x01 << 25),
+    OPC_RISC_DIVW   = OPC_RISC_ARITH_W | (0x4 << 12) | (0x01 << 25),
+    OPC_RISC_DIVUW  = OPC_RISC_ARITH_W | (0x5 << 12) | (0x01 << 25),
+    OPC_RISC_REMW   = OPC_RISC_ARITH_W | (0x6 << 12) | (0x01 << 25),
+    OPC_RISC_REMUW  = OPC_RISC_ARITH_W | (0x7 << 12) | (0x01 << 25),
+};
+
+#define MASK_OP_LOAD(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_LB   = OPC_RISC_LOAD | (0x0 << 12),
+    OPC_RISC_LH   = OPC_RISC_LOAD | (0x1 << 12),
+    OPC_RISC_LW   = OPC_RISC_LOAD | (0x2 << 12),
+    OPC_RISC_LD   = OPC_RISC_LOAD | (0x3 << 12),
+    OPC_RISC_LBU  = OPC_RISC_LOAD | (0x4 << 12),
+    OPC_RISC_LHU  = OPC_RISC_LOAD | (0x5 << 12),
+    OPC_RISC_LWU  = OPC_RISC_LOAD | (0x6 << 12),
+};
+
+#define MASK_OP_STORE(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_SB   = OPC_RISC_STORE | (0x0 << 12),
+    OPC_RISC_SH   = OPC_RISC_STORE | (0x1 << 12),
+    OPC_RISC_SW   = OPC_RISC_STORE | (0x2 << 12),
+    OPC_RISC_SD   = OPC_RISC_STORE | (0x3 << 12),
+};
+
+#define MASK_OP_JALR(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+/* no enum since OPC_RISC_JALR is the actual value */
+
+#define MASK_OP_ATOMIC(op) \
+    (MASK_OP_MAJOR(op) | (op & ((0x7 << 12) | (0x7F << 25))))
+#define MASK_OP_ATOMIC_NO_AQ_RL_SZ(op) \
+    (MASK_OP_MAJOR(op) | (op & (0x1F << 27)))
+
+enum {
+    OPC_RISC_LR          = OPC_RISC_ATOMIC | (0x02 << 27),
+    OPC_RISC_SC          = OPC_RISC_ATOMIC | (0x03 << 27),
+    OPC_RISC_AMOSWAP     = OPC_RISC_ATOMIC | (0x01 << 27),
+    OPC_RISC_AMOADD      = OPC_RISC_ATOMIC | (0x00 << 27),
+    OPC_RISC_AMOXOR      = OPC_RISC_ATOMIC | (0x04 << 27),
+    OPC_RISC_AMOAND      = OPC_RISC_ATOMIC | (0x0C << 27),
+    OPC_RISC_AMOOR       = OPC_RISC_ATOMIC | (0x08 << 27),
+    OPC_RISC_AMOMIN      = OPC_RISC_ATOMIC | (0x10 << 27),
+    OPC_RISC_AMOMAX      = OPC_RISC_ATOMIC | (0x14 << 27),
+    OPC_RISC_AMOMINU     = OPC_RISC_ATOMIC | (0x18 << 27),
+    OPC_RISC_AMOMAXU     = OPC_RISC_ATOMIC | (0x1C << 27),
+};
+
+#define MASK_OP_SYSTEM(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_ECALL       = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_EBREAK      = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_ERET        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_MRTS        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_MRTH        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_HRTS        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_WFI         = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_SFENCEVM    = OPC_RISC_SYSTEM | (0x0 << 12),
+
+    OPC_RISC_CSRRW       = OPC_RISC_SYSTEM | (0x1 << 12),
+    OPC_RISC_CSRRS       = OPC_RISC_SYSTEM | (0x2 << 12),
+    OPC_RISC_CSRRC       = OPC_RISC_SYSTEM | (0x3 << 12),
+    OPC_RISC_CSRRWI      = OPC_RISC_SYSTEM | (0x5 << 12),
+    OPC_RISC_CSRRSI      = OPC_RISC_SYSTEM | (0x6 << 12),
+    OPC_RISC_CSRRCI      = OPC_RISC_SYSTEM | (0x7 << 12),
+};
+
+#define MASK_OP_FP_LOAD(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_FLW   = OPC_RISC_FP_LOAD | (0x2 << 12),
+    OPC_RISC_FLD   = OPC_RISC_FP_LOAD | (0x3 << 12),
+};
+
+#define MASK_OP_FP_STORE(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_FSW   = OPC_RISC_FP_STORE | (0x2 << 12),
+    OPC_RISC_FSD   = OPC_RISC_FP_STORE | (0x3 << 12),
+};
+
+#define MASK_OP_FP_FMADD(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FMADD_S = OPC_RISC_FMADD | (0x0 << 25),
+    OPC_RISC_FMADD_D = OPC_RISC_FMADD | (0x1 << 25),
+};
+
+#define MASK_OP_FP_FMSUB(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FMSUB_S = OPC_RISC_FMSUB | (0x0 << 25),
+    OPC_RISC_FMSUB_D = OPC_RISC_FMSUB | (0x1 << 25),
+};
+
+#define MASK_OP_FP_FNMADD(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FNMADD_S = OPC_RISC_FNMADD | (0x0 << 25),
+    OPC_RISC_FNMADD_D = OPC_RISC_FNMADD | (0x1 << 25),
+};
+
+#define MASK_OP_FP_FNMSUB(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FNMSUB_S = OPC_RISC_FNMSUB | (0x0 << 25),
+    OPC_RISC_FNMSUB_D = OPC_RISC_FNMSUB | (0x1 << 25),
+};
+
+#define MASK_OP_FP_ARITH(op)   (MASK_OP_MAJOR(op) | (op & (0x7F << 25)))
+enum {
+    /* float */
+    OPC_RISC_FADD_S    = OPC_RISC_FP_ARITH | (0x0 << 25),
+    OPC_RISC_FSUB_S    = OPC_RISC_FP_ARITH | (0x4 << 25),
+    OPC_RISC_FMUL_S    = OPC_RISC_FP_ARITH | (0x8 << 25),
+    OPC_RISC_FDIV_S    = OPC_RISC_FP_ARITH | (0xC << 25),
+
+    OPC_RISC_FSGNJ_S   = OPC_RISC_FP_ARITH | (0x10 << 25),
+    OPC_RISC_FSGNJN_S  = OPC_RISC_FP_ARITH | (0x10 << 25),
+    OPC_RISC_FSGNJX_S  = OPC_RISC_FP_ARITH | (0x10 << 25),
+
+    OPC_RISC_FMIN_S    = OPC_RISC_FP_ARITH | (0x14 << 25),
+    OPC_RISC_FMAX_S    = OPC_RISC_FP_ARITH | (0x14 << 25),
+
+    OPC_RISC_FSQRT_S   = OPC_RISC_FP_ARITH | (0x2C << 25),
+
+    OPC_RISC_FEQ_S     = OPC_RISC_FP_ARITH | (0x50 << 25),
+    OPC_RISC_FLT_S     = OPC_RISC_FP_ARITH | (0x50 << 25),
+    OPC_RISC_FLE_S     = OPC_RISC_FP_ARITH | (0x50 << 25),
+
+    OPC_RISC_FCVT_W_S  = OPC_RISC_FP_ARITH | (0x60 << 25),
+    OPC_RISC_FCVT_WU_S = OPC_RISC_FP_ARITH | (0x60 << 25),
+    OPC_RISC_FCVT_L_S  = OPC_RISC_FP_ARITH | (0x60 << 25),
+    OPC_RISC_FCVT_LU_S = OPC_RISC_FP_ARITH | (0x60 << 25),
+
+    OPC_RISC_FCVT_S_W  = OPC_RISC_FP_ARITH | (0x68 << 25),
+    OPC_RISC_FCVT_S_WU = OPC_RISC_FP_ARITH | (0x68 << 25),
+    OPC_RISC_FCVT_S_L  = OPC_RISC_FP_ARITH | (0x68 << 25),
+    OPC_RISC_FCVT_S_LU = OPC_RISC_FP_ARITH | (0x68 << 25),
+
+    OPC_RISC_FMV_X_S   = OPC_RISC_FP_ARITH | (0x70 << 25),
+    OPC_RISC_FCLASS_S  = OPC_RISC_FP_ARITH | (0x70 << 25),
+
+    OPC_RISC_FMV_S_X   = OPC_RISC_FP_ARITH | (0x78 << 25),
+
+    /* double */
+    OPC_RISC_FADD_D    = OPC_RISC_FP_ARITH | (0x1 << 25),
+    OPC_RISC_FSUB_D    = OPC_RISC_FP_ARITH | (0x5 << 25),
+    OPC_RISC_FMUL_D    = OPC_RISC_FP_ARITH | (0x9 << 25),
+    OPC_RISC_FDIV_D    = OPC_RISC_FP_ARITH | (0xD << 25),
+
+    OPC_RISC_FSGNJ_D   = OPC_RISC_FP_ARITH | (0x11 << 25),
+    OPC_RISC_FSGNJN_D  = OPC_RISC_FP_ARITH | (0x11 << 25),
+    OPC_RISC_FSGNJX_D  = OPC_RISC_FP_ARITH | (0x11 << 25),
+
+    OPC_RISC_FMIN_D    = OPC_RISC_FP_ARITH | (0x15 << 25),
+    OPC_RISC_FMAX_D    = OPC_RISC_FP_ARITH | (0x15 << 25),
+
+    OPC_RISC_FCVT_S_D = OPC_RISC_FP_ARITH | (0x20 << 25),
+
+    OPC_RISC_FCVT_D_S = OPC_RISC_FP_ARITH | (0x21 << 25),
+
+    OPC_RISC_FSQRT_D   = OPC_RISC_FP_ARITH | (0x2D << 25),
+
+    OPC_RISC_FEQ_D     = OPC_RISC_FP_ARITH | (0x51 << 25),
+    OPC_RISC_FLT_D     = OPC_RISC_FP_ARITH | (0x51 << 25),
+    OPC_RISC_FLE_D     = OPC_RISC_FP_ARITH | (0x51 << 25),
+
+    OPC_RISC_FCVT_W_D  = OPC_RISC_FP_ARITH | (0x61 << 25),
+    OPC_RISC_FCVT_WU_D = OPC_RISC_FP_ARITH | (0x61 << 25),
+    OPC_RISC_FCVT_L_D  = OPC_RISC_FP_ARITH | (0x61 << 25),
+    OPC_RISC_FCVT_LU_D = OPC_RISC_FP_ARITH | (0x61 << 25),
+
+    OPC_RISC_FCVT_D_W  = OPC_RISC_FP_ARITH | (0x69 << 25),
+    OPC_RISC_FCVT_D_WU = OPC_RISC_FP_ARITH | (0x69 << 25),
+    OPC_RISC_FCVT_D_L  = OPC_RISC_FP_ARITH | (0x69 << 25),
+    OPC_RISC_FCVT_D_LU = OPC_RISC_FP_ARITH | (0x69 << 25),
+
+    OPC_RISC_FMV_X_D   = OPC_RISC_FP_ARITH | (0x71 << 25),
+    OPC_RISC_FCLASS_D  = OPC_RISC_FP_ARITH | (0x71 << 25),
+
+    OPC_RISC_FMV_D_X   = OPC_RISC_FP_ARITH | (0x79 << 25),
+};
+
+#define GET_B_IMM(inst) ((extract32(inst, 8, 4) << 1) \
+                         | (extract32(inst, 25, 6) << 5) \
+                         | (extract32(inst, 7, 1) << 11) \
+                         | (sextract64(inst, 31, 1) << 12))
+
+#define GET_STORE_IMM(inst) ((extract32(inst, 7, 5)) \
+                             | (sextract64(inst, 25, 7) << 5))
+
+#define GET_JAL_IMM(inst) ((extract32(inst, 21, 10) << 1) \
+                           | (extract32(inst, 20, 1) << 11) \
+                           | (extract32(inst, 12, 8) << 12) \
+                           | (sextract64(inst, 31, 1) << 20))
+
+#define GET_RM(inst)   extract32(inst, 12, 3)
+#define GET_RS3(inst)  extract32(inst, 27, 5)
+#define GET_RS1(inst)  extract32(inst, 15, 5)
+#define GET_RS2(inst)  extract32(inst, 20, 5)
+#define GET_RD(inst)   extract32(inst, 7, 5)
+#define GET_IMM(inst)  sextract64(inst, 20, 12)
+
+/* RVC decoding macros */
+#define GET_C_IMM(inst)             (extract32(inst, 2, 5) \
+                                    | (sextract64(inst, 12, 1) << 5))
+#define GET_C_ZIMM(inst)            (extract32(inst, 2, 5) \
+                                    | (extract32(inst, 12, 1) << 5))
+#define GET_C_ADDI4SPN_IMM(inst)    ((extract32(inst, 6, 1) << 2) \
+                                    | (extract32(inst, 5, 1) << 3) \
+                                    | (extract32(inst, 11, 2) << 4) \
+                                    | (extract32(inst, 7, 4) << 6))
+#define GET_C_ADDI16SP_IMM(inst)    ((extract32(inst, 6, 1) << 4) \
+                                    | (extract32(inst, 2, 1) << 5) \
+                                    | (extract32(inst, 5, 1) << 6) \
+                                    | (extract32(inst, 3, 2) << 7) \
+                                    | (sextract64(inst, 12, 1) << 9))
+#define GET_C_LWSP_IMM(inst)        ((extract32(inst, 4, 3) << 2) \
+                                    | (extract32(inst, 12, 1) << 5) \
+                                    | (extract32(inst, 2, 2) << 6))
+#define GET_C_LDSP_IMM(inst)        ((extract32(inst, 5, 2) << 3) \
+                                    | (extract32(inst, 12, 1) << 5) \
+                                    | (extract32(inst, 2, 3) << 6))
+#define GET_C_SWSP_IMM(inst)        ((extract32(inst, 9, 4) << 2) \
+                                    | (extract32(inst, 7, 2) << 6))
+#define GET_C_SDSP_IMM(inst)        ((extract32(inst, 10, 3) << 3) \
+                                    | (extract32(inst, 7, 3) << 6))
+#define GET_C_LW_IMM(inst)          ((extract32(inst, 6, 1) << 2) \
+                                    | (extract32(inst, 10, 3) << 3) \
+                                    | (extract32(inst, 5, 1) << 6))
+#define GET_C_LD_IMM(inst)          ((extract32(inst, 10, 3) << 3) \
+                                    | (extract32(inst, 5, 2) << 6))
+#define GET_C_J_IMM(inst)           ((extract32(inst, 3, 3) << 1) \
+                                    | (extract32(inst, 11, 1) << 4) \
+                                    | (extract32(inst, 2, 1) << 5) \
+                                    | (extract32(inst, 7, 1) << 6) \
+                                    | (extract32(inst, 6, 1) << 7) \
+                                    | (extract32(inst, 9, 2) << 8) \
+                                    | (extract32(inst, 8, 1) << 10) \
+                                    | (sextract64(inst, 12, 1) << 11))
+#define GET_C_B_IMM(inst)           ((extract32(inst, 3, 2) << 1) \
+                                    | (extract32(inst, 10, 2) << 3) \
+                                    | (extract32(inst, 2, 1) << 5) \
+                                    | (extract32(inst, 5, 2) << 6) \
+                                    | (sextract64(inst, 12, 1) << 8))
+#define GET_C_SIMM3(inst)           extract32(inst, 10, 3)
+#define GET_C_RD(inst)              GET_RD(inst)
+#define GET_C_RS1(inst)             GET_RD(inst)
+#define GET_C_RS2(inst)             extract32(inst, 2, 5)
+#define GET_C_RS1S(inst)            (8 + extract32(inst, 7, 3))
+#define GET_C_RS2S(inst)            (8 + extract32(inst, 2, 3))
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
new file mode 100644
index 0000000..808eab7
--- /dev/null
+++ b/target/riscv/translate.c
@@ -0,0 +1,1978 @@
+/*
+ * RISC-V emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "tcg-op.h"
+#include "disas/disas.h"
+#include "exec/cpu_ldst.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+
+#include "exec/log.h"
+
+#include "instmap.h"
+
+/* global register indices */
+static TCGv cpu_gpr[32], cpu_pc;
+static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */
+static TCGv load_res;
+static TCGv load_val;
+
+#include "exec/gen-icount.h"
+
+typedef struct DisasContext {
+    struct TranslationBlock *tb;
+    target_ulong pc;
+    target_ulong next_pc;
+    uint32_t opcode;
+    uint32_t flags;
+    uint32_t mem_idx;
+    int singlestep_enabled;
+    int bstate;
+    /* Remember the rounding mode encoded in the previous fp instruction,
+       which we have already installed into env->fp_status.  Or -1 for
+       no previous fp instruction.  Note that we exit the TB when writing
+       to any system register, which includes CSR_FRM, so we do not have
+       to reset this known value.  */
+    int frm;
+} DisasContext;
+
+enum {
+    BS_NONE     = 0, /* When seen outside of translation while loop, indicates
+                     need to exit tb due to end of page. */
+    BS_STOP     = 1, /* Need to exit tb for syscall, sret, etc. */
+    BS_BRANCH   = 2, /* Need to exit tb for branch, jal, etc. */
+};
+
+/* convert riscv funct3 to qemu memop for load/store */
+static const int tcg_memop_lookup[8] = {
+    [0 ... 7] = -1,
+    [0] = MO_SB,
+    [1] = MO_TESW,
+    [2] = MO_TESL,
+    [4] = MO_UB,
+    [5] = MO_TEUW,
+#ifdef TARGET_RISCV64
+    [3] = MO_TEQ,
+    [6] = MO_TEUL,
+#endif
+};
+
+#ifdef TARGET_RISCV64
+#define CASE_OP_32_64(X) case X: case glue(X, W)
+#else
+#define CASE_OP_32_64(X) case X
+#endif
+
+static void generate_exception(DisasContext *ctx, int excp)
+{
+    tcg_gen_movi_tl(cpu_pc, ctx->pc);
+    TCGv_i32 helper_tmp = tcg_const_i32(excp);
+    gen_helper_raise_exception(cpu_env, helper_tmp);
+    tcg_temp_free_i32(helper_tmp);
+    ctx->bstate = BS_BRANCH;
+}
+
+static void generate_exception_mbadaddr(DisasContext *ctx, int excp)
+{
+    tcg_gen_movi_tl(cpu_pc, ctx->pc);
+    tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr));
+    TCGv_i32 helper_tmp = tcg_const_i32(excp);
+    gen_helper_raise_exception(cpu_env, helper_tmp);
+    tcg_temp_free_i32(helper_tmp);
+    ctx->bstate = BS_BRANCH;
+}
+
+static void gen_exception_debug(void)
+{
+    TCGv_i32 helper_tmp = tcg_const_i32(EXCP_DEBUG);
+    gen_helper_raise_exception(cpu_env, helper_tmp);
+    tcg_temp_free_i32(helper_tmp);
+}
+
+static void gen_exception_illegal(DisasContext *ctx)
+{
+    generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST);
+}
+
+static void gen_exception_inst_addr_mis(DisasContext *ctx)
+{
+    generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+}
+
+static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
+{
+    if (unlikely(ctx->singlestep_enabled)) {
+        return false;
+    }
+
+#ifndef CONFIG_USER_ONLY
+    return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+#else
+    return true;
+#endif
+}
+
+static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+    if (use_goto_tb(ctx, dest)) {
+        /* chaining is only allowed when the jump is to the same page */
+        tcg_gen_goto_tb(n);
+        tcg_gen_movi_tl(cpu_pc, dest);
+        tcg_gen_exit_tb((uintptr_t)ctx->tb + n);
+    } else {
+        tcg_gen_movi_tl(cpu_pc, dest);
+        if (ctx->singlestep_enabled) {
+            gen_exception_debug();
+        } else {
+            tcg_gen_exit_tb(0);
+        }
+    }
+}
+
+/* Wrapper for getting reg values - need to check of reg is zero since
+ * cpu_gpr[0] is not actually allocated
+ */
+static inline void gen_get_gpr(TCGv t, int reg_num)
+{
+    if (reg_num == 0) {
+        tcg_gen_movi_tl(t, 0);
+    } else {
+        tcg_gen_mov_tl(t, cpu_gpr[reg_num]);
+    }
+}
+
+/* Wrapper for setting reg values - need to check of reg is zero since
+ * cpu_gpr[0] is not actually allocated. this is more for safety purposes,
+ * since we usually avoid calling the OP_TYPE_gen function if we see a write to
+ * $zero
+ */
+static inline void gen_set_gpr(int reg_num_dst, TCGv t)
+{
+    if (reg_num_dst != 0) {
+        tcg_gen_mov_tl(cpu_gpr[reg_num_dst], t);
+    }
+}
+
+static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv rl = tcg_temp_new();
+    TCGv rh = tcg_temp_new();
+
+    tcg_gen_mulu2_tl(rl, rh, arg1, arg2);
+    /* fix up for one negative */
+    tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1);
+    tcg_gen_and_tl(rl, rl, arg2);
+    tcg_gen_sub_tl(ret, rh, rl);
+
+    tcg_temp_free(rl);
+    tcg_temp_free(rh);
+}
+
+static void gen_fsgnj(DisasContext *ctx, uint32_t rd, uint32_t rs1,
+    uint32_t rs2, int rm, uint64_t min)
+{
+    switch (rm) {
+    case 0: /* fsgnj */
+        if (rs1 == rs2) { /* FMOV */
+            tcg_gen_mov_i64(cpu_fpr[rd], cpu_fpr[rs1]);
+        } else {
+            tcg_gen_deposit_i64(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1],
+                                0, min == INT32_MIN ? 31 : 63);
+        }
+        break;
+    case 1: /* fsgnjn */
+        if (rs1 == rs2) { /* FNEG */
+            tcg_gen_xori_i64(cpu_fpr[rd], cpu_fpr[rs1], min);
+        } else {
+            TCGv_i64 t0 = tcg_temp_new_i64();
+            tcg_gen_not_i64(t0, cpu_fpr[rs2]);
+            tcg_gen_deposit_i64(cpu_fpr[rd], t0, cpu_fpr[rs1],
+                                0, min == INT32_MIN ? 31 : 63);
+            tcg_temp_free_i64(t0);
+        }
+        break;
+    case 2: /* fsgnjx */
+        if (rs1 == rs2) { /* FABS */
+            tcg_gen_andi_i64(cpu_fpr[rd], cpu_fpr[rs1], ~min);
+        } else {
+            TCGv_i64 t0 = tcg_temp_new_i64();
+            tcg_gen_andi_i64(t0, cpu_fpr[rs2], min);
+            tcg_gen_xor_i64(cpu_fpr[rd], cpu_fpr[rs1], t0);
+            tcg_temp_free_i64(t0);
+        }
+        break;
+    default:
+        gen_exception_illegal(ctx);
+    }
+}
+
+static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+        int rs2)
+{
+    TCGv source1, source2, cond1, cond2, zeroreg, resultopt1;
+    source1 = tcg_temp_new();
+    source2 = tcg_temp_new();
+    gen_get_gpr(source1, rs1);
+    gen_get_gpr(source2, rs2);
+
+    switch (opc) {
+    CASE_OP_32_64(OPC_RISC_ADD):
+        tcg_gen_add_tl(source1, source1, source2);
+        break;
+    CASE_OP_32_64(OPC_RISC_SUB):
+        tcg_gen_sub_tl(source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SLLW:
+        tcg_gen_andi_tl(source2, source2, 0x1F);
+        tcg_gen_shl_tl(source1, source1, source2);
+        break;
+#endif
+    case OPC_RISC_SLL:
+        tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1);
+        tcg_gen_shl_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_SLT:
+        tcg_gen_setcond_tl(TCG_COND_LT, source1, source1, source2);
+        break;
+    case OPC_RISC_SLTU:
+        tcg_gen_setcond_tl(TCG_COND_LTU, source1, source1, source2);
+        break;
+    case OPC_RISC_XOR:
+        tcg_gen_xor_tl(source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SRLW:
+        /* clear upper 32 */
+        tcg_gen_ext32u_tl(source1, source1);
+        tcg_gen_andi_tl(source2, source2, 0x1F);
+        tcg_gen_shr_tl(source1, source1, source2);
+        break;
+#endif
+    case OPC_RISC_SRL:
+        tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1);
+        tcg_gen_shr_tl(source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SRAW:
+        /* first, trick to get it to act like working on 32 bits (get rid of
+        upper 32, sign extend to fill space) */
+        tcg_gen_ext32s_tl(source1, source1);
+        tcg_gen_andi_tl(source2, source2, 0x1F);
+        tcg_gen_sar_tl(source1, source1, source2);
+        break;
+        /* fall through to SRA */
+#endif
+    case OPC_RISC_SRA:
+        tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1);
+        tcg_gen_sar_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_OR:
+        tcg_gen_or_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_AND:
+        tcg_gen_and_tl(source1, source1, source2);
+        break;
+    CASE_OP_32_64(OPC_RISC_MUL):
+        tcg_gen_mul_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_MULH:
+        tcg_gen_muls2_tl(source2, source1, source1, source2);
+        break;
+    case OPC_RISC_MULHSU:
+        gen_mulhsu(source1, source1, source2);
+        break;
+    case OPC_RISC_MULHU:
+        tcg_gen_mulu2_tl(source2, source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_DIVW:
+        tcg_gen_ext32s_tl(source1, source1);
+        tcg_gen_ext32s_tl(source2, source2);
+        /* fall through to DIV */
+#endif
+    case OPC_RISC_DIV:
+        /* Handle by altering args to tcg_gen_div to produce req'd results:
+         * For overflow: want source1 in source1 and 1 in source2
+         * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */
+        cond1 = tcg_temp_new();
+        cond2 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L));
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
+                            ((target_ulong)1) << (TARGET_LONG_BITS - 1));
+        tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */
+        /* if div by zero, set source1 to -1, otherwise don't change */
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1,
+                resultopt1);
+        /* if overflow or div by zero, set source2 to 1, else don't change */
+        tcg_gen_or_tl(cond1, cond1, cond2);
+        tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+                resultopt1);
+        tcg_gen_div_tl(source1, source1, source2);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(cond2);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_DIVUW:
+        tcg_gen_ext32u_tl(source1, source1);
+        tcg_gen_ext32u_tl(source2, source2);
+        /* fall through to DIVU */
+#endif
+    case OPC_RISC_DIVU:
+        cond1 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
+        tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1,
+                resultopt1);
+        tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+                resultopt1);
+        tcg_gen_divu_tl(source1, source1, source2);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_REMW:
+        tcg_gen_ext32s_tl(source1, source1);
+        tcg_gen_ext32s_tl(source2, source2);
+        /* fall through to REM */
+#endif
+    case OPC_RISC_REM:
+        cond1 = tcg_temp_new();
+        cond2 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_movi_tl(resultopt1, 1L);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
+                            (target_ulong)1 << (TARGET_LONG_BITS - 1));
+        tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */
+        /* if overflow or div by zero, set source2 to 1, else don't change */
+        tcg_gen_or_tl(cond2, cond1, cond2);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2,
+                resultopt1);
+        tcg_gen_rem_tl(resultopt1, source1, source2);
+        /* if div by zero, just return the original dividend */
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1,
+                source1);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(cond2);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_REMUW:
+        tcg_gen_ext32u_tl(source1, source1);
+        tcg_gen_ext32u_tl(source2, source2);
+        /* fall through to REMU */
+#endif
+    case OPC_RISC_REMU:
+        cond1 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+                resultopt1);
+        tcg_gen_remu_tl(resultopt1, source1, source2);
+        /* if div by zero, just return the original dividend */
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1,
+                source1);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    if (opc & 0x8) { /* sign extend for W instructions */
+        tcg_gen_ext32s_tl(source1, source1);
+    }
+
+    gen_set_gpr(rd, source1);
+    tcg_temp_free(source1);
+    tcg_temp_free(source2);
+}
+
+static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd,
+        int rs1, target_long imm)
+{
+    TCGv source1 = tcg_temp_new();
+    int shift_len = TARGET_LONG_BITS;
+    int shift_a;
+
+    gen_get_gpr(source1, rs1);
+
+    switch (opc) {
+    case OPC_RISC_ADDI:
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_ADDIW:
+#endif
+        tcg_gen_addi_tl(source1, source1, imm);
+        break;
+    case OPC_RISC_SLTI:
+        tcg_gen_setcondi_tl(TCG_COND_LT, source1, source1, imm);
+        break;
+    case OPC_RISC_SLTIU:
+        tcg_gen_setcondi_tl(TCG_COND_LTU, source1, source1, imm);
+        break;
+    case OPC_RISC_XORI:
+        tcg_gen_xori_tl(source1, source1, imm);
+        break;
+    case OPC_RISC_ORI:
+        tcg_gen_ori_tl(source1, source1, imm);
+        break;
+    case OPC_RISC_ANDI:
+        tcg_gen_andi_tl(source1, source1, imm);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SLLIW:
+        shift_len = 32;
+        /* FALLTHRU */
+#endif
+    case OPC_RISC_SLLI:
+        if (imm >= shift_len) {
+            goto do_illegal;
+        }
+        tcg_gen_shli_tl(source1, source1, imm);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SHIFT_RIGHT_IW:
+        shift_len = 32;
+        /* FALLTHRU */
+#endif
+    case OPC_RISC_SHIFT_RIGHT_I:
+        /* differentiate on IMM */
+        shift_a = imm & 0x400;
+        imm &= 0x3ff;
+        if (imm >= shift_len) {
+            goto do_illegal;
+        }
+        if (imm != 0) {
+            if (shift_a) {
+                /* SRAI[W] */
+                tcg_gen_sextract_tl(source1, source1, imm, shift_len - imm);
+            } else {
+                /* SRLI[W] */
+                tcg_gen_extract_tl(source1, source1, imm, shift_len - imm);
+            }
+            /* No further sign-extension needed for W instructions.  */
+            opc &= ~0x8;
+        }
+        break;
+    default:
+    do_illegal:
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    if (opc & 0x8) { /* sign-extend for W instructions */
+        tcg_gen_ext32s_tl(source1, source1);
+    }
+
+    gen_set_gpr(rd, source1);
+    tcg_temp_free(source1);
+}
+
+static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd,
+                    target_ulong imm)
+{
+    target_ulong next_pc;
+
+    /* check misaligned: */
+    next_pc = ctx->pc + imm;
+    if (!riscv_has_ext(env, RVC)) {
+        if ((next_pc & 0x3) != 0) {
+            gen_exception_inst_addr_mis(ctx);
+            return;
+        }
+    }
+    if (rd != 0) {
+        tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc);
+    }
+
+    gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */
+    ctx->bstate = BS_BRANCH;
+}
+
+static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
+                     int rd, int rs1, target_long imm)
+{
+    /* no chaining with JALR */
+    TCGLabel *misaligned = NULL;
+    TCGv t0 = tcg_temp_new();
+
+    switch (opc) {
+    case OPC_RISC_JALR:
+        gen_get_gpr(cpu_pc, rs1);
+        tcg_gen_addi_tl(cpu_pc, cpu_pc, imm);
+        tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2);
+
+        if (!riscv_has_ext(env, RVC)) {
+            misaligned = gen_new_label();
+            tcg_gen_andi_tl(t0, cpu_pc, 0x2);
+            tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned);
+        }
+
+        if (rd != 0) {
+            tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc);
+        }
+        tcg_gen_exit_tb(0);
+
+        if (misaligned) {
+            gen_set_label(misaligned);
+            gen_exception_inst_addr_mis(ctx);
+        }
+        ctx->bstate = BS_BRANCH;
+        break;
+
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+    tcg_temp_free(t0);
+}
+
+static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
+                       int rs1, int rs2, target_long bimm)
+{
+    TCGLabel *l = gen_new_label();
+    TCGv source1, source2;
+    source1 = tcg_temp_new();
+    source2 = tcg_temp_new();
+    gen_get_gpr(source1, rs1);
+    gen_get_gpr(source2, rs2);
+
+    switch (opc) {
+    case OPC_RISC_BEQ:
+        tcg_gen_brcond_tl(TCG_COND_EQ, source1, source2, l);
+        break;
+    case OPC_RISC_BNE:
+        tcg_gen_brcond_tl(TCG_COND_NE, source1, source2, l);
+        break;
+    case OPC_RISC_BLT:
+        tcg_gen_brcond_tl(TCG_COND_LT, source1, source2, l);
+        break;
+    case OPC_RISC_BGE:
+        tcg_gen_brcond_tl(TCG_COND_GE, source1, source2, l);
+        break;
+    case OPC_RISC_BLTU:
+        tcg_gen_brcond_tl(TCG_COND_LTU, source1, source2, l);
+        break;
+    case OPC_RISC_BGEU:
+        tcg_gen_brcond_tl(TCG_COND_GEU, source1, source2, l);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        return;
+    }
+    tcg_temp_free(source1);
+    tcg_temp_free(source2);
+
+    gen_goto_tb(ctx, 1, ctx->next_pc);
+    gen_set_label(l); /* branch taken */
+    if (!riscv_has_ext(env, RVC) && ((ctx->pc + bimm) & 0x3)) {
+        /* misaligned */
+        gen_exception_inst_addr_mis(ctx);
+    } else {
+        gen_goto_tb(ctx, 0, ctx->pc + bimm);
+    }
+    ctx->bstate = BS_BRANCH;
+}
+
+static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+        target_long imm)
+{
+    TCGv t0 = tcg_temp_new();
+    TCGv t1 = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+    int memop = tcg_memop_lookup[(opc >> 12) & 0x7];
+
+    if (memop < 0) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, memop);
+    gen_set_gpr(rd, t1);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
+static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2,
+        target_long imm)
+{
+    TCGv t0 = tcg_temp_new();
+    TCGv dat = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+    gen_get_gpr(dat, rs2);
+    int memop = tcg_memop_lookup[(opc >> 12) & 0x7];
+
+    if (memop < 0) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx, memop);
+    tcg_temp_free(t0);
+    tcg_temp_free(dat);
+}
+
+static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
+        int rs1, target_long imm)
+{
+    TCGv t0;
+
+    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+
+    switch (opc) {
+    case OPC_RISC_FLW:
+        tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL);
+        /* RISC-V requires NaN-boxing of narrower width floating point values */
+        tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL);
+        break;
+    case OPC_RISC_FLD:
+        tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+    tcg_temp_free(t0);
+}
+
+static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
+        int rs2, target_long imm)
+{
+    TCGv t0;
+
+    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+
+    switch (opc) {
+    case OPC_RISC_FSW:
+        tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL);
+        break;
+    case OPC_RISC_FSD:
+        tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+
+    tcg_temp_free(t0);
+}
+
+static void gen_atomic(DisasContext *ctx, uint32_t opc,
+                      int rd, int rs1, int rs2)
+{
+    TCGv src1, src2, dat;
+    TCGLabel *l1, *l2;
+    TCGMemOp mop;
+    TCGCond cond;
+    bool aq, rl;
+
+    /* Extract the size of the atomic operation.  */
+    switch (extract32(opc, 12, 3)) {
+    case 2: /* 32-bit */
+        mop = MO_ALIGN | MO_TESL;
+        break;
+#if defined(TARGET_RISCV64)
+    case 3: /* 64-bit */
+        mop = MO_ALIGN | MO_TEQ;
+        break;
+#endif
+    default:
+        gen_exception_illegal(ctx);
+        return;
+    }
+    rl = extract32(opc, 25, 1);
+    aq = extract32(opc, 26, 1);
+
+    src1 = tcg_temp_new();
+    src2 = tcg_temp_new();
+
+    switch (MASK_OP_ATOMIC_NO_AQ_RL_SZ(opc)) {
+    case OPC_RISC_LR:
+        /* Put addr in load_res, data in load_val.  */
+        gen_get_gpr(src1, rs1);
+        if (rl) {
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
+        }
+        tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop);
+        if (aq) {
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
+        }
+        tcg_gen_mov_tl(load_res, src1);
+        gen_set_gpr(rd, load_val);
+        break;
+
+    case OPC_RISC_SC:
+        l1 = gen_new_label();
+        l2 = gen_new_label();
+        dat = tcg_temp_new();
+
+        gen_get_gpr(src1, rs1);
+        tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1);
+
+        gen_get_gpr(src2, rs2);
+        /* Note that the TCG atomic primitives are SC,
+           so we can ignore AQ/RL along this path.  */
+        tcg_gen_atomic_cmpxchg_tl(src1, load_res, load_val, src2,
+                                  ctx->mem_idx, mop);
+        tcg_gen_setcond_tl(TCG_COND_NE, dat, src1, load_val);
+        gen_set_gpr(rd, dat);
+        tcg_gen_br(l2);
+
+        gen_set_label(l1);
+        /* Address comparion failure.  However, we still need to
+           provide the memory barrier implied by AQ/RL.  */
+        tcg_gen_mb(TCG_MO_ALL + aq * TCG_BAR_LDAQ + rl * TCG_BAR_STRL);
+        tcg_gen_movi_tl(dat, 1);
+        gen_set_gpr(rd, dat);
+
+        gen_set_label(l2);
+        tcg_temp_free(dat);
+        break;
+
+    case OPC_RISC_AMOSWAP:
+        /* Note that the TCG atomic primitives are SC,
+           so we can ignore AQ/RL along this path.  */
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_xchg_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOADD:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_add_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOXOR:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_xor_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOAND:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_and_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOOR:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_or_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+
+    case OPC_RISC_AMOMIN:
+        cond = TCG_COND_LT;
+        goto do_minmax;
+    case OPC_RISC_AMOMAX:
+        cond = TCG_COND_GT;
+        goto do_minmax;
+    case OPC_RISC_AMOMINU:
+        cond = TCG_COND_LTU;
+        goto do_minmax;
+    case OPC_RISC_AMOMAXU:
+        cond = TCG_COND_GTU;
+        goto do_minmax;
+    do_minmax:
+        /* Handle the RL barrier.  The AQ barrier is handled along the
+           parallel path by the SC atomic cmpxchg.  On the serial path,
+           of course, barriers do not matter.  */
+        if (rl) {
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
+        }
+        if (tb_cflags(ctx->tb) & CF_PARALLEL) {
+            l1 = gen_new_label();
+            gen_set_label(l1);
+        } else {
+            l1 = NULL;
+        }
+
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        if ((mop & MO_SSIZE) == MO_SL) {
+            /* Sign-extend the register comparison input.  */
+            tcg_gen_ext32s_tl(src2, src2);
+        }
+        dat = tcg_temp_local_new();
+        tcg_gen_qemu_ld_tl(dat, src1, ctx->mem_idx, mop);
+        tcg_gen_movcond_tl(cond, src2, dat, src2, dat, src2);
+
+        if (tb_cflags(ctx->tb) & CF_PARALLEL) {
+            /* Parallel context.  Make this operation atomic by verifying
+               that the memory didn't change while we computed the result.  */
+            tcg_gen_atomic_cmpxchg_tl(src2, src1, dat, src2, ctx->mem_idx, mop);
+
+            /* If the cmpxchg failed, retry. */
+            /* ??? There is an assumption here that this will eventually
+               succeed, such that we don't live-lock.  This is not unlike
+               a similar loop that the compiler would generate for e.g.
+               __atomic_fetch_and_xor, so don't worry about it.  */
+            tcg_gen_brcond_tl(TCG_COND_NE, dat, src2, l1);
+        } else {
+            /* Serial context.  Directly store the result.  */
+            tcg_gen_qemu_st_tl(src2, src1, ctx->mem_idx, mop);
+        }
+        gen_set_gpr(rd, dat);
+        tcg_temp_free(dat);
+        break;
+
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+
+    tcg_temp_free(src1);
+    tcg_temp_free(src2);
+}
+
+static void gen_set_rm(DisasContext *ctx, int rm)
+{
+    TCGv_i32 t0;
+
+    if (ctx->frm == rm) {
+        return;
+    }
+    ctx->frm = rm;
+    t0 = tcg_const_i32(rm);
+    gen_helper_set_rounding_mode(cpu_env, t0);
+    tcg_temp_free_i32(t0);
+}
+
+static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd,
+                         int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FMADD_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FMADD_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd,
+                         int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FMSUB_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FMSUB_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd,
+                          int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FNMSUB_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FNMSUB_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd,
+                          int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FNMADD_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FNMADD_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
+                         int rs1, int rs2, int rm)
+{
+    TCGv t0 = NULL;
+
+    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+        goto do_illegal;
+    }
+
+    switch (opc) {
+    case OPC_RISC_FADD_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSUB_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FMUL_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FDIV_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSQRT_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+        break;
+    case OPC_RISC_FSGNJ_S:
+        gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN);
+        break;
+
+    case OPC_RISC_FMIN_S:
+        /* also handles: OPC_RISC_FMAX_S */
+        switch (rm) {
+        case 0x0:
+            gen_helper_fmin_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 0x1:
+            gen_helper_fmax_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FEQ_S:
+        /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */
+        t0 = tcg_temp_new();
+        switch (rm) {
+        case 0x0:
+            gen_helper_fle_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 0x1:
+            gen_helper_flt_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 0x2:
+            gen_helper_feq_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_W_S:
+        /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */
+        t0 = tcg_temp_new();
+        switch (rs2) {
+        case 0: /* FCVT_W_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_w_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 1: /* FCVT_WU_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_wu_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2: /* FCVT_L_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_l_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 3: /* FCVT_LU_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_lu_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_S_W:
+        /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+        switch (rs2) {
+        case 0: /* FCVT_S_W */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_w(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 1: /* FCVT_S_WU */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_wu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2: /* FCVT_S_L */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_l(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 3: /* FCVT_S_LU */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_lu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FMV_X_S:
+        /* also OPC_RISC_FCLASS_S */
+        t0 = tcg_temp_new();
+        switch (rm) {
+        case 0: /* FMV */
+#if defined(TARGET_RISCV64)
+            tcg_gen_ext32s_tl(t0, cpu_fpr[rs1]);
+#else
+            tcg_gen_extrl_i64_i32(t0, cpu_fpr[rs1]);
+#endif
+            break;
+        case 1:
+            gen_helper_fclass_s(t0, cpu_fpr[rs1]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FMV_S_X:
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+#if defined(TARGET_RISCV64)
+        tcg_gen_mov_i64(cpu_fpr[rd], t0);
+#else
+        tcg_gen_extu_i32_i64(cpu_fpr[rd], t0);
+#endif
+        tcg_temp_free(t0);
+        break;
+
+    /* double */
+    case OPC_RISC_FADD_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSUB_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FMUL_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FDIV_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSQRT_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+        break;
+    case OPC_RISC_FSGNJ_D:
+        gen_fsgnj(ctx, rd, rs1, rs2, rm, INT64_MIN);
+        break;
+
+    case OPC_RISC_FMIN_D:
+        /* also OPC_RISC_FMAX_D */
+        switch (rm) {
+        case 0:
+            gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 1:
+            gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FCVT_S_D:
+        switch (rs2) {
+        case 1:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FCVT_D_S:
+        switch (rs2) {
+        case 0:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FEQ_D:
+        /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */
+        t0 = tcg_temp_new();
+        switch (rm) {
+        case 0:
+            gen_helper_fle_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 1:
+            gen_helper_flt_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 2:
+            gen_helper_feq_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_W_D:
+        /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */
+        t0 = tcg_temp_new();
+        switch (rs2) {
+        case 0:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 1:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 3:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_D_W:
+        /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+        switch (rs2) {
+        case 0:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 1:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 3:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        tcg_temp_free(t0);
+        break;
+
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_FMV_X_D:
+        /* also OPC_RISC_FCLASS_D */
+        switch (rm) {
+        case 0: /* FMV */
+            gen_set_gpr(rd, cpu_fpr[rs1]);
+            break;
+        case 1:
+            t0 = tcg_temp_new();
+            gen_helper_fclass_d(t0, cpu_fpr[rs1]);
+            gen_set_gpr(rd, t0);
+            tcg_temp_free(t0);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FMV_D_X:
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+        tcg_gen_mov_tl(cpu_fpr[rd], t0);
+        tcg_temp_free(t0);
+        break;
+#endif
+
+    default:
+    do_illegal:
+        if (t0) {
+            tcg_temp_free(t0);
+        }
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
+                      int rd, int rs1, int csr)
+{
+    TCGv source1, csr_store, dest, rs1_pass, imm_rs1;
+    source1 = tcg_temp_new();
+    csr_store = tcg_temp_new();
+    dest = tcg_temp_new();
+    rs1_pass = tcg_temp_new();
+    imm_rs1 = tcg_temp_new();
+    gen_get_gpr(source1, rs1);
+    tcg_gen_movi_tl(cpu_pc, ctx->pc);
+    tcg_gen_movi_tl(rs1_pass, rs1);
+    tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */
+
+#ifndef CONFIG_USER_ONLY
+    /* Extract funct7 value and check whether it matches SFENCE.VMA */
+    if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) {
+        /* sfence.vma */
+        /* TODO: handle ASID specific fences */
+        gen_helper_tlb_flush(cpu_env);
+        return;
+    }
+#endif
+
+    switch (opc) {
+    case OPC_RISC_ECALL:
+        switch (csr) {
+        case 0x0: /* ECALL */
+            /* always generates U-level ECALL, fixed in do_interrupt handler */
+            generate_exception(ctx, RISCV_EXCP_U_ECALL);
+            tcg_gen_exit_tb(0); /* no chaining */
+            ctx->bstate = BS_BRANCH;
+            break;
+        case 0x1: /* EBREAK */
+            generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
+            tcg_gen_exit_tb(0); /* no chaining */
+            ctx->bstate = BS_BRANCH;
+            break;
+#ifndef CONFIG_USER_ONLY
+        case 0x002: /* URET */
+            gen_exception_illegal(ctx);
+            break;
+        case 0x102: /* SRET */
+            if (riscv_has_ext(env, RVS)) {
+                gen_helper_sret(cpu_pc, cpu_env, cpu_pc);
+                tcg_gen_exit_tb(0); /* no chaining */
+                ctx->bstate = BS_BRANCH;
+            } else {
+                gen_exception_illegal(ctx);
+            }
+            break;
+        case 0x202: /* HRET */
+            gen_exception_illegal(ctx);
+            break;
+        case 0x302: /* MRET */
+            gen_helper_mret(cpu_pc, cpu_env, cpu_pc);
+            tcg_gen_exit_tb(0); /* no chaining */
+            ctx->bstate = BS_BRANCH;
+            break;
+        case 0x7b2: /* DRET */
+            gen_exception_illegal(ctx);
+            break;
+        case 0x105: /* WFI */
+            tcg_gen_movi_tl(cpu_pc, ctx->next_pc);
+            gen_helper_wfi(cpu_env);
+            break;
+        case 0x104: /* SFENCE.VM */
+            gen_helper_tlb_flush(cpu_env);
+            break;
+#endif
+        default:
+            gen_exception_illegal(ctx);
+            break;
+        }
+        break;
+    default:
+        tcg_gen_movi_tl(imm_rs1, rs1);
+        switch (opc) {
+        case OPC_RISC_CSRRW:
+            gen_helper_csrrw(dest, cpu_env, source1, csr_store);
+            break;
+        case OPC_RISC_CSRRS:
+            gen_helper_csrrs(dest, cpu_env, source1, csr_store, rs1_pass);
+            break;
+        case OPC_RISC_CSRRC:
+            gen_helper_csrrc(dest, cpu_env, source1, csr_store, rs1_pass);
+            break;
+        case OPC_RISC_CSRRWI:
+            gen_helper_csrrw(dest, cpu_env, imm_rs1, csr_store);
+            break;
+        case OPC_RISC_CSRRSI:
+            gen_helper_csrrs(dest, cpu_env, imm_rs1, csr_store, rs1_pass);
+            break;
+        case OPC_RISC_CSRRCI:
+            gen_helper_csrrc(dest, cpu_env, imm_rs1, csr_store, rs1_pass);
+            break;
+        default:
+            gen_exception_illegal(ctx);
+            return;
+        }
+        gen_set_gpr(rd, dest);
+        /* end tb since we may be changing priv modes, to get mmu_index right */
+        tcg_gen_movi_tl(cpu_pc, ctx->next_pc);
+        tcg_gen_exit_tb(0); /* no chaining */
+        ctx->bstate = BS_BRANCH;
+        break;
+    }
+    tcg_temp_free(source1);
+    tcg_temp_free(csr_store);
+    tcg_temp_free(dest);
+    tcg_temp_free(rs1_pass);
+    tcg_temp_free(imm_rs1);
+}
+
+static void decode_RV32_64C0(DisasContext *ctx)
+{
+    uint8_t funct3 = extract32(ctx->opcode, 13, 3);
+    uint8_t rd_rs2 = GET_C_RS2S(ctx->opcode);
+    uint8_t rs1s = GET_C_RS1S(ctx->opcode);
+
+    switch (funct3) {
+    case 0:
+        /* illegal */
+        if (ctx->opcode == 0) {
+            gen_exception_illegal(ctx);
+        } else {
+            /* C.ADDI4SPN -> addi rd', x2, zimm[9:2]*/
+            gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs2, 2,
+                          GET_C_ADDI4SPN_IMM(ctx->opcode));
+        }
+        break;
+    case 1:
+        /* C.FLD -> fld rd', offset[7:3](rs1')*/
+        gen_fp_load(ctx, OPC_RISC_FLD, rd_rs2, rs1s,
+                    GET_C_LD_IMM(ctx->opcode));
+        /* C.LQ(RV128) */
+        break;
+    case 2:
+        /* C.LW -> lw rd', offset[6:2](rs1') */
+        gen_load(ctx, OPC_RISC_LW, rd_rs2, rs1s,
+                 GET_C_LW_IMM(ctx->opcode));
+        break;
+    case 3:
+#if defined(TARGET_RISCV64)
+        /* C.LD(RV64/128) -> ld rd', offset[7:3](rs1')*/
+        gen_load(ctx, OPC_RISC_LD, rd_rs2, rs1s,
+                 GET_C_LD_IMM(ctx->opcode));
+#else
+        /* C.FLW (RV32) -> flw rd', offset[6:2](rs1')*/
+        gen_fp_load(ctx, OPC_RISC_FLW, rd_rs2, rs1s,
+                    GET_C_LW_IMM(ctx->opcode));
+#endif
+        break;
+    case 4:
+        /* reserved */
+        gen_exception_illegal(ctx);
+        break;
+    case 5:
+        /* C.FSD(RV32/64) -> fsd rs2', offset[7:3](rs1') */
+        gen_fp_store(ctx, OPC_RISC_FSD, rs1s, rd_rs2,
+                     GET_C_LD_IMM(ctx->opcode));
+        /* C.SQ (RV128) */
+        break;
+    case 6:
+        /* C.SW -> sw rs2', offset[6:2](rs1')*/
+        gen_store(ctx, OPC_RISC_SW, rs1s, rd_rs2,
+                  GET_C_LW_IMM(ctx->opcode));
+        break;
+    case 7:
+#if defined(TARGET_RISCV64)
+        /* C.SD (RV64/128) -> sd rs2', offset[7:3](rs1')*/
+        gen_store(ctx, OPC_RISC_SD, rs1s, rd_rs2,
+                  GET_C_LD_IMM(ctx->opcode));
+#else
+        /* C.FSW (RV32) -> fsw rs2', offset[6:2](rs1')*/
+        gen_fp_store(ctx, OPC_RISC_FSW, rs1s, rd_rs2,
+                     GET_C_LW_IMM(ctx->opcode));
+#endif
+        break;
+    }
+}
+
+static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
+{
+    uint8_t funct3 = extract32(ctx->opcode, 13, 3);
+    uint8_t rd_rs1 = GET_C_RS1(ctx->opcode);
+    uint8_t rs1s, rs2s;
+    uint8_t funct2;
+
+    switch (funct3) {
+    case 0:
+        /* C.ADDI -> addi rd, rd, nzimm[5:0] */
+        gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, rd_rs1,
+                      GET_C_IMM(ctx->opcode));
+        break;
+    case 1:
+#if defined(TARGET_RISCV64)
+        /* C.ADDIW (RV64/128) -> addiw rd, rd, imm[5:0]*/
+        gen_arith_imm(ctx, OPC_RISC_ADDIW, rd_rs1, rd_rs1,
+                      GET_C_IMM(ctx->opcode));
+#else
+        /* C.JAL(RV32) -> jal x1, offset[11:1] */
+        gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode));
+#endif
+        break;
+    case 2:
+        /* C.LI -> addi rd, x0, imm[5:0]*/
+        gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, 0, GET_C_IMM(ctx->opcode));
+        break;
+    case 3:
+        if (rd_rs1 == 2) {
+            /* C.ADDI16SP -> addi x2, x2, nzimm[9:4]*/
+            gen_arith_imm(ctx, OPC_RISC_ADDI, 2, 2,
+                          GET_C_ADDI16SP_IMM(ctx->opcode));
+        } else if (rd_rs1 != 0) {
+            /* C.LUI (rs1/rd =/= {0,2}) -> lui rd, nzimm[17:12]*/
+            tcg_gen_movi_tl(cpu_gpr[rd_rs1],
+                            GET_C_IMM(ctx->opcode) << 12);
+        }
+        break;
+    case 4:
+        funct2 = extract32(ctx->opcode, 10, 2);
+        rs1s = GET_C_RS1S(ctx->opcode);
+        switch (funct2) {
+        case 0: /* C.SRLI(RV32) -> srli rd', rd', shamt[5:0] */
+            gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s,
+                               GET_C_ZIMM(ctx->opcode));
+            /* C.SRLI64(RV128) */
+            break;
+        case 1:
+            /* C.SRAI -> srai rd', rd', shamt[5:0]*/
+            gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s,
+                            GET_C_ZIMM(ctx->opcode) | 0x400);
+            /* C.SRAI64(RV128) */
+            break;
+        case 2:
+            /* C.ANDI -> andi rd', rd', imm[5:0]*/
+            gen_arith_imm(ctx, OPC_RISC_ANDI, rs1s, rs1s,
+                          GET_C_IMM(ctx->opcode));
+            break;
+        case 3:
+            funct2 = extract32(ctx->opcode, 5, 2);
+            rs2s = GET_C_RS2S(ctx->opcode);
+            switch (funct2) {
+            case 0:
+                /* C.SUB -> sub rd', rd', rs2' */
+                if (extract32(ctx->opcode, 12, 1) == 0) {
+                    gen_arith(ctx, OPC_RISC_SUB, rs1s, rs1s, rs2s);
+                }
+#if defined(TARGET_RISCV64)
+                else {
+                    gen_arith(ctx, OPC_RISC_SUBW, rs1s, rs1s, rs2s);
+                }
+#endif
+                break;
+            case 1:
+                /* C.XOR -> xor rs1', rs1', rs2' */
+                if (extract32(ctx->opcode, 12, 1) == 0) {
+                    gen_arith(ctx, OPC_RISC_XOR, rs1s, rs1s, rs2s);
+                }
+#if defined(TARGET_RISCV64)
+                else {
+                    /* C.ADDW (RV64/128) */
+                    gen_arith(ctx, OPC_RISC_ADDW, rs1s, rs1s, rs2s);
+                }
+#endif
+                break;
+            case 2:
+                /* C.OR -> or rs1', rs1', rs2' */
+                gen_arith(ctx, OPC_RISC_OR, rs1s, rs1s, rs2s);
+                break;
+            case 3:
+                /* C.AND -> and rs1', rs1', rs2' */
+                gen_arith(ctx, OPC_RISC_AND, rs1s, rs1s, rs2s);
+                break;
+            }
+            break;
+        }
+        break;
+    case 5:
+        /* C.J -> jal x0, offset[11:1]*/
+        gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode));
+        break;
+    case 6:
+        /* C.BEQZ -> beq rs1', x0, offset[8:1]*/
+        rs1s = GET_C_RS1S(ctx->opcode);
+        gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+        break;
+    case 7:
+        /* C.BNEZ -> bne rs1', x0, offset[8:1]*/
+        rs1s = GET_C_RS1S(ctx->opcode);
+        gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+        break;
+    }
+}
+
+static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
+{
+    uint8_t rd, rs2;
+    uint8_t funct3 = extract32(ctx->opcode, 13, 3);
+
+
+    rd = GET_RD(ctx->opcode);
+
+    switch (funct3) {
+    case 0: /* C.SLLI -> slli rd, rd, shamt[5:0]
+               C.SLLI64 -> */
+        gen_arith_imm(ctx, OPC_RISC_SLLI, rd, rd, GET_C_ZIMM(ctx->opcode));
+        break;
+    case 1: /* C.FLDSP(RV32/64DC) -> fld rd, offset[8:3](x2) */
+        gen_fp_load(ctx, OPC_RISC_FLD, rd, 2, GET_C_LDSP_IMM(ctx->opcode));
+        break;
+    case 2: /* C.LWSP -> lw rd, offset[7:2](x2) */
+        gen_load(ctx, OPC_RISC_LW, rd, 2, GET_C_LWSP_IMM(ctx->opcode));
+        break;
+    case 3:
+#if defined(TARGET_RISCV64)
+        /* C.LDSP(RVC64) -> ld rd, offset[8:3](x2) */
+        gen_load(ctx, OPC_RISC_LD, rd, 2, GET_C_LDSP_IMM(ctx->opcode));
+#else
+        /* C.FLWSP(RV32FC) -> flw rd, offset[7:2](x2) */
+        gen_fp_load(ctx, OPC_RISC_FLW, rd, 2, GET_C_LWSP_IMM(ctx->opcode));
+#endif
+        break;
+    case 4:
+        rs2 = GET_C_RS2(ctx->opcode);
+
+        if (extract32(ctx->opcode, 12, 1) == 0) {
+            if (rs2 == 0) {
+                /* C.JR -> jalr x0, rs1, 0*/
+                gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0);
+            } else {
+                /* C.MV -> add rd, x0, rs2 */
+                gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2);
+            }
+        } else {
+            if (rd == 0) {
+                /* C.EBREAK -> ebreak*/
+                gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1);
+            } else {
+                if (rs2 == 0) {
+                    /* C.JALR -> jalr x1, rs1, 0*/
+                    gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0);
+                } else {
+                    /* C.ADD -> add rd, rd, rs2 */
+                    gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2);
+                }
+            }
+        }
+        break;
+    case 5:
+        /* C.FSDSP -> fsd rs2, offset[8:3](x2)*/
+        gen_fp_store(ctx, OPC_RISC_FSD, 2, GET_C_RS2(ctx->opcode),
+                     GET_C_SDSP_IMM(ctx->opcode));
+        /* C.SQSP */
+        break;
+    case 6: /* C.SWSP -> sw rs2, offset[7:2](x2)*/
+        gen_store(ctx, OPC_RISC_SW, 2, GET_C_RS2(ctx->opcode),
+                  GET_C_SWSP_IMM(ctx->opcode));
+        break;
+    case 7:
+#if defined(TARGET_RISCV64)
+        /* C.SDSP(Rv64/128) -> sd rs2, offset[8:3](x2)*/
+        gen_store(ctx, OPC_RISC_SD, 2, GET_C_RS2(ctx->opcode),
+                  GET_C_SDSP_IMM(ctx->opcode));
+#else
+        /* C.FSWSP(RV32) -> fsw rs2, offset[7:2](x2) */
+        gen_fp_store(ctx, OPC_RISC_FSW, 2, GET_C_RS2(ctx->opcode),
+                     GET_C_SWSP_IMM(ctx->opcode));
+#endif
+        break;
+    }
+}
+
+static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx)
+{
+    uint8_t op = extract32(ctx->opcode, 0, 2);
+
+    switch (op) {
+    case 0:
+        decode_RV32_64C0(ctx);
+        break;
+    case 1:
+        decode_RV32_64C1(env, ctx);
+        break;
+    case 2:
+        decode_RV32_64C2(env, ctx);
+        break;
+    }
+}
+
+static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
+{
+    int rs1;
+    int rs2;
+    int rd;
+    uint32_t op;
+    target_long imm;
+
+    /* We do not do misaligned address check here: the address should never be
+     * misaligned at this point. Instructions that set PC must do the check,
+     * since epc must be the address of the instruction that caused us to
+     * perform the misaligned instruction fetch */
+
+    op = MASK_OP_MAJOR(ctx->opcode);
+    rs1 = GET_RS1(ctx->opcode);
+    rs2 = GET_RS2(ctx->opcode);
+    rd = GET_RD(ctx->opcode);
+    imm = GET_IMM(ctx->opcode);
+
+    switch (op) {
+    case OPC_RISC_LUI:
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        tcg_gen_movi_tl(cpu_gpr[rd], sextract64(ctx->opcode, 12, 20) << 12);
+        break;
+    case OPC_RISC_AUIPC:
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) +
+               ctx->pc);
+        break;
+    case OPC_RISC_JAL:
+        imm = GET_JAL_IMM(ctx->opcode);
+        gen_jal(env, ctx, rd, imm);
+        break;
+    case OPC_RISC_JALR:
+        gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_BRANCH:
+        gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2,
+                   GET_B_IMM(ctx->opcode));
+        break;
+    case OPC_RISC_LOAD:
+        gen_load(ctx, MASK_OP_LOAD(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_STORE:
+        gen_store(ctx, MASK_OP_STORE(ctx->opcode), rs1, rs2,
+                  GET_STORE_IMM(ctx->opcode));
+        break;
+    case OPC_RISC_ARITH_IMM:
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_ARITH_IMM_W:
+#endif
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        gen_arith_imm(ctx, MASK_OP_ARITH_IMM(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_ARITH:
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_ARITH_W:
+#endif
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        gen_arith(ctx, MASK_OP_ARITH(ctx->opcode), rd, rs1, rs2);
+        break;
+    case OPC_RISC_FP_LOAD:
+        gen_fp_load(ctx, MASK_OP_FP_LOAD(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_FP_STORE:
+        gen_fp_store(ctx, MASK_OP_FP_STORE(ctx->opcode), rs1, rs2,
+                     GET_STORE_IMM(ctx->opcode));
+        break;
+    case OPC_RISC_ATOMIC:
+        gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2);
+        break;
+    case OPC_RISC_FMADD:
+        gen_fp_fmadd(ctx, MASK_OP_FP_FMADD(ctx->opcode), rd, rs1, rs2,
+                     GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FMSUB:
+        gen_fp_fmsub(ctx, MASK_OP_FP_FMSUB(ctx->opcode), rd, rs1, rs2,
+                     GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FNMSUB:
+        gen_fp_fnmsub(ctx, MASK_OP_FP_FNMSUB(ctx->opcode), rd, rs1, rs2,
+                      GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FNMADD:
+        gen_fp_fnmadd(ctx, MASK_OP_FP_FNMADD(ctx->opcode), rd, rs1, rs2,
+                      GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FP_ARITH:
+        gen_fp_arith(ctx, MASK_OP_FP_ARITH(ctx->opcode), rd, rs1, rs2,
+                     GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FENCE:
+#ifndef CONFIG_USER_ONLY
+        if (ctx->opcode & 0x1000) {
+            /* FENCE_I is a no-op in QEMU,
+             * however we need to end the translation block */
+            tcg_gen_movi_tl(cpu_pc, ctx->next_pc);
+            tcg_gen_exit_tb(0);
+            ctx->bstate = BS_BRANCH;
+        } else {
+            /* FENCE is a full memory barrier. */
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+        }
+#endif
+        break;
+    case OPC_RISC_SYSTEM:
+        gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
+                   (ctx->opcode & 0xFFF00000) >> 20);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void decode_opc(CPURISCVState *env, DisasContext *ctx)
+{
+    /* check for compressed insn */
+    if (extract32(ctx->opcode, 0, 2) != 3) {
+        if (!riscv_has_ext(env, RVC)) {
+            gen_exception_illegal(ctx);
+        } else {
+            ctx->next_pc = ctx->pc + 2;
+            decode_RV32_64C(env, ctx);
+        }
+    } else {
+        ctx->next_pc = ctx->pc + 4;
+        decode_RV32_64G(env, ctx);
+    }
+}
+
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
+{
+    CPURISCVState *env = cs->env_ptr;
+    DisasContext ctx;
+    target_ulong pc_start;
+    target_ulong next_page_start;
+    int num_insns;
+    int max_insns;
+    pc_start = tb->pc;
+    next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+    ctx.pc = pc_start;
+
+    /* once we have GDB, the rest of the translate.c implementation should be
+       ready for singlestep */
+    ctx.singlestep_enabled = cs->singlestep_enabled;
+
+    ctx.tb = tb;
+    ctx.bstate = BS_NONE;
+    ctx.flags = tb->flags;
+    ctx.mem_idx = tb->flags & TB_FLAGS_MMU_MASK;
+    ctx.frm = -1;  /* unknown rounding mode */
+
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+    if (max_insns > TCG_MAX_INSNS) {
+        max_insns = TCG_MAX_INSNS;
+    }
+    gen_tb_start(tb);
+
+    while (ctx.bstate == BS_NONE) {
+        tcg_gen_insn_start(ctx.pc);
+        num_insns++;
+
+        if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) {
+            tcg_gen_movi_tl(cpu_pc, ctx.pc);
+            ctx.bstate = BS_BRANCH;
+            gen_exception_debug();
+            /* The address covered by the breakpoint must be included in
+               [tb->pc, tb->pc + tb->size) in order to for it to be
+               properly cleared -- thus we increment the PC here so that
+               the logic setting tb->size below does the right thing.  */
+            ctx.pc += 4;
+            goto done_generating;
+        }
+
+        if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        ctx.opcode = cpu_ldl_code(env, ctx.pc);
+        decode_opc(env, &ctx);
+        ctx.pc = ctx.next_pc;
+
+        if (cs->singlestep_enabled) {
+            break;
+        }
+        if (ctx.pc >= next_page_start) {
+            break;
+        }
+        if (tcg_op_buf_full()) {
+            break;
+        }
+        if (num_insns >= max_insns) {
+            break;
+        }
+        if (singlestep) {
+            break;
+        }
+
+    }
+    if (tb->cflags & CF_LAST_IO) {
+        gen_io_end();
+    }
+    switch (ctx.bstate) {
+    case BS_STOP:
+        gen_goto_tb(&ctx, 0, ctx.pc);
+        break;
+    case BS_NONE: /* handle end of page - DO NOT CHAIN. See gen_goto_tb. */
+        tcg_gen_movi_tl(cpu_pc, ctx.pc);
+        if (cs->singlestep_enabled) {
+            gen_exception_debug();
+        } else {
+            tcg_gen_exit_tb(0);
+        }
+        break;
+    case BS_BRANCH: /* ops using BS_BRANCH generate own exit seq */
+    default:
+        break;
+    }
+done_generating:
+    gen_tb_end(tb, num_insns);
+    tb->size = ctx.pc - pc_start;
+    tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
+        && qemu_log_in_addr_range(pc_start)) {
+        qemu_log("IN: %s\n", lookup_symbol(pc_start));
+        log_target_disas(cs, pc_start, ctx.pc - pc_start);
+        qemu_log("\n");
+    }
+#endif
+}
+
+void riscv_translate_init(void)
+{
+    int i;
+
+    /* cpu_gpr[0] is a placeholder for the zero register. Do not use it. */
+    /* Use the gen_set_gpr and gen_get_gpr helper functions when accessing */
+    /* registers, unless you specifically block reads/writes to reg 0 */
+    cpu_gpr[0] = NULL;
+
+    for (i = 1; i < 32; i++) {
+        cpu_gpr[i] = tcg_global_mem_new(cpu_env,
+            offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]);
+    }
+
+    for (i = 0; i < 32; i++) {
+        cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env,
+            offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]);
+    }
+
+    cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc");
+    load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res),
+                             "load_res");
+    load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val),
+                             "load_val");
+}
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 09/23] RISC-V Physical Memory Protection
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (7 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 08/23] RISC-V TCG Code Generation Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation Michael Clark
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches, Daire McNamara, Ivan Griffin

Implements the physical memory protection extension as specified in
Privileged ISA Version 1.10.

PMP (Physical Memory Protection) is as-of-yet unused and needs testing.
The SiFive verification team have PMP test cases that will be run.

Nothing currently depends on PMP support. It would be preferable to keep
the code in-tree for folk that are interested in RISC-V PMP support.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Daire McNamara <daire.mcnamara@emdalo.com>
Signed-off-by: Ivan Griffin <ivan.griffin@emdalo.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 target/riscv/pmp.c | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 target/riscv/pmp.h |  64 +++++++++
 2 files changed, 444 insertions(+)
 create mode 100644 target/riscv/pmp.c
 create mode 100644 target/riscv/pmp.h

diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
new file mode 100644
index 0000000..f432f3b
--- /dev/null
+++ b/target/riscv/pmp.c
@@ -0,0 +1,380 @@
+/*
+ * QEMU RISC-V PMP (Physical Memory Protection)
+ *
+ * Author: Daire McNamara, daire.mcnamara@emdalo.com
+ *         Ivan Griffin, ivan.griffin@emdalo.com
+ *
+ * This provides a RISC-V Physical Memory Protection implementation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+/*
+ * PMP (Physical Memory Protection) is as-of-yet unused and needs testing.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu-common.h"
+
+#ifndef CONFIG_USER_ONLY
+
+#define RISCV_DEBUG_PMP 0
+#define PMP_DEBUG(fmt, ...)                                                    \
+    do {                                                                       \
+        if (RISCV_DEBUG_PMP) {                                                 \
+            qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
+        }                                                                      \
+    } while (0)
+
+static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
+    uint8_t val);
+static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);
+static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index);
+
+/*
+ * Accessor method to extract address matching type 'a field' from cfg reg
+ */
+static inline uint8_t pmp_get_a_field(uint8_t cfg)
+{
+    uint8_t a = cfg >> 3;
+    return a & 0x3;
+}
+
+/*
+ * Check whether a PMP is locked or not.
+ */
+static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index)
+{
+
+    if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) {
+        return 1;
+    }
+
+    /* Top PMP has no 'next' to check */
+    if ((pmp_index + 1u) >= MAX_RISCV_PMPS) {
+        return 0;
+    }
+
+    /* In TOR mode, need to check the lock bit of the next pmp
+     * (if there is a next)
+     */
+    const uint8_t a_field =
+        pmp_get_a_field(env->pmp_state.pmp[pmp_index + 1].cfg_reg);
+    if ((env->pmp_state.pmp[pmp_index + 1u].cfg_reg & PMP_LOCK) &&
+         (PMP_AMATCH_TOR == a_field)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Count the number of active rules.
+ */
+static inline uint32_t pmp_get_num_rules(CPURISCVState *env)
+{
+     return env->pmp_state.num_rules;
+}
+
+/*
+ * Accessor to get the cfg reg for a specific PMP/HART
+ */
+static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)
+{
+    if (pmp_index < MAX_RISCV_PMPS) {
+        return env->pmp_state.pmp[pmp_index].cfg_reg;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Accessor to set the cfg reg for a specific PMP/HART
+ * Bounds checks and relevant lock bit.
+ */
+static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)
+{
+    if (pmp_index < MAX_RISCV_PMPS) {
+        if (!pmp_is_locked(env, pmp_index)) {
+            env->pmp_state.pmp[pmp_index].cfg_reg = val;
+            pmp_update_rule(env, pmp_index);
+        } else {
+            PMP_DEBUG("ignoring write - locked");
+        }
+    } else {
+        PMP_DEBUG("ignoring write - out of bounds");
+    }
+}
+
+static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea)
+{
+    /*
+       aaaa...aaa0   8-byte NAPOT range
+       aaaa...aa01   16-byte NAPOT range
+       aaaa...a011   32-byte NAPOT range
+       ...
+       aa01...1111   2^XLEN-byte NAPOT range
+       a011...1111   2^(XLEN+1)-byte NAPOT range
+       0111...1111   2^(XLEN+2)-byte NAPOT range
+       1111...1111   Reserved
+    */
+    if (a == -1) {
+        *sa = 0u;
+        *ea = -1;
+        return;
+    } else {
+        target_ulong t1 = ctz64(~a);
+        target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3;
+        target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1;
+        *sa = base;
+        *ea = base + range;
+    }
+}
+
+
+/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea'
+ *   end address values.
+ *   This function is called relatively infrequently whereas the check that
+ *   an address is within a pmp rule is called often, so optimise that one
+ */
+static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index)
+{
+    int i;
+
+    env->pmp_state.num_rules = 0;
+
+    uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg;
+    target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg;
+    target_ulong prev_addr = 0u;
+    target_ulong sa = 0u;
+    target_ulong ea = 0u;
+
+    if (pmp_index >= 1u) {
+        prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg;
+    }
+
+    switch (pmp_get_a_field(this_cfg)) {
+    case PMP_AMATCH_OFF:
+        sa = 0u;
+        ea = -1;
+        break;
+
+    case PMP_AMATCH_TOR:
+        sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
+        ea = (this_addr << 2) - 1u;
+        break;
+
+    case PMP_AMATCH_NA4:
+        sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
+        ea = (this_addr + 4u) - 1u;
+        break;
+
+    case PMP_AMATCH_NAPOT:
+        pmp_decode_napot(this_addr, &sa, &ea);
+        break;
+
+    default:
+        sa = 0u;
+        ea = 0u;
+        break;
+    }
+
+    env->pmp_state.addr[pmp_index].sa = sa;
+    env->pmp_state.addr[pmp_index].ea = ea;
+
+    for (i = 0; i < MAX_RISCV_PMPS; i++) {
+        const uint8_t a_field =
+            pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
+        if (PMP_AMATCH_OFF != a_field) {
+            env->pmp_state.num_rules++;
+        }
+    }
+}
+
+static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr)
+{
+    int result = 0;
+
+    if ((addr >= env->pmp_state.addr[pmp_index].sa)
+        && (addr <= env->pmp_state.addr[pmp_index].ea)) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+
+    return result;
+}
+
+
+/*
+ * Public Interface
+ */
+
+/*
+ * Check if the address has required RWX privs to complete desired operation
+ */
+bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
+    target_ulong size, pmp_priv_t privs)
+{
+    int i = 0;
+    int ret = -1;
+    target_ulong s = 0;
+    target_ulong e = 0;
+    pmp_priv_t allowed_privs = 0;
+
+    /* Short cut if no rules */
+    if (0 == pmp_get_num_rules(env)) {
+        return true;
+    }
+
+    /* 1.10 draft priv spec states there is an implicit order
+         from low to high */
+    for (i = 0; i < MAX_RISCV_PMPS; i++) {
+        s = pmp_is_in_range(env, i, addr);
+        e = pmp_is_in_range(env, i, addr + size);
+
+        /* partially inside */
+        if ((s + e) == 1) {
+            PMP_DEBUG("pmp violation - access is partially inside");
+            ret = 0;
+            break;
+        }
+
+        /* fully inside */
+        const uint8_t a_field =
+            pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
+        if ((s + e) == 2) {
+            if (PMP_AMATCH_OFF == a_field) {
+                return 1;
+            }
+
+            allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
+            if ((env->priv != PRV_M) || pmp_is_locked(env, i)) {
+                allowed_privs &= env->pmp_state.pmp[i].cfg_reg;
+            }
+
+            if ((privs & allowed_privs) == privs) {
+                ret = 1;
+                break;
+            } else {
+                ret = 0;
+                break;
+            }
+        }
+    }
+
+    /* No rule matched */
+    if (ret == -1) {
+        if (env->priv == PRV_M) {
+            ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an
+                      * M-Mode access, the access succeeds */
+        } else {
+            ret = 0; /* Other modes are not allowed to succeed if they don't
+                      * match a rule, but there are rules.  We've checked for
+                      * no rule earlier in this function. */
+        }
+    }
+
+    return ret == 1 ? true : false;
+}
+
+
+/*
+ * Handle a write to a pmpcfg CSP
+ */
+void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
+    target_ulong val)
+{
+    int i;
+    uint8_t cfg_val;
+
+    PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, reg_index, val);
+
+    if ((reg_index & 1) && (sizeof(target_ulong) == 8)) {
+        PMP_DEBUG("ignoring write - incorrect address");
+        return;
+    }
+
+    for (i = 0; i < sizeof(target_ulong); i++) {
+        cfg_val = (val >> 8 * i)  & 0xff;
+        pmp_write_cfg(env, (reg_index * sizeof(target_ulong)) + i,
+            cfg_val);
+    }
+}
+
+
+/*
+ * Handle a read from a pmpcfg CSP
+ */
+target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index)
+{
+    int i;
+    target_ulong cfg_val = 0;
+    uint8_t val = 0;
+
+    for (i = 0; i < sizeof(target_ulong); i++) {
+        val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i);
+        cfg_val |= (val << (i * 8));
+    }
+
+    PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, reg_index, cfg_val);
+
+    return cfg_val;
+}
+
+
+/*
+ * Handle a write to a pmpaddr CSP
+ */
+void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
+    target_ulong val)
+{
+    PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, addr_index, val);
+
+    if (addr_index < MAX_RISCV_PMPS) {
+        if (!pmp_is_locked(env, addr_index)) {
+            env->pmp_state.pmp[addr_index].addr_reg = val;
+            pmp_update_rule(env, addr_index);
+        } else {
+            PMP_DEBUG("ignoring write - locked");
+        }
+    } else {
+        PMP_DEBUG("ignoring write - out of bounds");
+    }
+}
+
+
+/*
+ * Handle a read from a pmpaddr CSP
+ */
+target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index)
+{
+    PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, addr_index,
+        env->pmp_state.pmp[addr_index].addr_reg);
+    if (addr_index < MAX_RISCV_PMPS) {
+        return env->pmp_state.pmp[addr_index].addr_reg;
+    } else {
+        PMP_DEBUG("ignoring read - out of bounds");
+        return 0;
+    }
+}
+
+#endif
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
new file mode 100644
index 0000000..e3953c8
--- /dev/null
+++ b/target/riscv/pmp.h
@@ -0,0 +1,64 @@
+/*
+ * QEMU RISC-V PMP (Physical Memory Protection)
+ *
+ * Author: Daire McNamara, daire.mcnamara@emdalo.com
+ *         Ivan Griffin, ivan.griffin@emdalo.com
+ *
+ * This provides a RISC-V Physical Memory Protection interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef _RISCV_PMP_H_
+#define _RISCV_PMP_H_
+
+typedef enum {
+    PMP_READ  = 1 << 0,
+    PMP_WRITE = 1 << 1,
+    PMP_EXEC  = 1 << 2,
+    PMP_LOCK  = 1 << 7
+} pmp_priv_t;
+
+typedef enum {
+    PMP_AMATCH_OFF,  /* Null (off)                            */
+    PMP_AMATCH_TOR,  /* Top of Range                          */
+    PMP_AMATCH_NA4,  /* Naturally aligned four-byte region    */
+    PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */
+} pmp_am_t;
+
+typedef struct {
+    target_ulong addr_reg;
+    uint8_t  cfg_reg;
+} pmp_entry_t;
+
+typedef struct {
+    target_ulong sa;
+    target_ulong ea;
+} pmp_addr_t;
+
+typedef struct {
+    pmp_entry_t pmp[MAX_RISCV_PMPS];
+    pmp_addr_t  addr[MAX_RISCV_PMPS];
+    uint32_t num_rules;
+} pmp_table_t;
+
+void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
+    target_ulong val);
+target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index);
+void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
+    target_ulong val);
+target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
+bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
+    target_ulong size, pmp_priv_t priv);
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (8 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 09/23] RISC-V Physical Memory Protection Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-04-04 12:44   ` Laurent Vivier
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 11/23] Add symbol table callback interface to load_elf Michael Clark
                   ` (14 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Implementation of linux user emulation for RISC-V.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 linux-user/elfload.c              |  22 +++
 linux-user/main.c                 |  99 +++++++++++++
 linux-user/riscv/syscall_nr.h     | 287 ++++++++++++++++++++++++++++++++++++++
 linux-user/riscv/target_cpu.h     |  18 +++
 linux-user/riscv/target_elf.h     |  14 ++
 linux-user/riscv/target_signal.h  |  23 +++
 linux-user/riscv/target_structs.h |  46 ++++++
 linux-user/riscv/target_syscall.h |  56 ++++++++
 linux-user/riscv/termbits.h       | 222 +++++++++++++++++++++++++++++
 linux-user/signal.c               | 203 ++++++++++++++++++++++++++-
 linux-user/syscall.c              |   2 +
 linux-user/syscall_defs.h         |  13 +-
 target/riscv/cpu_user.h           |  13 ++
 13 files changed, 1012 insertions(+), 6 deletions(-)
 create mode 100644 linux-user/riscv/syscall_nr.h
 create mode 100644 linux-user/riscv/target_cpu.h
 create mode 100644 linux-user/riscv/target_elf.h
 create mode 100644 linux-user/riscv/target_signal.h
 create mode 100644 linux-user/riscv/target_structs.h
 create mode 100644 linux-user/riscv/target_syscall.h
 create mode 100644 linux-user/riscv/termbits.h
 create mode 100644 target/riscv/cpu_user.h

diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 6689089..838f1c4 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1293,6 +1293,28 @@ static inline void init_thread(struct target_pt_regs *regs,
 
 #endif /* TARGET_TILEGX */
 
+#ifdef TARGET_RISCV
+
+#define ELF_START_MMAP 0x80000000
+#define ELF_ARCH  EM_RISCV
+
+#ifdef TARGET_RISCV32
+#define ELF_CLASS ELFCLASS32
+#else
+#define ELF_CLASS ELFCLASS64
+#endif
+
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
+{
+    regs->sepc = infop->entry;
+    regs->sp = infop->start_stack;
+}
+
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif /* TARGET_RISCV */
+
 #ifdef TARGET_HPPA
 
 #define ELF_START_MMAP  0x80000000
diff --git a/linux-user/main.c b/linux-user/main.c
index bbeb78f..7bc9bc7 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -3653,6 +3653,100 @@ void cpu_loop(CPUTLGState *env)
 
 #endif
 
+#ifdef TARGET_RISCV
+
+void cpu_loop(CPURISCVState *env)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    int trapnr, signum, sigcode;
+    target_ulong sigaddr;
+    target_ulong ret;
+
+    for (;;) {
+        cpu_exec_start(cs);
+        trapnr = cpu_exec(cs);
+        cpu_exec_end(cs);
+        process_queued_cpu_work(cs);
+
+        signum = 0;
+        sigcode = 0;
+        sigaddr = 0;
+
+        switch (trapnr) {
+        case EXCP_INTERRUPT:
+            /* just indicate that signals should be handled asap */
+            break;
+        case EXCP_ATOMIC:
+            cpu_exec_step_atomic(cs);
+            break;
+        case RISCV_EXCP_U_ECALL:
+            env->pc += 4;
+            if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) {
+                /* riscv_flush_icache_syscall is a no-op in QEMU as
+                   self-modifying code is automatically detected */
+                ret = 0;
+            } else {
+                ret = do_syscall(env,
+                                 env->gpr[xA7],
+                                 env->gpr[xA0],
+                                 env->gpr[xA1],
+                                 env->gpr[xA2],
+                                 env->gpr[xA3],
+                                 env->gpr[xA4],
+                                 env->gpr[xA5],
+                                 0, 0);
+            }
+            if (ret == -TARGET_ERESTARTSYS) {
+                env->pc -= 4;
+            } else if (ret != -TARGET_QEMU_ESIGRETURN) {
+                env->gpr[xA0] = ret;
+            }
+            if (cs->singlestep_enabled) {
+                goto gdbstep;
+            }
+            break;
+        case RISCV_EXCP_ILLEGAL_INST:
+            signum = TARGET_SIGILL;
+            sigcode = TARGET_ILL_ILLOPC;
+            break;
+        case RISCV_EXCP_BREAKPOINT:
+            signum = TARGET_SIGTRAP;
+            sigcode = TARGET_TRAP_BRKPT;
+            sigaddr = env->pc;
+            break;
+        case RISCV_EXCP_INST_PAGE_FAULT:
+        case RISCV_EXCP_LOAD_PAGE_FAULT:
+        case RISCV_EXCP_STORE_PAGE_FAULT:
+            signum = TARGET_SIGSEGV;
+            sigcode = TARGET_SEGV_MAPERR;
+            break;
+        case EXCP_DEBUG:
+        gdbstep:
+            signum = gdb_handlesig(cs, TARGET_SIGTRAP);
+            sigcode = TARGET_TRAP_BRKPT;
+            break;
+        default:
+            EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n",
+                     trapnr);
+            exit(EXIT_FAILURE);
+        }
+
+        if (signum) {
+            target_siginfo_t info = {
+                .si_signo = signum,
+                .si_errno = 0,
+                .si_code = sigcode,
+                ._sifields._sigfault._addr = sigaddr
+            };
+            queue_signal(env, info.si_signo, QEMU_SI_KILL, &info);
+        }
+
+        process_pending_signals(env);
+    }
+}
+
+#endif /* TARGET_RISCV */
+
 #ifdef TARGET_HPPA
 
 static abi_ulong hppa_lws(CPUHPPAState *env)
@@ -4803,6 +4897,11 @@ int main(int argc, char **argv, char **envp)
         env->pc = regs->pc;
         cpu_set_sr(env, regs->sr);
     }
+#elif defined(TARGET_RISCV)
+    {
+        env->pc = regs->sepc;
+        env->gpr[xSP] = regs->sp;
+    }
 #elif defined(TARGET_SH4)
     {
         int i;
diff --git a/linux-user/riscv/syscall_nr.h b/linux-user/riscv/syscall_nr.h
new file mode 100644
index 0000000..7e30f1f
--- /dev/null
+++ b/linux-user/riscv/syscall_nr.h
@@ -0,0 +1,287 @@
+/*
+ * Syscall numbers from asm-generic, common for most
+ * of recently-added arches including RISC-V.
+ */
+
+#define TARGET_NR_io_setup 0
+#define TARGET_NR_io_destroy 1
+#define TARGET_NR_io_submit 2
+#define TARGET_NR_io_cancel 3
+#define TARGET_NR_io_getevents 4
+#define TARGET_NR_setxattr 5
+#define TARGET_NR_lsetxattr 6
+#define TARGET_NR_fsetxattr 7
+#define TARGET_NR_getxattr 8
+#define TARGET_NR_lgetxattr 9
+#define TARGET_NR_fgetxattr 10
+#define TARGET_NR_listxattr 11
+#define TARGET_NR_llistxattr 12
+#define TARGET_NR_flistxattr 13
+#define TARGET_NR_removexattr 14
+#define TARGET_NR_lremovexattr 15
+#define TARGET_NR_fremovexattr 16
+#define TARGET_NR_getcwd 17
+#define TARGET_NR_lookup_dcookie 18
+#define TARGET_NR_eventfd2 19
+#define TARGET_NR_epoll_create1 20
+#define TARGET_NR_epoll_ctl 21
+#define TARGET_NR_epoll_pwait 22
+#define TARGET_NR_dup 23
+#define TARGET_NR_dup3 24
+#ifdef TARGET_RISCV32
+#define TARGET_NR_fcntl64 25
+#else
+#define TARGET_NR_fcntl 25
+#endif
+#define TARGET_NR_inotify_init1 26
+#define TARGET_NR_inotify_add_watch 27
+#define TARGET_NR_inotify_rm_watch 28
+#define TARGET_NR_ioctl 29
+#define TARGET_NR_ioprio_set 30
+#define TARGET_NR_ioprio_get 31
+#define TARGET_NR_flock 32
+#define TARGET_NR_mknodat 33
+#define TARGET_NR_mkdirat 34
+#define TARGET_NR_unlinkat 35
+#define TARGET_NR_symlinkat 36
+#define TARGET_NR_linkat 37
+#define TARGET_NR_renameat 38
+#define TARGET_NR_umount2 39
+#define TARGET_NR_mount 40
+#define TARGET_NR_pivot_root 41
+#define TARGET_NR_nfsservctl 42
+#define TARGET_NR_statfs 43
+#define TARGET_NR_fstatfs 44
+#define TARGET_NR_truncate 45
+#define TARGET_NR_ftruncate 46
+#define TARGET_NR_fallocate 47
+#define TARGET_NR_faccessat 48
+#define TARGET_NR_chdir 49
+#define TARGET_NR_fchdir 50
+#define TARGET_NR_chroot 51
+#define TARGET_NR_fchmod 52
+#define TARGET_NR_fchmodat 53
+#define TARGET_NR_fchownat 54
+#define TARGET_NR_fchown 55
+#define TARGET_NR_openat 56
+#define TARGET_NR_close 57
+#define TARGET_NR_vhangup 58
+#define TARGET_NR_pipe2 59
+#define TARGET_NR_quotactl 60
+#define TARGET_NR_getdents64 61
+#define TARGET_NR_lseek 62
+#define TARGET_NR_read 63
+#define TARGET_NR_write 64
+#define TARGET_NR_readv 65
+#define TARGET_NR_writev 66
+#define TARGET_NR_pread64 67
+#define TARGET_NR_pwrite64 68
+#define TARGET_NR_preadv 69
+#define TARGET_NR_pwritev 70
+#define TARGET_NR_sendfile 71
+#define TARGET_NR_pselect6 72
+#define TARGET_NR_ppoll 73
+#define TARGET_NR_signalfd4 74
+#define TARGET_NR_vmsplice 75
+#define TARGET_NR_splice 76
+#define TARGET_NR_tee 77
+#define TARGET_NR_readlinkat 78
+#define TARGET_NR_newfstatat 79
+#define TARGET_NR_fstat 80
+#define TARGET_NR_sync 81
+#define TARGET_NR_fsync 82
+#define TARGET_NR_fdatasync 83
+#define TARGET_NR_sync_file_range 84
+#define TARGET_NR_timerfd_create 85
+#define TARGET_NR_timerfd_settime 86
+#define TARGET_NR_timerfd_gettime 87
+#define TARGET_NR_utimensat 88
+#define TARGET_NR_acct 89
+#define TARGET_NR_capget 90
+#define TARGET_NR_capset 91
+#define TARGET_NR_personality 92
+#define TARGET_NR_exit 93
+#define TARGET_NR_exit_group 94
+#define TARGET_NR_waitid 95
+#define TARGET_NR_set_tid_address 96
+#define TARGET_NR_unshare 97
+#define TARGET_NR_futex 98
+#define TARGET_NR_set_robust_list 99
+#define TARGET_NR_get_robust_list 100
+#define TARGET_NR_nanosleep 101
+#define TARGET_NR_getitimer 102
+#define TARGET_NR_setitimer 103
+#define TARGET_NR_kexec_load 104
+#define TARGET_NR_init_module 105
+#define TARGET_NR_delete_module 106
+#define TARGET_NR_timer_create 107
+#define TARGET_NR_timer_gettime 108
+#define TARGET_NR_timer_getoverrun 109
+#define TARGET_NR_timer_settime 110
+#define TARGET_NR_timer_delete 111
+#define TARGET_NR_clock_settime 112
+#define TARGET_NR_clock_gettime 113
+#define TARGET_NR_clock_getres 114
+#define TARGET_NR_clock_nanosleep 115
+#define TARGET_NR_syslog 116
+#define TARGET_NR_ptrace 117
+#define TARGET_NR_sched_setparam 118
+#define TARGET_NR_sched_setscheduler 119
+#define TARGET_NR_sched_getscheduler 120
+#define TARGET_NR_sched_getparam 121
+#define TARGET_NR_sched_setaffinity 122
+#define TARGET_NR_sched_getaffinity 123
+#define TARGET_NR_sched_yield 124
+#define TARGET_NR_sched_get_priority_max 125
+#define TARGET_NR_sched_get_priority_min 126
+#define TARGET_NR_sched_rr_get_interval 127
+#define TARGET_NR_restart_syscall 128
+#define TARGET_NR_kill 129
+#define TARGET_NR_tkill 130
+#define TARGET_NR_tgkill 131
+#define TARGET_NR_sigaltstack 132
+#define TARGET_NR_rt_sigsuspend 133
+#define TARGET_NR_rt_sigaction 134
+#define TARGET_NR_rt_sigprocmask 135
+#define TARGET_NR_rt_sigpending 136
+#define TARGET_NR_rt_sigtimedwait 137
+#define TARGET_NR_rt_sigqueueinfo 138
+#define TARGET_NR_rt_sigreturn 139
+#define TARGET_NR_setpriority 140
+#define TARGET_NR_getpriority 141
+#define TARGET_NR_reboot 142
+#define TARGET_NR_setregid 143
+#define TARGET_NR_setgid 144
+#define TARGET_NR_setreuid 145
+#define TARGET_NR_setuid 146
+#define TARGET_NR_setresuid 147
+#define TARGET_NR_getresuid 148
+#define TARGET_NR_setresgid 149
+#define TARGET_NR_getresgid 150
+#define TARGET_NR_setfsuid 151
+#define TARGET_NR_setfsgid 152
+#define TARGET_NR_times 153
+#define TARGET_NR_setpgid 154
+#define TARGET_NR_getpgid 155
+#define TARGET_NR_getsid 156
+#define TARGET_NR_setsid 157
+#define TARGET_NR_getgroups 158
+#define TARGET_NR_setgroups 159
+#define TARGET_NR_uname 160
+#define TARGET_NR_sethostname 161
+#define TARGET_NR_setdomainname 162
+#define TARGET_NR_getrlimit 163
+#define TARGET_NR_setrlimit 164
+#define TARGET_NR_getrusage 165
+#define TARGET_NR_umask 166
+#define TARGET_NR_prctl 167
+#define TARGET_NR_getcpu 168
+#define TARGET_NR_gettimeofday 169
+#define TARGET_NR_settimeofday 170
+#define TARGET_NR_adjtimex 171
+#define TARGET_NR_getpid 172
+#define TARGET_NR_getppid 173
+#define TARGET_NR_getuid 174
+#define TARGET_NR_geteuid 175
+#define TARGET_NR_getgid 176
+#define TARGET_NR_getegid 177
+#define TARGET_NR_gettid 178
+#define TARGET_NR_sysinfo 179
+#define TARGET_NR_mq_open 180
+#define TARGET_NR_mq_unlink 181
+#define TARGET_NR_mq_timedsend 182
+#define TARGET_NR_mq_timedreceive 183
+#define TARGET_NR_mq_notify 184
+#define TARGET_NR_mq_getsetattr 185
+#define TARGET_NR_msgget 186
+#define TARGET_NR_msgctl 187
+#define TARGET_NR_msgrcv 188
+#define TARGET_NR_msgsnd 189
+#define TARGET_NR_semget 190
+#define TARGET_NR_semctl 191
+#define TARGET_NR_semtimedop 192
+#define TARGET_NR_semop 193
+#define TARGET_NR_shmget 194
+#define TARGET_NR_shmctl 195
+#define TARGET_NR_shmat 196
+#define TARGET_NR_shmdt 197
+#define TARGET_NR_socket 198
+#define TARGET_NR_socketpair 199
+#define TARGET_NR_bind 200
+#define TARGET_NR_listen 201
+#define TARGET_NR_accept 202
+#define TARGET_NR_connect 203
+#define TARGET_NR_getsockname 204
+#define TARGET_NR_getpeername 205
+#define TARGET_NR_sendto 206
+#define TARGET_NR_recvfrom 207
+#define TARGET_NR_setsockopt 208
+#define TARGET_NR_getsockopt 209
+#define TARGET_NR_shutdown 210
+#define TARGET_NR_sendmsg 211
+#define TARGET_NR_recvmsg 212
+#define TARGET_NR_readahead 213
+#define TARGET_NR_brk 214
+#define TARGET_NR_munmap 215
+#define TARGET_NR_mremap 216
+#define TARGET_NR_add_key 217
+#define TARGET_NR_request_key 218
+#define TARGET_NR_keyctl 219
+#define TARGET_NR_clone 220
+#define TARGET_NR_execve 221
+#ifdef TARGET_RISCV32
+#define TARGET_NR_mmap2 222
+#define TARGET_NR_fadvise64_64 223
+#else
+#define TARGET_NR_mmap 222
+#define TARGET_NR_fadvise64 223
+#endif
+#define TARGET_NR_swapon 224
+#define TARGET_NR_swapoff 225
+#define TARGET_NR_mprotect 226
+#define TARGET_NR_msync 227
+#define TARGET_NR_mlock 228
+#define TARGET_NR_munlock 229
+#define TARGET_NR_mlockall 230
+#define TARGET_NR_munlockall 231
+#define TARGET_NR_mincore 232
+#define TARGET_NR_madvise 233
+#define TARGET_NR_remap_file_pages 234
+#define TARGET_NR_mbind 235
+#define TARGET_NR_get_mempolicy 236
+#define TARGET_NR_set_mempolicy 237
+#define TARGET_NR_migrate_pages 238
+#define TARGET_NR_move_pages 239
+#define TARGET_NR_rt_tgsigqueueinfo 240
+#define TARGET_NR_perf_event_open 241
+#define TARGET_NR_accept4 242
+#define TARGET_NR_recvmmsg 243
+#define TARGET_NR_arch_specific_syscall 244
+#define TARGET_NR_wait4 260
+#define TARGET_NR_prlimit64 261
+#define TARGET_NR_fanotify_init 262
+#define TARGET_NR_fanotify_mark 263
+#define TARGET_NR_name_to_handle_at 264
+#define TARGET_NR_open_by_handle_at 265
+#define TARGET_NR_clock_adjtime 266
+#define TARGET_NR_syncfs 267
+#define TARGET_NR_setns 268
+#define TARGET_NR_sendmmsg 269
+#define TARGET_NR_process_vm_readv 270
+#define TARGET_NR_process_vm_writev 271
+#define TARGET_NR_kcmp 272
+#define TARGET_NR_finit_module 273
+#define TARGET_NR_sched_setattr 274
+#define TARGET_NR_sched_getattr 275
+#define TARGET_NR_renameat2 276
+#define TARGET_NR_seccomp 277
+#define TARGET_NR_getrandom 278
+#define TARGET_NR_memfd_create 279
+#define TARGET_NR_bpf 280
+#define TARGET_NR_execveat 281
+#define TARGET_NR_userfaultfd 282
+#define TARGET_NR_membarrier 283
+#define TARGET_NR_mlock2 284
+#define TARGET_NR_copy_file_range 285
+
+#define TARGET_NR_syscalls (TARGET_NR_copy_file_range + 1)
diff --git a/linux-user/riscv/target_cpu.h b/linux-user/riscv/target_cpu.h
new file mode 100644
index 0000000..c5549b1
--- /dev/null
+++ b/linux-user/riscv/target_cpu.h
@@ -0,0 +1,18 @@
+#ifndef TARGET_CPU_H
+#define TARGET_CPU_H
+
+static inline void cpu_clone_regs(CPURISCVState *env, target_ulong newsp)
+{
+    if (newsp) {
+        env->gpr[xSP] = newsp;
+    }
+
+    env->gpr[xA0] = 0;
+}
+
+static inline void cpu_set_tls(CPURISCVState *env, target_ulong newtls)
+{
+    env->gpr[xTP] = newtls;
+}
+
+#endif
diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h
new file mode 100644
index 0000000..a6716a6
--- /dev/null
+++ b/linux-user/riscv/target_elf.h
@@ -0,0 +1,14 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or (at your option) any
+ * later version. See the COPYING file in the top-level directory.
+ */
+
+#ifndef RISCV_TARGET_ELF_H
+#define RISCV_TARGET_ELF_H
+static inline const char *cpu_get_model(uint32_t eflags)
+{
+    return "any";
+}
+#endif
diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h
new file mode 100644
index 0000000..ce77f75
--- /dev/null
+++ b/linux-user/riscv/target_signal.h
@@ -0,0 +1,23 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+typedef struct target_sigaltstack {
+    abi_ulong ss_sp;
+    abi_int ss_flags;
+    abi_ulong ss_size;
+} target_stack_t;
+
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 2048
+#define TARGET_SIGSTKSZ 8192
+
+static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state)
+{
+   return state->gpr[xSP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/riscv/target_structs.h b/linux-user/riscv/target_structs.h
new file mode 100644
index 0000000..4f0462c
--- /dev/null
+++ b/linux-user/riscv/target_structs.h
@@ -0,0 +1,46 @@
+/*
+ * RISC-V specific structures for linux-user
+ *
+ * This is a copy of ../aarch64/target_structs.h atm.
+ *
+ */
+#ifndef TARGET_STRUCTS_H
+#define TARGET_STRUCTS_H
+
+struct target_ipc_perm {
+    abi_int __key;                      /* Key.  */
+    abi_uint uid;                       /* Owner's user ID.  */
+    abi_uint gid;                       /* Owner's group ID.  */
+    abi_uint cuid;                      /* Creator's user ID.  */
+    abi_uint cgid;                      /* Creator's group ID.  */
+    abi_ushort mode;                    /* Read/write permission.  */
+    abi_ushort __pad1;
+    abi_ushort __seq;                   /* Sequence number.  */
+    abi_ushort __pad2;
+    abi_ulong __unused1;
+    abi_ulong __unused2;
+};
+
+struct target_shmid_ds {
+    struct target_ipc_perm shm_perm;    /* operation permission struct */
+    abi_long shm_segsz;                 /* size of segment in bytes */
+    abi_ulong shm_atime;                /* time of last shmat() */
+#if TARGET_ABI_BITS == 32
+    abi_ulong __unused1;
+#endif
+    abi_ulong shm_dtime;                /* time of last shmdt() */
+#if TARGET_ABI_BITS == 32
+    abi_ulong __unused2;
+#endif
+    abi_ulong shm_ctime;                /* time of last change by shmctl() */
+#if TARGET_ABI_BITS == 32
+    abi_ulong __unused3;
+#endif
+    abi_int shm_cpid;                   /* pid of creator */
+    abi_int shm_lpid;                   /* pid of last shmop */
+    abi_ulong shm_nattch;               /* number of current attaches */
+    abi_ulong __unused4;
+    abi_ulong __unused5;
+};
+
+#endif
diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h
new file mode 100644
index 0000000..d4e109a
--- /dev/null
+++ b/linux-user/riscv/target_syscall.h
@@ -0,0 +1,56 @@
+/*
+ * This struct defines the way the registers are stored on the
+ *  stack during a system call.
+ *
+ * Reference: linux/arch/riscv/include/uapi/asm/ptrace.h
+ */
+
+struct target_pt_regs {
+    abi_long sepc;
+    abi_long ra;
+    abi_long sp;
+    abi_long gp;
+    abi_long tp;
+    abi_long t0;
+    abi_long t1;
+    abi_long t2;
+    abi_long s0;
+    abi_long s1;
+    abi_long a0;
+    abi_long a1;
+    abi_long a2;
+    abi_long a3;
+    abi_long a4;
+    abi_long a5;
+    abi_long a6;
+    abi_long a7;
+    abi_long s2;
+    abi_long s3;
+    abi_long s4;
+    abi_long s5;
+    abi_long s6;
+    abi_long s7;
+    abi_long s8;
+    abi_long s9;
+    abi_long s10;
+    abi_long s11;
+    abi_long t3;
+    abi_long t4;
+    abi_long t5;
+    abi_long t6;
+};
+
+#ifdef TARGET_RISCV32
+#define UNAME_MACHINE "riscv32"
+#else
+#define UNAME_MACHINE "riscv64"
+#endif
+#define UNAME_MINIMUM_RELEASE "3.8.0"
+
+#define TARGET_MINSIGSTKSZ 2048
+#define TARGET_MLOCKALL_MCL_CURRENT 1
+#define TARGET_MLOCKALL_MCL_FUTURE  2
+
+/* clone(flags, newsp, ptidptr, tls, ctidptr) for RISC-V */
+/* This comes from linux/kernel/fork.c, CONFIG_CLONE_BACKWARDS */
+#define TARGET_CLONE_BACKWARDS
diff --git a/linux-user/riscv/termbits.h b/linux-user/riscv/termbits.h
new file mode 100644
index 0000000..7e4e230
--- /dev/null
+++ b/linux-user/riscv/termbits.h
@@ -0,0 +1,222 @@
+/* from asm/termbits.h */
+/* NOTE: exactly the same as i386 */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+    unsigned int c_iflag;               /* input mode flags */
+    unsigned int c_oflag;               /* output mode flags */
+    unsigned int c_cflag;               /* control mode flags */
+    unsigned int c_lflag;               /* local mode flags */
+    unsigned char c_line;               /* line discipline */
+    unsigned char c_cc[TARGET_NCCS];    /* control characters */
+};
+
+/* c_iflag bits */
+#define TARGET_IGNBRK  0000001
+#define TARGET_BRKINT  0000002
+#define TARGET_IGNPAR  0000004
+#define TARGET_PARMRK  0000010
+#define TARGET_INPCK   0000020
+#define TARGET_ISTRIP  0000040
+#define TARGET_INLCR   0000100
+#define TARGET_IGNCR   0000200
+#define TARGET_ICRNL   0000400
+#define TARGET_IUCLC   0001000
+#define TARGET_IXON    0002000
+#define TARGET_IXANY   0004000
+#define TARGET_IXOFF   0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8   0040000
+
+/* c_oflag bits */
+#define TARGET_OPOST   0000001
+#define TARGET_OLCUC   0000002
+#define TARGET_ONLCR   0000004
+#define TARGET_OCRNL   0000010
+#define TARGET_ONOCR   0000020
+#define TARGET_ONLRET  0000040
+#define TARGET_OFILL   0000100
+#define TARGET_OFDEL   0000200
+#define TARGET_NLDLY   0000400
+#define   TARGET_NL0   0000000
+#define   TARGET_NL1   0000400
+#define TARGET_CRDLY   0003000
+#define   TARGET_CR0   0000000
+#define   TARGET_CR1   0001000
+#define   TARGET_CR2   0002000
+#define   TARGET_CR3   0003000
+#define TARGET_TABDLY  0014000
+#define   TARGET_TAB0  0000000
+#define   TARGET_TAB1  0004000
+#define   TARGET_TAB2  0010000
+#define   TARGET_TAB3  0014000
+#define   TARGET_XTABS 0014000
+#define TARGET_BSDLY   0020000
+#define   TARGET_BS0   0000000
+#define   TARGET_BS1   0020000
+#define TARGET_VTDLY   0040000
+#define   TARGET_VT0   0000000
+#define   TARGET_VT1   0040000
+#define TARGET_FFDLY   0100000
+#define   TARGET_FF0   0000000
+#define   TARGET_FF1   0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD   0010017
+#define  TARGET_B0     0000000         /* hang up */
+#define  TARGET_B50    0000001
+#define  TARGET_B75    0000002
+#define  TARGET_B110   0000003
+#define  TARGET_B134   0000004
+#define  TARGET_B150   0000005
+#define  TARGET_B200   0000006
+#define  TARGET_B300   0000007
+#define  TARGET_B600   0000010
+#define  TARGET_B1200  0000011
+#define  TARGET_B1800  0000012
+#define  TARGET_B2400  0000013
+#define  TARGET_B4800  0000014
+#define  TARGET_B9600  0000015
+#define  TARGET_B19200 0000016
+#define  TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE   0000060
+#define   TARGET_CS5   0000000
+#define   TARGET_CS6   0000020
+#define   TARGET_CS7   0000040
+#define   TARGET_CS8   0000060
+#define TARGET_CSTOPB  0000100
+#define TARGET_CREAD   0000200
+#define TARGET_PARENB  0000400
+#define TARGET_PARODD  0001000
+#define TARGET_HUPCL   0002000
+#define TARGET_CLOCAL  0004000
+#define TARGET_CBAUDEX 0010000
+#define  TARGET_B57600  0010001
+#define  TARGET_B115200 0010002
+#define  TARGET_B230400 0010003
+#define  TARGET_B460800 0010004
+#define TARGET_CIBAUD    002003600000  /* input baud rate (not used) */
+#define TARGET_CMSPAR    010000000000  /* mark or space (stick) parity */
+#define TARGET_CRTSCTS   020000000000  /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG    0000001
+#define TARGET_ICANON  0000002
+#define TARGET_XCASE   0000004
+#define TARGET_ECHO    0000010
+#define TARGET_ECHOE   0000020
+#define TARGET_ECHOK   0000040
+#define TARGET_ECHONL  0000100
+#define TARGET_NOFLSH  0000200
+#define TARGET_TOSTOP  0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE  0004000
+#define TARGET_FLUSHO  0010000
+#define TARGET_PENDIN  0040000
+#define TARGET_IEXTEN  0100000
+
+/* c_cc character offsets */
+#define TARGET_VINTR    0
+#define TARGET_VQUIT    1
+#define TARGET_VERASE   2
+#define TARGET_VKILL    3
+#define TARGET_VEOF     4
+#define TARGET_VTIME    5
+#define TARGET_VMIN     6
+#define TARGET_VSWTC    7
+#define TARGET_VSTART   8
+#define TARGET_VSTOP    9
+#define TARGET_VSUSP    10
+#define TARGET_VEOL     11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE  14
+#define TARGET_VLNEXT   15
+#define TARGET_VEOL2    16
+
+/* ioctls */
+
+#define TARGET_TCGETS           0x5401
+#define TARGET_TCSETS           0x5402
+#define TARGET_TCSETSW          0x5403
+#define TARGET_TCSETSF          0x5404
+#define TARGET_TCGETA           0x5405
+#define TARGET_TCSETA           0x5406
+#define TARGET_TCSETAW          0x5407
+#define TARGET_TCSETAF          0x5408
+#define TARGET_TCSBRK           0x5409
+#define TARGET_TCXONC           0x540A
+#define TARGET_TCFLSH           0x540B
+
+#define TARGET_TIOCEXCL         0x540C
+#define TARGET_TIOCNXCL         0x540D
+#define TARGET_TIOCSCTTY        0x540E
+#define TARGET_TIOCGPGRP        0x540F
+#define TARGET_TIOCSPGRP        0x5410
+#define TARGET_TIOCOUTQ         0x5411
+#define TARGET_TIOCSTI          0x5412
+#define TARGET_TIOCGWINSZ       0x5413
+#define TARGET_TIOCSWINSZ       0x5414
+#define TARGET_TIOCMGET         0x5415
+#define TARGET_TIOCMBIS         0x5416
+#define TARGET_TIOCMBIC         0x5417
+#define TARGET_TIOCMSET         0x5418
+#define TARGET_TIOCGSOFTCAR     0x5419
+#define TARGET_TIOCSSOFTCAR     0x541A
+#define TARGET_FIONREAD         0x541B
+#define TARGET_TIOCINQ          TARGET_FIONREAD
+#define TARGET_TIOCLINUX        0x541C
+#define TARGET_TIOCCONS         0x541D
+#define TARGET_TIOCGSERIAL      0x541E
+#define TARGET_TIOCSSERIAL      0x541F
+#define TARGET_TIOCPKT          0x5420
+#define TARGET_FIONBIO          0x5421
+#define TARGET_TIOCNOTTY        0x5422
+#define TARGET_TIOCSETD         0x5423
+#define TARGET_TIOCGETD         0x5424
+#define TARGET_TCSBRKP          0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCTTYGSTRUCT   0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK         0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK         0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID         0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN         TARGET_IOR('T', 0x30, unsigned int)
+        /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK       TARGET_IOW('T', 0x31, int)
+        /* Lock/unlock Pty */
+#define TARGET_TIOCGPTPEER      TARGET_IO('T', 0x41)
+        /* Safely open the slave */
+
+#define TARGET_FIONCLEX         0x5450  /* these numbers need to be adjusted. */
+#define TARGET_FIOCLEX          0x5451
+#define TARGET_FIOASYNC         0x5452
+#define TARGET_TIOCSERCONFIG    0x5453
+#define TARGET_TIOCSERGWILD     0x5454
+#define TARGET_TIOCSERSWILD     0x5455
+#define TARGET_TIOCGLCKTRMIOS   0x5456
+#define TARGET_TIOCSLCKTRMIOS   0x5457
+#define TARGET_TIOCSERGSTRUCT   0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR    0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI  0x545A /* Get multiport config  */
+#define TARGET_TIOCSERSETMULTI  0x545B /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT      0x545C
+        /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT     0x545D
+        /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+
+/* Used for packet mode */
+#define TARGET_TIOCPKT_DATA              0
+#define TARGET_TIOCPKT_FLUSHREAD         1
+#define TARGET_TIOCPKT_FLUSHWRITE        2
+#define TARGET_TIOCPKT_STOP              4
+#define TARGET_TIOCPKT_START             8
+#define TARGET_TIOCPKT_NOSTOP           16
+#define TARGET_TIOCPKT_DOSTOP           32
+
+#define TARGET_TIOCSER_TEMT    0x01 /* Transmitter physically empty */
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 9a380b9..4d3f244 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -535,6 +535,7 @@ static void force_sig(int sig)
  * up the signal frame. oldsig is the signal we were trying to handle
  * at the point of failure.
  */
+#if !defined(TARGET_RISCV)
 static void force_sigsegv(int oldsig)
 {
     if (oldsig == SIGSEGV) {
@@ -547,6 +548,8 @@ static void force_sigsegv(int oldsig)
 }
 #endif
 
+#endif
+
 /* abort execution with signal */
 static void QEMU_NORETURN dump_core_and_abort(int target_sig)
 {
@@ -6385,6 +6388,203 @@ long do_rt_sigreturn(CPUTLGState *env)
     return -TARGET_QEMU_ESIGRETURN;
 }
 
+#elif defined(TARGET_RISCV)
+
+/* Signal handler invocation must be transparent for the code being
+   interrupted. Complete CPU (hart) state is saved on entry and restored
+   before returning from the handler. Process sigmask is also saved to block
+   signals while the handler is running. The handler gets its own stack,
+   which also doubles as storage for the CPU state and sigmask.
+
+   The code below is qemu re-implementation of arch/riscv/kernel/signal.c */
+
+struct target_sigcontext {
+    abi_long pc;
+    abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */
+    uint64_t fpr[32];
+    uint32_t fcsr;
+}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */
+
+struct target_ucontext {
+    unsigned long uc_flags;
+    struct target_ucontext *uc_link;
+    target_stack_t uc_stack;
+    struct target_sigcontext uc_mcontext;
+    target_sigset_t uc_sigmask;
+};
+
+struct target_rt_sigframe {
+    uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */
+    struct target_siginfo info;
+    struct target_ucontext uc;
+};
+
+static abi_ulong get_sigframe(struct target_sigaction *ka,
+                              CPURISCVState *regs, size_t framesize)
+{
+    abi_ulong sp = regs->gpr[xSP];
+    int onsigstack = on_sig_stack(sp);
+
+    /* redzone */
+    /* This is the X/Open sanctioned signal stack switching.  */
+    if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) {
+        sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+    }
+
+    sp -= framesize;
+    sp &= ~3UL; /* align sp on 4-byte boundary */
+
+    /* If we are on the alternate signal stack and would overflow it, don't.
+       Return an always-bogus address instead so we will die with SIGSEGV. */
+    if (onsigstack && !likely(on_sig_stack(sp))) {
+        return -1L;
+    }
+
+    return sp;
+}
+
+static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
+{
+    int i;
+
+    __put_user(env->pc, &sc->pc);
+
+    for (i = 1; i < 32; i++) {
+        __put_user(env->gpr[i], &sc->gpr[i - 1]);
+    }
+    for (i = 0; i < 32; i++) {
+        __put_user(env->fpr[i], &sc->fpr[i]);
+    }
+
+    uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/
+    __put_user(fcsr, &sc->fcsr);
+}
+
+static void setup_ucontext(struct target_ucontext *uc,
+                           CPURISCVState *env, target_sigset_t *set)
+{
+    abi_ulong ss_sp = (target_ulong)target_sigaltstack_used.ss_sp;
+    abi_ulong ss_flags = sas_ss_flags(env->gpr[xSP]);
+    abi_ulong ss_size = target_sigaltstack_used.ss_size;
+
+    __put_user(0,    &(uc->uc_flags));
+    __put_user(0,    &(uc->uc_link));
+
+    __put_user(ss_sp,    &(uc->uc_stack.ss_sp));
+    __put_user(ss_flags, &(uc->uc_stack.ss_flags));
+    __put_user(ss_size,  &(uc->uc_stack.ss_size));
+
+    int i;
+    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+        __put_user(set->sig[i], &(uc->uc_sigmask.sig[i]));
+    }
+
+    setup_sigcontext(&uc->uc_mcontext, env);
+}
+
+static inline void install_sigtramp(uint32_t *tramp)
+{
+    __put_user(0x08b00893, tramp + 0);  /* li a7, 139 = __NR_rt_sigreturn */
+    __put_user(0x00000073, tramp + 1);  /* ecall */
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+                           target_siginfo_t *info,
+                           target_sigset_t *set, CPURISCVState *env)
+{
+    abi_ulong frame_addr;
+    struct target_rt_sigframe *frame;
+
+    frame_addr = get_sigframe(ka, env, sizeof(*frame));
+    trace_user_setup_rt_frame(env, frame_addr);
+
+    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+        goto badframe;
+    }
+
+    setup_ucontext(&frame->uc, env, set);
+    tswap_siginfo(&frame->info, info);
+    install_sigtramp(frame->tramp);
+
+    env->pc = ka->_sa_handler;
+    env->gpr[xSP] = frame_addr;
+    env->gpr[xA0] = sig;
+    env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info);
+    env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
+    env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp);
+
+    return;
+
+badframe:
+    unlock_user_struct(frame, frame_addr, 1);
+    if (sig == TARGET_SIGSEGV) {
+        ka->_sa_handler = TARGET_SIG_DFL;
+    }
+    force_sig(TARGET_SIGSEGV);
+}
+
+static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
+{
+    int i;
+
+    __get_user(env->pc, &sc->pc);
+
+    for (i = 1; i < 32; ++i) {
+        __get_user(env->gpr[i], &sc->gpr[i - 1]);
+    }
+    for (i = 0; i < 32; ++i) {
+        __get_user(env->fpr[i], &sc->fpr[i]);
+    }
+
+    uint32_t fcsr;
+    __get_user(fcsr, &sc->fcsr);
+    csr_write_helper(env, fcsr, CSR_FCSR);
+}
+
+static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
+{
+    sigset_t blocked;
+    target_sigset_t target_set;
+    int i;
+
+    target_sigemptyset(&target_set);
+    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+        __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i]));
+    }
+
+    target_to_host_sigset_internal(&blocked, &target_set);
+    set_sigmask(&blocked);
+
+    restore_sigcontext(env, &uc->uc_mcontext);
+}
+
+long do_rt_sigreturn(CPURISCVState *env)
+{
+    struct target_rt_sigframe *frame;
+    abi_ulong frame_addr;
+
+    frame_addr = env->gpr[xSP];
+    trace_user_do_sigreturn(env, frame_addr);
+    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+        goto badframe;
+    }
+
+    restore_ucontext(env, &frame->uc);
+
+    if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe,
+            uc.uc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) {
+        goto badframe;
+    }
+
+    unlock_user_struct(frame, frame_addr, 0);
+    return -TARGET_QEMU_ESIGRETURN;
+
+badframe:
+    unlock_user_struct(frame, frame_addr, 0);
+    force_sig(TARGET_SIGSEGV);
+    return 0;
+}
+
 #elif defined(TARGET_HPPA)
 
 struct target_sigcontext {
@@ -6676,7 +6876,8 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
 #if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \
         || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \
         || defined(TARGET_PPC64) || defined(TARGET_HPPA) \
-        || defined(TARGET_NIOS2) || defined(TARGET_X86_64)
+        || defined(TARGET_NIOS2) || defined(TARGET_X86_64) \
+        || defined(TARGET_RISCV)
         /* These targets do not have traditional signals.  */
         setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env);
 #else
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e24f43c..a8abfd4 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8555,9 +8555,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     case TARGET_NR_ioctl:
         ret = do_ioctl(arg1, arg2, arg3);
         break;
+#ifdef TARGET_NR_fcntl
     case TARGET_NR_fcntl:
         ret = do_fcntl(arg1, arg2, arg3);
         break;
+#endif
 #ifdef TARGET_NR_mpx
     case TARGET_NR_mpx:
         goto unimplemented;
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index a35c52a..e00e1b3 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -71,7 +71,7 @@
     || defined(TARGET_M68K) || defined(TARGET_CRIS) \
     || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \
     || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \
-    || defined(TARGET_NIOS2)
+    || defined(TARGET_NIOS2) || defined(TARGET_RISCV)
 
 #define TARGET_IOC_SIZEBITS	14
 #define TARGET_IOC_DIRBITS	2
@@ -435,7 +435,8 @@ int do_sigaction(int sig, const struct target_sigaction *act,
     || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \
     || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \
     || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \
-    || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2)
+    || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \
+    || defined(TARGET_RISCV)
 
 #if defined(TARGET_SPARC)
 #define TARGET_SA_NOCLDSTOP    8u
@@ -2093,7 +2094,7 @@ struct target_stat {
     unsigned int __unused[2];
 };
 #elif defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) || \
-      defined(TARGET_NIOS2)
+      defined(TARGET_NIOS2) || defined(TARGET_RISCV)
 
 /* These are the asm-generic versions of the stat and stat64 structures */
 
@@ -2120,6 +2121,7 @@ struct target_stat {
     unsigned int __unused5;
 };
 
+#if !defined(TARGET_RISCV64)
 #define TARGET_HAS_STRUCT_STAT64
 struct target_stat64 {
     uint64_t st_dev;
@@ -2143,6 +2145,7 @@ struct target_stat64 {
     unsigned int __unused4;
     unsigned int __unused5;
 };
+#endif
 
 #elif defined(TARGET_HPPA)
 
@@ -2258,8 +2261,8 @@ struct target_statfs64 {
 	uint32_t	f_spare[6];
 };
 #elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \
-       defined(TARGET_SPARC64) || defined(TARGET_AARCH64)) && \
-       !defined(TARGET_ABI32)
+       defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \
+       defined(TARGET_RISCV)) && !defined(TARGET_ABI32)
 struct target_statfs {
 	abi_long f_type;
 	abi_long f_bsize;
diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h
new file mode 100644
index 0000000..c219961
--- /dev/null
+++ b/target/riscv/cpu_user.h
@@ -0,0 +1,13 @@
+#define xRA 1   /* return address (aka link register) */
+#define xSP 2   /* stack pointer */
+#define xGP 3   /* global pointer */
+#define xTP 4   /* thread pointer */
+
+#define xA0 10  /* gpr[10-17] are syscall arguments */
+#define xA1 11
+#define xA2 12
+#define xA3 13
+#define xA4 14
+#define xA5 15
+#define xA6 16
+#define xA7 17  /* syscall number goes here */
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 11/23] Add symbol table callback interface to load_elf
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (9 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 11:34   ` Philippe Mathieu-Daudé
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 12/23] RISC-V HTIF Console Michael Clark
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

The RISC-V HTIF (Host Target Interface) console device requires access
to the symbol table to locate the 'tohost' and 'fromhost' symbols.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/core/loader.c     | 18 ++++++++++++++++--
 include/hw/elf_ops.h | 34 +++++++++++++++++++++-------------
 include/hw/loader.h  | 17 ++++++++++++++++-
 3 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/hw/core/loader.c b/hw/core/loader.c
index c08f130..1d26c6e 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -450,6 +450,20 @@ int load_elf_ram(const char *filename,
                  int clear_lsb, int data_swab, AddressSpace *as,
                  bool load_rom)
 {
+    return load_elf_ram_sym(filename, translate_fn, translate_opaque,
+                            pentry, lowaddr, highaddr, big_endian,
+                            elf_machine, clear_lsb, data_swab, as,
+                            load_rom, NULL);
+}
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf_ram_sym(const char *filename,
+                     uint64_t (*translate_fn)(void *, uint64_t),
+                     void *translate_opaque, uint64_t *pentry,
+                     uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
+                     int elf_machine, int clear_lsb, int data_swab,
+                     AddressSpace *as, bool load_rom, symbol_fn_t sym_cb)
+{
     int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
     uint8_t e_ident[EI_NIDENT];
 
@@ -488,11 +502,11 @@ int load_elf_ram(const char *filename,
     if (e_ident[EI_CLASS] == ELFCLASS64) {
         ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
                          pentry, lowaddr, highaddr, elf_machine, clear_lsb,
-                         data_swab, as, load_rom);
+                         data_swab, as, load_rom, sym_cb);
     } else {
         ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
                          pentry, lowaddr, highaddr, elf_machine, clear_lsb,
-                         data_swab, as, load_rom);
+                         data_swab, as, load_rom, sym_cb);
     }
 
  fail:
diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h
index d192e7e..b6e19e3 100644
--- a/include/hw/elf_ops.h
+++ b/include/hw/elf_ops.h
@@ -105,7 +105,7 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1)
 }
 
 static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
-                                  int clear_lsb)
+                                  int clear_lsb, symbol_fn_t sym_cb)
 {
     struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
     struct elf_sym *syms = NULL;
@@ -133,10 +133,26 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
 
     nsyms = symtab->sh_size / sizeof(struct elf_sym);
 
+    /* String table */
+    if (symtab->sh_link >= ehdr->e_shnum) {
+        goto fail;
+    }
+    strtab = &shdr_table[symtab->sh_link];
+
+    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
+    if (!str) {
+        goto fail;
+    }
+
     i = 0;
     while (i < nsyms) {
-        if (must_swab)
+        if (must_swab) {
             glue(bswap_sym, SZ)(&syms[i]);
+        }
+        if (sym_cb) {
+            sym_cb(str + syms[i].st_name, syms[i].st_info,
+                   syms[i].st_value, syms[i].st_size);
+        }
         /* We are only interested in function symbols.
            Throw everything else away.  */
         if (syms[i].st_shndx == SHN_UNDEF ||
@@ -163,15 +179,6 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
         }
     }
 
-    /* String table */
-    if (symtab->sh_link >= ehdr->e_shnum)
-        goto fail;
-    strtab = &shdr_table[symtab->sh_link];
-
-    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
-    if (!str)
-        goto fail;
-
     /* Commit */
     s = g_malloc0(sizeof(*s));
     s->lookup_symbol = glue(lookup_symbol, SZ);
@@ -264,7 +271,8 @@ static int glue(load_elf, SZ)(const char *name, int fd,
                               int must_swab, uint64_t *pentry,
                               uint64_t *lowaddr, uint64_t *highaddr,
                               int elf_machine, int clear_lsb, int data_swab,
-                              AddressSpace *as, bool load_rom)
+                              AddressSpace *as, bool load_rom,
+                              symbol_fn_t sym_cb)
 {
     struct elfhdr ehdr;
     struct elf_phdr *phdr = NULL, *ph;
@@ -329,7 +337,7 @@ static int glue(load_elf, SZ)(const char *name, int fd,
     if (pentry)
    	*pentry = (uint64_t)(elf_sword)ehdr.e_entry;
 
-    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb);
+    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb);
 
     size = ehdr.e_phnum * sizeof(phdr[0]);
     if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) {
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 5edbe02..7b05e8b 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -64,7 +64,7 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz);
 #define ELF_LOAD_WRONG_ENDIAN -4
 const char *load_elf_strerror(int error);
 
-/** load_elf_ram:
+/** load_elf_ram_sym:
  * @filename: Path of ELF file
  * @translate_fn: optional function to translate load addresses
  * @translate_opaque: opaque data passed to @translate_fn
@@ -81,6 +81,7 @@ const char *load_elf_strerror(int error);
  * @as: The AddressSpace to load the ELF to. The value of address_space_memory
  *      is used if nothing is supplied here.
  * @load_rom : Load ELF binary as ROM
+ * @sym_cb: Callback function for symbol table entries
  *
  * Load an ELF file's contents to the emulated system's address space.
  * Clients may optionally specify a callback to perform address
@@ -93,6 +94,20 @@ const char *load_elf_strerror(int error);
  * If @elf_machine is EM_NONE then the machine type will be read from the
  * ELF header and no checks will be carried out against the machine type.
  */
+typedef void (*symbol_fn_t)(const char *st_name, int st_info,
+                            uint64_t st_value, uint64_t st_size);
+
+int load_elf_ram_sym(const char *filename,
+                     uint64_t (*translate_fn)(void *, uint64_t),
+                     void *translate_opaque, uint64_t *pentry,
+                     uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
+                     int elf_machine, int clear_lsb, int data_swab,
+                     AddressSpace *as, bool load_rom, symbol_fn_t sym_cb);
+
+/** load_elf_ram:
+ * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a
+ * symbol callback function
+ */
 int load_elf_ram(const char *filename,
                  uint64_t (*translate_fn)(void *, uint64_t),
                  void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 12/23] RISC-V HTIF Console
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (10 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 11/23] Add symbol table callback interface to load_elf Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 11:52   ` Philippe Mathieu-Daudé
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array Michael Clark
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches, Stefan O'Rear

HTIF (Host Target Interface) provides console emulation for QEMU. HTIF
allows identical copies of BBL (Berkeley Boot Loader) and linux to run
on both Spike and QEMU. BBL provides HTIF console access via the
SBI (Supervisor Binary Interface) and the linux kernel SBI console.

The HTIT chardev implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/riscv_htif.c         | 258 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/riscv_htif.h |  61 ++++++++++
 2 files changed, 319 insertions(+)
 create mode 100644 hw/riscv/riscv_htif.c
 create mode 100644 include/hw/riscv/riscv_htif.h

diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c
new file mode 100644
index 0000000..3e17f30
--- /dev/null
+++ b/hw/riscv/riscv_htif.c
@@ -0,0 +1,258 @@
+/*
+ * QEMU RISC-V Host Target Interface (HTIF) Emulation
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This provides HTIF device emulation for QEMU. At the moment this allows
+ * for identical copies of bbl/linux to run on both spike and QEMU.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "hw/riscv/riscv_htif.h"
+#include "qemu/timer.h"
+#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
+
+#define RISCV_DEBUG_HTIF 0
+#define HTIF_DEBUG(fmt, ...)                                                   \
+    do {                                                                       \
+        if (RISCV_DEBUG_HTIF) {                                                \
+            qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
+        }                                                                      \
+    } while (0)
+
+static uint64_t fromhost_addr, tohost_addr;
+
+void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
+    uint64_t st_size)
+{
+    if (strcmp("fromhost", st_name) == 0) {
+        fromhost_addr = st_value;
+        if (st_size != 8) {
+            error_report("HTIF fromhost must be 8 bytes");
+            exit(1);
+        }
+    } else if (strcmp("tohost", st_name) == 0) {
+        tohost_addr = st_value;
+        if (st_size != 8) {
+            error_report("HTIF tohost must be 8 bytes");
+            exit(1);
+        }
+    }
+}
+
+/*
+ * Called by the char dev to see if HTIF is ready to accept input.
+ */
+static int htif_can_recv(void *opaque)
+{
+    return 1;
+}
+
+/*
+ * Called by the char dev to supply input to HTIF console.
+ * We assume that we will receive one character at a time.
+ */
+static void htif_recv(void *opaque, const uint8_t *buf, int size)
+{
+    HTIFState *htifstate = opaque;
+
+    if (size != 1) {
+        return;
+    }
+
+    /* TODO - we need to check whether mfromhost is zero which indicates
+              the device is ready to receive. The current implementation
+              will drop characters */
+
+    uint64_t val_written = htifstate->pending_read;
+    uint64_t resp = 0x100 | *buf;
+
+    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+}
+
+/*
+ * Called by the char dev to supply special events to the HTIF console.
+ * Not used for HTIF.
+ */
+static void htif_event(void *opaque, int event)
+{
+
+}
+
+static int htif_be_change(void *opaque)
+{
+    HTIFState *s = opaque;
+
+    qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
+        htif_be_change, s, NULL, true);
+
+    return 0;
+}
+
+static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
+{
+    uint8_t device = val_written >> 56;
+    uint8_t cmd = val_written >> 48;
+    uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
+    int resp = 0;
+
+    HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64
+        " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload);
+
+    /*
+     * Currently, there is a fixed mapping of devices:
+     * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy)
+     * 1: Console
+     */
+    if (unlikely(device == 0x0)) {
+        /* frontend syscall handler, shutdown and exit code support */
+        if (cmd == 0x0) {
+            if (payload & 0x1) {
+                /* exit code */
+                int exit_code = payload >> 1;
+                exit(exit_code);
+            } else {
+                qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n");
+            }
+        } else {
+            qemu_log("HTIF device %d: unknown command\n", device);
+        }
+    } else if (likely(device == 0x1)) {
+        /* HTIF Console */
+        if (cmd == 0x0) {
+            /* this should be a queue, but not yet implemented as such */
+            htifstate->pending_read = val_written;
+            htifstate->env->mtohost = 0; /* clear to indicate we read */
+            return;
+        } else if (cmd == 0x1) {
+            qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
+            resp = 0x100 | (uint8_t)payload;
+        } else {
+            qemu_log("HTIF device %d: unknown command\n", device);
+        }
+    } else {
+        qemu_log("HTIF unknown device or command\n");
+        HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64
+            " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload);
+    }
+    /*
+     * - latest bbl does not set fromhost to 0 if there is a value in tohost
+     * - with this code enabled, qemu hangs waiting for fromhost to go to 0
+     * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10
+     * - HTIF needs protocol documentation and a more complete state machine
+
+        while (!htifstate->fromhost_inprogress &&
+            htifstate->env->mfromhost != 0x0) {
+        }
+    */
+    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+    htifstate->env->mtohost = 0; /* clear to indicate we read */
+}
+
+#define TOHOST_OFFSET1 (htifstate->tohost_offset)
+#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4)
+#define FROMHOST_OFFSET1 (htifstate->fromhost_offset)
+#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4)
+
+/* CPU wants to read an HTIF register */
+static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size)
+{
+    HTIFState *htifstate = opaque;
+    if (addr == TOHOST_OFFSET1) {
+        return htifstate->env->mtohost & 0xFFFFFFFF;
+    } else if (addr == TOHOST_OFFSET2) {
+        return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF;
+    } else if (addr == FROMHOST_OFFSET1) {
+        return htifstate->env->mfromhost & 0xFFFFFFFF;
+    } else if (addr == FROMHOST_OFFSET2) {
+        return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF;
+    } else {
+        qemu_log("Invalid htif read: address %016" PRIx64 "\n",
+            (uint64_t)addr);
+        return 0;
+    }
+}
+
+/* CPU wrote to an HTIF register */
+static void htif_mm_write(void *opaque, hwaddr addr,
+                            uint64_t value, unsigned size)
+{
+    HTIFState *htifstate = opaque;
+    if (addr == TOHOST_OFFSET1) {
+        if (htifstate->env->mtohost == 0x0) {
+            htifstate->allow_tohost = 1;
+            htifstate->env->mtohost = value & 0xFFFFFFFF;
+        } else {
+            htifstate->allow_tohost = 0;
+        }
+    } else if (addr == TOHOST_OFFSET2) {
+        if (htifstate->allow_tohost) {
+            htifstate->env->mtohost |= value << 32;
+            htif_handle_tohost_write(htifstate, htifstate->env->mtohost);
+        }
+    } else if (addr == FROMHOST_OFFSET1) {
+        htifstate->fromhost_inprogress = 1;
+        htifstate->env->mfromhost = value & 0xFFFFFFFF;
+    } else if (addr == FROMHOST_OFFSET2) {
+        htifstate->env->mfromhost |= value << 32;
+        htifstate->fromhost_inprogress = 0;
+    } else {
+        qemu_log("Invalid htif write: address %016" PRIx64 "\n",
+            (uint64_t)addr);
+    }
+}
+
+static const MemoryRegionOps htif_mm_ops = {
+    .read = htif_mm_read,
+    .write = htif_mm_write,
+};
+
+HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
+    CPURISCVState *env, Chardev *chr)
+{
+    uint64_t base = MIN(tohost_addr, fromhost_addr);
+    uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
+    uint64_t tohost_offset = tohost_addr - base;
+    uint64_t fromhost_offset = fromhost_addr - base;
+
+    HTIFState *s = g_malloc0(sizeof(HTIFState));
+    s->address_space = address_space;
+    s->main_mem = main_mem;
+    s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem);
+    s->env = env;
+    s->tohost_offset = tohost_offset;
+    s->fromhost_offset = fromhost_offset;
+    s->pending_read = 0;
+    s->allow_tohost = 0;
+    s->fromhost_inprogress = 0;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
+        htif_be_change, s, NULL, true);
+    if (base) {
+        memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
+                            TYPE_HTIF_UART, size);
+        memory_region_add_subregion(address_space, base, &s->mmio);
+    }
+
+    return s;
+}
diff --git a/include/hw/riscv/riscv_htif.h b/include/hw/riscv/riscv_htif.h
new file mode 100644
index 0000000..fb5f881
--- /dev/null
+++ b/include/hw/riscv/riscv_htif.h
@@ -0,0 +1,61 @@
+/*
+ * QEMU RISCV Host Target Interface (HTIF) Emulation
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_RISCV_HTIF_H
+#define HW_RISCV_HTIF_H
+
+#include "hw/hw.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "sysemu/sysemu.h"
+#include "exec/memory.h"
+#include "target/riscv/cpu.h"
+
+#define TYPE_HTIF_UART "riscv.htif.uart"
+
+typedef struct HTIFState {
+    int allow_tohost;
+    int fromhost_inprogress;
+
+    hwaddr tohost_offset;
+    hwaddr fromhost_offset;
+    uint64_t tohost_size;
+    uint64_t fromhost_size;
+    MemoryRegion mmio;
+    MemoryRegion *address_space;
+    MemoryRegion *main_mem;
+    void *main_mem_ram_ptr;
+
+    CPURISCVState *env;
+    CharBackend chr;
+    uint64_t pending_read;
+} HTIFState;
+
+extern const VMStateDescription vmstate_htif;
+extern const MemoryRegionOps htif_io_ops;
+
+/* HTIF symbol callback */
+void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
+    uint64_t st_size);
+
+/* legacy pre qom */
+HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
+    CPURISCVState *env, Chardev *chr);
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (11 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 12/23] RISC-V HTIF Console Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 12:52   ` Philippe Mathieu-Daudé
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 14/23] SiFive RISC-V CLINT Block Michael Clark
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Holds the state of a heterogenous array of RISC-V hardware threads.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/riscv_hart.c         | 89 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/riscv_hart.h | 39 +++++++++++++++++++
 2 files changed, 128 insertions(+)
 create mode 100644 hw/riscv/riscv_hart.c
 create mode 100644 include/hw/riscv/riscv_hart.h

diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
new file mode 100644
index 0000000..14e3c18
--- /dev/null
+++ b/hw/riscv/riscv_hart.c
@@ -0,0 +1,89 @@
+/*
+ * QEMU RISCV Hart Array
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+
+static Property riscv_harts_props[] = {
+    DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
+    DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_harts_cpu_reset(void *opaque)
+{
+    RISCVCPU *cpu = opaque;
+    cpu_reset(CPU(cpu));
+}
+
+static void riscv_harts_realize(DeviceState *dev, Error **errp)
+{
+    RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
+    Error *err = NULL;
+    int n;
+
+    s->harts = g_new0(RISCVCPU, s->num_harts);
+
+    for (n = 0; n < s->num_harts; n++) {
+
+        object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type);
+        s->harts[n].env.mhartid = n;
+        object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]),
+                                  &error_abort);
+        qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
+        object_property_set_bool(OBJECT(&s->harts[n]), true,
+                                 "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+}
+
+static void riscv_harts_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = riscv_harts_props;
+    dc->realize = riscv_harts_realize;
+}
+
+static void riscv_harts_init(Object *obj)
+{
+    /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */
+}
+
+static const TypeInfo riscv_harts_info = {
+    .name          = TYPE_RISCV_HART_ARRAY,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RISCVHartArrayState),
+    .instance_init = riscv_harts_init,
+    .class_init    = riscv_harts_class_init,
+};
+
+static void riscv_harts_register_types(void)
+{
+    type_register_static(&riscv_harts_info);
+}
+
+type_init(riscv_harts_register_types)
diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
new file mode 100644
index 0000000..0671d88
--- /dev/null
+++ b/include/hw/riscv/riscv_hart.h
@@ -0,0 +1,39 @@
+/*
+ * QEMU RISC-V Hart Array interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_RISCV_HART_H
+#define HW_RISCV_HART_H
+
+#define TYPE_RISCV_HART_ARRAY "riscv.hart_array"
+
+#define RISCV_HART_ARRAY(obj) \
+    OBJECT_CHECK(RISCVHartArrayState, (obj), TYPE_RISCV_HART_ARRAY)
+
+typedef struct RISCVHartArrayState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    uint32_t num_harts;
+    char *cpu_type;
+    RISCVCPU *harts;
+} RISCVHartArrayState;
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 14/23] SiFive RISC-V CLINT Block
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (12 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 15/23] SiFive RISC-V PLIC Block Michael Clark
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches, Stefan O'Rear

The CLINT (Core Local Interruptor) device provides real-time clock, timer
and interprocessor interrupts based on SiFive's CLINT specification.

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_clint.c         | 254 ++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_clint.h |  50 ++++++++
 2 files changed, 304 insertions(+)
 create mode 100644 hw/riscv/sifive_clint.c
 create mode 100644 include/hw/riscv/sifive_clint.h

diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c
new file mode 100644
index 0000000..4893453
--- /dev/null
+++ b/hw/riscv/sifive_clint.c
@@ -0,0 +1,254 @@
+/*
+ * SiFive CLINT (Core Local Interruptor)
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides real-time clock, timer and interprocessor interrupts.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_clint.h"
+#include "qemu/timer.h"
+
+/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */
+#define TIMER_FREQ (10 * 1000 * 1000)
+
+static uint64_t cpu_riscv_read_rtc(void)
+{
+    return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ,
+                    NANOSECONDS_PER_SECOND);
+}
+
+/*
+ * Called when timecmp is written to update the QEMU timer or immediately
+ * trigger timer interrupt if mtimecmp <= current timer value.
+ */
+static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
+{
+    uint64_t next;
+    uint64_t diff;
+
+    uint64_t rtc_r = cpu_riscv_read_rtc();
+
+    cpu->env.timecmp = value;
+    if (cpu->env.timecmp <= rtc_r) {
+        /* if we're setting an MTIMECMP value in the "past",
+           immediately raise the timer interrupt */
+        riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+        return;
+    }
+
+    /* otherwise, set up the future timer interrupt */
+    riscv_set_local_interrupt(cpu, MIP_MTIP, 0);
+    diff = cpu->env.timecmp - rtc_r;
+    /* back to ns (note args switched in muldiv64) */
+    next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ);
+    timer_mod(cpu->env.timer, next);
+}
+
+/*
+ * Callback used when the timer set using timer_mod expires.
+ * Should raise the timer interrupt line
+ */
+static void sifive_clint_timer_cb(void *opaque)
+{
+    RISCVCPU *cpu = opaque;
+    riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+}
+
+/* CPU wants to read rtc or timecmp register */
+static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SiFiveCLINTState *clint = opaque;
+    if (addr >= clint->sip_base &&
+        addr < clint->sip_base + (clint->num_harts << 2)) {
+        size_t hartid = (addr - clint->sip_base) >> 2;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x3) == 0) {
+            return (env->mip & MIP_MSIP) > 0;
+        } else {
+            error_report("clint: invalid read: %08x", (uint32_t)addr);
+            return 0;
+        }
+    } else if (addr >= clint->timecmp_base &&
+        addr < clint->timecmp_base + (clint->num_harts << 3)) {
+        size_t hartid = (addr - clint->timecmp_base) >> 3;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x7) == 0) {
+            /* timecmp_lo */
+            uint64_t timecmp = env->timecmp;
+            return timecmp & 0xFFFFFFFF;
+        } else if ((addr & 0x7) == 4) {
+            /* timecmp_hi */
+            uint64_t timecmp = env->timecmp;
+            return (timecmp >> 32) & 0xFFFFFFFF;
+        } else {
+            error_report("clint: invalid read: %08x", (uint32_t)addr);
+            return 0;
+        }
+    } else if (addr == clint->time_base) {
+        /* time_lo */
+        return cpu_riscv_read_rtc() & 0xFFFFFFFF;
+    } else if (addr == clint->time_base + 4) {
+        /* time_hi */
+        return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF;
+    }
+
+    error_report("clint: invalid read: %08x", (uint32_t)addr);
+    return 0;
+}
+
+/* CPU wrote to rtc or timecmp register */
+static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
+        unsigned size)
+{
+    SiFiveCLINTState *clint = opaque;
+
+    if (addr >= clint->sip_base &&
+        addr < clint->sip_base + (clint->num_harts << 2)) {
+        size_t hartid = (addr - clint->sip_base) >> 2;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x3) == 0) {
+            riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0);
+        } else {
+            error_report("clint: invalid sip write: %08x", (uint32_t)addr);
+        }
+        return;
+    } else if (addr >= clint->timecmp_base &&
+        addr < clint->timecmp_base + (clint->num_harts << 3)) {
+        size_t hartid = (addr - clint->timecmp_base) >> 3;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x7) == 0) {
+            /* timecmp_lo */
+            uint64_t timecmp = env->timecmp;
+            sifive_clint_write_timecmp(RISCV_CPU(cpu),
+                timecmp << 32 | (value & 0xFFFFFFFF));
+            return;
+        } else if ((addr & 0x7) == 4) {
+            /* timecmp_hi */
+            uint64_t timecmp = env->timecmp;
+            sifive_clint_write_timecmp(RISCV_CPU(cpu),
+                value << 32 | (timecmp & 0xFFFFFFFF));
+        } else {
+            error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
+        }
+        return;
+    } else if (addr == clint->time_base) {
+        /* time_lo */
+        error_report("clint: time_lo write not implemented");
+        return;
+    } else if (addr == clint->time_base + 4) {
+        /* time_hi */
+        error_report("clint: time_hi write not implemented");
+        return;
+    }
+
+    error_report("clint: invalid write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps sifive_clint_ops = {
+    .read = sifive_clint_read,
+    .write = sifive_clint_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static Property sifive_clint_properties[] = {
+    DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
+    DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
+    DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
+    DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
+    DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_clint_realize(DeviceState *dev, Error **errp)
+{
+    SiFiveCLINTState *s = SIFIVE_CLINT(dev);
+    memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
+                          TYPE_SIFIVE_CLINT, s->aperture_size);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+}
+
+static void sifive_clint_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->realize = sifive_clint_realize;
+    dc->props = sifive_clint_properties;
+}
+
+static const TypeInfo sifive_clint_info = {
+    .name          = TYPE_SIFIVE_CLINT,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFiveCLINTState),
+    .class_init    = sifive_clint_class_init,
+};
+
+static void sifive_clint_register_types(void)
+{
+    type_register_static(&sifive_clint_info);
+}
+
+type_init(sifive_clint_register_types)
+
+
+/*
+ * Create CLINT device.
+ */
+DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts,
+    uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base)
+{
+    int i;
+    for (i = 0; i < num_harts; i++) {
+        CPUState *cpu = qemu_get_cpu(i);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            continue;
+        }
+        env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                  &sifive_clint_timer_cb, cpu);
+        env->timecmp = 0;
+    }
+
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_CLINT);
+    qdev_prop_set_uint32(dev, "num-harts", num_harts);
+    qdev_prop_set_uint32(dev, "sip-base", sip_base);
+    qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
+    qdev_prop_set_uint32(dev, "time-base", time_base);
+    qdev_prop_set_uint32(dev, "aperture-size", size);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h
new file mode 100644
index 0000000..aaa2a58
--- /dev/null
+++ b/include/hw/riscv/sifive_clint.h
@@ -0,0 +1,50 @@
+/*
+ * SiFive CLINT (Core Local Interruptor) interface
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_CLINT_H
+#define HW_SIFIVE_CLINT_H
+
+#define TYPE_SIFIVE_CLINT "riscv.sifive.clint"
+
+#define SIFIVE_CLINT(obj) \
+    OBJECT_CHECK(SiFiveCLINTState, (obj), TYPE_SIFIVE_CLINT)
+
+typedef struct SiFiveCLINTState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+    uint32_t num_harts;
+    uint32_t sip_base;
+    uint32_t timecmp_base;
+    uint32_t time_base;
+    uint32_t aperture_size;
+} SiFiveCLINTState;
+
+DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts,
+    uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base);
+
+enum {
+    SIFIVE_SIP_BASE     = 0x0,
+    SIFIVE_TIMECMP_BASE = 0x4000,
+    SIFIVE_TIME_BASE    = 0xBFF8
+};
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 15/23] SiFive RISC-V PLIC Block
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (13 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 14/23] SiFive RISC-V CLINT Block Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines Michael Clark
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches, Stefan O'Rear

The PLIC (Platform Level Interrupt Controller) device provides a
parameterizable interrupt controller based on SiFive's PLIC specification.

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_plic.c         | 505 +++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_plic.h |  85 +++++++
 2 files changed, 590 insertions(+)
 create mode 100644 hw/riscv/sifive_plic.c
 create mode 100644 include/hw/riscv/sifive_plic.h

diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
new file mode 100644
index 0000000..874de2e
--- /dev/null
+++ b/hw/riscv/sifive_plic.c
@@ -0,0 +1,505 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a parameterizable interrupt controller based on SiFive's PLIC.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_plic.h"
+
+#define RISCV_DEBUG_PLIC 0
+
+static PLICMode char_to_mode(char c)
+{
+    switch (c) {
+    case 'U': return PLICMode_U;
+    case 'S': return PLICMode_S;
+    case 'H': return PLICMode_H;
+    case 'M': return PLICMode_M;
+    default:
+        error_report("plic: invalid mode '%c'", c);
+        exit(1);
+    }
+}
+
+static char mode_to_char(PLICMode m)
+{
+    switch (m) {
+    case PLICMode_U: return 'U';
+    case PLICMode_S: return 'S';
+    case PLICMode_H: return 'H';
+    case PLICMode_M: return 'M';
+    default: return '?';
+    }
+}
+
+static void sifive_plic_print_state(SiFivePLICState *plic)
+{
+    int i;
+    int addrid;
+
+    /* pending */
+    qemu_log("pending       : ");
+    for (i = plic->bitfield_words - 1; i >= 0; i--) {
+        qemu_log("%08x", plic->pending[i]);
+    }
+    qemu_log("\n");
+
+    /* pending */
+    qemu_log("claimed       : ");
+    for (i = plic->bitfield_words - 1; i >= 0; i--) {
+        qemu_log("%08x", plic->claimed[i]);
+    }
+    qemu_log("\n");
+
+    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+        qemu_log("hart%d-%c enable: ",
+            plic->addr_config[addrid].hartid,
+            mode_to_char(plic->addr_config[addrid].mode));
+        for (i = plic->bitfield_words - 1; i >= 0; i--) {
+            qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
+        }
+        qemu_log("\n");
+    }
+}
+
+static
+void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
+{
+    qemu_mutex_lock(&plic->lock);
+    uint32_t word = irq >> 5;
+    if (pending) {
+        plic->pending[word] |= (1 << (irq & 31));
+    } else {
+        plic->pending[word] &= ~(1 << (irq & 31));
+    }
+    qemu_mutex_unlock(&plic->lock);
+}
+
+static
+void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
+{
+    qemu_mutex_lock(&plic->lock);
+    uint32_t word = irq >> 5;
+    if (claimed) {
+        plic->claimed[word] |= (1 << (irq & 31));
+    } else {
+        plic->claimed[word] &= ~(1 << (irq & 31));
+    }
+    qemu_mutex_unlock(&plic->lock);
+}
+
+static
+int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
+{
+    int i, j, count = 0;
+    for (i = 0; i < plic->bitfield_words; i++) {
+        uint32_t pending_enabled_not_claimed =
+            (plic->pending[i] & ~plic->claimed[i]) &
+            plic->enable[addrid * plic->bitfield_words + i];
+        if (!pending_enabled_not_claimed) {
+            continue;
+        }
+        for (j = 0; j < 32; j++) {
+            int irq = (i << 5) + j;
+            uint32_t prio = plic->source_priority[irq];
+            int enabled = pending_enabled_not_claimed & (1 << j);
+            if (enabled && prio > plic->target_priority[addrid]) {
+                count++;
+            }
+        }
+    }
+    return count;
+}
+
+static void sifive_plic_update(SiFivePLICState *plic)
+{
+    int addrid;
+
+    /* raise irq on harts where this irq is enabled */
+    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+        uint32_t hartid = plic->addr_config[addrid].hartid;
+        PLICMode mode = plic->addr_config[addrid].mode;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            continue;
+        }
+        int level = sifive_plic_num_irqs_pending(plic, addrid) > 0;
+        switch (mode) {
+        case PLICMode_M:
+            riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
+            break;
+        case PLICMode_S:
+            riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (RISCV_DEBUG_PLIC) {
+        sifive_plic_print_state(plic);
+    }
+}
+
+void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq)
+{
+    sifive_plic_set_pending(plic, irq, true);
+    sifive_plic_update(plic);
+}
+
+void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq)
+{
+    sifive_plic_set_pending(plic, irq, false);
+    sifive_plic_update(plic);
+}
+
+static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
+{
+    int i, j;
+    for (i = 0; i < plic->bitfield_words; i++) {
+        uint32_t pending_enabled_not_claimed =
+            (plic->pending[i] & ~plic->claimed[i]) &
+            plic->enable[addrid * plic->bitfield_words + i];
+        if (!pending_enabled_not_claimed) {
+            continue;
+        }
+        for (j = 0; j < 32; j++) {
+            int irq = (i << 5) + j;
+            uint32_t prio = plic->source_priority[irq];
+            int enabled = pending_enabled_not_claimed & (1 << j);
+            if (enabled && prio > plic->target_priority[addrid]) {
+                sifive_plic_set_pending(plic, irq, false);
+                sifive_plic_set_claimed(plic, irq, true);
+                return irq;
+            }
+        }
+    }
+    return 0;
+}
+
+static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SiFivePLICState *plic = opaque;
+
+    /* writes must be 4 byte words */
+    if ((addr & 0x3) != 0) {
+        goto err;
+    }
+
+    if (addr >= plic->priority_base && /* 4 bytes per source */
+        addr < plic->priority_base + (plic->num_sources << 2))
+    {
+        uint32_t irq = (addr - plic->priority_base) >> 2;
+        if (RISCV_DEBUG_PLIC) {
+            qemu_log("plic: read priority: irq=%d priority=%d\n",
+                irq, plic->source_priority[irq]);
+        }
+        return plic->source_priority[irq];
+    } else if (addr >= plic->pending_base && /* 1 bit per source */
+               addr < plic->pending_base + (plic->num_sources >> 3))
+    {
+        uint32_t word = (addr - plic->priority_base) >> 2;
+        if (RISCV_DEBUG_PLIC) {
+            qemu_log("plic: read pending: word=%d value=%d\n",
+                word, plic->pending[word]);
+        }
+        return plic->pending[word];
+    } else if (addr >= plic->enable_base && /* 1 bit per source */
+             addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
+    {
+        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
+        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
+        if (wordid < plic->bitfield_words) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode), wordid,
+                    plic->enable[addrid * plic->bitfield_words + wordid]);
+            }
+            return plic->enable[addrid * plic->bitfield_words + wordid];
+        }
+    } else if (addr >= plic->context_base && /* 1 bit per source */
+             addr < plic->context_base + plic->num_addrs * plic->context_stride)
+    {
+        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
+        uint32_t contextid = (addr & (plic->context_stride - 1));
+        if (contextid == 0) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: read priority: hart%d-%c priority=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    plic->target_priority[addrid]);
+            }
+            return plic->target_priority[addrid];
+        } else if (contextid == 4) {
+            uint32_t value = sifive_plic_claim(plic, addrid);
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: read claim: hart%d-%c irq=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    value);
+                sifive_plic_print_state(plic);
+            }
+            return value;
+        }
+    }
+
+err:
+    error_report("plic: invalid register read: %08x", (uint32_t)addr);
+    return 0;
+}
+
+static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
+        unsigned size)
+{
+    SiFivePLICState *plic = opaque;
+
+    /* writes must be 4 byte words */
+    if ((addr & 0x3) != 0) {
+        goto err;
+    }
+
+    if (addr >= plic->priority_base && /* 4 bytes per source */
+        addr < plic->priority_base + (plic->num_sources << 2))
+    {
+        uint32_t irq = (addr - plic->priority_base) >> 2;
+        plic->source_priority[irq] = value & 7;
+        if (RISCV_DEBUG_PLIC) {
+            qemu_log("plic: write priority: irq=%d priority=%d\n",
+                irq, plic->source_priority[irq]);
+        }
+        return;
+    } else if (addr >= plic->pending_base && /* 1 bit per source */
+               addr < plic->pending_base + (plic->num_sources >> 3))
+    {
+        error_report("plic: invalid pending write: %08x", (uint32_t)addr);
+        return;
+    } else if (addr >= plic->enable_base && /* 1 bit per source */
+        addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
+    {
+        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
+        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
+        if (wordid < plic->bitfield_words) {
+            plic->enable[addrid * plic->bitfield_words + wordid] = value;
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode), wordid,
+                    plic->enable[addrid * plic->bitfield_words + wordid]);
+            }
+            return;
+        }
+    } else if (addr >= plic->context_base && /* 4 bytes per reg */
+        addr < plic->context_base + plic->num_addrs * plic->context_stride)
+    {
+        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
+        uint32_t contextid = (addr & (plic->context_stride - 1));
+        if (contextid == 0) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: write priority: hart%d-%c priority=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    plic->target_priority[addrid]);
+            }
+            if (value <= plic->num_priorities) {
+                plic->target_priority[addrid] = value;
+                sifive_plic_update(plic);
+            }
+            return;
+        } else if (contextid == 4) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: write claim: hart%d-%c irq=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    (uint32_t)value);
+            }
+            if (value < plic->num_sources) {
+                sifive_plic_set_claimed(plic, value, false);
+                sifive_plic_update(plic);
+            }
+            return;
+        }
+    }
+
+err:
+    error_report("plic: invalid register write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps sifive_plic_ops = {
+    .read = sifive_plic_read,
+    .write = sifive_plic_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static Property sifive_plic_properties[] = {
+    DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
+    DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
+    DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
+    DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
+    DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
+    DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
+    DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
+    DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
+    DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
+    DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+/*
+ * parse PLIC hart/mode address offset config
+ *
+ * "M"              1 hart with M mode
+ * "MS,MS"          2 harts, 0-1 with M and S mode
+ * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
+ */
+static void parse_hart_config(SiFivePLICState *plic)
+{
+    int addrid, hartid, modes;
+    const char *p;
+    char c;
+
+    /* count and validate hart/mode combinations */
+    addrid = 0, hartid = 0, modes = 0;
+    p = plic->hart_config;
+    while ((c = *p++)) {
+        if (c == ',') {
+            addrid += __builtin_popcount(modes);
+            modes = 0;
+            hartid++;
+        } else {
+            int m = 1 << char_to_mode(c);
+            if (modes == (modes | m)) {
+                error_report("plic: duplicate mode '%c' in config: %s",
+                             c, plic->hart_config);
+                exit(1);
+            }
+            modes |= m;
+        }
+    }
+    if (modes) {
+        addrid += __builtin_popcount(modes);
+    }
+    hartid++;
+
+    /* store hart/mode combinations */
+    plic->num_addrs = addrid;
+    plic->addr_config = g_new(PLICAddr, plic->num_addrs);
+    addrid = 0, hartid = 0;
+    p = plic->hart_config;
+    while ((c = *p++)) {
+        if (c == ',') {
+            hartid++;
+        } else {
+            plic->addr_config[addrid].addrid = addrid;
+            plic->addr_config[addrid].hartid = hartid;
+            plic->addr_config[addrid].mode = char_to_mode(c);
+            addrid++;
+        }
+    }
+}
+
+static void sifive_plic_irq_request(void *opaque, int irq, int level)
+{
+    SiFivePLICState *plic = opaque;
+    if (RISCV_DEBUG_PLIC) {
+        qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
+    }
+    sifive_plic_set_pending(plic, irq, level > 0);
+    sifive_plic_update(plic);
+}
+
+static void sifive_plic_realize(DeviceState *dev, Error **errp)
+{
+    SiFivePLICState *plic = SIFIVE_PLIC(dev);
+    int i;
+
+    memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
+                          TYPE_SIFIVE_PLIC, plic->aperture_size);
+    parse_hart_config(plic);
+    qemu_mutex_init(&plic->lock);
+    plic->bitfield_words = (plic->num_sources + 31) >> 5;
+    plic->source_priority = g_new0(uint32_t, plic->num_sources);
+    plic->target_priority = g_new(uint32_t, plic->num_addrs);
+    plic->pending = g_new0(uint32_t, plic->bitfield_words);
+    plic->claimed = g_new0(uint32_t, plic->bitfield_words);
+    plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
+    plic->irqs = g_new0(qemu_irq, plic->num_sources + 1);
+    for (i = 0; i <= plic->num_sources; i++) {
+        plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i);
+    }
+}
+
+static void sifive_plic_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = sifive_plic_properties;
+    dc->realize = sifive_plic_realize;
+}
+
+static const TypeInfo sifive_plic_info = {
+    .name          = TYPE_SIFIVE_PLIC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFivePLICState),
+    .class_init    = sifive_plic_class_init,
+};
+
+static void sifive_plic_register_types(void)
+{
+    type_register_static(&sifive_plic_info);
+}
+
+type_init(sifive_plic_register_types)
+
+/*
+ * Create PLIC device.
+ */
+DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+    uint32_t num_sources, uint32_t num_priorities,
+    uint32_t priority_base, uint32_t pending_base,
+    uint32_t enable_base, uint32_t enable_stride,
+    uint32_t context_base, uint32_t context_stride,
+    uint32_t aperture_size)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC);
+    assert(enable_stride == (enable_stride & -enable_stride));
+    assert(context_stride == (context_stride & -context_stride));
+    qdev_prop_set_string(dev, "hart-config", hart_config);
+    qdev_prop_set_uint32(dev, "num-sources", num_sources);
+    qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
+    qdev_prop_set_uint32(dev, "priority-base", priority_base);
+    qdev_prop_set_uint32(dev, "pending-base", pending_base);
+    qdev_prop_set_uint32(dev, "enable-base", enable_base);
+    qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
+    qdev_prop_set_uint32(dev, "context-base", context_base);
+    qdev_prop_set_uint32(dev, "context-stride", context_stride);
+    qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h
new file mode 100644
index 0000000..11a5a98
--- /dev/null
+++ b/include/hw/riscv/sifive_plic.h
@@ -0,0 +1,85 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a RISC-V PLIC device
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_PLIC_H
+#define HW_SIFIVE_PLIC_H
+
+#include "hw/irq.h"
+
+#define TYPE_SIFIVE_PLIC "riscv.sifive.plic"
+
+#define SIFIVE_PLIC(obj) \
+    OBJECT_CHECK(SiFivePLICState, (obj), TYPE_SIFIVE_PLIC)
+
+typedef enum PLICMode {
+    PLICMode_U,
+    PLICMode_S,
+    PLICMode_H,
+    PLICMode_M
+} PLICMode;
+
+typedef struct PLICAddr {
+    uint32_t addrid;
+    uint32_t hartid;
+    PLICMode mode;
+} PLICAddr;
+
+typedef struct SiFivePLICState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+    uint32_t num_addrs;
+    uint32_t bitfield_words;
+    PLICAddr *addr_config;
+    uint32_t *source_priority;
+    uint32_t *target_priority;
+    uint32_t *pending;
+    uint32_t *claimed;
+    uint32_t *enable;
+    QemuMutex lock;
+    qemu_irq *irqs;
+
+    /* config */
+    char *hart_config;
+    uint32_t num_sources;
+    uint32_t num_priorities;
+    uint32_t priority_base;
+    uint32_t pending_base;
+    uint32_t enable_base;
+    uint32_t enable_stride;
+    uint32_t context_base;
+    uint32_t context_stride;
+    uint32_t aperture_size;
+} SiFivePLICState;
+
+void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq);
+void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq);
+
+DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+    uint32_t num_sources, uint32_t num_priorities,
+    uint32_t priority_base, uint32_t pending_base,
+    uint32_t enable_base, uint32_t enable_stride,
+    uint32_t context_base, uint32_t context_stride,
+    uint32_t aperture_size);
+
+#endif
+
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (14 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 15/23] SiFive RISC-V PLIC Block Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09  4:50   ` Michael Clark
  2018-05-14 16:49   ` Peter Maydell
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher Michael Clark
                   ` (8 subsequent siblings)
  24 siblings, 2 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

RISC-V machines compatble with Spike aka riscv-isa-sim, the RISC-V
Instruction Set Simulator. The following machines are implemented:

- 'spike_v1.9.1'; HTIF console, config-string, Privileged ISA Version 1.9.1
- 'spike_v1.10'; HTIF console, device-tree, Privileged ISA Version 1.10

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/spike.c         | 376 +++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/spike.h |  53 +++++++
 2 files changed, 429 insertions(+)
 create mode 100644 hw/riscv/spike.c
 create mode 100644 include/hw/riscv/spike.h

diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
new file mode 100644
index 0000000..2d1f114
--- /dev/null
+++ b/hw/riscv/spike.c
@@ -0,0 +1,376 @@
+/*
+ * QEMU RISC-V Spike Board
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This provides a RISC-V Board with the following devices:
+ *
+ * 0) HTIF Console and Poweroff
+ * 1) CLINT (Timer and IPI)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/spike.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} spike_memmap[] = {
+    [SPIKE_MROM] =     {     0x1000,     0x2000 },
+    [SPIKE_CLINT] =    {  0x2000000,    0x10000 },
+    [SPIKE_DRAM] =     { 0x80000000,        0x0 },
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+    int i;
+    for (i = 0; i < (len >> 2); i++) {
+        stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+    }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+    return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+    uint64_t kernel_entry, kernel_high;
+
+    if (load_elf_ram_sym(kernel_filename, identity_translate, NULL,
+            &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0,
+            NULL, true, htif_symbol_callback) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    uint32_t *cells;
+    char *nodename;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/htif");
+    qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
+
+    qemu_fdt_add_subnode(fdt, "/soc");
+    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
+    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    nodename = g_strdup_printf("/memory@%lx",
+        (long)memmap[SPIKE_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base,
+        mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+    for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+        nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+        char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+        qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+        qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
+        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+        qemu_fdt_add_subnode(fdt, intc);
+        qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
+        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
+        qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+        g_free(isa);
+        g_free(intc);
+        g_free(nodename);
+    }
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/clint@%lx",
+        (long)memmap[SPIKE_CLINT].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[SPIKE_CLINT].base,
+        0x0, memmap[SPIKE_CLINT].size);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    g_free(cells);
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ }
+
+static void spike_v1_10_0_board_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = spike_memmap;
+
+    SpikeState *s = g_new0(SpikeState, 1);
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+    /* Initialize SOC */
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+                              &error_abort);
+    object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
+        main_mem);
+
+    /* create device tree */
+    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+
+    /* boot rom */
+    memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
+                           s->fdt_size + 0x2000, &error_fatal);
+    memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+    if (machine->kernel_filename) {
+        load_kernel(machine->kernel_filename);
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x00000297,                  /* 1:  auipc  t0, %pcrel_hi(dtb) */
+        0x02028593,                  /*     addi   a1, t0, %pcrel_lo(1b) */
+        0xf1402573,                  /*     csrr   a0, mhartid  */
+#if defined(TARGET_RISCV32)
+        0x0182a283,                  /*     lw     t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+        0x0182b283,                  /*     ld     t0, 24(t0) */
+#endif
+        0x00028067,                  /*     jr     t0 */
+        0x00000000,
+        memmap[SPIKE_DRAM].base,     /* start: .dword DRAM_BASE */
+        0x00000000,
+                                     /* dtb: */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
+
+    /* copy in the device tree */
+    qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
+    cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
+        s->fdt, s->fdt_size);
+
+    /* initialize HTIF using symbols found in load_kernel */
+    htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]);
+
+    /* Core Local Interruptor (timer and IPI) */
+    sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
+        smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static void spike_v1_09_1_board_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = spike_memmap;
+
+    SpikeState *s = g_new0(SpikeState, 1);
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+    /* Initialize SOC */
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+                              &error_abort);
+    object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
+        main_mem);
+
+    /* boot rom */
+    memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
+                           0x40000, &error_fatal);
+    memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+    if (machine->kernel_filename) {
+        load_kernel(machine->kernel_filename);
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui */
+        0x00028067,                   /* jump to DRAM_BASE */
+        0x00000000,                   /* reserved */
+        memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string pointer */
+        0, 0, 0, 0                    /* trap vector */
+    };
+
+    /* part one of config string - before memory size specified */
+    const char *config_string_tmpl =
+        "platform {\n"
+        "  vendor ucb;\n"
+        "  arch spike;\n"
+        "};\n"
+        "rtc {\n"
+        "  addr 0x%" PRIx64 "x;\n"
+        "};\n"
+        "ram {\n"
+        "  0 {\n"
+        "    addr 0x%" PRIx64 "x;\n"
+        "    size 0x%" PRIx64 "x;\n"
+        "  };\n"
+        "};\n"
+        "core {\n"
+        "  0" " {\n"
+        "    " "0 {\n"
+        "      isa %s;\n"
+        "      timecmp 0x%" PRIx64 "x;\n"
+        "      ipi 0x%" PRIx64 "x;\n"
+        "    };\n"
+        "  };\n"
+        "};\n";
+
+    /* build config string with supplied memory size */
+    char *isa = riscv_isa_string(&s->soc.harts[0]);
+    size_t config_string_size = strlen(config_string_tmpl) + 48;
+    char *config_string = malloc(config_string_size);
+    snprintf(config_string, config_string_size, config_string_tmpl,
+        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE,
+        (uint64_t)memmap[SPIKE_DRAM].base,
+        (uint64_t)ram_size, isa,
+        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE,
+        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE);
+    g_free(isa);
+    size_t config_string_len = strlen(config_string);
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
+
+    /* copy in the config string */
+    cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
+        config_string, config_string_len);
+
+    /* initialize HTIF using symbols found in load_kernel */
+    htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]);
+
+    /* Core Local Interruptor (timer and IPI) */
+    sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
+        smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static const TypeInfo spike_v_1_09_1_device = {
+    .name          = TYPE_RISCV_SPIKE_V1_09_1_BOARD,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SpikeState),
+};
+
+static const TypeInfo spike_v_1_10_0_device = {
+    .name          = TYPE_RISCV_SPIKE_V1_10_0_BOARD,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SpikeState),
+};
+
+static void spike_v1_09_1_machine_init(MachineClass *mc)
+{
+    mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)";
+    mc->init = spike_v1_09_1_board_init;
+    mc->max_cpus = 1;
+}
+
+static void spike_v1_10_0_machine_init(MachineClass *mc)
+{
+    mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)";
+    mc->init = spike_v1_10_0_board_init;
+    mc->max_cpus = 1;
+    mc->is_default = 1;
+}
+
+DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init)
+DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init)
+
+static void riscv_spike_board_register_types(void)
+{
+    type_register_static(&spike_v_1_09_1_device);
+    type_register_static(&spike_v_1_10_0_device);
+}
+
+type_init(riscv_spike_board_register_types);
diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h
new file mode 100644
index 0000000..53d5eea
--- /dev/null
+++ b/include/hw/riscv/spike.h
@@ -0,0 +1,53 @@
+/*
+ * Spike machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SPIKE_H
+#define HW_SPIKE_H
+
+#define TYPE_RISCV_SPIKE_V1_09_1_BOARD "riscv.spike_v1_9"
+#define TYPE_RISCV_SPIKE_V1_10_0_BOARD "riscv.spike_v1_10"
+
+#define SPIKE(obj) \
+    OBJECT_CHECK(SpikeState, (obj), TYPE_RISCV_SPIKE_BOARD)
+
+typedef struct {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    void *fdt;
+    int fdt_size;
+} SpikeState;
+
+
+enum {
+    SPIKE_MROM,
+    SPIKE_CLINT,
+    SPIKE_DRAM
+};
+
+#if defined(TARGET_RISCV32)
+#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV32GCSU_V1_09_1
+#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0
+#elif defined(TARGET_RISCV64)
+#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV64GCSU_V1_09_1
+#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0
+#endif
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (15 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 11:57   ` Philippe Mathieu-Daudé
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine Michael Clark
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Test finisher memory mapped device used to exit simulation.

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_test.c         | 93 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_test.h | 42 +++++++++++++++++++
 2 files changed, 135 insertions(+)
 create mode 100644 hw/riscv/sifive_test.c
 create mode 100644 include/hw/riscv/sifive_test.h

diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c
new file mode 100644
index 0000000..8abd2cd
--- /dev/null
+++ b/hw/riscv/sifive_test.c
@@ -0,0 +1,93 @@
+/*
+ * QEMU SiFive Test Finisher
+ *
+ * Copyright (c) 2018 SiFive, Inc.
+ *
+ * Test finisher memory mapped device used to exit simulation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_test.h"
+
+static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    return 0;
+}
+
+static void sifive_test_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    if (addr == 0) {
+        int status = val64 & 0xffff;
+        int code = (val64 >> 16) & 0xffff;
+        switch (status) {
+        case FINISHER_FAIL:
+            exit(code);
+        case FINISHER_PASS:
+            exit(0);
+        default:
+            break;
+        }
+    }
+    hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
+        __func__, (int)addr, val64);
+}
+
+static const MemoryRegionOps sifive_test_ops = {
+    .read = sifive_test_read,
+    .write = sifive_test_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void sifive_test_init(Object *obj)
+{
+    SiFiveTestState *s = SIFIVE_TEST(obj);
+
+    memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s,
+                          TYPE_SIFIVE_TEST, 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const TypeInfo sifive_test_info = {
+    .name          = TYPE_SIFIVE_TEST,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFiveTestState),
+    .instance_init = sifive_test_init,
+};
+
+static void sifive_test_register_types(void)
+{
+    type_register_static(&sifive_test_info);
+}
+
+type_init(sifive_test_register_types)
+
+
+/*
+ * Create Test device.
+ */
+DeviceState *sifive_test_create(hwaddr addr)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_test.h
new file mode 100644
index 0000000..71d4c9f
--- /dev/null
+++ b/include/hw/riscv/sifive_test.h
@@ -0,0 +1,42 @@
+/*
+ * QEMU Test Finisher interface
+ *
+ * Copyright (c) 2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_TEST_H
+#define HW_SIFIVE_TEST_H
+
+#define TYPE_SIFIVE_TEST "riscv.sifive.test"
+
+#define SIFIVE_TEST(obj) \
+    OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST)
+
+typedef struct SiFiveTestState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+} SiFiveTestState;
+
+enum {
+    FINISHER_FAIL = 0x3333,
+    FINISHER_PASS = 0x5555
+};
+
+DeviceState *sifive_test_create(hwaddr addr);
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (16 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-04-27 14:17   ` Peter Maydell
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device Michael Clark
                   ` (6 subsequent siblings)
  24 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

RISC-V machine with device-tree, 16550a UART and VirtIO MMIO.
The following machine is implemented:

- 'virt'; CLINT, PLIC, 16550A UART, VirtIO MMIO, device-tree

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/virt.c         | 420 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/virt.h |  74 +++++++++
 2 files changed, 494 insertions(+)
 create mode 100644 hw/riscv/virt.c
 create mode 100644 include/hw/riscv/virt.h

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
new file mode 100644
index 0000000..e2c214e
--- /dev/null
+++ b/hw/riscv/virt.c
@@ -0,0 +1,420 @@
+/*
+ * QEMU RISC-V VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * RISC-V machine with 16550a UART and VirtIO MMIO
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_test.h"
+#include "hw/riscv/virt.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} virt_memmap[] = {
+    [VIRT_DEBUG] =    {        0x0,      0x100 },
+    [VIRT_MROM] =     {     0x1000,     0x2000 },
+    [VIRT_TEST] =     {     0x4000,     0x1000 },
+    [VIRT_CLINT] =    {  0x2000000,    0x10000 },
+    [VIRT_PLIC] =     {  0xc000000,  0x4000000 },
+    [VIRT_UART0] =    { 0x10000000,      0x100 },
+    [VIRT_VIRTIO] =   { 0x10001000,     0x1000 },
+    [VIRT_DRAM] =     { 0x80000000,        0x0 },
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+    int i;
+    for (i = 0; i < (len >> 2); i++) {
+        stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+    }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+    return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+    uint64_t kernel_entry, kernel_high;
+
+    if (load_elf(kernel_filename, identity_translate, NULL,
+                 &kernel_entry, NULL, &kernel_high,
+                 0, ELF_MACHINE, 1, 0) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static hwaddr load_initrd(const char *filename, uint64_t mem_size,
+                          uint64_t kernel_entry, hwaddr *start)
+{
+    int size;
+
+    /* We want to put the initrd far enough into RAM that when the
+     * kernel is uncompressed it will not clobber the initrd. However
+     * on boards without much RAM we must ensure that we still leave
+     * enough room for a decent sized initrd, and on boards with large
+     * amounts of RAM we must avoid the initrd being so far up in RAM
+     * that it is outside lowmem and inaccessible to the kernel.
+     * So for boards with less  than 256MB of RAM we put the initrd
+     * halfway into RAM, and for boards with 256MB of RAM or more we put
+     * the initrd at 128MB.
+     */
+    *start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024);
+
+    size = load_ramdisk(filename, *start, mem_size - *start);
+    if (size == -1) {
+        size = load_image_targphys(filename, *start, mem_size - *start);
+        if (size == -1) {
+            error_report("qemu: could not load ramdisk '%s'", filename);
+            exit(1);
+        }
+    }
+    return *start + size;
+}
+
+static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    uint32_t *cells;
+    char *nodename;
+    uint32_t plic_phandle, phandle = 1;
+    int i;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/soc");
+    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc");
+    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    nodename = g_strdup_printf("/memory@%lx",
+        (long)memmap[VIRT_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        memmap[VIRT_DRAM].base >> 32, memmap[VIRT_DRAM].base,
+        mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+    for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+        int cpu_phandle = phandle++;
+        nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+        char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+        qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+        qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
+        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+        qemu_fdt_add_subnode(fdt, intc);
+        qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle);
+        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle);
+        qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+        g_free(isa);
+        g_free(intc);
+        g_free(nodename);
+    }
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/clint@%lx",
+        (long)memmap[VIRT_CLINT].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_CLINT].base,
+        0x0, memmap[VIRT_CLINT].size);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    g_free(cells);
+    g_free(nodename);
+
+    plic_phandle = phandle++;
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
+        (long)memmap[VIRT_PLIC].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
+    qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_PLIC].base,
+        0x0, memmap[VIRT_PLIC].size);
+    qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
+    qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
+    qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", VIRTIO_NDEV);
+    qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle);
+    qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
+    plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
+    g_free(cells);
+    g_free(nodename);
+
+    for (i = 0; i < VIRTIO_COUNT; i++) {
+        nodename = g_strdup_printf("/virtio_mmio@%lx",
+            (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size));
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio");
+        qemu_fdt_setprop_cells(fdt, nodename, "reg",
+            0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
+            0x0, memmap[VIRT_VIRTIO].size);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i);
+        g_free(nodename);
+    }
+
+    nodename = g_strdup_printf("/test@%lx",
+        (long)memmap[VIRT_TEST].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_TEST].base,
+        0x0, memmap[VIRT_TEST].size);
+
+    nodename = g_strdup_printf("/uart@%lx",
+        (long)memmap[VIRT_UART0].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_UART0].base,
+        0x0, memmap[VIRT_UART0].size);
+    qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    g_free(nodename);
+
+    return fdt;
+}
+
+static void riscv_virt_board_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = virt_memmap;
+
+    RISCVVirtState *s = g_new0(RISCVVirtState, 1);
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+    char *plic_hart_config;
+    size_t plic_hart_config_len;
+    int i;
+    void *fdt;
+
+    /* Initialize SOC */
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+                              &error_abort);
+    object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base,
+        main_mem);
+
+    /* create device tree */
+    fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+
+    /* boot rom */
+    memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom",
+                           s->fdt_size + 0x2000, &error_fatal);
+    memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+    if (machine->kernel_filename) {
+        uint64_t kernel_entry = load_kernel(machine->kernel_filename);
+
+        if (machine->initrd_filename) {
+            hwaddr start;
+            hwaddr end = load_initrd(machine->initrd_filename,
+                                     machine->ram_size, kernel_entry,
+                                     &start);
+            qemu_fdt_setprop_cell(fdt, "/chosen",
+                                  "linux,initrd-start", start);
+            qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+                                  end);
+        }
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x00000297,                  /* 1:  auipc  t0, %pcrel_hi(dtb) */
+        0x02028593,                  /*     addi   a1, t0, %pcrel_lo(1b) */
+        0xf1402573,                  /*     csrr   a0, mhartid  */
+#if defined(TARGET_RISCV32)
+        0x0182a283,                  /*     lw     t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+        0x0182b283,                  /*     ld     t0, 24(t0) */
+#endif
+        0x00028067,                  /*     jr     t0 */
+        0x00000000,
+        memmap[VIRT_DRAM].base,      /* start: .dword memmap[VIRT_DRAM].base */
+        0x00000000,
+                                     /* dtb: */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec));
+
+    /* copy in the device tree */
+    qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
+    cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec),
+        s->fdt, s->fdt_size);
+
+    /* create PLIC hart topology configuration string */
+    plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus;
+    plic_hart_config = g_malloc0(plic_hart_config_len);
+    for (i = 0; i < smp_cpus; i++) {
+        if (i != 0) {
+            strncat(plic_hart_config, ",", plic_hart_config_len);
+        }
+        strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len);
+        plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1);
+    }
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[VIRT_PLIC].base,
+        plic_hart_config,
+        VIRT_PLIC_NUM_SOURCES,
+        VIRT_PLIC_NUM_PRIORITIES,
+        VIRT_PLIC_PRIORITY_BASE,
+        VIRT_PLIC_PENDING_BASE,
+        VIRT_PLIC_ENABLE_BASE,
+        VIRT_PLIC_ENABLE_STRIDE,
+        VIRT_PLIC_CONTEXT_BASE,
+        VIRT_PLIC_CONTEXT_STRIDE,
+        memmap[VIRT_PLIC].size);
+    sifive_clint_create(memmap[VIRT_CLINT].base,
+        memmap[VIRT_CLINT].size, smp_cpus,
+        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+    sifive_test_create(memmap[VIRT_TEST].base);
+
+    for (i = 0; i < VIRTIO_COUNT; i++) {
+        sysbus_create_simple("virtio-mmio",
+            memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
+            SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
+    }
+
+    serial_mm_init(system_memory, memmap[VIRT_UART0].base,
+        0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
+        serial_hds[0], DEVICE_LITTLE_ENDIAN);
+}
+
+static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+    return 0;
+}
+
+static void riscv_virt_board_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    k->init = riscv_virt_board_sysbus_device_init;
+}
+
+static const TypeInfo riscv_virt_board_device = {
+    .name          = TYPE_RISCV_VIRT_BOARD,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RISCVVirtState),
+    .class_init    = riscv_virt_board_class_init,
+};
+
+static void riscv_virt_board_machine_init(MachineClass *mc)
+{
+    mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)";
+    mc->init = riscv_virt_board_init;
+    mc->max_cpus = 8; /* hardcoded limit in BBL */
+}
+
+DEFINE_MACHINE("virt", riscv_virt_board_machine_init)
+
+static void riscv_virt_board_register_types(void)
+{
+    type_register_static(&riscv_virt_board_device);
+}
+
+type_init(riscv_virt_board_register_types);
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
new file mode 100644
index 0000000..7525647
--- /dev/null
+++ b/include/hw/riscv/virt.h
@@ -0,0 +1,74 @@
+/*
+ * SiFive VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_VIRT_H
+#define HW_VIRT_H
+
+#define TYPE_RISCV_VIRT_BOARD "riscv.virt"
+#define VIRT(obj) \
+    OBJECT_CHECK(RISCVVirtState, (obj), TYPE_RISCV_VIRT_BOARD)
+
+enum { ROM_BASE = 0x1000 };
+
+typedef struct {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    DeviceState *plic;
+    void *fdt;
+    int fdt_size;
+} RISCVVirtState;
+
+enum {
+    VIRT_DEBUG,
+    VIRT_MROM,
+    VIRT_TEST,
+    VIRT_CLINT,
+    VIRT_PLIC,
+    VIRT_UART0,
+    VIRT_VIRTIO,
+    VIRT_DRAM
+};
+
+
+enum {
+    UART0_IRQ = 10,
+    VIRTIO_IRQ = 1, /* 1 to 8 */
+    VIRTIO_COUNT = 8,
+    VIRTIO_NDEV = 10
+};
+
+#define VIRT_PLIC_HART_CONFIG "MS"
+#define VIRT_PLIC_NUM_SOURCES 127
+#define VIRT_PLIC_NUM_PRIORITIES 7
+#define VIRT_PLIC_PRIORITY_BASE 0x0
+#define VIRT_PLIC_PENDING_BASE 0x1000
+#define VIRT_PLIC_ENABLE_BASE 0x2000
+#define VIRT_PLIC_ENABLE_STRIDE 0x80
+#define VIRT_PLIC_CONTEXT_BASE 0x200000
+#define VIRT_PLIC_CONTEXT_STRIDE 0x1000
+
+#if defined(TARGET_RISCV32)
+#define VIRT_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0
+#elif defined(TARGET_RISCV64)
+#define VIRT_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0
+#endif
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (17 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-09 12:39   ` Philippe Mathieu-Daudé
  2018-04-10  3:21   ` Antony Pavlov
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 20/23] SiFive RISC-V PRCI Block Michael Clark
                   ` (5 subsequent siblings)
  24 siblings, 2 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches, Stefan O'Rear

QEMU model of the UART on the SiFive E300 and U500 series SOCs.
BBL supports the SiFive UART for early console access via the SBI
(Supervisor Binary Interface) and the linux kernel SBI console.

The SiFive UART implements the pre qom legacy interface consistent
with the 16550a UART in 'hw/char/serial.c'.

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_uart.c         | 176 +++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
 2 files changed, 247 insertions(+)
 create mode 100644 hw/riscv/sifive_uart.c
 create mode 100644 include/hw/riscv/sifive_uart.h

diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000..b0c3798
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,176 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_uart.h"
+
+/*
+ * Not yet implemented:
+ *
+ * Transmit FIFO using "qemu/fifo8.h"
+ * SIFIVE_UART_IE_TXWM interrupts
+ * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
+ * Rx FIFO watermark interrupt trigger threshold
+ * Tx FIFO watermark interrupt trigger threshold.
+ */
+
+static void update_irq(SiFiveUARTState *s)
+{
+    int cond = 0;
+    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
+        cond = 1;
+    }
+    if (cond) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    SiFiveUARTState *s = opaque;
+    unsigned char r;
+    switch (addr) {
+    case SIFIVE_UART_RXFIFO:
+        if (s->rx_fifo_len) {
+            r = s->rx_fifo[0];
+            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
+            s->rx_fifo_len--;
+            qemu_chr_fe_accept_input(&s->chr);
+            update_irq(s);
+            return r;
+        }
+        return 0x80000000;
+
+    case SIFIVE_UART_TXFIFO:
+        return 0; /* Should check tx fifo */
+    case SIFIVE_UART_IE:
+        return s->ie;
+    case SIFIVE_UART_IP:
+        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+    case SIFIVE_UART_TXCTRL:
+        return s->txctrl;
+    case SIFIVE_UART_RXCTRL:
+        return s->rxctrl;
+    case SIFIVE_UART_DIV:
+        return s->div;
+    }
+
+    hw_error("%s: bad read: addr=0x%x\n",
+        __func__, (int)addr);
+    return 0;
+}
+
+static void
+uart_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    SiFiveUARTState *s = opaque;
+    uint32_t value = val64;
+    unsigned char ch = value;
+
+    switch (addr) {
+    case SIFIVE_UART_TXFIFO:
+        qemu_chr_fe_write(&s->chr, &ch, 1);
+        return;
+    case SIFIVE_UART_IE:
+        s->ie = val64;
+        update_irq(s);
+        return;
+    case SIFIVE_UART_TXCTRL:
+        s->txctrl = val64;
+        return;
+    case SIFIVE_UART_RXCTRL:
+        s->rxctrl = val64;
+        return;
+    case SIFIVE_UART_DIV:
+        s->div = val64;
+        return;
+    }
+    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
+        __func__, (int)addr, (int)value);
+}
+
+static const MemoryRegionOps uart_ops = {
+    .read = uart_read,
+    .write = uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+    SiFiveUARTState *s = opaque;
+
+    /* Got a byte.  */
+    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+        printf("WARNING: UART dropped char.\n");
+        return;
+    }
+    s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+    update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+    SiFiveUARTState *s = opaque;
+
+    return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+    SiFiveUARTState *s = opaque;
+
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+        uart_be_change, s, NULL, true);
+
+    return 0;
+}
+
+/*
+ * Create UART device.
+ */
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+    Chardev *chr, qemu_irq irq)
+{
+    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
+    s->irq = irq;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+        uart_be_change, s, NULL, true);
+    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+    memory_region_add_subregion(address_space, base, &s->mmio);
+    return s;
+}
diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
new file mode 100644
index 0000000..504f18a
--- /dev/null
+++ b/include/hw/riscv/sifive_uart.h
@@ -0,0 +1,71 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_UART_H
+#define HW_SIFIVE_UART_H
+
+enum {
+    SIFIVE_UART_TXFIFO        = 0,
+    SIFIVE_UART_RXFIFO        = 4,
+    SIFIVE_UART_TXCTRL        = 8,
+    SIFIVE_UART_TXMARK        = 10,
+    SIFIVE_UART_RXCTRL        = 12,
+    SIFIVE_UART_RXMARK        = 14,
+    SIFIVE_UART_IE            = 16,
+    SIFIVE_UART_IP            = 20,
+    SIFIVE_UART_DIV           = 24,
+    SIFIVE_UART_MAX           = 32
+};
+
+enum {
+    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt enable */
+    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt enable */
+};
+
+enum {
+    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt pending */
+    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt pending */
+};
+
+#define TYPE_SIFIVE_UART "riscv.sifive.uart"
+
+#define SIFIVE_UART(obj) \
+    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
+
+typedef struct SiFiveUARTState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    qemu_irq irq;
+    MemoryRegion mmio;
+    CharBackend chr;
+    uint8_t rx_fifo[8];
+    unsigned int rx_fifo_len;
+    uint32_t ie;
+    uint32_t ip;
+    uint32_t txctrl;
+    uint32_t rxctrl;
+    uint32_t div;
+} SiFiveUARTState;
+
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+    Chardev *chr, qemu_irq irq);
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 20/23] SiFive RISC-V PRCI Block
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (18 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 21/23] SiFive Freedom E Series RISC-V Machine Michael Clark
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

Simple model of the PRCI  (Power, Reset, Clock, Interrupt) to emulate
register reads made by the SDK BSP.

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_prci.c         | 89 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_prci.h | 37 ++++++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 hw/riscv/sifive_prci.c
 create mode 100644 include/hw/riscv/sifive_prci.h

diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
new file mode 100644
index 0000000..0910ea3
--- /dev/null
+++ b/hw/riscv/sifive_prci.c
@@ -0,0 +1,89 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Simple model of the PRCI to emulate register reads made by the SDK BSP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_prci.h"
+
+/* currently implements enough to mock freedom-e-sdk BSP clock programming */
+
+static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    if (addr == 0 /* PRCI_HFROSCCFG */) {
+        return 1 << 31; /* ROSC_RDY */
+    }
+    if (addr == 8 /* PRCI_PLLCFG    */) {
+        return 1 << 31; /* PLL_LOCK */
+    }
+    hw_error("%s: read: addr=0x%x\n", __func__, (int)addr);
+    return 0;
+}
+
+static void sifive_prci_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    /* discard writes */
+}
+
+static const MemoryRegionOps sifive_prci_ops = {
+    .read = sifive_prci_read,
+    .write = sifive_prci_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void sifive_prci_init(Object *obj)
+{
+    SiFivePRCIState *s = SIFIVE_PRCI(obj);
+
+    memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s,
+                          TYPE_SIFIVE_PRCI, 0x8000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const TypeInfo sifive_prci_info = {
+    .name          = TYPE_SIFIVE_PRCI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFivePRCIState),
+    .instance_init = sifive_prci_init,
+};
+
+static void sifive_prci_register_types(void)
+{
+    type_register_static(&sifive_prci_info);
+}
+
+type_init(sifive_prci_register_types)
+
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_prci_create(hwaddr addr)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h
new file mode 100644
index 0000000..b6f4c48
--- /dev/null
+++ b/include/hw/riscv/sifive_prci.h
@@ -0,0 +1,37 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_PRCI_H
+#define HW_SIFIVE_PRCI_H
+
+#define TYPE_SIFIVE_PRCI "riscv.sifive.prci"
+
+#define SIFIVE_PRCI(obj) \
+    OBJECT_CHECK(SiFivePRCIState, (obj), TYPE_SIFIVE_PRCI)
+
+typedef struct SiFivePRCIState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+} SiFivePRCIState;
+
+DeviceState *sifive_prci_create(hwaddr addr);
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 21/23] SiFive Freedom E Series RISC-V Machine
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (19 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 20/23] SiFive RISC-V PRCI Block Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 22/23] SiFive Freedom U " Michael Clark
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

This provides a RISC-V Board compatible with the the SiFive Freedom E SDK.
The following machine is implemented:

- 'sifive_e'; CLINT, PLIC, UART, AON, GPIO, QSPI, PWM

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_e.c         | 234 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_e.h |  79 +++++++++++++++
 2 files changed, 313 insertions(+)
 create mode 100644 hw/riscv/sifive_e.c
 create mode 100644 include/hw/riscv/sifive_e.h

diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
new file mode 100644
index 0000000..19eca36
--- /dev/null
+++ b/hw/riscv/sifive_e.c
@@ -0,0 +1,234 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive Freedom E SDK
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the SiFive Freedom E SDK:
+ *
+ * 0) UART
+ * 1) CLINT (Core Level Interruptor)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ * 3) PRCI (Power, Reset, Clock, Interrupt)
+ * 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM
+ * 5) Flash memory emulated as RAM
+ *
+ * The Mask ROM reset vector jumps to the flash payload at 0x2040_0000.
+ * The OTP ROM and Flash boot code will be emulated in a future version.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_prci.h"
+#include "hw/riscv/sifive_uart.h"
+#include "hw/riscv/sifive_e.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} sifive_e_memmap[] = {
+    [SIFIVE_E_DEBUG] =    {        0x0,      0x100 },
+    [SIFIVE_E_MROM] =     {     0x1000,     0x2000 },
+    [SIFIVE_E_OTP] =      {    0x20000,     0x2000 },
+    [SIFIVE_E_CLINT] =    {  0x2000000,    0x10000 },
+    [SIFIVE_E_PLIC] =     {  0xc000000,  0x4000000 },
+    [SIFIVE_E_AON] =      { 0x10000000,     0x8000 },
+    [SIFIVE_E_PRCI] =     { 0x10008000,     0x8000 },
+    [SIFIVE_E_OTP_CTRL] = { 0x10010000,     0x1000 },
+    [SIFIVE_E_GPIO0] =    { 0x10012000,     0x1000 },
+    [SIFIVE_E_UART0] =    { 0x10013000,     0x1000 },
+    [SIFIVE_E_QSPI0] =    { 0x10014000,     0x1000 },
+    [SIFIVE_E_PWM0] =     { 0x10015000,     0x1000 },
+    [SIFIVE_E_UART1] =    { 0x10023000,     0x1000 },
+    [SIFIVE_E_QSPI1] =    { 0x10024000,     0x1000 },
+    [SIFIVE_E_PWM1] =     { 0x10025000,     0x1000 },
+    [SIFIVE_E_QSPI2] =    { 0x10034000,     0x1000 },
+    [SIFIVE_E_PWM2] =     { 0x10035000,     0x1000 },
+    [SIFIVE_E_XIP] =      { 0x20000000, 0x20000000 },
+    [SIFIVE_E_DTIM] =     { 0x80000000,     0x4000 }
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+    int i;
+    for (i = 0; i < (len >> 2); i++) {
+        stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+    }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+    return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+    uint64_t kernel_entry, kernel_high;
+
+    if (load_elf(kernel_filename, identity_translate, NULL,
+                 &kernel_entry, NULL, &kernel_high,
+                 0, ELF_MACHINE, 1, 0) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
+                             uintptr_t offset, uintptr_t length)
+{
+    MemoryRegion *mock_mmio = g_new(MemoryRegion, 1);
+    memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal);
+    memory_region_add_subregion(parent, offset, mock_mmio);
+}
+
+static void riscv_sifive_e_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = sifive_e_memmap;
+
+    SiFiveEState *s = g_new0(SiFiveEState, 1);
+    MemoryRegion *sys_mem = get_system_memory();
+    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+    MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
+
+    /* Initialize SOC */
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+                              &error_abort);
+    object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* Data Tightly Integrated Memory */
+    memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram",
+        memmap[SIFIVE_E_DTIM].size, &error_fatal);
+    memory_region_add_subregion(sys_mem,
+        memmap[SIFIVE_E_DTIM].base, main_mem);
+
+    /* Mask ROM */
+    memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom",
+        memmap[SIFIVE_E_MROM].size, &error_fatal);
+    memory_region_add_subregion(sys_mem,
+        memmap[SIFIVE_E_MROM].base, mask_rom);
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base,
+        (char *)SIFIVE_E_PLIC_HART_CONFIG,
+        SIFIVE_E_PLIC_NUM_SOURCES,
+        SIFIVE_E_PLIC_NUM_PRIORITIES,
+        SIFIVE_E_PLIC_PRIORITY_BASE,
+        SIFIVE_E_PLIC_PENDING_BASE,
+        SIFIVE_E_PLIC_ENABLE_BASE,
+        SIFIVE_E_PLIC_ENABLE_STRIDE,
+        SIFIVE_E_PLIC_CONTEXT_BASE,
+        SIFIVE_E_PLIC_CONTEXT_STRIDE,
+        memmap[SIFIVE_E_PLIC].size);
+    sifive_clint_create(memmap[SIFIVE_E_CLINT].base,
+        memmap[SIFIVE_E_CLINT].size, smp_cpus,
+        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon",
+        memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size);
+    sifive_prci_create(memmap[SIFIVE_E_PRCI].base);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0",
+        memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size);
+    sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base,
+        serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0",
+        memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0",
+        memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size);
+    /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base,
+        serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1",
+        memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1",
+        memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2",
+        memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2",
+        memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size);
+
+    /* Flash memory */
+    memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip",
+        memmap[SIFIVE_E_XIP].size, &error_fatal);
+    memory_region_set_readonly(xip_mem, true);
+    memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem);
+
+    /* Mask ROM reset vector */
+    uint32_t reset_vec[2] = {
+        0x204002b7,        /* 0x1000: lui     t0,0x20400 */
+        0x00028067,        /* 0x1004: jr      t0 */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec));
+    memory_region_set_readonly(mask_rom, true);
+
+    if (machine->kernel_filename) {
+        load_kernel(machine->kernel_filename);
+    }
+}
+
+static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+    return 0;
+}
+
+static void riscv_sifive_e_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    k->init = riscv_sifive_e_sysbus_device_init;
+}
+
+static const TypeInfo riscv_sifive_e_device = {
+    .name          = TYPE_SIFIVE_E,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFiveEState),
+    .class_init    = riscv_sifive_e_class_init,
+};
+
+static void riscv_sifive_e_machine_init(MachineClass *mc)
+{
+    mc->desc = "RISC-V Board compatible with SiFive E SDK";
+    mc->init = riscv_sifive_e_init;
+    mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
+
+static void riscv_sifive_e_register_types(void)
+{
+    type_register_static(&riscv_sifive_e_device);
+}
+
+type_init(riscv_sifive_e_register_types);
diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h
new file mode 100644
index 0000000..0aebc57
--- /dev/null
+++ b/include/hw/riscv/sifive_e.h
@@ -0,0 +1,79 @@
+/*
+ * SiFive E series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_E_H
+#define HW_SIFIVE_E_H
+
+#define TYPE_SIFIVE_E "riscv.sifive_e"
+
+#define SIFIVE_E(obj) \
+    OBJECT_CHECK(SiFiveEState, (obj), TYPE_SIFIVE_E)
+
+typedef struct SiFiveEState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    DeviceState *plic;
+} SiFiveEState;
+
+enum {
+    SIFIVE_E_DEBUG,
+    SIFIVE_E_MROM,
+    SIFIVE_E_OTP,
+    SIFIVE_E_CLINT,
+    SIFIVE_E_PLIC,
+    SIFIVE_E_AON,
+    SIFIVE_E_PRCI,
+    SIFIVE_E_OTP_CTRL,
+    SIFIVE_E_GPIO0,
+    SIFIVE_E_UART0,
+    SIFIVE_E_QSPI0,
+    SIFIVE_E_PWM0,
+    SIFIVE_E_UART1,
+    SIFIVE_E_QSPI1,
+    SIFIVE_E_PWM1,
+    SIFIVE_E_QSPI2,
+    SIFIVE_E_PWM2,
+    SIFIVE_E_XIP,
+    SIFIVE_E_DTIM
+};
+
+enum {
+    SIFIVE_E_UART0_IRQ = 3,
+    SIFIVE_E_UART1_IRQ = 4
+};
+
+#define SIFIVE_E_PLIC_HART_CONFIG "M"
+#define SIFIVE_E_PLIC_NUM_SOURCES 127
+#define SIFIVE_E_PLIC_NUM_PRIORITIES 7
+#define SIFIVE_E_PLIC_PRIORITY_BASE 0x0
+#define SIFIVE_E_PLIC_PENDING_BASE 0x1000
+#define SIFIVE_E_PLIC_ENABLE_BASE 0x2000
+#define SIFIVE_E_PLIC_ENABLE_STRIDE 0x80
+#define SIFIVE_E_PLIC_CONTEXT_BASE 0x200000
+#define SIFIVE_E_PLIC_CONTEXT_STRIDE 0x1000
+
+#if defined(TARGET_RISCV32)
+#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E31
+#elif defined(TARGET_RISCV64)
+#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E51
+#endif
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 22/23] SiFive Freedom U Series RISC-V Machine
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (20 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 21/23] SiFive Freedom E Series RISC-V Machine Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure Michael Clark
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

This provides a RISC-V Board compatible with the the SiFive Freedom U SDK.
The following machine is implemented:

- 'sifive_u'; CLINT, PLIC, UART, device-tree

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 hw/riscv/sifive_u.c         | 339 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/sifive_u.h |  69 +++++++++
 2 files changed, 408 insertions(+)
 create mode 100644 hw/riscv/sifive_u.c
 create mode 100644 include/hw/riscv/sifive_u.h

diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
new file mode 100644
index 0000000..1c2deef
--- /dev/null
+++ b/hw/riscv/sifive_u.c
@@ -0,0 +1,339 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive Freedom U SDK
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the SiFive Freedom U SDK:
+ *
+ * 0) UART
+ * 1) CLINT (Core Level Interruptor)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ *
+ * This board currently uses a hardcoded devicetree that indicates one hart.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_uart.h"
+#include "hw/riscv/sifive_prci.h"
+#include "hw/riscv/sifive_u.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} sifive_u_memmap[] = {
+    [SIFIVE_U_DEBUG] =    {        0x0,      0x100 },
+    [SIFIVE_U_MROM] =     {     0x1000,     0x2000 },
+    [SIFIVE_U_CLINT] =    {  0x2000000,    0x10000 },
+    [SIFIVE_U_PLIC] =     {  0xc000000,  0x4000000 },
+    [SIFIVE_U_UART0] =    { 0x10013000,     0x1000 },
+    [SIFIVE_U_UART1] =    { 0x10023000,     0x1000 },
+    [SIFIVE_U_DRAM] =     { 0x80000000,        0x0 },
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+    int i;
+    for (i = 0; i < (len >> 2); i++) {
+        stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+    }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+    return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+    uint64_t kernel_entry, kernel_high;
+
+    if (load_elf(kernel_filename, identity_translate, NULL,
+                 &kernel_entry, NULL, &kernel_high,
+                 0, ELF_MACHINE, 1, 0) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    uint32_t *cells;
+    char *nodename;
+    uint32_t plic_phandle;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/soc");
+    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
+    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    nodename = g_strdup_printf("/memory@%lx",
+        (long)memmap[SIFIVE_U_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_DRAM].base,
+        mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+    for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+        nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+        char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+        qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+        qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
+        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+        qemu_fdt_add_subnode(fdt, intc);
+        qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
+        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
+        qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+        g_free(isa);
+        g_free(intc);
+        g_free(nodename);
+    }
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/clint@%lx",
+        (long)memmap[SIFIVE_U_CLINT].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[SIFIVE_U_CLINT].base,
+        0x0, memmap[SIFIVE_U_CLINT].size);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    g_free(cells);
+    g_free(nodename);
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
+        (long)memmap[SIFIVE_U_PLIC].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
+    qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[SIFIVE_U_PLIC].base,
+        0x0, memmap[SIFIVE_U_PLIC].size);
+    qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
+    qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
+    qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4);
+    qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2);
+    qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2);
+    plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
+    g_free(cells);
+    g_free(nodename);
+
+    nodename = g_strdup_printf("/uart@%lx",
+        (long)memmap[SIFIVE_U_UART0].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[SIFIVE_U_UART0].base,
+        0x0, memmap[SIFIVE_U_UART0].size);
+    qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+    qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    g_free(nodename);
+}
+
+static void riscv_sifive_u_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = sifive_u_memmap;
+
+    SiFiveUState *s = g_new0(SiFiveUState, 1);
+    MemoryRegion *sys_memory = get_system_memory();
+    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+    /* Initialize SOC */
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+                              &error_abort);
+    object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register RAM */
+    memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base,
+        main_mem);
+
+    /* create device tree */
+    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+
+    /* boot rom */
+    memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom",
+                           memmap[SIFIVE_U_MROM].base, &error_fatal);
+    memory_region_set_readonly(boot_rom, true);
+    memory_region_add_subregion(sys_memory, 0x0, boot_rom);
+
+    if (machine->kernel_filename) {
+        load_kernel(machine->kernel_filename);
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x00000297,                    /* 1:  auipc  t0, %pcrel_hi(dtb) */
+        0x02028593,                    /*     addi   a1, t0, %pcrel_lo(1b) */
+        0xf1402573,                    /*     csrr   a0, mhartid  */
+#if defined(TARGET_RISCV32)
+        0x0182a283,                    /*     lw     t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+        0x0182b283,                    /*     ld     t0, 24(t0) */
+#endif
+        0x00028067,                    /*     jr     t0 */
+        0x00000000,
+        memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */
+        0x00000000,
+                                       /* dtb: */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec));
+
+    /* copy in the device tree */
+    qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
+    cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base +
+        sizeof(reset_vec), s->fdt, s->fdt_size);
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
+        (char *)SIFIVE_U_PLIC_HART_CONFIG,
+        SIFIVE_U_PLIC_NUM_SOURCES,
+        SIFIVE_U_PLIC_NUM_PRIORITIES,
+        SIFIVE_U_PLIC_PRIORITY_BASE,
+        SIFIVE_U_PLIC_PENDING_BASE,
+        SIFIVE_U_PLIC_ENABLE_BASE,
+        SIFIVE_U_PLIC_ENABLE_STRIDE,
+        SIFIVE_U_PLIC_CONTEXT_BASE,
+        SIFIVE_U_PLIC_CONTEXT_STRIDE,
+        memmap[SIFIVE_U_PLIC].size);
+    sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base,
+        serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]);
+    /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base,
+        serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */
+    sifive_clint_create(memmap[SIFIVE_U_CLINT].base,
+        memmap[SIFIVE_U_CLINT].size, smp_cpus,
+        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+    return 0;
+}
+
+static void riscv_sifive_u_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    k->init = riscv_sifive_u_sysbus_device_init;
+}
+
+static const TypeInfo riscv_sifive_u_device = {
+    .name          = TYPE_SIFIVE_U,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFiveUState),
+    .class_init    = riscv_sifive_u_class_init,
+};
+
+static void riscv_sifive_u_register_types(void)
+{
+    type_register_static(&riscv_sifive_u_device);
+}
+
+type_init(riscv_sifive_u_register_types);
+
+static void riscv_sifive_u_machine_init(MachineClass *mc)
+{
+    mc->desc = "RISC-V Board compatible with SiFive U SDK";
+    mc->init = riscv_sifive_u_init;
+    mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
new file mode 100644
index 0000000..662e8a1
--- /dev/null
+++ b/include/hw/riscv/sifive_u.h
@@ -0,0 +1,69 @@
+/*
+ * SiFive U series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_SIFIVE_U_H
+#define HW_SIFIVE_U_H
+
+#define TYPE_SIFIVE_U "riscv.sifive_u"
+
+#define SIFIVE_U(obj) \
+    OBJECT_CHECK(SiFiveUState, (obj), TYPE_SIFIVE_U)
+
+typedef struct SiFiveUState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    DeviceState *plic;
+    void *fdt;
+    int fdt_size;
+} SiFiveUState;
+
+enum {
+    SIFIVE_U_DEBUG,
+    SIFIVE_U_MROM,
+    SIFIVE_U_CLINT,
+    SIFIVE_U_PLIC,
+    SIFIVE_U_UART0,
+    SIFIVE_U_UART1,
+    SIFIVE_U_DRAM
+};
+
+enum {
+    SIFIVE_U_UART0_IRQ = 3,
+    SIFIVE_U_UART1_IRQ = 4
+};
+
+#define SIFIVE_U_PLIC_HART_CONFIG "MS"
+#define SIFIVE_U_PLIC_NUM_SOURCES 127
+#define SIFIVE_U_PLIC_NUM_PRIORITIES 7
+#define SIFIVE_U_PLIC_PRIORITY_BASE 0x0
+#define SIFIVE_U_PLIC_PENDING_BASE 0x1000
+#define SIFIVE_U_PLIC_ENABLE_BASE 0x2000
+#define SIFIVE_U_PLIC_ENABLE_STRIDE 0x80
+#define SIFIVE_U_PLIC_CONTEXT_BASE 0x200000
+#define SIFIVE_U_PLIC_CONTEXT_STRIDE 0x1000
+
+#if defined(TARGET_RISCV32)
+#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U34
+#elif defined(TARGET_RISCV64)
+#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U54
+#endif
+
+#endif
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (21 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 22/23] SiFive Freedom U " Michael Clark
@ 2018-03-02 13:51 ` Michael Clark
  2018-03-02 14:33   ` Eric Blake
  2018-03-09 13:03   ` Philippe Mathieu-Daudé
  2018-03-02 14:17 ` [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission no-reply
  2018-03-05  8:41 ` Richard W.M. Jones
  24 siblings, 2 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-02 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

This adds RISC-V into the build system enabling the following targets:

- riscv32-softmmu
- riscv64-softmmu
- riscv32-linux-user
- riscv64-linux-user

This adds defaults configs for RISC-V, enables the build for the RISC-V
CPU core, hardware, and Linux User Emulation. The 'qemu-binfmt-conf.sh'
script is updated to add the RISC-V ELF magic.

Expected checkpatch errors for consistency reasons:

ERROR: line over 90 characters
FILE: scripts/qemu-binfmt-conf.sh

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 arch_init.c                            |  2 ++
 configure                              | 13 +++++++++++++
 cpus.c                                 |  6 ++++++
 default-configs/riscv32-linux-user.mak |  1 +
 default-configs/riscv32-softmmu.mak    |  4 ++++
 default-configs/riscv64-linux-user.mak |  1 +
 default-configs/riscv64-softmmu.mak    |  4 ++++
 hw/riscv/Makefile.objs                 | 11 +++++++++++
 include/sysemu/arch_init.h             |  1 +
 qapi-schema.json                       | 17 ++++++++++++++++-
 scripts/qemu-binfmt-conf.sh            | 13 ++++++++++++-
 target/riscv/Makefile.objs             |  1 +
 12 files changed, 72 insertions(+), 2 deletions(-)
 create mode 100644 default-configs/riscv32-linux-user.mak
 create mode 100644 default-configs/riscv32-softmmu.mak
 create mode 100644 default-configs/riscv64-linux-user.mak
 create mode 100644 default-configs/riscv64-softmmu.mak
 create mode 100644 hw/riscv/Makefile.objs
 create mode 100644 target/riscv/Makefile.objs

diff --git a/arch_init.c b/arch_init.c
index 4c36f2b..e157619 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -71,6 +71,8 @@ int graphic_depth = 32;
 #define QEMU_ARCH QEMU_ARCH_OPENRISC
 #elif defined(TARGET_PPC)
 #define QEMU_ARCH QEMU_ARCH_PPC
+#elif defined(TARGET_RISCV)
+#define QEMU_ARCH QEMU_ARCH_RISCV
 #elif defined(TARGET_S390X)
 #define QEMU_ARCH QEMU_ARCH_S390X
 #elif defined(TARGET_SH4)
diff --git a/configure b/configure
index 00c4b63..a3240a8 100755
--- a/configure
+++ b/configure
@@ -6801,6 +6801,16 @@ case "$target_name" in
     echo "TARGET_ABI32=y" >> $config_target_mak
     gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
   ;;
+  riscv32)
+    TARGET_BASE_ARCH=riscv
+    TARGET_ABI_DIR=riscv
+    mttcg=yes
+  ;;
+  riscv64)
+    TARGET_BASE_ARCH=riscv
+    TARGET_ABI_DIR=riscv
+    mttcg=yes
+  ;;
   sh4|sh4eb)
     TARGET_ARCH=sh4
     bflt="yes"
@@ -6970,6 +6980,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
   ppc*)
     disas_config "PPC"
   ;;
+  riscv)
+    disas_config "RISCV"
+  ;;
   s390*)
     disas_config "S390"
   ;;
diff --git a/cpus.c b/cpus.c
index af67826..407c3f8 100644
--- a/cpus.c
+++ b/cpus.c
@@ -2094,6 +2094,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
 #elif defined(TARGET_SPARC)
         SPARCCPU *sparc_cpu = SPARC_CPU(cpu);
         CPUSPARCState *env = &sparc_cpu->env;
+#elif defined(TARGET_RISCV)
+        RISCVCPU *riscv_cpu = RISCV_CPU(cpu);
+        CPURISCVState *env = &riscv_cpu->env;
 #elif defined(TARGET_MIPS)
         MIPSCPU *mips_cpu = MIPS_CPU(cpu);
         CPUMIPSState *env = &mips_cpu->env;
@@ -2133,6 +2136,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
 #elif defined(TARGET_S390X)
         info->value->arch = CPU_INFO_ARCH_S390;
         info->value->u.s390.cpu_state = env->cpu_state;
+#elif defined(TARGET_RISCV)
+        info->value->arch = CPU_INFO_ARCH_RISCV;
+        info->value->u.riscv.pc = env->pc;
 #else
         info->value->arch = CPU_INFO_ARCH_OTHER;
 #endif
diff --git a/default-configs/riscv32-linux-user.mak b/default-configs/riscv32-linux-user.mak
new file mode 100644
index 0000000..865b362
--- /dev/null
+++ b/default-configs/riscv32-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for riscv-linux-user
diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak
new file mode 100644
index 0000000..f9e7421
--- /dev/null
+++ b/default-configs/riscv32-softmmu.mak
@@ -0,0 +1,4 @@
+# Default configuration for riscv-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_VIRTIO=y
diff --git a/default-configs/riscv64-linux-user.mak b/default-configs/riscv64-linux-user.mak
new file mode 100644
index 0000000..865b362
--- /dev/null
+++ b/default-configs/riscv64-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for riscv-linux-user
diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak
new file mode 100644
index 0000000..f9e7421
--- /dev/null
+++ b/default-configs/riscv64-softmmu.mak
@@ -0,0 +1,4 @@
+# Default configuration for riscv-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_VIRTIO=y
diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
new file mode 100644
index 0000000..1dde01d
--- /dev/null
+++ b/hw/riscv/Makefile.objs
@@ -0,0 +1,11 @@
+obj-y += riscv_htif.o
+obj-y += riscv_hart.o
+obj-y += sifive_e.o
+obj-y += sifive_clint.o
+obj-y += sifive_prci.o
+obj-y += sifive_plic.o
+obj-y += sifive_test.o
+obj-y += sifive_u.o
+obj-y += sifive_uart.o
+obj-y += spike.o
+obj-y += virt.o
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index d40d882..ef52ae0 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -24,6 +24,7 @@ enum {
     QEMU_ARCH_TRICORE = (1 << 16),
     QEMU_ARCH_NIOS2 = (1 << 17),
     QEMU_ARCH_HPPA = (1 << 18),
+    QEMU_ARCH_RISCV = (1 << 19),
 };
 
 extern const uint32_t arch_type;
diff --git a/qapi-schema.json b/qapi-schema.json
index cd98a94..0ff2ed3 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -412,10 +412,12 @@
 #
 # @s390: since 2.12
 #
+# @riscv: since 2.12
+#
 # Since: 2.6
 ##
 { 'enum': 'CpuInfoArch',
-  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] }
+  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'riscv', 'other' ] }
 
 ##
 # @CpuInfo:
@@ -455,6 +457,7 @@
             'mips': 'CpuInfoMIPS',
             'tricore': 'CpuInfoTricore',
             's390': 'CpuInfoS390',
+            'riscv': 'CpuInfoRISCV',
             'other': 'CpuInfoOther' } }
 
 ##
@@ -515,6 +518,17 @@
 { 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }
 
 ##
+# @CpuInfoRISCV:
+#
+# Additional information about a virtual RISCV CPU
+#
+# @pc: the instruction pointer
+#
+# Since 2.12
+##
+{ 'struct': 'CpuInfoRISCV', 'data': { 'pc': 'int' } }
+
+##
 # @CpuInfoOther:
 #
 # No additional information is available about the virtual CPU
@@ -625,6 +639,7 @@
             'mips': 'CpuInfoOther',
             'tricore': 'CpuInfoOther',
             's390': 'CpuInfoS390',
+            'riscv': 'CpuInfoRISCV',
             'other': 'CpuInfoOther' } }
 
 ##
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index ea5a748..bdb21bd 100755
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -4,7 +4,7 @@
 
 qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \
 mips mipsel mipsn32 mipsn32el mips64 mips64el \
-sh4 sh4eb s390x aarch64 aarch64_be hppa"
+sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64"
 
 i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00'
 i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
@@ -100,6 +100,14 @@ hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
 hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
 hppa_family=hppa
 
+riscv32_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
+riscv32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
+riscv32_family=riscv
+
+riscv64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
+riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
+riscv64_family=riscv
+
 qemu_get_family() {
     cpu=${HOST_ARCH:-$(uname -m)}
     case "$cpu" in
@@ -124,6 +132,9 @@ qemu_get_family() {
     sparc*)
         echo "sparc"
         ;;
+    riscv*)
+        echo "riscv"
+        ;;
     *)
         echo "$cpu"
         ;;
diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs
new file mode 100644
index 0000000..abd0a7c
--- /dev/null
+++ b/target/riscv/Makefile.objs
@@ -0,0 +1 @@
+obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (22 preceding siblings ...)
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure Michael Clark
@ 2018-03-02 14:17 ` no-reply
  2018-03-05  8:41 ` Richard W.M. Jones
  24 siblings, 0 replies; 70+ messages in thread
From: no-reply @ 2018-03-02 14:17 UTC (permalink / raw)
  To: mjc; +Cc: famz, qemu-devel, kbastian, palmer, sagark, patches

Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 1519998711-73430-1-git-send-email-mjc@sifive.com
Subject: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission

=== TEST SCRIPT BEGIN ===
#!/bin/bash

BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0

git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram

commits="$(git log --format=%H --reverse $BASE..)"
for c in $commits; do
    echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
    if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
        failed=1
        echo
    fi
    n=$((n+1))
done

exit $failed
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
   427cbc7e41..2e7b766594  master     -> master
 * [new tag]               patchew/1519998711-73430-1-git-send-email-mjc@sifive.com -> patchew/1519998711-73430-1-git-send-email-mjc@sifive.com
 t [tag update]            patchew/20171228180814.9749-1-lukeshu@lukeshu.com -> patchew/20171228180814.9749-1-lukeshu@lukeshu.com
Switched to a new branch 'test'
21ffb8bdc0 RISC-V Build Infrastructure
ca5d7c8a1c SiFive Freedom U Series RISC-V Machine
da56267fc5 SiFive Freedom E Series RISC-V Machine
b78021cbc8 SiFive RISC-V PRCI Block
29220fc177 SiFive RISC-V UART Device
1b4ad0c360 RISC-V VirtIO Machine
7cbaa5fca0 SiFive RISC-V Test Finisher
1bd4951f4f RISC-V Spike Machines
21b1a969f4 SiFive RISC-V PLIC Block
20e07d9c45 SiFive RISC-V CLINT Block
6c6815bfc3 RISC-V HART Array
811ef36cf0 RISC-V HTIF Console
b802b6c3ce Add symbol table callback interface to load_elf
5c62396737 RISC-V Linux User Emulation
1989f74d4a RISC-V Physical Memory Protection
0ed6709cf3 RISC-V TCG Code Generation
e72021bda7 RISC-V GDB Stub
edc96cf22a RISC-V FPU Support
ea3e025b4c RISC-V CPU Helpers
3a84b79cb8 RISC-V Disassembler
c64a4f09ac RISC-V CPU Core Definition
1c8b9826bd RISC-V ELF Machine Definition
279429e751 RISC-V Maintainers

=== OUTPUT BEGIN ===
Checking PATCH 1/23: RISC-V Maintainers...
Checking PATCH 2/23: RISC-V ELF Machine Definition...
Checking PATCH 3/23: RISC-V CPU Core Definition...
Checking PATCH 4/23: RISC-V Disassembler...
WARNING: line over 80 characters
#649: FILE: disas/riscv.c:597:
+static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end };

ERROR: line over 90 characters
#650: FILE: disas/riscv.c:598:
+static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end };

WARNING: line over 80 characters
#680: FILE: disas/riscv.c:628:
+static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end };

WARNING: line over 80 characters
#681: FILE: disas/riscv.c:629:
+static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end };

WARNING: line over 80 characters
#682: FILE: disas/riscv.c:630:
+static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, rvc_end };

WARNING: line over 80 characters
#683: FILE: disas/riscv.c:631:
+static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, rvc_end };

ERROR: line over 90 characters
#684: FILE: disas/riscv.c:632:
+static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end };

ERROR: line over 90 characters
#685: FILE: disas/riscv.c:633:
+static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };

WARNING: line over 80 characters
#686: FILE: disas/riscv.c:634:
+static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end };

ERROR: line over 90 characters
#687: FILE: disas/riscv.c:635:
+static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };

WARNING: line over 80 characters
#688: FILE: disas/riscv.c:636:
+static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end };

WARNING: line over 80 characters
#689: FILE: disas/riscv.c:637:
+static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end };

WARNING: line over 80 characters
#690: FILE: disas/riscv.c:638:
+static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end };

ERROR: line over 90 characters
#1089: FILE: disas/riscv.c:1037:
+    { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },

WARNING: line over 80 characters
#1090: FILE: disas/riscv.c:1038:
+    { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 },

WARNING: line over 80 characters
#1091: FILE: disas/riscv.c:1039:
+    { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },

WARNING: line over 80 characters
#1093: FILE: disas/riscv.c:1041:
+    { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, 0 },

WARNING: line over 80 characters
#1094: FILE: disas/riscv.c:1042:
+    { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },

WARNING: line over 80 characters
#1096: FILE: disas/riscv.c:1044:
+    { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi },

ERROR: line over 90 characters
#1097: FILE: disas/riscv.c:1045:
+    { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },

ERROR: line over 90 characters
#1099: FILE: disas/riscv.c:1047:
+    { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },

ERROR: line over 90 characters
#1100: FILE: disas/riscv.c:1048:
+    { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },

WARNING: line over 80 characters
#1101: FILE: disas/riscv.c:1049:
+    { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui },

ERROR: line over 90 characters
#1102: FILE: disas/riscv.c:1050:
+    { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli },

ERROR: line over 90 characters
#1103: FILE: disas/riscv.c:1051:
+    { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai },

ERROR: line over 90 characters
#1104: FILE: disas/riscv.c:1052:
+    { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi },

WARNING: line over 80 characters
#1105: FILE: disas/riscv.c:1053:
+    { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub },

WARNING: line over 80 characters
#1106: FILE: disas/riscv.c:1054:
+    { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor },

WARNING: line over 80 characters
#1107: FILE: disas/riscv.c:1055:
+    { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or },

WARNING: line over 80 characters
#1108: FILE: disas/riscv.c:1056:
+    { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, rv_op_and },

ERROR: line over 90 characters
#1109: FILE: disas/riscv.c:1057:
+    { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, rv_op_subw },

ERROR: line over 90 characters
#1110: FILE: disas/riscv.c:1058:
+    { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, rv_op_addw },

WARNING: line over 80 characters
#1111: FILE: disas/riscv.c:1059:
+    { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal },

ERROR: line over 90 characters
#1112: FILE: disas/riscv.c:1060:
+    { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq },

ERROR: line over 90 characters
#1113: FILE: disas/riscv.c:1061:
+    { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne },

ERROR: line over 90 characters
#1114: FILE: disas/riscv.c:1062:
+    { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli },

ERROR: line over 90 characters
#1115: FILE: disas/riscv.c:1063:
+    { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld },

ERROR: line over 90 characters
#1116: FILE: disas/riscv.c:1064:
+    { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },

WARNING: line over 80 characters
#1117: FILE: disas/riscv.c:1065:
+    { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },

ERROR: line over 90 characters
#1118: FILE: disas/riscv.c:1066:
+    { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr },

ERROR: line over 90 characters
#1119: FILE: disas/riscv.c:1067:
+    { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, rv_op_addi },

ERROR: line over 90 characters
#1120: FILE: disas/riscv.c:1068:
+    { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, rv_op_ebreak, rv_op_ebreak },

ERROR: line over 90 characters
#1121: FILE: disas/riscv.c:1069:
+    { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr },

WARNING: line over 80 characters
#1122: FILE: disas/riscv.c:1070:
+    { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, rv_op_add },

ERROR: line over 90 characters
#1123: FILE: disas/riscv.c:1071:
+    { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, rv_op_fsd },

ERROR: line over 90 characters
#1124: FILE: disas/riscv.c:1072:
+    { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },

WARNING: line over 80 characters
#1125: FILE: disas/riscv.c:1073:
+    { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 },

WARNING: line over 80 characters
#1126: FILE: disas/riscv.c:1074:
+    { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld },

WARNING: line over 80 characters
#1127: FILE: disas/riscv.c:1075:
+    { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd },

WARNING: line over 80 characters
#1128: FILE: disas/riscv.c:1076:
+    { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, rv_op_addiw },

WARNING: line over 80 characters
#1129: FILE: disas/riscv.c:1077:
+    { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld },

WARNING: line over 80 characters
#1130: FILE: disas/riscv.c:1078:
+    { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd },

WARNING: line over 80 characters
#1134: FILE: disas/riscv.c:1082:
+    { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq },

ERROR: trailing statements should be on next line
#1399: FILE: disas/riscv.c:1347:
+        case 0: op = rv_op_c_addi4spn; break;

ERROR: trailing statements should be on next line
#1407: FILE: disas/riscv.c:1355:
+        case 2: op = rv_op_c_lw; break;

ERROR: trailing statements should be on next line
#1422: FILE: disas/riscv.c:1370:
+        case 6: op = rv_op_c_sw; break;

ERROR: trailing statements should be on next line
#1436: FILE: disas/riscv.c:1384:
+            case 0: op = rv_op_c_nop; break;

ERROR: trailing statements should be on next line
#1437: FILE: disas/riscv.c:1385:
+            default: op = rv_op_c_addi; break;

ERROR: trailing statements should be on next line
#1447: FILE: disas/riscv.c:1395:
+        case 2: op = rv_op_c_li; break;

ERROR: trailing statements should be on next line
#1450: FILE: disas/riscv.c:1398:
+            case 2: op = rv_op_c_addi16sp; break;

ERROR: trailing statements should be on next line
#1451: FILE: disas/riscv.c:1399:
+            default: op = rv_op_c_lui; break;

ERROR: trailing statements should be on next line
#1462: FILE: disas/riscv.c:1410:
+            case 2: op = rv_op_c_andi; break;

ERROR: trailing statements should be on next line
#1465: FILE: disas/riscv.c:1413:
+                case 0: op = rv_op_c_sub; break;

ERROR: trailing statements should be on next line
#1466: FILE: disas/riscv.c:1414:
+                case 1: op = rv_op_c_xor; break;

ERROR: trailing statements should be on next line
#1467: FILE: disas/riscv.c:1415:
+                case 2: op = rv_op_c_or; break;

ERROR: trailing statements should be on next line
#1468: FILE: disas/riscv.c:1416:
+                case 3: op = rv_op_c_and; break;

ERROR: trailing statements should be on next line
#1469: FILE: disas/riscv.c:1417:
+                case 4: op = rv_op_c_subw; break;

ERROR: trailing statements should be on next line
#1470: FILE: disas/riscv.c:1418:
+                case 5: op = rv_op_c_addw; break;

ERROR: trailing statements should be on next line
#1475: FILE: disas/riscv.c:1423:
+        case 5: op = rv_op_c_j; break;

ERROR: trailing statements should be on next line
#1476: FILE: disas/riscv.c:1424:
+        case 6: op = rv_op_c_beqz; break;

ERROR: trailing statements should be on next line
#1477: FILE: disas/riscv.c:1425:
+        case 7: op = rv_op_c_bnez; break;

ERROR: trailing statements should be on next line
#1492: FILE: disas/riscv.c:1440:
+        case 2: op = rv_op_c_lwsp; break;

ERROR: trailing statements should be on next line
#1504: FILE: disas/riscv.c:1452:
+                case 0: op = rv_op_c_jr; break;

ERROR: trailing statements should be on next line
#1505: FILE: disas/riscv.c:1453:
+                default: op = rv_op_c_mv; break;

ERROR: trailing statements should be on next line
#1512: FILE: disas/riscv.c:1460:
+                    case 0: op = rv_op_c_ebreak; break;

ERROR: trailing statements should be on next line
#1513: FILE: disas/riscv.c:1461:
+                    default: op = rv_op_c_jalr; break;

ERROR: trailing statements should be on next line
#1516: FILE: disas/riscv.c:1464:
+                default: op = rv_op_c_add; break;

ERROR: trailing statements should be on next line
#1527: FILE: disas/riscv.c:1475:
+        case 6: op = rv_op_c_swsp; break;

ERROR: trailing statements should be on next line
#1541: FILE: disas/riscv.c:1489:
+            case 0: op = rv_op_lb; break;

ERROR: trailing statements should be on next line
#1542: FILE: disas/riscv.c:1490:
+            case 1: op = rv_op_lh; break;

ERROR: trailing statements should be on next line
#1543: FILE: disas/riscv.c:1491:
+            case 2: op = rv_op_lw; break;

ERROR: trailing statements should be on next line
#1544: FILE: disas/riscv.c:1492:
+            case 3: op = rv_op_ld; break;

ERROR: trailing statements should be on next line
#1545: FILE: disas/riscv.c:1493:
+            case 4: op = rv_op_lbu; break;

ERROR: trailing statements should be on next line
#1546: FILE: disas/riscv.c:1494:
+            case 5: op = rv_op_lhu; break;

ERROR: trailing statements should be on next line
#1547: FILE: disas/riscv.c:1495:
+            case 6: op = rv_op_lwu; break;

ERROR: trailing statements should be on next line
#1548: FILE: disas/riscv.c:1496:
+            case 7: op = rv_op_ldu; break;

ERROR: trailing statements should be on next line
#1553: FILE: disas/riscv.c:1501:
+            case 2: op = rv_op_flw; break;

ERROR: trailing statements should be on next line
#1554: FILE: disas/riscv.c:1502:
+            case 3: op = rv_op_fld; break;

ERROR: trailing statements should be on next line
#1555: FILE: disas/riscv.c:1503:
+            case 4: op = rv_op_flq; break;

ERROR: trailing statements should be on next line
#1560: FILE: disas/riscv.c:1508:
+            case 0: op = rv_op_fence; break;

ERROR: trailing statements should be on next line
#1561: FILE: disas/riscv.c:1509:
+            case 1: op = rv_op_fence_i; break;

ERROR: trailing statements should be on next line
#1562: FILE: disas/riscv.c:1510:
+            case 2: op = rv_op_lq; break;

ERROR: trailing statements should be on next line
#1567: FILE: disas/riscv.c:1515:
+            case 0: op = rv_op_addi; break;

ERROR: trailing statements should be on next line
#1570: FILE: disas/riscv.c:1518:
+                case 0: op = rv_op_slli; break;

ERROR: trailing statements should be on next line
#1573: FILE: disas/riscv.c:1521:
+            case 2: op = rv_op_slti; break;

ERROR: trailing statements should be on next line
#1574: FILE: disas/riscv.c:1522:
+            case 3: op = rv_op_sltiu; break;

ERROR: trailing statements should be on next line
#1575: FILE: disas/riscv.c:1523:
+            case 4: op = rv_op_xori; break;

ERROR: trailing statements should be on next line
#1578: FILE: disas/riscv.c:1526:
+                case 0: op = rv_op_srli; break;

ERROR: trailing statements should be on next line
#1579: FILE: disas/riscv.c:1527:
+                case 8: op = rv_op_srai; break;

ERROR: trailing statements should be on next line
#1582: FILE: disas/riscv.c:1530:
+            case 6: op = rv_op_ori; break;

ERROR: trailing statements should be on next line
#1583: FILE: disas/riscv.c:1531:
+            case 7: op = rv_op_andi; break;

ERROR: trailing statements should be on next line
#1586: FILE: disas/riscv.c:1534:
+        case 5: op = rv_op_auipc; break;

ERROR: trailing statements should be on next line
#1589: FILE: disas/riscv.c:1537:
+            case 0: op = rv_op_addiw; break;

ERROR: trailing statements should be on next line
#1592: FILE: disas/riscv.c:1540:
+                case 0: op = rv_op_slliw; break;

ERROR: trailing statements should be on next line
#1597: FILE: disas/riscv.c:1545:
+                case 0: op = rv_op_srliw; break;

ERROR: trailing statements should be on next line
#1598: FILE: disas/riscv.c:1546:
+                case 32: op = rv_op_sraiw; break;

ERROR: trailing statements should be on next line
#1605: FILE: disas/riscv.c:1553:
+            case 0: op = rv_op_sb; break;

ERROR: trailing statements should be on next line
#1606: FILE: disas/riscv.c:1554:
+            case 1: op = rv_op_sh; break;

ERROR: trailing statements should be on next line
#1607: FILE: disas/riscv.c:1555:
+            case 2: op = rv_op_sw; break;

ERROR: trailing statements should be on next line
#1608: FILE: disas/riscv.c:1556:
+            case 3: op = rv_op_sd; break;

ERROR: trailing statements should be on next line
#1609: FILE: disas/riscv.c:1557:
+            case 4: op = rv_op_sq; break;

ERROR: trailing statements should be on next line
#1614: FILE: disas/riscv.c:1562:
+            case 2: op = rv_op_fsw; break;

ERROR: trailing statements should be on next line
#1615: FILE: disas/riscv.c:1563:
+            case 3: op = rv_op_fsd; break;

ERROR: trailing statements should be on next line
#1616: FILE: disas/riscv.c:1564:
+            case 4: op = rv_op_fsq; break;

ERROR: trailing statements should be on next line
#1621: FILE: disas/riscv.c:1569:
+            case 2: op = rv_op_amoadd_w; break;

ERROR: trailing statements should be on next line
#1622: FILE: disas/riscv.c:1570:
+            case 3: op = rv_op_amoadd_d; break;

ERROR: trailing statements should be on next line
#1623: FILE: disas/riscv.c:1571:
+            case 4: op = rv_op_amoadd_q; break;

ERROR: trailing statements should be on next line
#1624: FILE: disas/riscv.c:1572:
+            case 10: op = rv_op_amoswap_w; break;

ERROR: trailing statements should be on next line
#1625: FILE: disas/riscv.c:1573:
+            case 11: op = rv_op_amoswap_d; break;

ERROR: trailing statements should be on next line
#1626: FILE: disas/riscv.c:1574:
+            case 12: op = rv_op_amoswap_q; break;

ERROR: trailing statements should be on next line
#1629: FILE: disas/riscv.c:1577:
+                case 0: op = rv_op_lr_w; break;

ERROR: trailing statements should be on next line
#1634: FILE: disas/riscv.c:1582:
+                case 0: op = rv_op_lr_d; break;

ERROR: trailing statements should be on next line
#1639: FILE: disas/riscv.c:1587:
+                case 0: op = rv_op_lr_q; break;

ERROR: trailing statements should be on next line
#1642: FILE: disas/riscv.c:1590:
+            case 26: op = rv_op_sc_w; break;

ERROR: trailing statements should be on next line
#1643: FILE: disas/riscv.c:1591:
+            case 27: op = rv_op_sc_d; break;

ERROR: trailing statements should be on next line
#1644: FILE: disas/riscv.c:1592:
+            case 28: op = rv_op_sc_q; break;

ERROR: trailing statements should be on next line
#1645: FILE: disas/riscv.c:1593:
+            case 34: op = rv_op_amoxor_w; break;

ERROR: trailing statements should be on next line
#1646: FILE: disas/riscv.c:1594:
+            case 35: op = rv_op_amoxor_d; break;

ERROR: trailing statements should be on next line
#1647: FILE: disas/riscv.c:1595:
+            case 36: op = rv_op_amoxor_q; break;

ERROR: trailing statements should be on next line
#1648: FILE: disas/riscv.c:1596:
+            case 66: op = rv_op_amoor_w; break;

ERROR: trailing statements should be on next line
#1649: FILE: disas/riscv.c:1597:
+            case 67: op = rv_op_amoor_d; break;

ERROR: trailing statements should be on next line
#1650: FILE: disas/riscv.c:1598:
+            case 68: op = rv_op_amoor_q; break;

ERROR: trailing statements should be on next line
#1651: FILE: disas/riscv.c:1599:
+            case 98: op = rv_op_amoand_w; break;

ERROR: trailing statements should be on next line
#1652: FILE: disas/riscv.c:1600:
+            case 99: op = rv_op_amoand_d; break;

ERROR: trailing statements should be on next line
#1653: FILE: disas/riscv.c:1601:
+            case 100: op = rv_op_amoand_q; break;

ERROR: trailing statements should be on next line
#1654: FILE: disas/riscv.c:1602:
+            case 130: op = rv_op_amomin_w; break;

ERROR: trailing statements should be on next line
#1655: FILE: disas/riscv.c:1603:
+            case 131: op = rv_op_amomin_d; break;

ERROR: trailing statements should be on next line
#1656: FILE: disas/riscv.c:1604:
+            case 132: op = rv_op_amomin_q; break;

ERROR: trailing statements should be on next line
#1657: FILE: disas/riscv.c:1605:
+            case 162: op = rv_op_amomax_w; break;

ERROR: trailing statements should be on next line
#1658: FILE: disas/riscv.c:1606:
+            case 163: op = rv_op_amomax_d; break;

ERROR: trailing statements should be on next line
#1659: FILE: disas/riscv.c:1607:
+            case 164: op = rv_op_amomax_q; break;

ERROR: trailing statements should be on next line
#1660: FILE: disas/riscv.c:1608:
+            case 194: op = rv_op_amominu_w; break;

ERROR: trailing statements should be on next line
#1661: FILE: disas/riscv.c:1609:
+            case 195: op = rv_op_amominu_d; break;

ERROR: trailing statements should be on next line
#1662: FILE: disas/riscv.c:1610:
+            case 196: op = rv_op_amominu_q; break;

ERROR: trailing statements should be on next line
#1663: FILE: disas/riscv.c:1611:
+            case 226: op = rv_op_amomaxu_w; break;

ERROR: trailing statements should be on next line
#1664: FILE: disas/riscv.c:1612:
+            case 227: op = rv_op_amomaxu_d; break;

ERROR: trailing statements should be on next line
#1665: FILE: disas/riscv.c:1613:
+            case 228: op = rv_op_amomaxu_q; break;

WARNING: line over 80 characters
#1669: FILE: disas/riscv.c:1617:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {

ERROR: trailing statements should be on next line
#1670: FILE: disas/riscv.c:1618:
+            case 0: op = rv_op_add; break;

ERROR: trailing statements should be on next line
#1671: FILE: disas/riscv.c:1619:
+            case 1: op = rv_op_sll; break;

ERROR: trailing statements should be on next line
#1672: FILE: disas/riscv.c:1620:
+            case 2: op = rv_op_slt; break;

ERROR: trailing statements should be on next line
#1673: FILE: disas/riscv.c:1621:
+            case 3: op = rv_op_sltu; break;

ERROR: trailing statements should be on next line
#1674: FILE: disas/riscv.c:1622:
+            case 4: op = rv_op_xor; break;

ERROR: trailing statements should be on next line
#1675: FILE: disas/riscv.c:1623:
+            case 5: op = rv_op_srl; break;

ERROR: trailing statements should be on next line
#1676: FILE: disas/riscv.c:1624:
+            case 6: op = rv_op_or; break;

ERROR: trailing statements should be on next line
#1677: FILE: disas/riscv.c:1625:
+            case 7: op = rv_op_and; break;

ERROR: trailing statements should be on next line
#1678: FILE: disas/riscv.c:1626:
+            case 8: op = rv_op_mul; break;

ERROR: trailing statements should be on next line
#1679: FILE: disas/riscv.c:1627:
+            case 9: op = rv_op_mulh; break;

ERROR: trailing statements should be on next line
#1680: FILE: disas/riscv.c:1628:
+            case 10: op = rv_op_mulhsu; break;

ERROR: trailing statements should be on next line
#1681: FILE: disas/riscv.c:1629:
+            case 11: op = rv_op_mulhu; break;

ERROR: trailing statements should be on next line
#1682: FILE: disas/riscv.c:1630:
+            case 12: op = rv_op_div; break;

ERROR: trailing statements should be on next line
#1683: FILE: disas/riscv.c:1631:
+            case 13: op = rv_op_divu; break;

ERROR: trailing statements should be on next line
#1684: FILE: disas/riscv.c:1632:
+            case 14: op = rv_op_rem; break;

ERROR: trailing statements should be on next line
#1685: FILE: disas/riscv.c:1633:
+            case 15: op = rv_op_remu; break;

ERROR: trailing statements should be on next line
#1686: FILE: disas/riscv.c:1634:
+            case 256: op = rv_op_sub; break;

ERROR: trailing statements should be on next line
#1687: FILE: disas/riscv.c:1635:
+            case 261: op = rv_op_sra; break;

ERROR: trailing statements should be on next line
#1690: FILE: disas/riscv.c:1638:
+        case 13: op = rv_op_lui; break;

WARNING: line over 80 characters
#1692: FILE: disas/riscv.c:1640:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {

ERROR: trailing statements should be on next line
#1693: FILE: disas/riscv.c:1641:
+            case 0: op = rv_op_addw; break;

ERROR: trailing statements should be on next line
#1694: FILE: disas/riscv.c:1642:
+            case 1: op = rv_op_sllw; break;

ERROR: trailing statements should be on next line
#1695: FILE: disas/riscv.c:1643:
+            case 5: op = rv_op_srlw; break;

ERROR: trailing statements should be on next line
#1696: FILE: disas/riscv.c:1644:
+            case 8: op = rv_op_mulw; break;

ERROR: trailing statements should be on next line
#1697: FILE: disas/riscv.c:1645:
+            case 12: op = rv_op_divw; break;

ERROR: trailing statements should be on next line
#1698: FILE: disas/riscv.c:1646:
+            case 13: op = rv_op_divuw; break;

ERROR: trailing statements should be on next line
#1699: FILE: disas/riscv.c:1647:
+            case 14: op = rv_op_remw; break;

ERROR: trailing statements should be on next line
#1700: FILE: disas/riscv.c:1648:
+            case 15: op = rv_op_remuw; break;

ERROR: trailing statements should be on next line
#1701: FILE: disas/riscv.c:1649:
+            case 256: op = rv_op_subw; break;

ERROR: trailing statements should be on next line
#1702: FILE: disas/riscv.c:1650:
+            case 261: op = rv_op_sraw; break;

ERROR: trailing statements should be on next line
#1707: FILE: disas/riscv.c:1655:
+            case 0: op = rv_op_fmadd_s; break;

ERROR: trailing statements should be on next line
#1708: FILE: disas/riscv.c:1656:
+            case 1: op = rv_op_fmadd_d; break;

ERROR: trailing statements should be on next line
#1709: FILE: disas/riscv.c:1657:
+            case 3: op = rv_op_fmadd_q; break;

ERROR: trailing statements should be on next line
#1714: FILE: disas/riscv.c:1662:
+            case 0: op = rv_op_fmsub_s; break;

ERROR: trailing statements should be on next line
#1715: FILE: disas/riscv.c:1663:
+            case 1: op = rv_op_fmsub_d; break;

ERROR: trailing statements should be on next line
#1716: FILE: disas/riscv.c:1664:
+            case 3: op = rv_op_fmsub_q; break;

ERROR: trailing statements should be on next line
#1721: FILE: disas/riscv.c:1669:
+            case 0: op = rv_op_fnmsub_s; break;

ERROR: trailing statements should be on next line
#1722: FILE: disas/riscv.c:1670:
+            case 1: op = rv_op_fnmsub_d; break;

ERROR: trailing statements should be on next line
#1723: FILE: disas/riscv.c:1671:
+            case 3: op = rv_op_fnmsub_q; break;

ERROR: trailing statements should be on next line
#1728: FILE: disas/riscv.c:1676:
+            case 0: op = rv_op_fnmadd_s; break;

ERROR: trailing statements should be on next line
#1729: FILE: disas/riscv.c:1677:
+            case 1: op = rv_op_fnmadd_d; break;

ERROR: trailing statements should be on next line
#1730: FILE: disas/riscv.c:1678:
+            case 3: op = rv_op_fnmadd_q; break;

ERROR: trailing statements should be on next line
#1735: FILE: disas/riscv.c:1683:
+            case 0: op = rv_op_fadd_s; break;

ERROR: trailing statements should be on next line
#1736: FILE: disas/riscv.c:1684:
+            case 1: op = rv_op_fadd_d; break;

ERROR: trailing statements should be on next line
#1737: FILE: disas/riscv.c:1685:
+            case 3: op = rv_op_fadd_q; break;

ERROR: trailing statements should be on next line
#1738: FILE: disas/riscv.c:1686:
+            case 4: op = rv_op_fsub_s; break;

ERROR: trailing statements should be on next line
#1739: FILE: disas/riscv.c:1687:
+            case 5: op = rv_op_fsub_d; break;

ERROR: trailing statements should be on next line
#1740: FILE: disas/riscv.c:1688:
+            case 7: op = rv_op_fsub_q; break;

ERROR: trailing statements should be on next line
#1741: FILE: disas/riscv.c:1689:
+            case 8: op = rv_op_fmul_s; break;

ERROR: trailing statements should be on next line
#1742: FILE: disas/riscv.c:1690:
+            case 9: op = rv_op_fmul_d; break;

ERROR: trailing statements should be on next line
#1743: FILE: disas/riscv.c:1691:
+            case 11: op = rv_op_fmul_q; break;

ERROR: trailing statements should be on next line
#1744: FILE: disas/riscv.c:1692:
+            case 12: op = rv_op_fdiv_s; break;

ERROR: trailing statements should be on next line
#1745: FILE: disas/riscv.c:1693:
+            case 13: op = rv_op_fdiv_d; break;

ERROR: trailing statements should be on next line
#1746: FILE: disas/riscv.c:1694:
+            case 15: op = rv_op_fdiv_q; break;

ERROR: trailing statements should be on next line
#1749: FILE: disas/riscv.c:1697:
+                case 0: op = rv_op_fsgnj_s; break;

ERROR: trailing statements should be on next line
#1750: FILE: disas/riscv.c:1698:
+                case 1: op = rv_op_fsgnjn_s; break;

ERROR: trailing statements should be on next line
#1751: FILE: disas/riscv.c:1699:
+                case 2: op = rv_op_fsgnjx_s; break;

ERROR: trailing statements should be on next line
#1756: FILE: disas/riscv.c:1704:
+                case 0: op = rv_op_fsgnj_d; break;

ERROR: trailing statements should be on next line
#1757: FILE: disas/riscv.c:1705:
+                case 1: op = rv_op_fsgnjn_d; break;

ERROR: trailing statements should be on next line
#1758: FILE: disas/riscv.c:1706:
+                case 2: op = rv_op_fsgnjx_d; break;

ERROR: trailing statements should be on next line
#1763: FILE: disas/riscv.c:1711:
+                case 0: op = rv_op_fsgnj_q; break;

ERROR: trailing statements should be on next line
#1764: FILE: disas/riscv.c:1712:
+                case 1: op = rv_op_fsgnjn_q; break;

ERROR: trailing statements should be on next line
#1765: FILE: disas/riscv.c:1713:
+                case 2: op = rv_op_fsgnjx_q; break;

ERROR: trailing statements should be on next line
#1770: FILE: disas/riscv.c:1718:
+                case 0: op = rv_op_fmin_s; break;

ERROR: trailing statements should be on next line
#1771: FILE: disas/riscv.c:1719:
+                case 1: op = rv_op_fmax_s; break;

ERROR: trailing statements should be on next line
#1776: FILE: disas/riscv.c:1724:
+                case 0: op = rv_op_fmin_d; break;

ERROR: trailing statements should be on next line
#1777: FILE: disas/riscv.c:1725:
+                case 1: op = rv_op_fmax_d; break;

ERROR: trailing statements should be on next line
#1782: FILE: disas/riscv.c:1730:
+                case 0: op = rv_op_fmin_q; break;

ERROR: trailing statements should be on next line
#1783: FILE: disas/riscv.c:1731:
+                case 1: op = rv_op_fmax_q; break;

ERROR: trailing statements should be on next line
#1788: FILE: disas/riscv.c:1736:
+                case 1: op = rv_op_fcvt_s_d; break;

ERROR: trailing statements should be on next line
#1789: FILE: disas/riscv.c:1737:
+                case 3: op = rv_op_fcvt_s_q; break;

ERROR: trailing statements should be on next line
#1794: FILE: disas/riscv.c:1742:
+                case 0: op = rv_op_fcvt_d_s; break;

ERROR: trailing statements should be on next line
#1795: FILE: disas/riscv.c:1743:
+                case 3: op = rv_op_fcvt_d_q; break;

ERROR: trailing statements should be on next line
#1800: FILE: disas/riscv.c:1748:
+                case 0: op = rv_op_fcvt_q_s; break;

ERROR: trailing statements should be on next line
#1801: FILE: disas/riscv.c:1749:
+                case 1: op = rv_op_fcvt_q_d; break;

ERROR: trailing statements should be on next line
#1806: FILE: disas/riscv.c:1754:
+                case 0: op = rv_op_fsqrt_s; break;

ERROR: trailing statements should be on next line
#1811: FILE: disas/riscv.c:1759:
+                case 0: op = rv_op_fsqrt_d; break;

ERROR: trailing statements should be on next line
#1816: FILE: disas/riscv.c:1764:
+                case 0: op = rv_op_fsqrt_q; break;

ERROR: trailing statements should be on next line
#1821: FILE: disas/riscv.c:1769:
+                case 0: op = rv_op_fle_s; break;

ERROR: trailing statements should be on next line
#1822: FILE: disas/riscv.c:1770:
+                case 1: op = rv_op_flt_s; break;

ERROR: trailing statements should be on next line
#1823: FILE: disas/riscv.c:1771:
+                case 2: op = rv_op_feq_s; break;

ERROR: trailing statements should be on next line
#1828: FILE: disas/riscv.c:1776:
+                case 0: op = rv_op_fle_d; break;

ERROR: trailing statements should be on next line
#1829: FILE: disas/riscv.c:1777:
+                case 1: op = rv_op_flt_d; break;

ERROR: trailing statements should be on next line
#1830: FILE: disas/riscv.c:1778:
+                case 2: op = rv_op_feq_d; break;

ERROR: trailing statements should be on next line
#1835: FILE: disas/riscv.c:1783:
+                case 0: op = rv_op_fle_q; break;

ERROR: trailing statements should be on next line
#1836: FILE: disas/riscv.c:1784:
+                case 1: op = rv_op_flt_q; break;

ERROR: trailing statements should be on next line
#1837: FILE: disas/riscv.c:1785:
+                case 2: op = rv_op_feq_q; break;

ERROR: trailing statements should be on next line
#1842: FILE: disas/riscv.c:1790:
+                case 0: op = rv_op_fcvt_w_s; break;

ERROR: trailing statements should be on next line
#1843: FILE: disas/riscv.c:1791:
+                case 1: op = rv_op_fcvt_wu_s; break;

ERROR: trailing statements should be on next line
#1844: FILE: disas/riscv.c:1792:
+                case 2: op = rv_op_fcvt_l_s; break;

ERROR: trailing statements should be on next line
#1845: FILE: disas/riscv.c:1793:
+                case 3: op = rv_op_fcvt_lu_s; break;

ERROR: trailing statements should be on next line
#1850: FILE: disas/riscv.c:1798:
+                case 0: op = rv_op_fcvt_w_d; break;

ERROR: trailing statements should be on next line
#1851: FILE: disas/riscv.c:1799:
+                case 1: op = rv_op_fcvt_wu_d; break;

ERROR: trailing statements should be on next line
#1852: FILE: disas/riscv.c:1800:
+                case 2: op = rv_op_fcvt_l_d; break;

ERROR: trailing statements should be on next line
#1853: FILE: disas/riscv.c:1801:
+                case 3: op = rv_op_fcvt_lu_d; break;

ERROR: trailing statements should be on next line
#1858: FILE: disas/riscv.c:1806:
+                case 0: op = rv_op_fcvt_w_q; break;

ERROR: trailing statements should be on next line
#1859: FILE: disas/riscv.c:1807:
+                case 1: op = rv_op_fcvt_wu_q; break;

ERROR: trailing statements should be on next line
#1860: FILE: disas/riscv.c:1808:
+                case 2: op = rv_op_fcvt_l_q; break;

ERROR: trailing statements should be on next line
#1861: FILE: disas/riscv.c:1809:
+                case 3: op = rv_op_fcvt_lu_q; break;

ERROR: trailing statements should be on next line
#1866: FILE: disas/riscv.c:1814:
+                case 0: op = rv_op_fcvt_s_w; break;

ERROR: trailing statements should be on next line
#1867: FILE: disas/riscv.c:1815:
+                case 1: op = rv_op_fcvt_s_wu; break;

ERROR: trailing statements should be on next line
#1868: FILE: disas/riscv.c:1816:
+                case 2: op = rv_op_fcvt_s_l; break;

ERROR: trailing statements should be on next line
#1869: FILE: disas/riscv.c:1817:
+                case 3: op = rv_op_fcvt_s_lu; break;

ERROR: trailing statements should be on next line
#1874: FILE: disas/riscv.c:1822:
+                case 0: op = rv_op_fcvt_d_w; break;

ERROR: trailing statements should be on next line
#1875: FILE: disas/riscv.c:1823:
+                case 1: op = rv_op_fcvt_d_wu; break;

ERROR: trailing statements should be on next line
#1876: FILE: disas/riscv.c:1824:
+                case 2: op = rv_op_fcvt_d_l; break;

ERROR: trailing statements should be on next line
#1877: FILE: disas/riscv.c:1825:
+                case 3: op = rv_op_fcvt_d_lu; break;

ERROR: trailing statements should be on next line
#1882: FILE: disas/riscv.c:1830:
+                case 0: op = rv_op_fcvt_q_w; break;

ERROR: trailing statements should be on next line
#1883: FILE: disas/riscv.c:1831:
+                case 1: op = rv_op_fcvt_q_wu; break;

ERROR: trailing statements should be on next line
#1884: FILE: disas/riscv.c:1832:
+                case 2: op = rv_op_fcvt_q_l; break;

ERROR: trailing statements should be on next line
#1885: FILE: disas/riscv.c:1833:
+                case 3: op = rv_op_fcvt_q_lu; break;

WARNING: line over 80 characters
#1889: FILE: disas/riscv.c:1837:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {

ERROR: trailing statements should be on next line
#1890: FILE: disas/riscv.c:1838:
+                case 0: op = rv_op_fmv_x_s; break;

ERROR: trailing statements should be on next line
#1891: FILE: disas/riscv.c:1839:
+                case 1: op = rv_op_fclass_s; break;

WARNING: line over 80 characters
#1895: FILE: disas/riscv.c:1843:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {

ERROR: trailing statements should be on next line
#1896: FILE: disas/riscv.c:1844:
+                case 0: op = rv_op_fmv_x_d; break;

ERROR: trailing statements should be on next line
#1897: FILE: disas/riscv.c:1845:
+                case 1: op = rv_op_fclass_d; break;

WARNING: line over 80 characters
#1901: FILE: disas/riscv.c:1849:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {

ERROR: trailing statements should be on next line
#1902: FILE: disas/riscv.c:1850:
+                case 0: op = rv_op_fmv_x_q; break;

ERROR: trailing statements should be on next line
#1903: FILE: disas/riscv.c:1851:
+                case 1: op = rv_op_fclass_q; break;

WARNING: line over 80 characters
#1907: FILE: disas/riscv.c:1855:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {

ERROR: trailing statements should be on next line
#1908: FILE: disas/riscv.c:1856:
+                case 0: op = rv_op_fmv_s_x; break;

WARNING: line over 80 characters
#1912: FILE: disas/riscv.c:1860:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {

ERROR: trailing statements should be on next line
#1913: FILE: disas/riscv.c:1861:
+                case 0: op = rv_op_fmv_d_x; break;

WARNING: line over 80 characters
#1917: FILE: disas/riscv.c:1865:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {

ERROR: trailing statements should be on next line
#1918: FILE: disas/riscv.c:1866:
+                case 0: op = rv_op_fmv_q_x; break;

ERROR: trailing statements should be on next line
#1925: FILE: disas/riscv.c:1873:
+            case 0: op = rv_op_addid; break;

ERROR: trailing statements should be on next line
#1928: FILE: disas/riscv.c:1876:
+                case 0: op = rv_op_sllid; break;

ERROR: trailing statements should be on next line
#1933: FILE: disas/riscv.c:1881:
+                case 0: op = rv_op_srlid; break;

ERROR: trailing statements should be on next line
#1934: FILE: disas/riscv.c:1882:
+                case 16: op = rv_op_sraid; break;

ERROR: trailing statements should be on next line
#1941: FILE: disas/riscv.c:1889:
+            case 0: op = rv_op_beq; break;

ERROR: trailing statements should be on next line
#1942: FILE: disas/riscv.c:1890:
+            case 1: op = rv_op_bne; break;

ERROR: trailing statements should be on next line
#1943: FILE: disas/riscv.c:1891:
+            case 4: op = rv_op_blt; break;

ERROR: trailing statements should be on next line
#1944: FILE: disas/riscv.c:1892:
+            case 5: op = rv_op_bge; break;

ERROR: trailing statements should be on next line
#1945: FILE: disas/riscv.c:1893:
+            case 6: op = rv_op_bltu; break;

ERROR: trailing statements should be on next line
#1946: FILE: disas/riscv.c:1894:
+            case 7: op = rv_op_bgeu; break;

ERROR: trailing statements should be on next line
#1951: FILE: disas/riscv.c:1899:
+            case 0: op = rv_op_jalr; break;

ERROR: trailing statements should be on next line
#1954: FILE: disas/riscv.c:1902:
+        case 27: op = rv_op_jal; break;

ERROR: line over 90 characters
#1958: FILE: disas/riscv.c:1906:
+                switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) {

ERROR: trailing statements should be on next line
#1961: FILE: disas/riscv.c:1909:
+                    case 0: op = rv_op_ecall; break;

ERROR: trailing statements should be on next line
#1962: FILE: disas/riscv.c:1910:
+                    case 32: op = rv_op_ebreak; break;

ERROR: trailing statements should be on next line
#1963: FILE: disas/riscv.c:1911:
+                    case 64: op = rv_op_uret; break;

ERROR: trailing statements should be on next line
#1970: FILE: disas/riscv.c:1918:
+                        case 0: op = rv_op_sret; break;

ERROR: trailing statements should be on next line
#1973: FILE: disas/riscv.c:1921:
+                    case 4: op = rv_op_sfence_vm; break;

ERROR: trailing statements should be on next line
#1976: FILE: disas/riscv.c:1924:
+                        case 0: op = rv_op_wfi; break;

ERROR: trailing statements should be on next line
#1981: FILE: disas/riscv.c:1929:
+                case 288: op = rv_op_sfence_vma; break;

ERROR: trailing statements should be on next line
#1984: FILE: disas/riscv.c:1932:
+                    case 64: op = rv_op_hret; break;

ERROR: trailing statements should be on next line
#1989: FILE: disas/riscv.c:1937:
+                    case 64: op = rv_op_mret; break;

ERROR: trailing statements should be on next line
#1994: FILE: disas/riscv.c:1942:
+                    case 576: op = rv_op_dret; break;

ERROR: trailing statements should be on next line
#1999: FILE: disas/riscv.c:1947:
+            case 1: op = rv_op_csrrw; break;

ERROR: trailing statements should be on next line
#2000: FILE: disas/riscv.c:1948:
+            case 2: op = rv_op_csrrs; break;

ERROR: trailing statements should be on next line
#2001: FILE: disas/riscv.c:1949:
+            case 3: op = rv_op_csrrc; break;

ERROR: trailing statements should be on next line
#2002: FILE: disas/riscv.c:1950:
+            case 5: op = rv_op_csrrwi; break;

ERROR: trailing statements should be on next line
#2003: FILE: disas/riscv.c:1951:
+            case 6: op = rv_op_csrrsi; break;

ERROR: trailing statements should be on next line
#2004: FILE: disas/riscv.c:1952:
+            case 7: op = rv_op_csrrci; break;

WARNING: line over 80 characters
#2008: FILE: disas/riscv.c:1956:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {

ERROR: trailing statements should be on next line
#2009: FILE: disas/riscv.c:1957:
+            case 0: op = rv_op_addd; break;

ERROR: trailing statements should be on next line
#2010: FILE: disas/riscv.c:1958:
+            case 1: op = rv_op_slld; break;

ERROR: trailing statements should be on next line
#2011: FILE: disas/riscv.c:1959:
+            case 5: op = rv_op_srld; break;

ERROR: trailing statements should be on next line
#2012: FILE: disas/riscv.c:1960:
+            case 8: op = rv_op_muld; break;

ERROR: trailing statements should be on next line
#2013: FILE: disas/riscv.c:1961:
+            case 12: op = rv_op_divd; break;

ERROR: trailing statements should be on next line
#2014: FILE: disas/riscv.c:1962:
+            case 13: op = rv_op_divud; break;

ERROR: trailing statements should be on next line
#2015: FILE: disas/riscv.c:1963:
+            case 14: op = rv_op_remd; break;

ERROR: trailing statements should be on next line
#2016: FILE: disas/riscv.c:1964:
+            case 15: op = rv_op_remud; break;

ERROR: trailing statements should be on next line
#2017: FILE: disas/riscv.c:1965:
+            case 256: op = rv_op_subd; break;

ERROR: trailing statements should be on next line
#2018: FILE: disas/riscv.c:1966:
+            case 261: op = rv_op_srad; break;

ERROR: trailing statements should be on next line
#2781: FILE: disas/riscv.c:2729:
+        default: break;

ERROR: space prohibited between function name and open parenthesis '('
#3109: FILE: include/disas/bfd.h:432:
+int print_insn_riscv32          (bfd_vma, disassemble_info*);

ERROR: space prohibited between function name and open parenthesis '('
#3110: FILE: include/disas/bfd.h:433:
+int print_insn_riscv64          (bfd_vma, disassemble_info*);

total: 279 errors, 38 warnings, 3071 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking PATCH 5/23: RISC-V CPU Helpers...
Checking PATCH 6/23: RISC-V FPU Support...
Checking PATCH 7/23: RISC-V GDB Stub...
Checking PATCH 8/23: RISC-V TCG Code Generation...
ERROR: spaces required around that ':' (ctx:VxE)
#633: FILE: target/riscv/translate.c:238:
+    CASE_OP_32_64(OPC_RISC_ADD):
                                ^

ERROR: spaces required around that ':' (ctx:VxE)
#636: FILE: target/riscv/translate.c:241:
+    CASE_OP_32_64(OPC_RISC_SUB):
                                ^

ERROR: spaces required around that ':' (ctx:VxE)
#690: FILE: target/riscv/translate.c:295:
+    CASE_OP_32_64(OPC_RISC_MUL):
                                ^

total: 3 errors, 0 warnings, 2342 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking PATCH 9/23: RISC-V Physical Memory Protection...
Checking PATCH 10/23: RISC-V Linux User Emulation...
Checking PATCH 11/23: Add symbol table callback interface to load_elf...
Checking PATCH 12/23: RISC-V HTIF Console...
Checking PATCH 13/23: RISC-V HART Array...
Checking PATCH 14/23: SiFive RISC-V CLINT Block...
Checking PATCH 15/23: SiFive RISC-V PLIC Block...
Checking PATCH 16/23: RISC-V Spike Machines...
Checking PATCH 17/23: SiFive RISC-V Test Finisher...
Checking PATCH 18/23: RISC-V VirtIO Machine...
Checking PATCH 19/23: SiFive RISC-V UART Device...
Checking PATCH 20/23: SiFive RISC-V PRCI Block...
Checking PATCH 21/23: SiFive Freedom E Series RISC-V Machine...
Checking PATCH 22/23: SiFive Freedom U Series RISC-V Machine...
Checking PATCH 23/23: RISC-V Build Infrastructure...
WARNING: line over 80 characters
#227: FILE: scripts/qemu-binfmt-conf.sh:103:
+riscv32_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'

ERROR: line over 90 characters
#228: FILE: scripts/qemu-binfmt-conf.sh:104:
+riscv32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'

WARNING: line over 80 characters
#231: FILE: scripts/qemu-binfmt-conf.sh:107:
+riscv64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'

ERROR: line over 90 characters
#232: FILE: scripts/qemu-binfmt-conf.sh:108:
+riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'

total: 2 errors, 2 warnings, 155 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

=== OUTPUT END ===

Test command exited with code: 1


---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@freelists.org

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure Michael Clark
@ 2018-03-02 14:33   ` Eric Blake
  2018-03-03  2:37     ` Michael Clark
  2018-03-09 13:03   ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 70+ messages in thread
From: Eric Blake @ 2018-03-02 14:33 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 07:51 AM, Michael Clark wrote:
> This adds RISC-V into the build system enabling the following targets:
> 
> - riscv32-softmmu
> - riscv64-softmmu
> - riscv32-linux-user
> - riscv64-linux-user
> 
> This adds defaults configs for RISC-V, enables the build for the RISC-V
> CPU core, hardware, and Linux User Emulation. The 'qemu-binfmt-conf.sh'
> script is updated to add the RISC-V ELF magic.
> 
> Expected checkpatch errors for consistency reasons:
> 
> ERROR: line over 90 characters
> FILE: scripts/qemu-binfmt-conf.sh
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>   arch_init.c                            |  2 ++
>   configure                              | 13 +++++++++++++
>   cpus.c                                 |  6 ++++++
>   default-configs/riscv32-linux-user.mak |  1 +
>   default-configs/riscv32-softmmu.mak    |  4 ++++
>   default-configs/riscv64-linux-user.mak |  1 +
>   default-configs/riscv64-softmmu.mak    |  4 ++++
>   hw/riscv/Makefile.objs                 | 11 +++++++++++
>   include/sysemu/arch_init.h             |  1 +
>   qapi-schema.json                       | 17 ++++++++++++++++-

Will need rebasing to modify qapi/misc.json if my pending PULL request 
issues get resolved first:

https://lists.gnu.org/archive/html/qemu-devel/2018-03/msg00469.html

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition Michael Clark
@ 2018-03-03  2:23   ` Michael Clark
  2018-03-03  2:34     ` Michael Clark
  2018-03-05  9:44   ` Igor Mammedov
  1 sibling, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-03  2:23 UTC (permalink / raw)
  To: QEMU Developers, Richard Henderson, Palmer Dabbelt, Andrew Waterman
  Cc: Michael Clark, Sagar Karandikar, Bastian Koppelmann, RISC-V Patches

We were able to remove several ifdefs and figured out a problem with
masking out cores for qemu-system-riscv32 and qemu-system-riscv64.

This version of the core patch seems cleaner to me and we have fixed a few
spec compliance issues with regard to alignment of mtvec/stvec when the C
extension is enabled or disabled.

We haven't addressed runtime changes to 'misa' as it is legal for 'misa' to
be read-only. A later "feature" patch will add runtime support for misa
changes, likely just invalidating the translation cache due to the limited
number of bits in mmu_index. I can say for sure we not going to do anything
this risky so close to soft-freeze. The last minute changes were focused at
specification compliance for MMU vs NOMMU and respecting the 'S' and 'U'
misa bits with respect to allowable privilege modes. SiFive's E Series
cores do not support S mode but they do support U mode.

We also had an internal discussion about S-mode vs mmu, and it was decided
that it is a legal combination to have a nommu core that implements S mode.
In this use case, M mode could configure PMP (Physical Memory Protection),
and it would potentially possible to port FDPIC nommu linux to work on a
core with S mode and nommu. The other S mode registers besides 'satp'
(Supervisor Address Translation Pointer) are available in S-mode with nommu.

The v8 patch series just tightens up compliance with the specification, and
makes use of the mmu register (satp), selecting S-mode or calling SRET
cause illegal instruction traps.

Writes to mstatus.mpp of un unsupported modes (i.e. 'S' or 'U' misa bits
not present) are silently dropped, as that is the behaviour I am told
should be implemented. We may have to raise some issues against the RISC-V
Privileged ISA specification to make sure that this behaviour is specified,
as it currently does not specify explicitly the behaviour for a core that
supports S-mode with no-mmu.

Regards,
Michael.

On Sat, Mar 3, 2018 at 2:51 AM, Michael Clark <mjc@sifive.com> wrote:

> Add CPU state header, CPU definitions and initialization routines
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++
> ++++++++++++++++++
>  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
>  target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
> +++++++++++++++
>  3 files changed, 1139 insertions(+)
>  create mode 100644 target/riscv/cpu.c
>  create mode 100644 target/riscv/cpu.h
>  create mode 100644 target/riscv/cpu_bits.h
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> new file mode 100644
> index 0000000..4851890
> --- /dev/null
> +++ b/target/riscv/cpu.c
> @@ -0,0 +1,432 @@
> +/*
> + * QEMU RISC-V CPU
> + *
> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
> + * Copyright (c) 2017-2018 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "cpu.h"
> +#include "exec/exec-all.h"
> +#include "qapi/error.h"
> +#include "migration/vmstate.h"
> +
> +/* RISC-V CPU definitions */
> +
> +static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG";
> +
> +const char * const riscv_int_regnames[] = {
> +  "zero", "ra  ", "sp  ", "gp  ", "tp  ", "t0  ", "t1  ", "t2  ",
> +  "s0  ", "s1  ", "a0  ", "a1  ", "a2  ", "a3  ", "a4  ", "a5  ",
> +  "a6  ", "a7  ", "s2  ", "s3  ", "s4  ", "s5  ", "s6  ", "s7  ",
> +  "s8  ", "s9  ", "s10 ", "s11 ", "t3  ", "t4  ", "t5  ", "t6  "
> +};
> +
> +const char * const riscv_fpr_regnames[] = {
> +  "ft0 ", "ft1 ", "ft2 ", "ft3 ", "ft4 ", "ft5 ", "ft6 ",  "ft7 ",
> +  "fs0 ", "fs1 ", "fa0 ", "fa1 ", "fa2 ", "fa3 ", "fa4 ",  "fa5 ",
> +  "fa6 ", "fa7 ", "fs2 ", "fs3 ", "fs4 ", "fs5 ", "fs6 ",  "fs7 ",
> +  "fs8 ", "fs9 ", "fs10", "fs11", "ft8 ", "ft9 ", "ft10",  "ft11"
> +};
> +
> +const char * const riscv_excp_names[] = {
> +    "misaligned_fetch",
> +    "fault_fetch",
> +    "illegal_instruction",
> +    "breakpoint",
> +    "misaligned_load",
> +    "fault_load",
> +    "misaligned_store",
> +    "fault_store",
> +    "user_ecall",
> +    "supervisor_ecall",
> +    "hypervisor_ecall",
> +    "machine_ecall",
> +    "exec_page_fault",
> +    "load_page_fault",
> +    "reserved",
> +    "store_page_fault"
> +};
> +
> +const char * const riscv_intr_names[] = {
> +    "u_software",
> +    "s_software",
> +    "h_software",
> +    "m_software",
> +    "u_timer",
> +    "s_timer",
> +    "h_timer",
> +    "m_timer",
> +    "u_external",
> +    "s_external",
> +    "h_external",
> +    "m_external",
> +    "coprocessor",
> +    "host"
> +};
> +
> +typedef struct RISCVCPUInfo {
> +    const int bit_widths;
> +    const char *name;
> +    void (*initfn)(Object *obj);
> +} RISCVCPUInfo;
> +
> +static void set_misa(CPURISCVState *env, target_ulong misa)
> +{
> +    env->misa = misa;
> +}
> +
> +static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
> +{
> +    env->user_ver = user_ver;
> +    env->priv_ver = priv_ver;
> +}
> +
> +static void set_feature(CPURISCVState *env, int feature)
> +{
> +    env->features |= (1ULL << feature);
> +}
> +
> +static void set_resetvec(CPURISCVState *env, int resetvec)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    env->resetvec = resetvec;
> +#endif
> +}
> +
> +static void riscv_any_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +}
> +
> +static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +    set_feature(env, RISCV_FEATURE_MMU);
> +}
> +
> +static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +    set_feature(env, RISCV_FEATURE_MMU);
> +}
> +
> +static void rv32imacu_nommu_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +}
> +
> +static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +    set_feature(env, RISCV_FEATURE_MMU);
> +}
> +
> +static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +    set_feature(env, RISCV_FEATURE_MMU);
> +}
> +
> +static void rv64imacu_nommu_cpu_init(Object *obj)
> +{
> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
> +    set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
> +    set_resetvec(env, DEFAULT_RSTVEC);
> +}
> +
> +static const RISCVCPUInfo riscv_cpus[] = {
> +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
> +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init
> },
> +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init
> },
> +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
> +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
> +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init },
> +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init
> },
> +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init
> },
> +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
> +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
> +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init },
> +    { 0, NULL, NULL }
> +};
> +
> +static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
> +{
> +    ObjectClass *oc;
> +    char *typename;
> +    char **cpuname;
> +
> +    cpuname = g_strsplit(cpu_model, ",", 1);
> +    typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]);
> +    oc = object_class_by_name(typename);
> +    g_strfreev(cpuname);
> +    g_free(typename);
> +    if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) ||
> +        object_class_is_abstract(oc)) {
> +        return NULL;
> +    }
> +    return oc;
> +}
> +
> +static void riscv_cpu_dump_state(CPUState *cs, FILE *f,
> +    fprintf_function cpu_fprintf, int flags)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    CPURISCVState *env = &cpu->env;
> +    int i;
> +
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc      ", env->pc);
> +#ifndef CONFIG_USER_ONLY
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip     ",
> +        (target_ulong)atomic_read(&env->mip));
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie     ", env->mie);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec   ", env->mtvec);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc    ", env->mepc);
> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause  ", env->mcause);
> +#endif
> +
> +    for (i = 0; i < 32; i++) {
> +        cpu_fprintf(f, " %s " TARGET_FMT_lx,
> +            riscv_int_regnames[i], env->gpr[i]);
> +        if ((i & 3) == 3) {
> +            cpu_fprintf(f, "\n");
> +        }
> +    }
> +    for (i = 0; i < 32; i++) {
> +        cpu_fprintf(f, " %s %016" PRIx64,
> +            riscv_fpr_regnames[i], env->fpr[i]);
> +        if ((i & 3) == 3) {
> +            cpu_fprintf(f, "\n");
> +        }
> +    }
> +}
> +
> +static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    CPURISCVState *env = &cpu->env;
> +    env->pc = value;
> +}
> +
> +static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock
> *tb)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    CPURISCVState *env = &cpu->env;
> +    env->pc = tb->pc;
> +}
> +
> +static bool riscv_cpu_has_work(CPUState *cs)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    CPURISCVState *env = &cpu->env;
> +    /*
> +     * Definition of the WFI instruction requires it to ignore the
> privilege
> +     * mode and delegation registers, but respect individual enables
> +     */
> +    return (atomic_read(&env->mip) & env->mie) != 0;
> +#else
> +    return true;
> +#endif
> +}
> +
> +void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
> +                          target_ulong *data)
> +{
> +    env->pc = data[0];
> +}
> +
> +static void riscv_cpu_reset(CPUState *cs)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
> +    CPURISCVState *env = &cpu->env;
> +
> +    mcc->parent_reset(cs);
> +#ifndef CONFIG_USER_ONLY
> +    env->priv = PRV_M;
> +    env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
> +    env->mcause = 0;
> +    env->pc = env->resetvec;
> +#endif
> +    cs->exception_index = EXCP_NONE;
> +    set_default_nan_mode(1, &env->fp_status);
> +}
> +
> +static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
> +{
> +#if defined(TARGET_RISCV32)
> +    info->print_insn = print_insn_riscv32;
> +#elif defined(TARGET_RISCV64)
> +    info->print_insn = print_insn_riscv64;
> +#endif
> +}
> +
> +static void riscv_cpu_realize(DeviceState *dev, Error **errp)
> +{
> +    CPUState *cs = CPU(dev);
> +    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
> +    Error *local_err = NULL;
> +
> +    cpu_exec_realizefn(cs, &local_err);
> +    if (local_err != NULL) {
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +
> +    qemu_init_vcpu(cs);
> +    cpu_reset(cs);
> +
> +    mcc->parent_realize(dev, errp);
> +}
> +
> +static void riscv_cpu_init(Object *obj)
> +{
> +    CPUState *cs = CPU(obj);
> +    RISCVCPU *cpu = RISCV_CPU(obj);
> +
> +    cs->env_ptr = &cpu->env;
> +}
> +
> +static const VMStateDescription vmstate_riscv_cpu = {
> +    .name = "cpu",
> +    .unmigratable = 1,
> +};
> +
> +static void riscv_cpu_class_init(ObjectClass *c, void *data)
> +{
> +    RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
> +    CPUClass *cc = CPU_CLASS(c);
> +    DeviceClass *dc = DEVICE_CLASS(c);
> +
> +    mcc->parent_realize = dc->realize;
> +    dc->realize = riscv_cpu_realize;
> +
> +    mcc->parent_reset = cc->reset;
> +    cc->reset = riscv_cpu_reset;
> +
> +    cc->class_by_name = riscv_cpu_class_by_name;
> +    cc->has_work = riscv_cpu_has_work;
> +    cc->do_interrupt = riscv_cpu_do_interrupt;
> +    cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
> +    cc->dump_state = riscv_cpu_dump_state;
> +    cc->set_pc = riscv_cpu_set_pc;
> +    cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb;
> +    cc->gdb_read_register = riscv_cpu_gdb_read_register;
> +    cc->gdb_write_register = riscv_cpu_gdb_write_register;
> +    cc->gdb_num_core_regs = 65;
> +    cc->gdb_stop_before_watchpoint = true;
> +    cc->disas_set_info = riscv_cpu_disas_set_info;
> +#ifdef CONFIG_USER_ONLY
> +    cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault;
> +#else
> +    cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
> +    cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
> +#endif
> +#ifdef CONFIG_TCG
> +    cc->tcg_initialize = riscv_translate_init;
> +#endif
> +    /* For now, mark unmigratable: */
> +    cc->vmsd = &vmstate_riscv_cpu;
> +}
> +
> +static void cpu_register(const RISCVCPUInfo *info)
> +{
> +    TypeInfo type_info = {
> +        .name = info->name,
> +        .parent = TYPE_RISCV_CPU,
> +        .instance_size = sizeof(RISCVCPU),
> +        .instance_init = info->initfn,
> +    };
> +
> +    type_register(&type_info);
> +}
> +
> +static const TypeInfo riscv_cpu_type_info = {
> +    .name = TYPE_RISCV_CPU,
> +    .parent = TYPE_CPU,
> +    .instance_size = sizeof(RISCVCPU),
> +    .instance_init = riscv_cpu_init,
> +    .abstract = false,
> +    .class_size = sizeof(RISCVCPUClass),
> +    .class_init = riscv_cpu_class_init,
> +};
> +
> +char *riscv_isa_string(RISCVCPU *cpu)
> +{
> +    int i;
> +    size_t maxlen = 5 + ctz32(cpu->env.misa);
> +    char *isa_string = g_new0(char, maxlen);
> +    snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS);
> +    for (i = 0; i < sizeof(riscv_exts); i++) {
> +        if (cpu->env.misa & RV(riscv_exts[i])) {
> +            isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a';
> +
> +        }
> +    }
> +    return isa_string;
> +}
> +
> +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
> +{
> +    const RISCVCPUInfo *info = riscv_cpus;
> +
> +    while (info->name) {
> +        if (info->bit_widths & TARGET_LONG_BITS) {
> +            (*cpu_fprintf)(f, "%s\n", info->name);
> +        }
> +        info++;
> +    }
> +}
> +
> +static void riscv_cpu_register_types(void)
> +{
> +    const RISCVCPUInfo *info = riscv_cpus;
> +
> +    type_register_static(&riscv_cpu_type_info);
> +
> +    while (info->name) {
> +        if (info->bit_widths & TARGET_LONG_BITS) {
> +            cpu_register(info);
> +        }
> +        info++;
> +    }
> +}
> +
> +type_init(riscv_cpu_register_types)
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> new file mode 100644
> index 0000000..cff02a2
> --- /dev/null
> +++ b/target/riscv/cpu.h
> @@ -0,0 +1,296 @@
> +/*
> + * QEMU RISC-V CPU
> + *
> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
> + * Copyright (c) 2017-2018 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef RISCV_CPU_H
> +#define RISCV_CPU_H
> +
> +/* QEMU addressing/paging config */
> +#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
> +#if defined(TARGET_RISCV64)
> +#define TARGET_LONG_BITS 64
> +#define TARGET_PHYS_ADDR_SPACE_BITS 50
> +#define TARGET_VIRT_ADDR_SPACE_BITS 39
> +#elif defined(TARGET_RISCV32)
> +#define TARGET_LONG_BITS 32
> +#define TARGET_PHYS_ADDR_SPACE_BITS 34
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +#endif
> +
> +#define TCG_GUEST_DEFAULT_MO 0
> +
> +#define ELF_MACHINE EM_RISCV
> +#define CPUArchState struct CPURISCVState
> +
> +#include "qemu-common.h"
> +#include "qom/cpu.h"
> +#include "exec/cpu-defs.h"
> +#include "fpu/softfloat.h"
> +
> +#define TYPE_RISCV_CPU "riscv-cpu"
> +
> +#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
> +#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
> +
> +#define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
> +#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-
> v1.9.1")
> +#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-
> v1.10.0")
> +#define TYPE_RISCV_CPU_RV32IMACU_NOMMU  RISCV_CPU_TYPE_NAME("
> rv32imacu-nommu")
> +#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-
> v1.9.1")
> +#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-
> v1.10.0")
> +#define TYPE_RISCV_CPU_RV64IMACU_NOMMU  RISCV_CPU_TYPE_NAME("
> rv64imacu-nommu")
> +#define TYPE_RISCV_CPU_SIFIVE_E31       RISCV_CPU_TYPE_NAME("sifive-e31")
> +#define TYPE_RISCV_CPU_SIFIVE_E51       RISCV_CPU_TYPE_NAME("sifive-e51")
> +#define TYPE_RISCV_CPU_SIFIVE_U34       RISCV_CPU_TYPE_NAME("sifive-u34")
> +#define TYPE_RISCV_CPU_SIFIVE_U54       RISCV_CPU_TYPE_NAME("sifive-u54")
> +
> +#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2))
> +#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2))
> +
> +#if defined(TARGET_RISCV32)
> +#define RVXLEN RV32
> +#elif defined(TARGET_RISCV64)
> +#define RVXLEN RV64
> +#endif
> +
> +#define RV(x) ((target_ulong)1 << (x - 'A'))
> +
> +#define RVI RV('I')
> +#define RVM RV('M')
> +#define RVA RV('A')
> +#define RVF RV('F')
> +#define RVD RV('D')
> +#define RVC RV('C')
> +#define RVS RV('S')
> +#define RVU RV('U')
> +
> +/* S extension denotes that Supervisor mode exists, however it is possible
> +   to have a core that support S mode but does not have an MMU and there
> +   is currently no bit in misa to indicate whether an MMU exists or not
> +   so a cpu features bitfield is required */
> +enum {
> +    RISCV_FEATURE_MMU
> +};
> +
> +#define USER_VERSION_2_02_0 0x00020200
> +#define PRIV_VERSION_1_09_1 0x00010901
> +#define PRIV_VERSION_1_10_0 0x00011000
> +
> +#define TRANSLATE_FAIL 1
> +#define TRANSLATE_SUCCESS 0
> +#define NB_MMU_MODES 4
> +#define MMU_USER_IDX 3
> +
> +#define MAX_RISCV_PMPS (16)
> +
> +typedef struct CPURISCVState CPURISCVState;
> +
> +#include "pmp.h"
> +
> +struct CPURISCVState {
> +    target_ulong gpr[32];
> +    uint64_t fpr[32]; /* assume both F and D extensions */
> +    target_ulong pc;
> +    target_ulong load_res;
> +    target_ulong load_val;
> +
> +    target_ulong frm;
> +
> +    target_ulong badaddr;
> +
> +    target_ulong user_ver;
> +    target_ulong priv_ver;
> +    target_ulong misa;
> +
> +    uint32_t features;
> +
> +#ifndef CONFIG_USER_ONLY
> +    target_ulong priv;
> +    target_ulong resetvec;
> +
> +    target_ulong mhartid;
> +    target_ulong mstatus;
> +    /*
> +     * CAUTION! Unlike the rest of this struct, mip is accessed
> asynchonously
> +     * by I/O threads and other vCPUs, so hold the iothread mutex before
> +     * operating on it.  CPU_INTERRUPT_HARD should be in effect iff this
> is
> +     * non-zero.  Use riscv_cpu_set_local_interrupt.
> +     */
> +    uint32_t mip;        /* allow atomic_read for >= 32-bit hosts */
> +    target_ulong mie;
> +    target_ulong mideleg;
> +
> +    target_ulong sptbr;  /* until: priv-1.9.1 */
> +    target_ulong satp;   /* since: priv-1.10.0 */
> +    target_ulong sbadaddr;
> +    target_ulong mbadaddr;
> +    target_ulong medeleg;
> +
> +    target_ulong stvec;
> +    target_ulong sepc;
> +    target_ulong scause;
> +
> +    target_ulong mtvec;
> +    target_ulong mepc;
> +    target_ulong mcause;
> +    target_ulong mtval;  /* since: priv-1.10.0 */
> +
> +    uint32_t mucounteren;
> +    uint32_t mscounteren;
> +    target_ulong scounteren; /* since: priv-1.10.0 */
> +    target_ulong mcounteren; /* since: priv-1.10.0 */
> +
> +    target_ulong sscratch;
> +    target_ulong mscratch;
> +
> +    /* temporary htif regs */
> +    uint64_t mfromhost;
> +    uint64_t mtohost;
> +    uint64_t timecmp;
> +
> +    /* physical memory protection */
> +    pmp_table_t pmp_state;
> +#endif
> +
> +    float_status fp_status;
> +
> +    /* QEMU */
> +    CPU_COMMON
> +
> +    /* Fields from here on are preserved across CPU reset. */
> +    QEMUTimer *timer; /* Internal timer */
> +};
> +
> +#define RISCV_CPU_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(RISCVCPUClass, (klass), TYPE_RISCV_CPU)
> +#define RISCV_CPU(obj) \
> +    OBJECT_CHECK(RISCVCPU, (obj), TYPE_RISCV_CPU)
> +#define RISCV_CPU_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(RISCVCPUClass, (obj), TYPE_RISCV_CPU)
> +
> +/**
> + * RISCVCPUClass:
> + * @parent_realize: The parent class' realize handler.
> + * @parent_reset: The parent class' reset handler.
> + *
> + * A RISCV CPU model.
> + */
> +typedef struct RISCVCPUClass {
> +    /*< private >*/
> +    CPUClass parent_class;
> +    /*< public >*/
> +    DeviceRealize parent_realize;
> +    void (*parent_reset)(CPUState *cpu);
> +} RISCVCPUClass;
> +
> +/**
> + * RISCVCPU:
> + * @env: #CPURISCVState
> + *
> + * A RISCV CPU.
> + */
> +typedef struct RISCVCPU {
> +    /*< private >*/
> +    CPUState parent_obj;
> +    /*< public >*/
> +    CPURISCVState env;
> +} RISCVCPU;
> +
> +static inline RISCVCPU *riscv_env_get_cpu(CPURISCVState *env)
> +{
> +    return container_of(env, RISCVCPU, env);
> +}
> +
> +static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext)
> +{
> +    return (env->misa & ext) != 0;
> +}
> +
> +static inline bool riscv_feature(CPURISCVState *env, int feature)
> +{
> +    return env->features & (1ULL << feature);
> +}
> +
> +#include "cpu_user.h"
> +#include "cpu_bits.h"
> +
> +extern const char * const riscv_int_regnames[];
> +extern const char * const riscv_fpr_regnames[];
> +extern const char * const riscv_excp_names[];
> +extern const char * const riscv_intr_names[];
> +
> +#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
> +#define ENV_OFFSET offsetof(RISCVCPU, env)
> +
> +void riscv_cpu_do_interrupt(CPUState *cpu);
> +int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
> +int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
> +bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
> +int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
> +hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
> +void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
> +                                    MMUAccessType access_type, int
> mmu_idx,
> +                                    uintptr_t retaddr);
> +int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
> +                              int rw, int mmu_idx);
> +
> +char *riscv_isa_string(RISCVCPU *cpu);
> +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
> +
> +#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model)
> +#define cpu_signal_handler cpu_riscv_signal_handler
> +#define cpu_list riscv_cpu_list
> +#define cpu_mmu_index riscv_cpu_mmu_index
> +
> +void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
> +
> +void riscv_translate_init(void);
> +RISCVCPU *cpu_riscv_init(const char *cpu_model);
> +int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc);
> +void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
> +                                          uint32_t exception, uintptr_t
> pc);
> +
> +target_ulong cpu_riscv_get_fflags(CPURISCVState *env);
> +void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong);
> +
> +#define TB_FLAGS_MMU_MASK  3
> +#define TB_FLAGS_FP_ENABLE MSTATUS_FS
> +
> +static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong
> *pc,
> +                                        target_ulong *cs_base, uint32_t
> *flags)
> +{
> +    *pc = env->pc;
> +    *cs_base = 0;
> +#ifdef CONFIG_USER_ONLY
> +    *flags = TB_FLAGS_FP_ENABLE;
> +#else
> +    *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
> +#endif
> +}
> +
> +void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
> +        target_ulong csrno);
> +target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
> +
> +#ifndef CONFIG_USER_ONLY
> +void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int
> value);
> +#endif
> +
> +#include "exec/cpu-all.h"
> +
> +#endif /* RISCV_CPU_H */
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> new file mode 100644
> index 0000000..64aa097
> --- /dev/null
> +++ b/target/riscv/cpu_bits.h
> @@ -0,0 +1,411 @@
> +/* RISC-V ISA constants */
> +
> +#define get_field(reg, mask) (((reg) & \
> +                 (target_ulong)(mask)) / ((mask) & ~((mask) << 1)))
> +#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \
> +                 (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \
> +                 (target_ulong)(mask)))
> +
> +#define PGSHIFT 12
> +
> +#define FSR_RD_SHIFT 5
> +#define FSR_RD   (0x7 << FSR_RD_SHIFT)
> +
> +#define FPEXC_NX 0x01
> +#define FPEXC_UF 0x02
> +#define FPEXC_OF 0x04
> +#define FPEXC_DZ 0x08
> +#define FPEXC_NV 0x10
> +
> +#define FSR_AEXC_SHIFT 0
> +#define FSR_NVA  (FPEXC_NV << FSR_AEXC_SHIFT)
> +#define FSR_OFA  (FPEXC_OF << FSR_AEXC_SHIFT)
> +#define FSR_UFA  (FPEXC_UF << FSR_AEXC_SHIFT)
> +#define FSR_DZA  (FPEXC_DZ << FSR_AEXC_SHIFT)
> +#define FSR_NXA  (FPEXC_NX << FSR_AEXC_SHIFT)
> +#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
> +
> +/* CSR numbers */
> +#define CSR_FFLAGS 0x1
> +#define CSR_FRM 0x2
> +#define CSR_FCSR 0x3
> +#define CSR_CYCLE 0xc00
> +#define CSR_TIME 0xc01
> +#define CSR_INSTRET 0xc02
> +#define CSR_HPMCOUNTER3 0xc03
> +#define CSR_HPMCOUNTER4 0xc04
> +#define CSR_HPMCOUNTER5 0xc05
> +#define CSR_HPMCOUNTER6 0xc06
> +#define CSR_HPMCOUNTER7 0xc07
> +#define CSR_HPMCOUNTER8 0xc08
> +#define CSR_HPMCOUNTER9 0xc09
> +#define CSR_HPMCOUNTER10 0xc0a
> +#define CSR_HPMCOUNTER11 0xc0b
> +#define CSR_HPMCOUNTER12 0xc0c
> +#define CSR_HPMCOUNTER13 0xc0d
> +#define CSR_HPMCOUNTER14 0xc0e
> +#define CSR_HPMCOUNTER15 0xc0f
> +#define CSR_HPMCOUNTER16 0xc10
> +#define CSR_HPMCOUNTER17 0xc11
> +#define CSR_HPMCOUNTER18 0xc12
> +#define CSR_HPMCOUNTER19 0xc13
> +#define CSR_HPMCOUNTER20 0xc14
> +#define CSR_HPMCOUNTER21 0xc15
> +#define CSR_HPMCOUNTER22 0xc16
> +#define CSR_HPMCOUNTER23 0xc17
> +#define CSR_HPMCOUNTER24 0xc18
> +#define CSR_HPMCOUNTER25 0xc19
> +#define CSR_HPMCOUNTER26 0xc1a
> +#define CSR_HPMCOUNTER27 0xc1b
> +#define CSR_HPMCOUNTER28 0xc1c
> +#define CSR_HPMCOUNTER29 0xc1d
> +#define CSR_HPMCOUNTER30 0xc1e
> +#define CSR_HPMCOUNTER31 0xc1f
> +#define CSR_SSTATUS 0x100
> +#define CSR_SIE 0x104
> +#define CSR_STVEC 0x105
> +#define CSR_SCOUNTEREN 0x106
> +#define CSR_SSCRATCH 0x140
> +#define CSR_SEPC 0x141
> +#define CSR_SCAUSE 0x142
> +#define CSR_SBADADDR 0x143
> +#define CSR_SIP 0x144
> +#define CSR_SPTBR 0x180
> +#define CSR_SATP 0x180
> +#define CSR_MSTATUS 0x300
> +#define CSR_MISA 0x301
> +#define CSR_MEDELEG 0x302
> +#define CSR_MIDELEG 0x303
> +#define CSR_MIE 0x304
> +#define CSR_MTVEC 0x305
> +#define CSR_MCOUNTEREN 0x306
> +#define CSR_MSCRATCH 0x340
> +#define CSR_MEPC 0x341
> +#define CSR_MCAUSE 0x342
> +#define CSR_MBADADDR 0x343
> +#define CSR_MIP 0x344
> +#define CSR_PMPCFG0 0x3a0
> +#define CSR_PMPCFG1 0x3a1
> +#define CSR_PMPCFG2 0x3a2
> +#define CSR_PMPCFG3 0x3a3
> +#define CSR_PMPADDR0 0x3b0
> +#define CSR_PMPADDR1 0x3b1
> +#define CSR_PMPADDR2 0x3b2
> +#define CSR_PMPADDR3 0x3b3
> +#define CSR_PMPADDR4 0x3b4
> +#define CSR_PMPADDR5 0x3b5
> +#define CSR_PMPADDR6 0x3b6
> +#define CSR_PMPADDR7 0x3b7
> +#define CSR_PMPADDR8 0x3b8
> +#define CSR_PMPADDR9 0x3b9
> +#define CSR_PMPADDR10 0x3ba
> +#define CSR_PMPADDR11 0x3bb
> +#define CSR_PMPADDR12 0x3bc
> +#define CSR_PMPADDR13 0x3bd
> +#define CSR_PMPADDR14 0x3be
> +#define CSR_PMPADDR15 0x3bf
> +#define CSR_TSELECT 0x7a0
> +#define CSR_TDATA1 0x7a1
> +#define CSR_TDATA2 0x7a2
> +#define CSR_TDATA3 0x7a3
> +#define CSR_DCSR 0x7b0
> +#define CSR_DPC 0x7b1
> +#define CSR_DSCRATCH 0x7b2
> +#define CSR_MCYCLE 0xb00
> +#define CSR_MINSTRET 0xb02
> +#define CSR_MHPMCOUNTER3 0xb03
> +#define CSR_MHPMCOUNTER4 0xb04
> +#define CSR_MHPMCOUNTER5 0xb05
> +#define CSR_MHPMCOUNTER6 0xb06
> +#define CSR_MHPMCOUNTER7 0xb07
> +#define CSR_MHPMCOUNTER8 0xb08
> +#define CSR_MHPMCOUNTER9 0xb09
> +#define CSR_MHPMCOUNTER10 0xb0a
> +#define CSR_MHPMCOUNTER11 0xb0b
> +#define CSR_MHPMCOUNTER12 0xb0c
> +#define CSR_MHPMCOUNTER13 0xb0d
> +#define CSR_MHPMCOUNTER14 0xb0e
> +#define CSR_MHPMCOUNTER15 0xb0f
> +#define CSR_MHPMCOUNTER16 0xb10
> +#define CSR_MHPMCOUNTER17 0xb11
> +#define CSR_MHPMCOUNTER18 0xb12
> +#define CSR_MHPMCOUNTER19 0xb13
> +#define CSR_MHPMCOUNTER20 0xb14
> +#define CSR_MHPMCOUNTER21 0xb15
> +#define CSR_MHPMCOUNTER22 0xb16
> +#define CSR_MHPMCOUNTER23 0xb17
> +#define CSR_MHPMCOUNTER24 0xb18
> +#define CSR_MHPMCOUNTER25 0xb19
> +#define CSR_MHPMCOUNTER26 0xb1a
> +#define CSR_MHPMCOUNTER27 0xb1b
> +#define CSR_MHPMCOUNTER28 0xb1c
> +#define CSR_MHPMCOUNTER29 0xb1d
> +#define CSR_MHPMCOUNTER30 0xb1e
> +#define CSR_MHPMCOUNTER31 0xb1f
> +#define CSR_MUCOUNTEREN 0x320
> +#define CSR_MSCOUNTEREN 0x321
> +#define CSR_MHPMEVENT3 0x323
> +#define CSR_MHPMEVENT4 0x324
> +#define CSR_MHPMEVENT5 0x325
> +#define CSR_MHPMEVENT6 0x326
> +#define CSR_MHPMEVENT7 0x327
> +#define CSR_MHPMEVENT8 0x328
> +#define CSR_MHPMEVENT9 0x329
> +#define CSR_MHPMEVENT10 0x32a
> +#define CSR_MHPMEVENT11 0x32b
> +#define CSR_MHPMEVENT12 0x32c
> +#define CSR_MHPMEVENT13 0x32d
> +#define CSR_MHPMEVENT14 0x32e
> +#define CSR_MHPMEVENT15 0x32f
> +#define CSR_MHPMEVENT16 0x330
> +#define CSR_MHPMEVENT17 0x331
> +#define CSR_MHPMEVENT18 0x332
> +#define CSR_MHPMEVENT19 0x333
> +#define CSR_MHPMEVENT20 0x334
> +#define CSR_MHPMEVENT21 0x335
> +#define CSR_MHPMEVENT22 0x336
> +#define CSR_MHPMEVENT23 0x337
> +#define CSR_MHPMEVENT24 0x338
> +#define CSR_MHPMEVENT25 0x339
> +#define CSR_MHPMEVENT26 0x33a
> +#define CSR_MHPMEVENT27 0x33b
> +#define CSR_MHPMEVENT28 0x33c
> +#define CSR_MHPMEVENT29 0x33d
> +#define CSR_MHPMEVENT30 0x33e
> +#define CSR_MHPMEVENT31 0x33f
> +#define CSR_MVENDORID 0xf11
> +#define CSR_MARCHID 0xf12
> +#define CSR_MIMPID 0xf13
> +#define CSR_MHARTID 0xf14
> +#define CSR_CYCLEH 0xc80
> +#define CSR_TIMEH 0xc81
> +#define CSR_INSTRETH 0xc82
> +#define CSR_HPMCOUNTER3H 0xc83
> +#define CSR_HPMCOUNTER4H 0xc84
> +#define CSR_HPMCOUNTER5H 0xc85
> +#define CSR_HPMCOUNTER6H 0xc86
> +#define CSR_HPMCOUNTER7H 0xc87
> +#define CSR_HPMCOUNTER8H 0xc88
> +#define CSR_HPMCOUNTER9H 0xc89
> +#define CSR_HPMCOUNTER10H 0xc8a
> +#define CSR_HPMCOUNTER11H 0xc8b
> +#define CSR_HPMCOUNTER12H 0xc8c
> +#define CSR_HPMCOUNTER13H 0xc8d
> +#define CSR_HPMCOUNTER14H 0xc8e
> +#define CSR_HPMCOUNTER15H 0xc8f
> +#define CSR_HPMCOUNTER16H 0xc90
> +#define CSR_HPMCOUNTER17H 0xc91
> +#define CSR_HPMCOUNTER18H 0xc92
> +#define CSR_HPMCOUNTER19H 0xc93
> +#define CSR_HPMCOUNTER20H 0xc94
> +#define CSR_HPMCOUNTER21H 0xc95
> +#define CSR_HPMCOUNTER22H 0xc96
> +#define CSR_HPMCOUNTER23H 0xc97
> +#define CSR_HPMCOUNTER24H 0xc98
> +#define CSR_HPMCOUNTER25H 0xc99
> +#define CSR_HPMCOUNTER26H 0xc9a
> +#define CSR_HPMCOUNTER27H 0xc9b
> +#define CSR_HPMCOUNTER28H 0xc9c
> +#define CSR_HPMCOUNTER29H 0xc9d
> +#define CSR_HPMCOUNTER30H 0xc9e
> +#define CSR_HPMCOUNTER31H 0xc9f
> +#define CSR_MCYCLEH 0xb80
> +#define CSR_MINSTRETH 0xb82
> +#define CSR_MHPMCOUNTER3H 0xb83
> +#define CSR_MHPMCOUNTER4H 0xb84
> +#define CSR_MHPMCOUNTER5H 0xb85
> +#define CSR_MHPMCOUNTER6H 0xb86
> +#define CSR_MHPMCOUNTER7H 0xb87
> +#define CSR_MHPMCOUNTER8H 0xb88
> +#define CSR_MHPMCOUNTER9H 0xb89
> +#define CSR_MHPMCOUNTER10H 0xb8a
> +#define CSR_MHPMCOUNTER11H 0xb8b
> +#define CSR_MHPMCOUNTER12H 0xb8c
> +#define CSR_MHPMCOUNTER13H 0xb8d
> +#define CSR_MHPMCOUNTER14H 0xb8e
> +#define CSR_MHPMCOUNTER15H 0xb8f
> +#define CSR_MHPMCOUNTER16H 0xb90
> +#define CSR_MHPMCOUNTER17H 0xb91
> +#define CSR_MHPMCOUNTER18H 0xb92
> +#define CSR_MHPMCOUNTER19H 0xb93
> +#define CSR_MHPMCOUNTER20H 0xb94
> +#define CSR_MHPMCOUNTER21H 0xb95
> +#define CSR_MHPMCOUNTER22H 0xb96
> +#define CSR_MHPMCOUNTER23H 0xb97
> +#define CSR_MHPMCOUNTER24H 0xb98
> +#define CSR_MHPMCOUNTER25H 0xb99
> +#define CSR_MHPMCOUNTER26H 0xb9a
> +#define CSR_MHPMCOUNTER27H 0xb9b
> +#define CSR_MHPMCOUNTER28H 0xb9c
> +#define CSR_MHPMCOUNTER29H 0xb9d
> +#define CSR_MHPMCOUNTER30H 0xb9e
> +#define CSR_MHPMCOUNTER31H 0xb9f
> +
> +/* mstatus bits */
> +#define MSTATUS_UIE         0x00000001
> +#define MSTATUS_SIE         0x00000002
> +#define MSTATUS_HIE         0x00000004
> +#define MSTATUS_MIE         0x00000008
> +#define MSTATUS_UPIE        0x00000010
> +#define MSTATUS_SPIE        0x00000020
> +#define MSTATUS_HPIE        0x00000040
> +#define MSTATUS_MPIE        0x00000080
> +#define MSTATUS_SPP         0x00000100
> +#define MSTATUS_HPP         0x00000600
> +#define MSTATUS_MPP         0x00001800
> +#define MSTATUS_FS          0x00006000
> +#define MSTATUS_XS          0x00018000
> +#define MSTATUS_MPRV        0x00020000
> +#define MSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
> +#define MSTATUS_SUM         0x00040000 /* since: priv-1.10 */
> +#define MSTATUS_MXR         0x00080000
> +#define MSTATUS_VM          0x1F000000 /* until: priv-1.9.1 */
> +#define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
> +#define MSTATUS_TW          0x20000000 /* since: priv-1.10 */
> +#define MSTATUS_TSR         0x40000000 /* since: priv-1.10 */
> +
> +#define MSTATUS64_UXL       0x0000000300000000ULL
> +#define MSTATUS64_SXL       0x0000000C00000000ULL
> +
> +#define MSTATUS32_SD        0x80000000
> +#define MSTATUS64_SD        0x8000000000000000ULL
> +
> +#if defined(TARGET_RISCV32)
> +#define MSTATUS_SD MSTATUS32_SD
> +#elif defined(TARGET_RISCV64)
> +#define MSTATUS_SD MSTATUS64_SD
> +#endif
> +
> +/* sstatus bits */
> +#define SSTATUS_UIE         0x00000001
> +#define SSTATUS_SIE         0x00000002
> +#define SSTATUS_UPIE        0x00000010
> +#define SSTATUS_SPIE        0x00000020
> +#define SSTATUS_SPP         0x00000100
> +#define SSTATUS_FS          0x00006000
> +#define SSTATUS_XS          0x00018000
> +#define SSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
> +#define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
> +#define SSTATUS_MXR         0x00080000
> +
> +#define SSTATUS32_SD        0x80000000
> +#define SSTATUS64_SD        0x8000000000000000ULL
> +
> +#if defined(TARGET_RISCV32)
> +#define SSTATUS_SD SSTATUS32_SD
> +#elif defined(TARGET_RISCV64)
> +#define SSTATUS_SD SSTATUS64_SD
> +#endif
> +
> +/* irqs */
> +#define MIP_SSIP            (1 << IRQ_S_SOFT)
> +#define MIP_HSIP            (1 << IRQ_H_SOFT)
> +#define MIP_MSIP            (1 << IRQ_M_SOFT)
> +#define MIP_STIP            (1 << IRQ_S_TIMER)
> +#define MIP_HTIP            (1 << IRQ_H_TIMER)
> +#define MIP_MTIP            (1 << IRQ_M_TIMER)
> +#define MIP_SEIP            (1 << IRQ_S_EXT)
> +#define MIP_HEIP            (1 << IRQ_H_EXT)
> +#define MIP_MEIP            (1 << IRQ_M_EXT)
> +
> +#define SIP_SSIP            MIP_SSIP
> +#define SIP_STIP            MIP_STIP
> +#define SIP_SEIP            MIP_SEIP
> +
> +#define PRV_U 0
> +#define PRV_S 1
> +#define PRV_H 2
> +#define PRV_M 3
> +
> +/* privileged ISA 1.9.1 VM modes (mstatus.vm) */
> +#define VM_1_09_MBARE 0
> +#define VM_1_09_MBB   1
> +#define VM_1_09_MBBID 2
> +#define VM_1_09_SV32  8
> +#define VM_1_09_SV39  9
> +#define VM_1_09_SV48  10
> +
> +/* privileged ISA 1.10.0 VM modes (satp.mode) */
> +#define VM_1_10_MBARE 0
> +#define VM_1_10_SV32  1
> +#define VM_1_10_SV39  8
> +#define VM_1_10_SV48  9
> +#define VM_1_10_SV57  10
> +#define VM_1_10_SV64  11
> +
> +/* privileged ISA interrupt causes */
> +#define IRQ_U_SOFT      0  /* since: priv-1.10 */
> +#define IRQ_S_SOFT      1
> +#define IRQ_H_SOFT      2  /* until: priv-1.9.1 */
> +#define IRQ_M_SOFT      3  /* until: priv-1.9.1 */
> +#define IRQ_U_TIMER     4  /* since: priv-1.10 */
> +#define IRQ_S_TIMER     5
> +#define IRQ_H_TIMER     6  /* until: priv-1.9.1 */
> +#define IRQ_M_TIMER     7  /* until: priv-1.9.1 */
> +#define IRQ_U_EXT       8  /* since: priv-1.10 */
> +#define IRQ_S_EXT       9
> +#define IRQ_H_EXT       10 /* until: priv-1.9.1 */
> +#define IRQ_M_EXT       11 /* until: priv-1.9.1 */
> +#define IRQ_X_COP       12 /* non-standard */
> +
> +/* Default addresses */
> +#define DEFAULT_RSTVEC     0x00001000
> +
> +/* RV32 satp field masks */
> +#define SATP32_MODE 0x80000000
> +#define SATP32_ASID 0x7fc00000
> +#define SATP32_PPN  0x003fffff
> +
> +/* RV64 satp field masks */
> +#define SATP64_MODE 0xF000000000000000ULL
> +#define SATP64_ASID 0x0FFFF00000000000ULL
> +#define SATP64_PPN  0x00000FFFFFFFFFFFULL
> +
> +#if defined(TARGET_RISCV32)
> +#define SATP_MODE SATP32_MODE
> +#define SATP_ASID SATP32_ASID
> +#define SATP_PPN  SATP32_PPN
> +#endif
> +#if defined(TARGET_RISCV64)
> +#define SATP_MODE SATP64_MODE
> +#define SATP_ASID SATP64_ASID
> +#define SATP_PPN  SATP64_PPN
> +#endif
> +
> +/* RISCV Exception Codes */
> +#define EXCP_NONE                       -1 /* not a real RISCV exception
> code */
> +#define RISCV_EXCP_INST_ADDR_MIS           0x0
> +#define RISCV_EXCP_INST_ACCESS_FAULT       0x1
> +#define RISCV_EXCP_ILLEGAL_INST            0x2
> +#define RISCV_EXCP_BREAKPOINT              0x3
> +#define RISCV_EXCP_LOAD_ADDR_MIS           0x4
> +#define RISCV_EXCP_LOAD_ACCESS_FAULT       0x5
> +#define RISCV_EXCP_STORE_AMO_ADDR_MIS      0x6
> +#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT  0x7
> +#define RISCV_EXCP_U_ECALL                 0x8 /* for convenience, report
> all
> +                                                  ECALLs as this, handler
> +                                                  fixes */
> +#define RISCV_EXCP_S_ECALL                 0x9
> +#define RISCV_EXCP_H_ECALL                 0xa
> +#define RISCV_EXCP_M_ECALL                 0xb
> +#define RISCV_EXCP_INST_PAGE_FAULT         0xc /* since: priv-1.10.0 */
> +#define RISCV_EXCP_LOAD_PAGE_FAULT         0xd /* since: priv-1.10.0 */
> +#define RISCV_EXCP_STORE_PAGE_FAULT        0xf /* since: priv-1.10.0 */
> +
> +#define RISCV_EXCP_INT_FLAG                0x80000000
> +#define RISCV_EXCP_INT_MASK                0x7fffffff
> +
> +/* page table entry (PTE) fields */
> +#define PTE_V     0x001 /* Valid */
> +#define PTE_R     0x002 /* Read */
> +#define PTE_W     0x004 /* Write */
> +#define PTE_X     0x008 /* Execute */
> +#define PTE_U     0x010 /* User */
> +#define PTE_G     0x020 /* Global */
> +#define PTE_A     0x040 /* Accessed */
> +#define PTE_D     0x080 /* Dirty */
> +#define PTE_SOFT  0x300 /* Reserved for Software */
> +
> +#define PTE_PPN_SHIFT 10
> +
> +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) ==
> PTE_V)
> --
> 2.7.0
>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-03  2:23   ` Michael Clark
@ 2018-03-03  2:34     ` Michael Clark
  0 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-03  2:34 UTC (permalink / raw)
  To: QEMU Developers, Richard Henderson, Palmer Dabbelt, Andrew Waterman
  Cc: Michael Clark, Sagar Karandikar, Bastian Koppelmann, RISC-V Patches

Paraphrase this as, we should be closer to reproducing the behaviour of the
SiFive E31, E51, U34 and U54 cores when running RISC-V and SiFive
verification tests. i.e. now if one attempts to configure the MMU on E
cores one will get an illegal instruction trap.

We still have an E21 core to add but I need some more details on
behavioural differences between the SiFive E21 and E31. We're happy to keep
this in the riscv branch for after the 2.12 release. We don't have any more
features to add pre QEMU 2.12, and if we do we will maintain them on a
branch in the https://github.com/riscv/riscv-qemu.git repository.

So now I think we're mostly at the point where we will be in a rebase loop,
assuming we make it in to qemu before 2.12.

If we make a v9 it will contain extremely minor changes. e.g. replacing
hard-coded values with constants and fixing typos.

- https://github.com/michaeljclark/riscv-qemu/commits/qemu-devel

I believe we have all of the necessary licensing changes and sign-offs done.

On Sat, Mar 3, 2018 at 3:23 PM, Michael Clark <mjc@sifive.com> wrote:

> We were able to remove several ifdefs and figured out a problem with
> masking out cores for qemu-system-riscv32 and qemu-system-riscv64.
>
> This version of the core patch seems cleaner to me and we have fixed a few
> spec compliance issues with regard to alignment of mtvec/stvec when the C
> extension is enabled or disabled.
>
> We haven't addressed runtime changes to 'misa' as it is legal for 'misa'
> to be read-only. A later "feature" patch will add runtime support for misa
> changes, likely just invalidating the translation cache due to the limited
> number of bits in mmu_index. I can say for sure we not going to do anything
> this risky so close to soft-freeze. The last minute changes were focused at
> specification compliance for MMU vs NOMMU and respecting the 'S' and 'U'
> misa bits with respect to allowable privilege modes. SiFive's E Series
> cores do not support S mode but they do support U mode.
>
> We also had an internal discussion about S-mode vs mmu, and it was decided
> that it is a legal combination to have a nommu core that implements S mode.
> In this use case, M mode could configure PMP (Physical Memory Protection),
> and it would potentially possible to port FDPIC nommu linux to work on a
> core with S mode and nommu. The other S mode registers besides 'satp'
> (Supervisor Address Translation Pointer) are available in S-mode with nommu.
>
> The v8 patch series just tightens up compliance with the specification,
> and makes use of the mmu register (satp), selecting S-mode or calling SRET
> cause illegal instruction traps.
>
> Writes to mstatus.mpp of un unsupported modes (i.e. 'S' or 'U' misa bits
> not present) are silently dropped, as that is the behaviour I am told
> should be implemented. We may have to raise some issues against the RISC-V
> Privileged ISA specification to make sure that this behaviour is specified,
> as it currently does not specify explicitly the behaviour for a core that
> supports S-mode with no-mmu.
>
> Regards,
> Michael.
>
> On Sat, Mar 3, 2018 at 2:51 AM, Michael Clark <mjc@sifive.com> wrote:
>
>> Add CPU state header, CPU definitions and initialization routines
>>
>> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
>> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
>> Signed-off-by: Michael Clark <mjc@sifive.com>
>> ---
>>  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++
>> ++++++++++++++++++
>>  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
>>  target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
>> +++++++++++++++
>>  3 files changed, 1139 insertions(+)
>>  create mode 100644 target/riscv/cpu.c
>>  create mode 100644 target/riscv/cpu.h
>>  create mode 100644 target/riscv/cpu_bits.h
>>
>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>> new file mode 100644
>> index 0000000..4851890
>> --- /dev/null
>> +++ b/target/riscv/cpu.c
>> @@ -0,0 +1,432 @@
>> +/*
>> + * QEMU RISC-V CPU
>> + *
>> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
>> + * Copyright (c) 2017-2018 SiFive, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2 or later, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/log.h"
>> +#include "cpu.h"
>> +#include "exec/exec-all.h"
>> +#include "qapi/error.h"
>> +#include "migration/vmstate.h"
>> +
>> +/* RISC-V CPU definitions */
>> +
>> +static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG";
>> +
>> +const char * const riscv_int_regnames[] = {
>> +  "zero", "ra  ", "sp  ", "gp  ", "tp  ", "t0  ", "t1  ", "t2  ",
>> +  "s0  ", "s1  ", "a0  ", "a1  ", "a2  ", "a3  ", "a4  ", "a5  ",
>> +  "a6  ", "a7  ", "s2  ", "s3  ", "s4  ", "s5  ", "s6  ", "s7  ",
>> +  "s8  ", "s9  ", "s10 ", "s11 ", "t3  ", "t4  ", "t5  ", "t6  "
>> +};
>> +
>> +const char * const riscv_fpr_regnames[] = {
>> +  "ft0 ", "ft1 ", "ft2 ", "ft3 ", "ft4 ", "ft5 ", "ft6 ",  "ft7 ",
>> +  "fs0 ", "fs1 ", "fa0 ", "fa1 ", "fa2 ", "fa3 ", "fa4 ",  "fa5 ",
>> +  "fa6 ", "fa7 ", "fs2 ", "fs3 ", "fs4 ", "fs5 ", "fs6 ",  "fs7 ",
>> +  "fs8 ", "fs9 ", "fs10", "fs11", "ft8 ", "ft9 ", "ft10",  "ft11"
>> +};
>> +
>> +const char * const riscv_excp_names[] = {
>> +    "misaligned_fetch",
>> +    "fault_fetch",
>> +    "illegal_instruction",
>> +    "breakpoint",
>> +    "misaligned_load",
>> +    "fault_load",
>> +    "misaligned_store",
>> +    "fault_store",
>> +    "user_ecall",
>> +    "supervisor_ecall",
>> +    "hypervisor_ecall",
>> +    "machine_ecall",
>> +    "exec_page_fault",
>> +    "load_page_fault",
>> +    "reserved",
>> +    "store_page_fault"
>> +};
>> +
>> +const char * const riscv_intr_names[] = {
>> +    "u_software",
>> +    "s_software",
>> +    "h_software",
>> +    "m_software",
>> +    "u_timer",
>> +    "s_timer",
>> +    "h_timer",
>> +    "m_timer",
>> +    "u_external",
>> +    "s_external",
>> +    "h_external",
>> +    "m_external",
>> +    "coprocessor",
>> +    "host"
>> +};
>> +
>> +typedef struct RISCVCPUInfo {
>> +    const int bit_widths;
>> +    const char *name;
>> +    void (*initfn)(Object *obj);
>> +} RISCVCPUInfo;
>> +
>> +static void set_misa(CPURISCVState *env, target_ulong misa)
>> +{
>> +    env->misa = misa;
>> +}
>> +
>> +static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
>> +{
>> +    env->user_ver = user_ver;
>> +    env->priv_ver = priv_ver;
>> +}
>> +
>> +static void set_feature(CPURISCVState *env, int feature)
>> +{
>> +    env->features |= (1ULL << feature);
>> +}
>> +
>> +static void set_resetvec(CPURISCVState *env, int resetvec)
>> +{
>> +#ifndef CONFIG_USER_ONLY
>> +    env->resetvec = resetvec;
>> +#endif
>> +}
>> +
>> +static void riscv_any_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +}
>> +
>> +static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +    set_feature(env, RISCV_FEATURE_MMU);
>> +}
>> +
>> +static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +    set_feature(env, RISCV_FEATURE_MMU);
>> +}
>> +
>> +static void rv32imacu_nommu_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +}
>> +
>> +static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +    set_feature(env, RISCV_FEATURE_MMU);
>> +}
>> +
>> +static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +    set_feature(env, RISCV_FEATURE_MMU);
>> +}
>> +
>> +static void rv64imacu_nommu_cpu_init(Object *obj)
>> +{
>> +    CPURISCVState *env = &RISCV_CPU(obj)->env;
>> +    set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
>> +    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>> +    set_resetvec(env, DEFAULT_RSTVEC);
>> +}
>> +
>> +static const RISCVCPUInfo riscv_cpus[] = {
>> +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
>> +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init
>> },
>> +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init
>> },
>> +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
>> +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
>> +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init
>> },
>> +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init
>> },
>> +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init
>> },
>> +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
>> +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
>> +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init
>> },
>> +    { 0, NULL, NULL }
>> +};
>> +
>> +static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
>> +{
>> +    ObjectClass *oc;
>> +    char *typename;
>> +    char **cpuname;
>> +
>> +    cpuname = g_strsplit(cpu_model, ",", 1);
>> +    typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]);
>> +    oc = object_class_by_name(typename);
>> +    g_strfreev(cpuname);
>> +    g_free(typename);
>> +    if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) ||
>> +        object_class_is_abstract(oc)) {
>> +        return NULL;
>> +    }
>> +    return oc;
>> +}
>> +
>> +static void riscv_cpu_dump_state(CPUState *cs, FILE *f,
>> +    fprintf_function cpu_fprintf, int flags)
>> +{
>> +    RISCVCPU *cpu = RISCV_CPU(cs);
>> +    CPURISCVState *env = &cpu->env;
>> +    int i;
>> +
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc      ", env->pc);
>> +#ifndef CONFIG_USER_ONLY
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip     ",
>> +        (target_ulong)atomic_read(&env->mip));
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie     ", env->mie);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec   ", env->mtvec);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc    ", env->mepc);
>> +    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause  ", env->mcause);
>> +#endif
>> +
>> +    for (i = 0; i < 32; i++) {
>> +        cpu_fprintf(f, " %s " TARGET_FMT_lx,
>> +            riscv_int_regnames[i], env->gpr[i]);
>> +        if ((i & 3) == 3) {
>> +            cpu_fprintf(f, "\n");
>> +        }
>> +    }
>> +    for (i = 0; i < 32; i++) {
>> +        cpu_fprintf(f, " %s %016" PRIx64,
>> +            riscv_fpr_regnames[i], env->fpr[i]);
>> +        if ((i & 3) == 3) {
>> +            cpu_fprintf(f, "\n");
>> +        }
>> +    }
>> +}
>> +
>> +static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
>> +{
>> +    RISCVCPU *cpu = RISCV_CPU(cs);
>> +    CPURISCVState *env = &cpu->env;
>> +    env->pc = value;
>> +}
>> +
>> +static void riscv_cpu_synchronize_from_tb(CPUState *cs,
>> TranslationBlock *tb)
>> +{
>> +    RISCVCPU *cpu = RISCV_CPU(cs);
>> +    CPURISCVState *env = &cpu->env;
>> +    env->pc = tb->pc;
>> +}
>> +
>> +static bool riscv_cpu_has_work(CPUState *cs)
>> +{
>> +#ifndef CONFIG_USER_ONLY
>> +    RISCVCPU *cpu = RISCV_CPU(cs);
>> +    CPURISCVState *env = &cpu->env;
>> +    /*
>> +     * Definition of the WFI instruction requires it to ignore the
>> privilege
>> +     * mode and delegation registers, but respect individual enables
>> +     */
>> +    return (atomic_read(&env->mip) & env->mie) != 0;
>> +#else
>> +    return true;
>> +#endif
>> +}
>> +
>> +void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
>> +                          target_ulong *data)
>> +{
>> +    env->pc = data[0];
>> +}
>> +
>> +static void riscv_cpu_reset(CPUState *cs)
>> +{
>> +    RISCVCPU *cpu = RISCV_CPU(cs);
>> +    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
>> +    CPURISCVState *env = &cpu->env;
>> +
>> +    mcc->parent_reset(cs);
>> +#ifndef CONFIG_USER_ONLY
>> +    env->priv = PRV_M;
>> +    env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
>> +    env->mcause = 0;
>> +    env->pc = env->resetvec;
>> +#endif
>> +    cs->exception_index = EXCP_NONE;
>> +    set_default_nan_mode(1, &env->fp_status);
>> +}
>> +
>> +static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info
>> *info)
>> +{
>> +#if defined(TARGET_RISCV32)
>> +    info->print_insn = print_insn_riscv32;
>> +#elif defined(TARGET_RISCV64)
>> +    info->print_insn = print_insn_riscv64;
>> +#endif
>> +}
>> +
>> +static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>> +{
>> +    CPUState *cs = CPU(dev);
>> +    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
>> +    Error *local_err = NULL;
>> +
>> +    cpu_exec_realizefn(cs, &local_err);
>> +    if (local_err != NULL) {
>> +        error_propagate(errp, local_err);
>> +        return;
>> +    }
>> +
>> +    qemu_init_vcpu(cs);
>> +    cpu_reset(cs);
>> +
>> +    mcc->parent_realize(dev, errp);
>> +}
>> +
>> +static void riscv_cpu_init(Object *obj)
>> +{
>> +    CPUState *cs = CPU(obj);
>> +    RISCVCPU *cpu = RISCV_CPU(obj);
>> +
>> +    cs->env_ptr = &cpu->env;
>> +}
>> +
>> +static const VMStateDescription vmstate_riscv_cpu = {
>> +    .name = "cpu",
>> +    .unmigratable = 1,
>> +};
>> +
>> +static void riscv_cpu_class_init(ObjectClass *c, void *data)
>> +{
>> +    RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
>> +    CPUClass *cc = CPU_CLASS(c);
>> +    DeviceClass *dc = DEVICE_CLASS(c);
>> +
>> +    mcc->parent_realize = dc->realize;
>> +    dc->realize = riscv_cpu_realize;
>> +
>> +    mcc->parent_reset = cc->reset;
>> +    cc->reset = riscv_cpu_reset;
>> +
>> +    cc->class_by_name = riscv_cpu_class_by_name;
>> +    cc->has_work = riscv_cpu_has_work;
>> +    cc->do_interrupt = riscv_cpu_do_interrupt;
>> +    cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
>> +    cc->dump_state = riscv_cpu_dump_state;
>> +    cc->set_pc = riscv_cpu_set_pc;
>> +    cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb;
>> +    cc->gdb_read_register = riscv_cpu_gdb_read_register;
>> +    cc->gdb_write_register = riscv_cpu_gdb_write_register;
>> +    cc->gdb_num_core_regs = 65;
>> +    cc->gdb_stop_before_watchpoint = true;
>> +    cc->disas_set_info = riscv_cpu_disas_set_info;
>> +#ifdef CONFIG_USER_ONLY
>> +    cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault;
>> +#else
>> +    cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
>> +    cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
>> +#endif
>> +#ifdef CONFIG_TCG
>> +    cc->tcg_initialize = riscv_translate_init;
>> +#endif
>> +    /* For now, mark unmigratable: */
>> +    cc->vmsd = &vmstate_riscv_cpu;
>> +}
>> +
>> +static void cpu_register(const RISCVCPUInfo *info)
>> +{
>> +    TypeInfo type_info = {
>> +        .name = info->name,
>> +        .parent = TYPE_RISCV_CPU,
>> +        .instance_size = sizeof(RISCVCPU),
>> +        .instance_init = info->initfn,
>> +    };
>> +
>> +    type_register(&type_info);
>> +}
>> +
>> +static const TypeInfo riscv_cpu_type_info = {
>> +    .name = TYPE_RISCV_CPU,
>> +    .parent = TYPE_CPU,
>> +    .instance_size = sizeof(RISCVCPU),
>> +    .instance_init = riscv_cpu_init,
>> +    .abstract = false,
>> +    .class_size = sizeof(RISCVCPUClass),
>> +    .class_init = riscv_cpu_class_init,
>> +};
>> +
>> +char *riscv_isa_string(RISCVCPU *cpu)
>> +{
>> +    int i;
>> +    size_t maxlen = 5 + ctz32(cpu->env.misa);
>> +    char *isa_string = g_new0(char, maxlen);
>> +    snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS);
>> +    for (i = 0; i < sizeof(riscv_exts); i++) {
>> +        if (cpu->env.misa & RV(riscv_exts[i])) {
>> +            isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a';
>> +
>> +        }
>> +    }
>> +    return isa_string;
>> +}
>> +
>> +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
>> +{
>> +    const RISCVCPUInfo *info = riscv_cpus;
>> +
>> +    while (info->name) {
>> +        if (info->bit_widths & TARGET_LONG_BITS) {
>> +            (*cpu_fprintf)(f, "%s\n", info->name);
>> +        }
>> +        info++;
>> +    }
>> +}
>> +
>> +static void riscv_cpu_register_types(void)
>> +{
>> +    const RISCVCPUInfo *info = riscv_cpus;
>> +
>> +    type_register_static(&riscv_cpu_type_info);
>> +
>> +    while (info->name) {
>> +        if (info->bit_widths & TARGET_LONG_BITS) {
>> +            cpu_register(info);
>> +        }
>> +        info++;
>> +    }
>> +}
>> +
>> +type_init(riscv_cpu_register_types)
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> new file mode 100644
>> index 0000000..cff02a2
>> --- /dev/null
>> +++ b/target/riscv/cpu.h
>> @@ -0,0 +1,296 @@
>> +/*
>> + * QEMU RISC-V CPU
>> + *
>> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
>> + * Copyright (c) 2017-2018 SiFive, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2 or later, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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/>.
>> + */
>> +
>> +#ifndef RISCV_CPU_H
>> +#define RISCV_CPU_H
>> +
>> +/* QEMU addressing/paging config */
>> +#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
>> +#if defined(TARGET_RISCV64)
>> +#define TARGET_LONG_BITS 64
>> +#define TARGET_PHYS_ADDR_SPACE_BITS 50
>> +#define TARGET_VIRT_ADDR_SPACE_BITS 39
>> +#elif defined(TARGET_RISCV32)
>> +#define TARGET_LONG_BITS 32
>> +#define TARGET_PHYS_ADDR_SPACE_BITS 34
>> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
>> +#endif
>> +
>> +#define TCG_GUEST_DEFAULT_MO 0
>> +
>> +#define ELF_MACHINE EM_RISCV
>> +#define CPUArchState struct CPURISCVState
>> +
>> +#include "qemu-common.h"
>> +#include "qom/cpu.h"
>> +#include "exec/cpu-defs.h"
>> +#include "fpu/softfloat.h"
>> +
>> +#define TYPE_RISCV_CPU "riscv-cpu"
>> +
>> +#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>> +#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>> +
>> +#define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>> +#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-
>> v1.9.1")
>> +#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-
>> v1.10.0")
>> +#define TYPE_RISCV_CPU_RV32IMACU_NOMMU  RISCV_CPU_TYPE_NAME("rv32imacu
>> -nommu")
>> +#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-
>> v1.9.1")
>> +#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-
>> v1.10.0")
>> +#define TYPE_RISCV_CPU_RV64IMACU_NOMMU  RISCV_CPU_TYPE_NAME("rv64imacu
>> -nommu")
>> +#define TYPE_RISCV_CPU_SIFIVE_E31       RISCV_CPU_TYPE_NAME("sifive-e
>> 31")
>> +#define TYPE_RISCV_CPU_SIFIVE_E51       RISCV_CPU_TYPE_NAME("sifive-e
>> 51")
>> +#define TYPE_RISCV_CPU_SIFIVE_U34       RISCV_CPU_TYPE_NAME("sifive-u
>> 34")
>> +#define TYPE_RISCV_CPU_SIFIVE_U54       RISCV_CPU_TYPE_NAME("sifive-u
>> 54")
>> +
>> +#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2))
>> +#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2))
>> +
>> +#if defined(TARGET_RISCV32)
>> +#define RVXLEN RV32
>> +#elif defined(TARGET_RISCV64)
>> +#define RVXLEN RV64
>> +#endif
>> +
>> +#define RV(x) ((target_ulong)1 << (x - 'A'))
>> +
>> +#define RVI RV('I')
>> +#define RVM RV('M')
>> +#define RVA RV('A')
>> +#define RVF RV('F')
>> +#define RVD RV('D')
>> +#define RVC RV('C')
>> +#define RVS RV('S')
>> +#define RVU RV('U')
>> +
>> +/* S extension denotes that Supervisor mode exists, however it is
>> possible
>> +   to have a core that support S mode but does not have an MMU and there
>> +   is currently no bit in misa to indicate whether an MMU exists or not
>> +   so a cpu features bitfield is required */
>> +enum {
>> +    RISCV_FEATURE_MMU
>> +};
>> +
>> +#define USER_VERSION_2_02_0 0x00020200
>> +#define PRIV_VERSION_1_09_1 0x00010901
>> +#define PRIV_VERSION_1_10_0 0x00011000
>> +
>> +#define TRANSLATE_FAIL 1
>> +#define TRANSLATE_SUCCESS 0
>> +#define NB_MMU_MODES 4
>> +#define MMU_USER_IDX 3
>> +
>> +#define MAX_RISCV_PMPS (16)
>> +
>> +typedef struct CPURISCVState CPURISCVState;
>> +
>> +#include "pmp.h"
>> +
>> +struct CPURISCVState {
>> +    target_ulong gpr[32];
>> +    uint64_t fpr[32]; /* assume both F and D extensions */
>> +    target_ulong pc;
>> +    target_ulong load_res;
>> +    target_ulong load_val;
>> +
>> +    target_ulong frm;
>> +
>> +    target_ulong badaddr;
>> +
>> +    target_ulong user_ver;
>> +    target_ulong priv_ver;
>> +    target_ulong misa;
>> +
>> +    uint32_t features;
>> +
>> +#ifndef CONFIG_USER_ONLY
>> +    target_ulong priv;
>> +    target_ulong resetvec;
>> +
>> +    target_ulong mhartid;
>> +    target_ulong mstatus;
>> +    /*
>> +     * CAUTION! Unlike the rest of this struct, mip is accessed
>> asynchonously
>> +     * by I/O threads and other vCPUs, so hold the iothread mutex before
>> +     * operating on it.  CPU_INTERRUPT_HARD should be in effect iff this
>> is
>> +     * non-zero.  Use riscv_cpu_set_local_interrupt.
>> +     */
>> +    uint32_t mip;        /* allow atomic_read for >= 32-bit hosts */
>> +    target_ulong mie;
>> +    target_ulong mideleg;
>> +
>> +    target_ulong sptbr;  /* until: priv-1.9.1 */
>> +    target_ulong satp;   /* since: priv-1.10.0 */
>> +    target_ulong sbadaddr;
>> +    target_ulong mbadaddr;
>> +    target_ulong medeleg;
>> +
>> +    target_ulong stvec;
>> +    target_ulong sepc;
>> +    target_ulong scause;
>> +
>> +    target_ulong mtvec;
>> +    target_ulong mepc;
>> +    target_ulong mcause;
>> +    target_ulong mtval;  /* since: priv-1.10.0 */
>> +
>> +    uint32_t mucounteren;
>> +    uint32_t mscounteren;
>> +    target_ulong scounteren; /* since: priv-1.10.0 */
>> +    target_ulong mcounteren; /* since: priv-1.10.0 */
>> +
>> +    target_ulong sscratch;
>> +    target_ulong mscratch;
>> +
>> +    /* temporary htif regs */
>> +    uint64_t mfromhost;
>> +    uint64_t mtohost;
>> +    uint64_t timecmp;
>> +
>> +    /* physical memory protection */
>> +    pmp_table_t pmp_state;
>> +#endif
>> +
>> +    float_status fp_status;
>> +
>> +    /* QEMU */
>> +    CPU_COMMON
>> +
>> +    /* Fields from here on are preserved across CPU reset. */
>> +    QEMUTimer *timer; /* Internal timer */
>> +};
>> +
>> +#define RISCV_CPU_CLASS(klass) \
>> +    OBJECT_CLASS_CHECK(RISCVCPUClass, (klass), TYPE_RISCV_CPU)
>> +#define RISCV_CPU(obj) \
>> +    OBJECT_CHECK(RISCVCPU, (obj), TYPE_RISCV_CPU)
>> +#define RISCV_CPU_GET_CLASS(obj) \
>> +    OBJECT_GET_CLASS(RISCVCPUClass, (obj), TYPE_RISCV_CPU)
>> +
>> +/**
>> + * RISCVCPUClass:
>> + * @parent_realize: The parent class' realize handler.
>> + * @parent_reset: The parent class' reset handler.
>> + *
>> + * A RISCV CPU model.
>> + */
>> +typedef struct RISCVCPUClass {
>> +    /*< private >*/
>> +    CPUClass parent_class;
>> +    /*< public >*/
>> +    DeviceRealize parent_realize;
>> +    void (*parent_reset)(CPUState *cpu);
>> +} RISCVCPUClass;
>> +
>> +/**
>> + * RISCVCPU:
>> + * @env: #CPURISCVState
>> + *
>> + * A RISCV CPU.
>> + */
>> +typedef struct RISCVCPU {
>> +    /*< private >*/
>> +    CPUState parent_obj;
>> +    /*< public >*/
>> +    CPURISCVState env;
>> +} RISCVCPU;
>> +
>> +static inline RISCVCPU *riscv_env_get_cpu(CPURISCVState *env)
>> +{
>> +    return container_of(env, RISCVCPU, env);
>> +}
>> +
>> +static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext)
>> +{
>> +    return (env->misa & ext) != 0;
>> +}
>> +
>> +static inline bool riscv_feature(CPURISCVState *env, int feature)
>> +{
>> +    return env->features & (1ULL << feature);
>> +}
>> +
>> +#include "cpu_user.h"
>> +#include "cpu_bits.h"
>> +
>> +extern const char * const riscv_int_regnames[];
>> +extern const char * const riscv_fpr_regnames[];
>> +extern const char * const riscv_excp_names[];
>> +extern const char * const riscv_intr_names[];
>> +
>> +#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
>> +#define ENV_OFFSET offsetof(RISCVCPU, env)
>> +
>> +void riscv_cpu_do_interrupt(CPUState *cpu);
>> +int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
>> +int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
>> +bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
>> +int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
>> +hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
>> +void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
>> +                                    MMUAccessType access_type, int
>> mmu_idx,
>> +                                    uintptr_t retaddr);
>> +int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
>> +                              int rw, int mmu_idx);
>> +
>> +char *riscv_isa_string(RISCVCPU *cpu);
>> +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
>> +
>> +#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model)
>> +#define cpu_signal_handler cpu_riscv_signal_handler
>> +#define cpu_list riscv_cpu_list
>> +#define cpu_mmu_index riscv_cpu_mmu_index
>> +
>> +void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
>> +
>> +void riscv_translate_init(void);
>> +RISCVCPU *cpu_riscv_init(const char *cpu_model);
>> +int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc);
>> +void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
>> +                                          uint32_t exception, uintptr_t
>> pc);
>> +
>> +target_ulong cpu_riscv_get_fflags(CPURISCVState *env);
>> +void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong);
>> +
>> +#define TB_FLAGS_MMU_MASK  3
>> +#define TB_FLAGS_FP_ENABLE MSTATUS_FS
>> +
>> +static inline void cpu_get_tb_cpu_state(CPURISCVState *env,
>> target_ulong *pc,
>> +                                        target_ulong *cs_base, uint32_t
>> *flags)
>> +{
>> +    *pc = env->pc;
>> +    *cs_base = 0;
>> +#ifdef CONFIG_USER_ONLY
>> +    *flags = TB_FLAGS_FP_ENABLE;
>> +#else
>> +    *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
>> +#endif
>> +}
>> +
>> +void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
>> +        target_ulong csrno);
>> +target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
>> +
>> +#ifndef CONFIG_USER_ONLY
>> +void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int
>> value);
>> +#endif
>> +
>> +#include "exec/cpu-all.h"
>> +
>> +#endif /* RISCV_CPU_H */
>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>> new file mode 100644
>> index 0000000..64aa097
>> --- /dev/null
>> +++ b/target/riscv/cpu_bits.h
>> @@ -0,0 +1,411 @@
>> +/* RISC-V ISA constants */
>> +
>> +#define get_field(reg, mask) (((reg) & \
>> +                 (target_ulong)(mask)) / ((mask) & ~((mask) << 1)))
>> +#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \
>> +                 (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \
>> +                 (target_ulong)(mask)))
>> +
>> +#define PGSHIFT 12
>> +
>> +#define FSR_RD_SHIFT 5
>> +#define FSR_RD   (0x7 << FSR_RD_SHIFT)
>> +
>> +#define FPEXC_NX 0x01
>> +#define FPEXC_UF 0x02
>> +#define FPEXC_OF 0x04
>> +#define FPEXC_DZ 0x08
>> +#define FPEXC_NV 0x10
>> +
>> +#define FSR_AEXC_SHIFT 0
>> +#define FSR_NVA  (FPEXC_NV << FSR_AEXC_SHIFT)
>> +#define FSR_OFA  (FPEXC_OF << FSR_AEXC_SHIFT)
>> +#define FSR_UFA  (FPEXC_UF << FSR_AEXC_SHIFT)
>> +#define FSR_DZA  (FPEXC_DZ << FSR_AEXC_SHIFT)
>> +#define FSR_NXA  (FPEXC_NX << FSR_AEXC_SHIFT)
>> +#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
>> +
>> +/* CSR numbers */
>> +#define CSR_FFLAGS 0x1
>> +#define CSR_FRM 0x2
>> +#define CSR_FCSR 0x3
>> +#define CSR_CYCLE 0xc00
>> +#define CSR_TIME 0xc01
>> +#define CSR_INSTRET 0xc02
>> +#define CSR_HPMCOUNTER3 0xc03
>> +#define CSR_HPMCOUNTER4 0xc04
>> +#define CSR_HPMCOUNTER5 0xc05
>> +#define CSR_HPMCOUNTER6 0xc06
>> +#define CSR_HPMCOUNTER7 0xc07
>> +#define CSR_HPMCOUNTER8 0xc08
>> +#define CSR_HPMCOUNTER9 0xc09
>> +#define CSR_HPMCOUNTER10 0xc0a
>> +#define CSR_HPMCOUNTER11 0xc0b
>> +#define CSR_HPMCOUNTER12 0xc0c
>> +#define CSR_HPMCOUNTER13 0xc0d
>> +#define CSR_HPMCOUNTER14 0xc0e
>> +#define CSR_HPMCOUNTER15 0xc0f
>> +#define CSR_HPMCOUNTER16 0xc10
>> +#define CSR_HPMCOUNTER17 0xc11
>> +#define CSR_HPMCOUNTER18 0xc12
>> +#define CSR_HPMCOUNTER19 0xc13
>> +#define CSR_HPMCOUNTER20 0xc14
>> +#define CSR_HPMCOUNTER21 0xc15
>> +#define CSR_HPMCOUNTER22 0xc16
>> +#define CSR_HPMCOUNTER23 0xc17
>> +#define CSR_HPMCOUNTER24 0xc18
>> +#define CSR_HPMCOUNTER25 0xc19
>> +#define CSR_HPMCOUNTER26 0xc1a
>> +#define CSR_HPMCOUNTER27 0xc1b
>> +#define CSR_HPMCOUNTER28 0xc1c
>> +#define CSR_HPMCOUNTER29 0xc1d
>> +#define CSR_HPMCOUNTER30 0xc1e
>> +#define CSR_HPMCOUNTER31 0xc1f
>> +#define CSR_SSTATUS 0x100
>> +#define CSR_SIE 0x104
>> +#define CSR_STVEC 0x105
>> +#define CSR_SCOUNTEREN 0x106
>> +#define CSR_SSCRATCH 0x140
>> +#define CSR_SEPC 0x141
>> +#define CSR_SCAUSE 0x142
>> +#define CSR_SBADADDR 0x143
>> +#define CSR_SIP 0x144
>> +#define CSR_SPTBR 0x180
>> +#define CSR_SATP 0x180
>> +#define CSR_MSTATUS 0x300
>> +#define CSR_MISA 0x301
>> +#define CSR_MEDELEG 0x302
>> +#define CSR_MIDELEG 0x303
>> +#define CSR_MIE 0x304
>> +#define CSR_MTVEC 0x305
>> +#define CSR_MCOUNTEREN 0x306
>> +#define CSR_MSCRATCH 0x340
>> +#define CSR_MEPC 0x341
>> +#define CSR_MCAUSE 0x342
>> +#define CSR_MBADADDR 0x343
>> +#define CSR_MIP 0x344
>> +#define CSR_PMPCFG0 0x3a0
>> +#define CSR_PMPCFG1 0x3a1
>> +#define CSR_PMPCFG2 0x3a2
>> +#define CSR_PMPCFG3 0x3a3
>> +#define CSR_PMPADDR0 0x3b0
>> +#define CSR_PMPADDR1 0x3b1
>> +#define CSR_PMPADDR2 0x3b2
>> +#define CSR_PMPADDR3 0x3b3
>> +#define CSR_PMPADDR4 0x3b4
>> +#define CSR_PMPADDR5 0x3b5
>> +#define CSR_PMPADDR6 0x3b6
>> +#define CSR_PMPADDR7 0x3b7
>> +#define CSR_PMPADDR8 0x3b8
>> +#define CSR_PMPADDR9 0x3b9
>> +#define CSR_PMPADDR10 0x3ba
>> +#define CSR_PMPADDR11 0x3bb
>> +#define CSR_PMPADDR12 0x3bc
>> +#define CSR_PMPADDR13 0x3bd
>> +#define CSR_PMPADDR14 0x3be
>> +#define CSR_PMPADDR15 0x3bf
>> +#define CSR_TSELECT 0x7a0
>> +#define CSR_TDATA1 0x7a1
>> +#define CSR_TDATA2 0x7a2
>> +#define CSR_TDATA3 0x7a3
>> +#define CSR_DCSR 0x7b0
>> +#define CSR_DPC 0x7b1
>> +#define CSR_DSCRATCH 0x7b2
>> +#define CSR_MCYCLE 0xb00
>> +#define CSR_MINSTRET 0xb02
>> +#define CSR_MHPMCOUNTER3 0xb03
>> +#define CSR_MHPMCOUNTER4 0xb04
>> +#define CSR_MHPMCOUNTER5 0xb05
>> +#define CSR_MHPMCOUNTER6 0xb06
>> +#define CSR_MHPMCOUNTER7 0xb07
>> +#define CSR_MHPMCOUNTER8 0xb08
>> +#define CSR_MHPMCOUNTER9 0xb09
>> +#define CSR_MHPMCOUNTER10 0xb0a
>> +#define CSR_MHPMCOUNTER11 0xb0b
>> +#define CSR_MHPMCOUNTER12 0xb0c
>> +#define CSR_MHPMCOUNTER13 0xb0d
>> +#define CSR_MHPMCOUNTER14 0xb0e
>> +#define CSR_MHPMCOUNTER15 0xb0f
>> +#define CSR_MHPMCOUNTER16 0xb10
>> +#define CSR_MHPMCOUNTER17 0xb11
>> +#define CSR_MHPMCOUNTER18 0xb12
>> +#define CSR_MHPMCOUNTER19 0xb13
>> +#define CSR_MHPMCOUNTER20 0xb14
>> +#define CSR_MHPMCOUNTER21 0xb15
>> +#define CSR_MHPMCOUNTER22 0xb16
>> +#define CSR_MHPMCOUNTER23 0xb17
>> +#define CSR_MHPMCOUNTER24 0xb18
>> +#define CSR_MHPMCOUNTER25 0xb19
>> +#define CSR_MHPMCOUNTER26 0xb1a
>> +#define CSR_MHPMCOUNTER27 0xb1b
>> +#define CSR_MHPMCOUNTER28 0xb1c
>> +#define CSR_MHPMCOUNTER29 0xb1d
>> +#define CSR_MHPMCOUNTER30 0xb1e
>> +#define CSR_MHPMCOUNTER31 0xb1f
>> +#define CSR_MUCOUNTEREN 0x320
>> +#define CSR_MSCOUNTEREN 0x321
>> +#define CSR_MHPMEVENT3 0x323
>> +#define CSR_MHPMEVENT4 0x324
>> +#define CSR_MHPMEVENT5 0x325
>> +#define CSR_MHPMEVENT6 0x326
>> +#define CSR_MHPMEVENT7 0x327
>> +#define CSR_MHPMEVENT8 0x328
>> +#define CSR_MHPMEVENT9 0x329
>> +#define CSR_MHPMEVENT10 0x32a
>> +#define CSR_MHPMEVENT11 0x32b
>> +#define CSR_MHPMEVENT12 0x32c
>> +#define CSR_MHPMEVENT13 0x32d
>> +#define CSR_MHPMEVENT14 0x32e
>> +#define CSR_MHPMEVENT15 0x32f
>> +#define CSR_MHPMEVENT16 0x330
>> +#define CSR_MHPMEVENT17 0x331
>> +#define CSR_MHPMEVENT18 0x332
>> +#define CSR_MHPMEVENT19 0x333
>> +#define CSR_MHPMEVENT20 0x334
>> +#define CSR_MHPMEVENT21 0x335
>> +#define CSR_MHPMEVENT22 0x336
>> +#define CSR_MHPMEVENT23 0x337
>> +#define CSR_MHPMEVENT24 0x338
>> +#define CSR_MHPMEVENT25 0x339
>> +#define CSR_MHPMEVENT26 0x33a
>> +#define CSR_MHPMEVENT27 0x33b
>> +#define CSR_MHPMEVENT28 0x33c
>> +#define CSR_MHPMEVENT29 0x33d
>> +#define CSR_MHPMEVENT30 0x33e
>> +#define CSR_MHPMEVENT31 0x33f
>> +#define CSR_MVENDORID 0xf11
>> +#define CSR_MARCHID 0xf12
>> +#define CSR_MIMPID 0xf13
>> +#define CSR_MHARTID 0xf14
>> +#define CSR_CYCLEH 0xc80
>> +#define CSR_TIMEH 0xc81
>> +#define CSR_INSTRETH 0xc82
>> +#define CSR_HPMCOUNTER3H 0xc83
>> +#define CSR_HPMCOUNTER4H 0xc84
>> +#define CSR_HPMCOUNTER5H 0xc85
>> +#define CSR_HPMCOUNTER6H 0xc86
>> +#define CSR_HPMCOUNTER7H 0xc87
>> +#define CSR_HPMCOUNTER8H 0xc88
>> +#define CSR_HPMCOUNTER9H 0xc89
>> +#define CSR_HPMCOUNTER10H 0xc8a
>> +#define CSR_HPMCOUNTER11H 0xc8b
>> +#define CSR_HPMCOUNTER12H 0xc8c
>> +#define CSR_HPMCOUNTER13H 0xc8d
>> +#define CSR_HPMCOUNTER14H 0xc8e
>> +#define CSR_HPMCOUNTER15H 0xc8f
>> +#define CSR_HPMCOUNTER16H 0xc90
>> +#define CSR_HPMCOUNTER17H 0xc91
>> +#define CSR_HPMCOUNTER18H 0xc92
>> +#define CSR_HPMCOUNTER19H 0xc93
>> +#define CSR_HPMCOUNTER20H 0xc94
>> +#define CSR_HPMCOUNTER21H 0xc95
>> +#define CSR_HPMCOUNTER22H 0xc96
>> +#define CSR_HPMCOUNTER23H 0xc97
>> +#define CSR_HPMCOUNTER24H 0xc98
>> +#define CSR_HPMCOUNTER25H 0xc99
>> +#define CSR_HPMCOUNTER26H 0xc9a
>> +#define CSR_HPMCOUNTER27H 0xc9b
>> +#define CSR_HPMCOUNTER28H 0xc9c
>> +#define CSR_HPMCOUNTER29H 0xc9d
>> +#define CSR_HPMCOUNTER30H 0xc9e
>> +#define CSR_HPMCOUNTER31H 0xc9f
>> +#define CSR_MCYCLEH 0xb80
>> +#define CSR_MINSTRETH 0xb82
>> +#define CSR_MHPMCOUNTER3H 0xb83
>> +#define CSR_MHPMCOUNTER4H 0xb84
>> +#define CSR_MHPMCOUNTER5H 0xb85
>> +#define CSR_MHPMCOUNTER6H 0xb86
>> +#define CSR_MHPMCOUNTER7H 0xb87
>> +#define CSR_MHPMCOUNTER8H 0xb88
>> +#define CSR_MHPMCOUNTER9H 0xb89
>> +#define CSR_MHPMCOUNTER10H 0xb8a
>> +#define CSR_MHPMCOUNTER11H 0xb8b
>> +#define CSR_MHPMCOUNTER12H 0xb8c
>> +#define CSR_MHPMCOUNTER13H 0xb8d
>> +#define CSR_MHPMCOUNTER14H 0xb8e
>> +#define CSR_MHPMCOUNTER15H 0xb8f
>> +#define CSR_MHPMCOUNTER16H 0xb90
>> +#define CSR_MHPMCOUNTER17H 0xb91
>> +#define CSR_MHPMCOUNTER18H 0xb92
>> +#define CSR_MHPMCOUNTER19H 0xb93
>> +#define CSR_MHPMCOUNTER20H 0xb94
>> +#define CSR_MHPMCOUNTER21H 0xb95
>> +#define CSR_MHPMCOUNTER22H 0xb96
>> +#define CSR_MHPMCOUNTER23H 0xb97
>> +#define CSR_MHPMCOUNTER24H 0xb98
>> +#define CSR_MHPMCOUNTER25H 0xb99
>> +#define CSR_MHPMCOUNTER26H 0xb9a
>> +#define CSR_MHPMCOUNTER27H 0xb9b
>> +#define CSR_MHPMCOUNTER28H 0xb9c
>> +#define CSR_MHPMCOUNTER29H 0xb9d
>> +#define CSR_MHPMCOUNTER30H 0xb9e
>> +#define CSR_MHPMCOUNTER31H 0xb9f
>> +
>> +/* mstatus bits */
>> +#define MSTATUS_UIE         0x00000001
>> +#define MSTATUS_SIE         0x00000002
>> +#define MSTATUS_HIE         0x00000004
>> +#define MSTATUS_MIE         0x00000008
>> +#define MSTATUS_UPIE        0x00000010
>> +#define MSTATUS_SPIE        0x00000020
>> +#define MSTATUS_HPIE        0x00000040
>> +#define MSTATUS_MPIE        0x00000080
>> +#define MSTATUS_SPP         0x00000100
>> +#define MSTATUS_HPP         0x00000600
>> +#define MSTATUS_MPP         0x00001800
>> +#define MSTATUS_FS          0x00006000
>> +#define MSTATUS_XS          0x00018000
>> +#define MSTATUS_MPRV        0x00020000
>> +#define MSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
>> +#define MSTATUS_SUM         0x00040000 /* since: priv-1.10 */
>> +#define MSTATUS_MXR         0x00080000
>> +#define MSTATUS_VM          0x1F000000 /* until: priv-1.9.1 */
>> +#define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
>> +#define MSTATUS_TW          0x20000000 /* since: priv-1.10 */
>> +#define MSTATUS_TSR         0x40000000 /* since: priv-1.10 */
>> +
>> +#define MSTATUS64_UXL       0x0000000300000000ULL
>> +#define MSTATUS64_SXL       0x0000000C00000000ULL
>> +
>> +#define MSTATUS32_SD        0x80000000
>> +#define MSTATUS64_SD        0x8000000000000000ULL
>> +
>> +#if defined(TARGET_RISCV32)
>> +#define MSTATUS_SD MSTATUS32_SD
>> +#elif defined(TARGET_RISCV64)
>> +#define MSTATUS_SD MSTATUS64_SD
>> +#endif
>> +
>> +/* sstatus bits */
>> +#define SSTATUS_UIE         0x00000001
>> +#define SSTATUS_SIE         0x00000002
>> +#define SSTATUS_UPIE        0x00000010
>> +#define SSTATUS_SPIE        0x00000020
>> +#define SSTATUS_SPP         0x00000100
>> +#define SSTATUS_FS          0x00006000
>> +#define SSTATUS_XS          0x00018000
>> +#define SSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
>> +#define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
>> +#define SSTATUS_MXR         0x00080000
>> +
>> +#define SSTATUS32_SD        0x80000000
>> +#define SSTATUS64_SD        0x8000000000000000ULL
>> +
>> +#if defined(TARGET_RISCV32)
>> +#define SSTATUS_SD SSTATUS32_SD
>> +#elif defined(TARGET_RISCV64)
>> +#define SSTATUS_SD SSTATUS64_SD
>> +#endif
>> +
>> +/* irqs */
>> +#define MIP_SSIP            (1 << IRQ_S_SOFT)
>> +#define MIP_HSIP            (1 << IRQ_H_SOFT)
>> +#define MIP_MSIP            (1 << IRQ_M_SOFT)
>> +#define MIP_STIP            (1 << IRQ_S_TIMER)
>> +#define MIP_HTIP            (1 << IRQ_H_TIMER)
>> +#define MIP_MTIP            (1 << IRQ_M_TIMER)
>> +#define MIP_SEIP            (1 << IRQ_S_EXT)
>> +#define MIP_HEIP            (1 << IRQ_H_EXT)
>> +#define MIP_MEIP            (1 << IRQ_M_EXT)
>> +
>> +#define SIP_SSIP            MIP_SSIP
>> +#define SIP_STIP            MIP_STIP
>> +#define SIP_SEIP            MIP_SEIP
>> +
>> +#define PRV_U 0
>> +#define PRV_S 1
>> +#define PRV_H 2
>> +#define PRV_M 3
>> +
>> +/* privileged ISA 1.9.1 VM modes (mstatus.vm) */
>> +#define VM_1_09_MBARE 0
>> +#define VM_1_09_MBB   1
>> +#define VM_1_09_MBBID 2
>> +#define VM_1_09_SV32  8
>> +#define VM_1_09_SV39  9
>> +#define VM_1_09_SV48  10
>> +
>> +/* privileged ISA 1.10.0 VM modes (satp.mode) */
>> +#define VM_1_10_MBARE 0
>> +#define VM_1_10_SV32  1
>> +#define VM_1_10_SV39  8
>> +#define VM_1_10_SV48  9
>> +#define VM_1_10_SV57  10
>> +#define VM_1_10_SV64  11
>> +
>> +/* privileged ISA interrupt causes */
>> +#define IRQ_U_SOFT      0  /* since: priv-1.10 */
>> +#define IRQ_S_SOFT      1
>> +#define IRQ_H_SOFT      2  /* until: priv-1.9.1 */
>> +#define IRQ_M_SOFT      3  /* until: priv-1.9.1 */
>> +#define IRQ_U_TIMER     4  /* since: priv-1.10 */
>> +#define IRQ_S_TIMER     5
>> +#define IRQ_H_TIMER     6  /* until: priv-1.9.1 */
>> +#define IRQ_M_TIMER     7  /* until: priv-1.9.1 */
>> +#define IRQ_U_EXT       8  /* since: priv-1.10 */
>> +#define IRQ_S_EXT       9
>> +#define IRQ_H_EXT       10 /* until: priv-1.9.1 */
>> +#define IRQ_M_EXT       11 /* until: priv-1.9.1 */
>> +#define IRQ_X_COP       12 /* non-standard */
>> +
>> +/* Default addresses */
>> +#define DEFAULT_RSTVEC     0x00001000
>> +
>> +/* RV32 satp field masks */
>> +#define SATP32_MODE 0x80000000
>> +#define SATP32_ASID 0x7fc00000
>> +#define SATP32_PPN  0x003fffff
>> +
>> +/* RV64 satp field masks */
>> +#define SATP64_MODE 0xF000000000000000ULL
>> +#define SATP64_ASID 0x0FFFF00000000000ULL
>> +#define SATP64_PPN  0x00000FFFFFFFFFFFULL
>> +
>> +#if defined(TARGET_RISCV32)
>> +#define SATP_MODE SATP32_MODE
>> +#define SATP_ASID SATP32_ASID
>> +#define SATP_PPN  SATP32_PPN
>> +#endif
>> +#if defined(TARGET_RISCV64)
>> +#define SATP_MODE SATP64_MODE
>> +#define SATP_ASID SATP64_ASID
>> +#define SATP_PPN  SATP64_PPN
>> +#endif
>> +
>> +/* RISCV Exception Codes */
>> +#define EXCP_NONE                       -1 /* not a real RISCV exception
>> code */
>> +#define RISCV_EXCP_INST_ADDR_MIS           0x0
>> +#define RISCV_EXCP_INST_ACCESS_FAULT       0x1
>> +#define RISCV_EXCP_ILLEGAL_INST            0x2
>> +#define RISCV_EXCP_BREAKPOINT              0x3
>> +#define RISCV_EXCP_LOAD_ADDR_MIS           0x4
>> +#define RISCV_EXCP_LOAD_ACCESS_FAULT       0x5
>> +#define RISCV_EXCP_STORE_AMO_ADDR_MIS      0x6
>> +#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT  0x7
>> +#define RISCV_EXCP_U_ECALL                 0x8 /* for convenience,
>> report all
>> +                                                  ECALLs as this, handler
>> +                                                  fixes */
>> +#define RISCV_EXCP_S_ECALL                 0x9
>> +#define RISCV_EXCP_H_ECALL                 0xa
>> +#define RISCV_EXCP_M_ECALL                 0xb
>> +#define RISCV_EXCP_INST_PAGE_FAULT         0xc /* since: priv-1.10.0 */
>> +#define RISCV_EXCP_LOAD_PAGE_FAULT         0xd /* since: priv-1.10.0 */
>> +#define RISCV_EXCP_STORE_PAGE_FAULT        0xf /* since: priv-1.10.0 */
>> +
>> +#define RISCV_EXCP_INT_FLAG                0x80000000
>> +#define RISCV_EXCP_INT_MASK                0x7fffffff
>> +
>> +/* page table entry (PTE) fields */
>> +#define PTE_V     0x001 /* Valid */
>> +#define PTE_R     0x002 /* Read */
>> +#define PTE_W     0x004 /* Write */
>> +#define PTE_X     0x008 /* Execute */
>> +#define PTE_U     0x010 /* User */
>> +#define PTE_G     0x020 /* Global */
>> +#define PTE_A     0x040 /* Accessed */
>> +#define PTE_D     0x080 /* Dirty */
>> +#define PTE_SOFT  0x300 /* Reserved for Software */
>> +
>> +#define PTE_PPN_SHIFT 10
>> +
>> +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) ==
>> PTE_V)
>> --
>> 2.7.0
>>
>>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure
  2018-03-02 14:33   ` Eric Blake
@ 2018-03-03  2:37     ` Michael Clark
  2018-03-05 15:59       ` Eric Blake
  0 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-03  2:37 UTC (permalink / raw)
  To: Eric Blake
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

Let me know if you have a branch for me to pull and rebase against.

We are passing all build and make check tests in travis (except for a
couple of build timeouts because we are hitting the default 50 minute
timeout)

https://travis-ci.org/riscv/riscv-qemu/builds/348234736

On Sat, Mar 3, 2018 at 3:33 AM, Eric Blake <eblake@redhat.com> wrote:

> On 03/02/2018 07:51 AM, Michael Clark wrote:
>
>> This adds RISC-V into the build system enabling the following targets:
>>
>> - riscv32-softmmu
>> - riscv64-softmmu
>> - riscv32-linux-user
>> - riscv64-linux-user
>>
>> This adds defaults configs for RISC-V, enables the build for the RISC-V
>> CPU core, hardware, and Linux User Emulation. The 'qemu-binfmt-conf.sh'
>> script is updated to add the RISC-V ELF magic.
>>
>> Expected checkpatch errors for consistency reasons:
>>
>> ERROR: line over 90 characters
>> FILE: scripts/qemu-binfmt-conf.sh
>>
>> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
>> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
>> Signed-off-by: Michael Clark <mjc@sifive.com>
>> ---
>>   arch_init.c                            |  2 ++
>>   configure                              | 13 +++++++++++++
>>   cpus.c                                 |  6 ++++++
>>   default-configs/riscv32-linux-user.mak |  1 +
>>   default-configs/riscv32-softmmu.mak    |  4 ++++
>>   default-configs/riscv64-linux-user.mak |  1 +
>>   default-configs/riscv64-softmmu.mak    |  4 ++++
>>   hw/riscv/Makefile.objs                 | 11 +++++++++++
>>   include/sysemu/arch_init.h             |  1 +
>>   qapi-schema.json                       | 17 ++++++++++++++++-
>>
>
> Will need rebasing to modify qapi/misc.json if my pending PULL request
> issues get resolved first:
>
> https://lists.gnu.org/archive/html/qemu-devel/2018-03/msg00469.html
>
> --
> Eric Blake, Principal Software Engineer
> Red Hat, Inc.           +1-919-301-3266
> Virtualization:  qemu.org | libvirt.org
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
  2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
                   ` (23 preceding siblings ...)
  2018-03-02 14:17 ` [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission no-reply
@ 2018-03-05  8:41 ` Richard W.M. Jones
  2018-03-05 10:02   ` Alex Bennée
                     ` (2 more replies)
  24 siblings, 3 replies; 70+ messages in thread
From: Richard W.M. Jones @ 2018-03-05  8:41 UTC (permalink / raw)
  To: Michael Clark
  Cc: qemu-devel, Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar,
	RISC-V Patches

[-- Attachment #1: Type: text/plain, Size: 422 bytes --]


The attached patch is also needed to avoid crashes during various
math-heavy test suites.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into KVM guests.
http://libguestfs.org/virt-v2v

[-- Attachment #2: 0001-softfloat-fix-crash-on-int-conversion-of-SNaN.patch --]
[-- Type: text/plain, Size: 1119 bytes --]

>From 8465cbda688cdc65dfe4037608c14cac087eae03 Mon Sep 17 00:00:00 2001
From: Stef O'Rear <sorear2@gmail.com>
Date: Sat, 3 Mar 2018 03:46:00 -0800
Subject: [PATCH] softfloat: fix crash on int conversion of SNaN

Signed-off-by: Stef O'Rear <sorear2@gmail.com>
---
 fpu/softfloat.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index e7fb0d357a..1da1db377e 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -1342,6 +1342,8 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode,
     switch (p.cls) {
     case float_class_snan:
     case float_class_qnan:
+    case float_class_dnan:
+    case float_class_msnan:
         return max;
     case float_class_inf:
         return p.sign ? min : max;
@@ -1430,6 +1432,8 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max,
     switch (p.cls) {
     case float_class_snan:
     case float_class_qnan:
+    case float_class_dnan:
+    case float_class_msnan:
         s->float_exception_flags = orig_flags | float_flag_invalid;
         return max;
     case float_class_inf:
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition Michael Clark
  2018-03-03  2:23   ` Michael Clark
@ 2018-03-05  9:44   ` Igor Mammedov
  2018-03-05 22:24     ` Michael Clark
  1 sibling, 1 reply; 70+ messages in thread
From: Igor Mammedov @ 2018-03-05  9:44 UTC (permalink / raw)
  To: Michael Clark
  Cc: qemu-devel, Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar,
	RISC-V Patches

On Sat,  3 Mar 2018 02:51:31 +1300
Michael Clark <mjc@sifive.com> wrote:

> Add CPU state header, CPU definitions and initialization routines
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++++++++++++++++++++
>  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
>  target/riscv/cpu_bits.h | 411 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1139 insertions(+)
>  create mode 100644 target/riscv/cpu.c
>  create mode 100644 target/riscv/cpu.h
>  create mode 100644 target/riscv/cpu_bits.h
> 
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> new file mode 100644
> index 0000000..4851890
> --- /dev/null
> +++ b/target/riscv/cpu.c
[...]

> +
> +typedef struct RISCVCPUInfo {
> +    const int bit_widths;
> +    const char *name;
> +    void (*initfn)(Object *obj);
> +} RISCVCPUInfo;
> +
[...]

> +static const RISCVCPUInfo riscv_cpus[] = {
> +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
> +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init },
> +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init },
> +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
> +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
> +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init },
> +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init },
> +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init },
> +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
> +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
> +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init },
> +    { 0, NULL, NULL }
> +};
> +
[...]

> +static void cpu_register(const RISCVCPUInfo *info)
> +{
> +    TypeInfo type_info = {
> +        .name = info->name,
> +        .parent = TYPE_RISCV_CPU,
> +        .instance_size = sizeof(RISCVCPU),
> +        .instance_init = info->initfn,
> +    };
> +
> +    type_register(&type_info);
> +}
[...]

> +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
> +{
> +    const RISCVCPUInfo *info = riscv_cpus;
> +
> +    while (info->name) {
> +        if (info->bit_widths & TARGET_LONG_BITS) {
> +            (*cpu_fprintf)(f, "%s\n", info->name);
> +        }
> +        info++;
> +    }
> +}
> +
> +static void riscv_cpu_register_types(void)
> +{
> +    const RISCVCPUInfo *info = riscv_cpus;
> +
> +    type_register_static(&riscv_cpu_type_info);
> +
> +    while (info->name) {
> +        if (info->bit_widths & TARGET_LONG_BITS) {
> +            cpu_register(info);
> +        }
> +        info++;
> +    }
> +}
> +
> +type_init(riscv_cpu_register_types)
This still isn't fixed as requested
 http://lists.gnu.org/archive/html/qemu-devel/2018-02/msg06412.html

[...]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
  2018-03-05  8:41 ` Richard W.M. Jones
@ 2018-03-05 10:02   ` Alex Bennée
  2018-03-09 15:07   ` Michael Clark
  2018-03-09 16:43   ` Peter Maydell
  2 siblings, 0 replies; 70+ messages in thread
From: Alex Bennée @ 2018-03-05 10:02 UTC (permalink / raw)
  To: Richard W.M. Jones
  Cc: Michael Clark, Bastian Koppelmann, Palmer Dabbelt, qemu-devel,
	Sagar Karandikar, RISC-V Patches


Richard W.M. Jones <rjones@redhat.com> writes:

> The attached patch is also needed to avoid crashes during various
> math-heavy test suites.
>
> Rich.
>
> --
<snip>
> From: Stef O'Rear <sorear2@gmail.com>
> Date: Sat, 3 Mar 2018 03:46:00 -0800
> Subject: [PATCH] softfloat: fix crash on int conversion of SNaN
>
> Signed-off-by: Stef O'Rear <sorear2@gmail.com>
> ---
>  fpu/softfloat.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/fpu/softfloat.c b/fpu/softfloat.c
> index e7fb0d357a..1da1db377e 100644
> --- a/fpu/softfloat.c
> +++ b/fpu/softfloat.c
> @@ -1342,6 +1342,8 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode,
>      switch (p.cls) {
>      case float_class_snan:
>      case float_class_qnan:
> +    case float_class_dnan:
> +    case float_class_msnan:
>          return max;
>      case float_class_inf:
>          return p.sign ? min : max;
> @@ -1430,6 +1432,8 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max,
>      switch (p.cls) {
>      case float_class_snan:
>      case float_class_qnan:
> +    case float_class_dnan:
> +    case float_class_msnan:
>          s->float_exception_flags = orig_flags | float_flag_invalid;
>          return max;
>      case float_class_inf:

Obviously I wasn't exercising the NaN exit paths enough and we added the
return_nan() common code fairly late in the series.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

--
Alex Bennée

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure
  2018-03-03  2:37     ` Michael Clark
@ 2018-03-05 15:59       ` Eric Blake
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Blake @ 2018-03-05 15:59 UTC (permalink / raw)
  To: Michael Clark
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On 03/02/2018 08:37 PM, Michael Clark wrote:
> Let me know if you have a branch for me to pull and rebase against.

http://repo.or.cz/qemu/ericb.git qapi

But that has landed in master now.

> 
> We are passing all build and make check tests in travis (except for a
> couple of build timeouts because we are hitting the default 50 minute
> timeout)
> 
> https://travis-ci.org/riscv/riscv-qemu/builds/348234736
> 

Also, top-posting makes it a bit harder to see what you are replying to.


>>>    qapi-schema.json                       | 17 ++++++++++++++++-
>>>
>>
>> Will need rebasing to modify qapi/misc.json if my pending PULL request
>> issues get resolved first:
>>
>> https://lists.gnu.org/archive/html/qemu-devel/2018-03/msg00469.html
>>


-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-05  9:44   ` Igor Mammedov
@ 2018-03-05 22:24     ` Michael Clark
  2018-03-06  8:58       ` Igor Mammedov
  0 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-05 22:24 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Mon, Mar 5, 2018 at 10:44 PM, Igor Mammedov <imammedo@redhat.com> wrote:

> On Sat,  3 Mar 2018 02:51:31 +1300
> Michael Clark <mjc@sifive.com> wrote:
>
> > Add CPU state header, CPU definitions and initialization routines
> >
> > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> > Signed-off-by: Michael Clark <mjc@sifive.com>
> > ---
> >  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++
> ++++++++++++++++++
> >  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
> >  target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
> +++++++++++++++
> >  3 files changed, 1139 insertions(+)
> >  create mode 100644 target/riscv/cpu.c
> >  create mode 100644 target/riscv/cpu.h
> >  create mode 100644 target/riscv/cpu_bits.h
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > new file mode 100644
> > index 0000000..4851890
> > --- /dev/null
> > +++ b/target/riscv/cpu.c
> [...]
>
> > +
> > +typedef struct RISCVCPUInfo {
> > +    const int bit_widths;
> > +    const char *name;
> > +    void (*initfn)(Object *obj);
> > +} RISCVCPUInfo;
> > +
> [...]
>
> > +static const RISCVCPUInfo riscv_cpus[] = {
> > +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
> > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1,
> rv32gcsu_priv1_09_1_cpu_init },
> > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0,
> rv32gcsu_priv1_10_0_cpu_init },
> > +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
> > +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
> > +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init
> },
> > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1,
> rv64gcsu_priv1_09_1_cpu_init },
> > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0,
> rv64gcsu_priv1_10_0_cpu_init },
> > +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
> > +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
> > +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init
> },
> > +    { 0, NULL, NULL }
> > +};
> > +
> [...]
>
> > +static void cpu_register(const RISCVCPUInfo *info)
> > +{
> > +    TypeInfo type_info = {
> > +        .name = info->name,
> > +        .parent = TYPE_RISCV_CPU,
> > +        .instance_size = sizeof(RISCVCPU),
> > +        .instance_init = info->initfn,
> > +    };
> > +
> > +    type_register(&type_info);
> > +}
> [...]
>
> > +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
> > +{
> > +    const RISCVCPUInfo *info = riscv_cpus;
> > +
> > +    while (info->name) {
> > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > +            (*cpu_fprintf)(f, "%s\n", info->name);
> > +        }
> > +        info++;
> > +    }
> > +}
> > +
> > +static void riscv_cpu_register_types(void)
> > +{
> > +    const RISCVCPUInfo *info = riscv_cpus;
> > +
> > +    type_register_static(&riscv_cpu_type_info);
> > +
> > +    while (info->name) {
> > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > +            cpu_register(info);
> > +        }
> > +        info++;
> > +    }
> > +}
> > +
> > +type_init(riscv_cpu_register_types)
> This still isn't fixed as requested
>  http://lists.gnu.org/archive/html/qemu-devel/2018-02/msg06412.html


It's possibly because I explicitly requested a clarification. Pointing at a
commit and being asked to infer what the desired change is, is not what I
would call reasonable feedback. The code has already been reviewed. We have
just expanded on it in a manner consistent with how the ARM port handled
cpu initialization.

I'm happy to comply if you give me detailed instructions on what is wrong,
why, and how to fix it versus infer your problem from this commit to
another architecture.

Apologies if i'm a bit slow, but I really don't understand the change you
intend us to make.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-05 22:24     ` Michael Clark
@ 2018-03-06  8:58       ` Igor Mammedov
  2018-03-06 10:41         ` Igor Mammedov
  2018-03-07  3:23         ` Michael Clark
  0 siblings, 2 replies; 70+ messages in thread
From: Igor Mammedov @ 2018-03-06  8:58 UTC (permalink / raw)
  To: Michael Clark
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Tue, 6 Mar 2018 11:24:02 +1300
Michael Clark <mjc@sifive.com> wrote:

> On Mon, Mar 5, 2018 at 10:44 PM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
> > On Sat,  3 Mar 2018 02:51:31 +1300
> > Michael Clark <mjc@sifive.com> wrote:
> >  
> > > Add CPU state header, CPU definitions and initialization routines
> > >
> > > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> > > Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> > > Signed-off-by: Michael Clark <mjc@sifive.com>
> > > ---
> > >  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++  
> > ++++++++++++++++++  
> > >  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
> > >  target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++  
> > +++++++++++++++  
> > >  3 files changed, 1139 insertions(+)
> > >  create mode 100644 target/riscv/cpu.c
> > >  create mode 100644 target/riscv/cpu.h
> > >  create mode 100644 target/riscv/cpu_bits.h
> > >
> > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > > new file mode 100644
> > > index 0000000..4851890
> > > --- /dev/null
> > > +++ b/target/riscv/cpu.c  
> > [...]
> >  
> > > +
> > > +typedef struct RISCVCPUInfo {
> > > +    const int bit_widths;
> > > +    const char *name;
> > > +    void (*initfn)(Object *obj);
> > > +} RISCVCPUInfo;
> > > +  
> > [...]
> >  
> > > +static const RISCVCPUInfo riscv_cpus[] = {
> > > +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
> > > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1,  
> > rv32gcsu_priv1_09_1_cpu_init },  
> > > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0,  
> > rv32gcsu_priv1_10_0_cpu_init },  
> > > +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
> > > +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
> > > +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init  
> > },  
> > > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1,  
> > rv64gcsu_priv1_09_1_cpu_init },  
> > > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0,  
> > rv64gcsu_priv1_10_0_cpu_init },  
> > > +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
> > > +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
> > > +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init  
> > },  
> > > +    { 0, NULL, NULL }
> > > +};
> > > +  
> > [...]
> >  
> > > +static void cpu_register(const RISCVCPUInfo *info)
> > > +{
> > > +    TypeInfo type_info = {
> > > +        .name = info->name,
> > > +        .parent = TYPE_RISCV_CPU,
> > > +        .instance_size = sizeof(RISCVCPU),
> > > +        .instance_init = info->initfn,
> > > +    };
> > > +
> > > +    type_register(&type_info);
> > > +}  
> > [...]
> >  
> > > +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
> > > +{
> > > +    const RISCVCPUInfo *info = riscv_cpus;
> > > +
> > > +    while (info->name) {
> > > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > > +            (*cpu_fprintf)(f, "%s\n", info->name);
> > > +        }
> > > +        info++;
> > > +    }
> > > +}
> > > +
> > > +static void riscv_cpu_register_types(void)
> > > +{
> > > +    const RISCVCPUInfo *info = riscv_cpus;
> > > +
> > > +    type_register_static(&riscv_cpu_type_info);
> > > +
> > > +    while (info->name) {
> > > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > > +            cpu_register(info);
> > > +        }
> > > +        info++;
> > > +    }
> > > +}
> > > +
> > > +type_init(riscv_cpu_register_types)  
> > This still isn't fixed as requested
> >  http://lists.gnu.org/archive/html/qemu-devel/2018-02/msg06412.html  
> 
> 
> It's possibly because I explicitly requested a clarification. Pointing at a
> commit and being asked to infer what the desired change is, is not what I
> would call reasonable feedback. The code has already been reviewed.
Well, it's been pointed since v4 (it's not like change has been asked for
at the last moment) and no one asked for clarification.


> We have
> just expanded on it in a manner consistent with how the ARM port handled
> cpu initialization.
> I'm happy to comply if you give me detailed instructions on what is wrong,
> why, and how to fix it versus infer your problem from this commit to
> another architecture.
> 
> Apologies if i'm a bit slow, but I really don't understand the change you
> intend us to make.
There is nothing wrong and it's totally ok to use existing code to
start with writing new patches. The only thing is that it's moving
codebase and new patches shouldn't interfere with ongoing work done
by others. Hence sometimes you see comments requesting to use
a particular approach to do something that could be done in
various ways.

In this specific case used reference code (ARM) still uses old way
register CPU types that is phased out in favor of DEFINE_TYPES.

There is nothing that warrants usage of custom 'struct RISCVCPUInfo'
and riscv_cpu_register_types().
You should use pretty trivial approach used in 974e58d2, namely:

 1   rewrite riscv_cpu_list() to use object_class_get_list()
     instead of 'struct RISCVCPUInfo', example sh4_cpu_list()
 2.1 drop 'struct RISCVCPUInfo' and use TypeInfo array instead
     superh_cpu_type_infos[] and DEFINE_SUPERH_CPU_TYPE() could serve as example
 2.2 replace riscv_cpu_register_types/type_init with DEFINE_TYPES()

That way your code will be consistent with direction this part
moves towards and when others work on generalizing CPU related parts
they won't have to deal with one more instance of old style cpu
types init and listing.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-06  8:58       ` Igor Mammedov
@ 2018-03-06 10:41         ` Igor Mammedov
  2018-03-07  3:23         ` Michael Clark
  1 sibling, 0 replies; 70+ messages in thread
From: Igor Mammedov @ 2018-03-06 10:41 UTC (permalink / raw)
  To: Michael Clark
  Cc: Bastian Koppelmann, Palmer Dabbelt, QEMU Developers,
	Sagar Karandikar, RISC-V Patches

On Tue, 6 Mar 2018 09:58:34 +0100
Igor Mammedov <imammedo@redhat.com> wrote:

> On Tue, 6 Mar 2018 11:24:02 +1300
> Michael Clark <mjc@sifive.com> wrote:
> 
> > On Mon, Mar 5, 2018 at 10:44 PM, Igor Mammedov <imammedo@redhat.com> wrote:
> >   
> > > On Sat,  3 Mar 2018 02:51:31 +1300
> > > Michael Clark <mjc@sifive.com> wrote:
> > >    
> > > > Add CPU state header, CPU definitions and initialization routines
> > > >
> > > > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> > > > Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> > > > Signed-off-by: Michael Clark <mjc@sifive.com>
> > > > ---
> > > >  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++    
> > > ++++++++++++++++++    
> > > >  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
> > > >  target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++    
> > > +++++++++++++++    
> > > >  3 files changed, 1139 insertions(+)
> > > >  create mode 100644 target/riscv/cpu.c
> > > >  create mode 100644 target/riscv/cpu.h
> > > >  create mode 100644 target/riscv/cpu_bits.h
> > > >
> > > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > > > new file mode 100644
> > > > index 0000000..4851890
> > > > --- /dev/null
> > > > +++ b/target/riscv/cpu.c    
> > > [...]
> > >    
> > > > +
> > > > +typedef struct RISCVCPUInfo {
> > > > +    const int bit_widths;
> > > > +    const char *name;
> > > > +    void (*initfn)(Object *obj);
> > > > +} RISCVCPUInfo;
> > > > +    
> > > [...]
> > >    
> > > > +static const RISCVCPUInfo riscv_cpus[] = {
> > > > +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1,    
> > > rv32gcsu_priv1_09_1_cpu_init },    
> > > > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0,    
> > > rv32gcsu_priv1_10_0_cpu_init },    
> > > > +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init    
> > > },    
> > > > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1,    
> > > rv64gcsu_priv1_09_1_cpu_init },    
> > > > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0,    
> > > rv64gcsu_priv1_10_0_cpu_init },    
> > > > +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
> > > > +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
> > > > +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init    
> > > },    
> > > > +    { 0, NULL, NULL }
> > > > +};
> > > > +    
> > > [...]
> > >    
> > > > +static void cpu_register(const RISCVCPUInfo *info)
> > > > +{
> > > > +    TypeInfo type_info = {
> > > > +        .name = info->name,
> > > > +        .parent = TYPE_RISCV_CPU,
> > > > +        .instance_size = sizeof(RISCVCPU),
> > > > +        .instance_init = info->initfn,
> > > > +    };
> > > > +
> > > > +    type_register(&type_info);
> > > > +}    
> > > [...]
> > >    
> > > > +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
> > > > +{
> > > > +    const RISCVCPUInfo *info = riscv_cpus;
> > > > +
> > > > +    while (info->name) {
> > > > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > > > +            (*cpu_fprintf)(f, "%s\n", info->name);
> > > > +        }
> > > > +        info++;
> > > > +    }
> > > > +}
> > > > +
> > > > +static void riscv_cpu_register_types(void)
> > > > +{
> > > > +    const RISCVCPUInfo *info = riscv_cpus;
> > > > +
> > > > +    type_register_static(&riscv_cpu_type_info);
> > > > +
> > > > +    while (info->name) {
> > > > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > > > +            cpu_register(info);
> > > > +        }
> > > > +        info++;
> > > > +    }
> > > > +}
> > > > +
> > > > +type_init(riscv_cpu_register_types)    
> > > This still isn't fixed as requested
> > >  http://lists.gnu.org/archive/html/qemu-devel/2018-02/msg06412.html    
> > 
> > 
> > It's possibly because I explicitly requested a clarification. Pointing at a
> > commit and being asked to infer what the desired change is, is not what I
> > would call reasonable feedback. The code has already been reviewed.  
> Well, it's been pointed since v4 (it's not like change has been asked for
> at the last moment) and no one asked for clarification.
> 
> 
> > We have
> > just expanded on it in a manner consistent with how the ARM port handled
> > cpu initialization.
> > I'm happy to comply if you give me detailed instructions on what is wrong,
> > why, and how to fix it versus infer your problem from this commit to
> > another architecture.
> > 
> > Apologies if i'm a bit slow, but I really don't understand the change you
> > intend us to make.  
> There is nothing wrong and it's totally ok to use existing code to
> start with writing new patches. The only thing is that it's moving
> codebase and new patches shouldn't interfere with ongoing work done
> by others. Hence sometimes you see comments requesting to use
> a particular approach to do something that could be done in
> various ways.
> 
> In this specific case used reference code (ARM) still uses old way
> register CPU types that is phased out in favor of DEFINE_TYPES.
> 
> There is nothing that warrants usage of custom 'struct RISCVCPUInfo'
> and riscv_cpu_register_types().
> You should use pretty trivial approach used in 974e58d2, namely:
> 
>  1   rewrite riscv_cpu_list() to use object_class_get_list()
>      instead of 'struct RISCVCPUInfo', example sh4_cpu_list()
>  2.1 drop 'struct RISCVCPUInfo' and use TypeInfo array instead
>      superh_cpu_type_infos[] and DEFINE_SUPERH_CPU_TYPE() could serve as example
>  2.2 replace riscv_cpu_register_types/type_init with DEFINE_TYPES()
> 
> That way your code will be consistent with direction this part
> moves towards and when others work on generalizing CPU related parts
> they won't have to deal with one more instance of old style cpu
> types init and listing.

PS:

Considering upcomming soft-freeze,
It's fine to do this change on top of this series instead
of re-spinning whole series.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition
  2018-03-06  8:58       ` Igor Mammedov
  2018-03-06 10:41         ` Igor Mammedov
@ 2018-03-07  3:23         ` Michael Clark
  1 sibling, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-07  3:23 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Tue, Mar 6, 2018 at 9:58 PM, Igor Mammedov <imammedo@redhat.com> wrote:

> On Tue, 6 Mar 2018 11:24:02 +1300
> Michael Clark <mjc@sifive.com> wrote:
>
> > On Mon, Mar 5, 2018 at 10:44 PM, Igor Mammedov <imammedo@redhat.com>
> wrote:
> >
> > > On Sat,  3 Mar 2018 02:51:31 +1300
> > > Michael Clark <mjc@sifive.com> wrote:
> > >
> > > > Add CPU state header, CPU definitions and initialization routines
> > > >
> > > > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> > > > Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> > > > Signed-off-by: Michael Clark <mjc@sifive.com>
> > > > ---
> > > >  target/riscv/cpu.c      | 432 ++++++++++++++++++++++++++++++
> > > ++++++++++++++++++
> > > >  target/riscv/cpu.h      | 296 +++++++++++++++++++++++++++++++++
> > > >  target/riscv/cpu_bits.h | 411 ++++++++++++++++++++++++++++++
> > > +++++++++++++++
> > > >  3 files changed, 1139 insertions(+)
> > > >  create mode 100644 target/riscv/cpu.c
> > > >  create mode 100644 target/riscv/cpu.h
> > > >  create mode 100644 target/riscv/cpu_bits.h
> > > >
> > > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > > > new file mode 100644
> > > > index 0000000..4851890
> > > > --- /dev/null
> > > > +++ b/target/riscv/cpu.c
> > > [...]
> > >
> > > > +
> > > > +typedef struct RISCVCPUInfo {
> > > > +    const int bit_widths;
> > > > +    const char *name;
> > > > +    void (*initfn)(Object *obj);
> > > > +} RISCVCPUInfo;
> > > > +
> > > [...]
> > >
> > > > +static const RISCVCPUInfo riscv_cpus[] = {
> > > > +    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1,
> > > rv32gcsu_priv1_09_1_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0,
> > > rv32gcsu_priv1_10_0_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,
> rv32imacu_nommu_cpu_init },
> > > > +    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init
> },
> > > > +    { 32, TYPE_RISCV_CPU_SIFIVE_U34,
>  rv32gcsu_priv1_10_0_cpu_init
> > > },
> > > > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1,
> > > rv64gcsu_priv1_09_1_cpu_init },
> > > > +    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0,
> > > rv64gcsu_priv1_10_0_cpu_init },
> > > > +    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,
> rv64imacu_nommu_cpu_init },
> > > > +    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init
> },
> > > > +    { 64, TYPE_RISCV_CPU_SIFIVE_U54,
>  rv64gcsu_priv1_10_0_cpu_init
> > > },
> > > > +    { 0, NULL, NULL }
> > > > +};
> > > > +
> > > [...]
> > >
> > > > +static void cpu_register(const RISCVCPUInfo *info)
> > > > +{
> > > > +    TypeInfo type_info = {
> > > > +        .name = info->name,
> > > > +        .parent = TYPE_RISCV_CPU,
> > > > +        .instance_size = sizeof(RISCVCPU),
> > > > +        .instance_init = info->initfn,
> > > > +    };
> > > > +
> > > > +    type_register(&type_info);
> > > > +}
> > > [...]
> > >
> > > > +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
> > > > +{
> > > > +    const RISCVCPUInfo *info = riscv_cpus;
> > > > +
> > > > +    while (info->name) {
> > > > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > > > +            (*cpu_fprintf)(f, "%s\n", info->name);
> > > > +        }
> > > > +        info++;
> > > > +    }
> > > > +}
> > > > +
> > > > +static void riscv_cpu_register_types(void)
> > > > +{
> > > > +    const RISCVCPUInfo *info = riscv_cpus;
> > > > +
> > > > +    type_register_static(&riscv_cpu_type_info);
> > > > +
> > > > +    while (info->name) {
> > > > +        if (info->bit_widths & TARGET_LONG_BITS) {
> > > > +            cpu_register(info);
> > > > +        }
> > > > +        info++;
> > > > +    }
> > > > +}
> > > > +
> > > > +type_init(riscv_cpu_register_types)
> > > This still isn't fixed as requested
> > >  http://lists.gnu.org/archive/html/qemu-devel/2018-02/msg06412.html
> >
> >
> > It's possibly because I explicitly requested a clarification. Pointing
> at a
> > commit and being asked to infer what the desired change is, is not what I
> > would call reasonable feedback. The code has already been reviewed.
> Well, it's been pointed since v4 (it's not like change has been asked for
> at the last moment) and no one asked for clarification.
>
>
> > We have
> > just expanded on it in a manner consistent with how the ARM port handled
> > cpu initialization.
> > I'm happy to comply if you give me detailed instructions on what is
> wrong,
> > why, and how to fix it versus infer your problem from this commit to
> > another architecture.
> >
> > Apologies if i'm a bit slow, but I really don't understand the change you
> > intend us to make.
> There is nothing wrong and it's totally ok to use existing code to
> start with writing new patches. The only thing is that it's moving
> codebase and new patches shouldn't interfere with ongoing work done
> by others. Hence sometimes you see comments requesting to use
> a particular approach to do something that could be done in
> various ways.
>
> In this specific case used reference code (ARM) still uses old way
> register CPU types that is phased out in favor of DEFINE_TYPES.
>
> There is nothing that warrants usage of custom 'struct RISCVCPUInfo'
> and riscv_cpu_register_types().
> You should use pretty trivial approach used in 974e58d2, namely:
>
>  1   rewrite riscv_cpu_list() to use object_class_get_list()
>      instead of 'struct RISCVCPUInfo', example sh4_cpu_list()
>

I've done this. I'll send a patches shortly. Diffstat after this change is
not promising.:

 cpu.c |   43 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 8 deletions(-)

 2.1 drop 'struct RISCVCPUInfo' and use TypeInfo array instead
>      superh_cpu_type_infos[] and DEFINE_SUPERH_CPU_TYPE() could serve as
> example
>

The RISC-V port has a common codebase for riscv32 and riscv64 and we are
now using RISCVCPUInfo to contain a mask of the supported bitwidths of each
CPU. It allowed us to remove #ifdef at the expense of several cycles at
runtime in cpu_register. Some of the generic models can be used on any
bitwidth. e.g. "any" can be instantiated on any bitwidth. We could further
reduce code if we were to format the bitwidth into the model name, with the
current cpu initialisation model. This was added so that we can use the
RISC-V ISA strings as model names for the generic CPUs and mask the SiFive
in or out depending on whether we are compiling for TARGET_RISCV32
or TARGET_RISCV64

The arm style is intrinsicly compact without the use of macros because it
uses a templace type in cpu_register. In any case this particular change is
a win in terms of lines of code.

 cpu.c |   87
++++++++++++++++++++++++++++--------------------------------------
 1 file changed, 38 insertions(+), 49 deletions(-)

 2.2 replace riscv_cpu_register_types/type_init with DEFINE_TYPES()
>
> That way your code will be consistent with direction this part
> moves towards and when others work on generalizing CPU related parts
> they won't have to deal with one more instance of old style cpu
> types init and listing.
>

I just squashed them. Now we have added some more C macros, #ifdefs and
several more lines of code. BTW, all of the current machines select
specific processors, however, this may change, but it is likely we may want
to use RISCVCPUInfo to contain additional information as to which
processors are compatible with which boards/machines. With this patch we
lose our custom meta-data; currently bit width mask.

 cpu.c |  124
+++++++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 70 insertions(+), 54 deletions(-)

Okay. Done. I'll send a patch for review in a moment... Tell me what you
think when you do a side-by-side comparison...

It doesn't break anything so I'm not fussed either way...

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines Michael Clark
@ 2018-03-09  4:50   ` Michael Clark
  2018-05-14 16:49   ` Peter Maydell
  1 sibling, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-09  4:50 UTC (permalink / raw)
  To: QEMU Developers
  Cc: Michael Clark, Palmer Dabbelt, Sagar Karandikar,
	Bastian Koppelmann, RISC-V Patches

On Sat, Mar 3, 2018 at 2:51 AM, Michael Clark <mjc@sifive.com> wrote:

> RISC-V machines compatble with Spike aka riscv-isa-sim, the RISC-V
> Instruction Set Simulator. The following machines are implemented:
>
> - 'spike_v1.9.1'; HTIF console, config-string, Privileged ISA Version 1.9.1
> - 'spike_v1.10'; HTIF console, device-tree, Privileged ISA Version 1.10
>
> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  hw/riscv/spike.c         | 376 ++++++++++++++++++++++++++++++
> +++++++++++++++++
>  include/hw/riscv/spike.h |  53 +++++++
>  2 files changed, 429 insertions(+)
>  create mode 100644 hw/riscv/spike.c
>  create mode 100644 include/hw/riscv/spike.h
>
> diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
> new file mode 100644
> index 0000000..2d1f114
> --- /dev/null
> +++ b/hw/riscv/spike.c
> @@ -0,0 +1,376 @@
> +/*
> + * QEMU RISC-V Spike Board
> + *
> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
> + * Copyright (c) 2017-2018 SiFive, Inc.
> + *
> + * This provides a RISC-V Board with the following devices:
> + *
> + * 0) HTIF Console and Poweroff
> + * 1) CLINT (Timer and IPI)
> + * 2) PLIC (Platform Level Interrupt Controller)
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qemu/error-report.h"
> +#include "qapi/error.h"
> +#include "hw/hw.h"
> +#include "hw/boards.h"
> +#include "hw/loader.h"
> +#include "hw/sysbus.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/riscv_htif.h"
> +#include "hw/riscv/riscv_hart.h"
> +#include "hw/riscv/sifive_clint.h"
> +#include "hw/riscv/spike.h"
> +#include "chardev/char.h"
> +#include "sysemu/arch_init.h"
> +#include "sysemu/device_tree.h"
> +#include "exec/address-spaces.h"
> +#include "elf.h"
> +
> +static const struct MemmapEntry {
> +    hwaddr base;
> +    hwaddr size;
> +} spike_memmap[] = {
> +    [SPIKE_MROM] =     {     0x1000,     0x2000 },
> +    [SPIKE_CLINT] =    {  0x2000000,    0x10000 },
> +    [SPIKE_DRAM] =     { 0x80000000,        0x0 },
> +};
> +
> +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
> +{
> +    int i;
> +    for (i = 0; i < (len >> 2); i++) {
> +        stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
> +    }
> +}


We may very well have an endianness bug here. I wasn't sure if we had to
use stl_le_phys or whether stl_phys know the difference between the host
and guest endianness and does the right thing.

We use copy_le32_to_phys to copy in the reset vector which is an array of
32-bit words containing RISC-V machine code. Consider it a placeholder for
correct code.

There has been no testing on big-endian hosts as I don't have access to
any. I guess I could simulate an environment with qemu and nest qemu-riscv
inside of qemu-ppc-be

+static uint64_t identity_translate(void *opaque, uint64_t addr)
> +{
> +    return addr;
> +}
> +
> +static uint64_t load_kernel(const char *kernel_filename)
> +{
> +    uint64_t kernel_entry, kernel_high;
> +
> +    if (load_elf_ram_sym(kernel_filename, identity_translate, NULL,
> +            &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0,
> +            NULL, true, htif_symbol_callback) < 0) {
> +        error_report("qemu: could not load kernel '%s'", kernel_filename);
> +        exit(1);
> +    }
> +    return kernel_entry;
> +}
> +
> +static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
> +    uint64_t mem_size, const char *cmdline)
> +{
> +    void *fdt;
> +    int cpu;
> +    uint32_t *cells;
> +    char *nodename;
> +
> +    fdt = s->fdt = create_device_tree(&s->fdt_size);
> +    if (!fdt) {
> +        error_report("create_device_tree() failed");
> +        exit(1);
> +    }
> +
> +    qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
> +    qemu_fdt_setprop_string(fdt, "/", "compatible",
> "ucbbar,spike-bare-dev");
> +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> +
> +    qemu_fdt_add_subnode(fdt, "/htif");
> +    qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
> +
> +    qemu_fdt_add_subnode(fdt, "/soc");
> +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> +    qemu_fdt_setprop_string(fdt, "/soc", "compatible",
> "ucbbar,spike-bare-soc");
> +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> +
> +    nodename = g_strdup_printf("/memory@%lx",
> +        (long)memmap[SPIKE_DRAM].base);
> +    qemu_fdt_add_subnode(fdt, nodename);
> +    qemu_fdt_setprop_cells(fdt, nodename, "reg",
> +        memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base,
> +        mem_size >> 32, mem_size);
> +    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
> +    g_free(nodename);
> +
> +    qemu_fdt_add_subnode(fdt, "/cpus");
> +    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
> +    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> +    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> +
> +    for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
> +        nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
> +        char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller",
> cpu);
> +        char *isa = riscv_isa_string(&s->soc.harts[cpu]);
> +        qemu_fdt_add_subnode(fdt, nodename);
> +        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
> 1000000000);
> +        qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
> +        qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
> +        qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
> +        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
> +        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
> +        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
> +        qemu_fdt_add_subnode(fdt, intc);
> +        qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
> +        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
> +        qemu_fdt_setprop_string(fdt, intc, "compatible",
> "riscv,cpu-intc");
> +        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
> +        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
> +        g_free(isa);
> +        g_free(intc);
> +        g_free(nodename);
> +    }
> +
> +    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
> +    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
> +        nodename =
> +            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
> +        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
> +        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
> +        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
> +        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
> +        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
> +        g_free(nodename);
> +    }
> +    nodename = g_strdup_printf("/soc/clint@%lx",
> +        (long)memmap[SPIKE_CLINT].base);
> +    qemu_fdt_add_subnode(fdt, nodename);
> +    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
> +    qemu_fdt_setprop_cells(fdt, nodename, "reg",
> +        0x0, memmap[SPIKE_CLINT].base,
> +        0x0, memmap[SPIKE_CLINT].size);
> +    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
> +        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
> +    g_free(cells);
> +    g_free(nodename);
> +
> +    qemu_fdt_add_subnode(fdt, "/chosen");
> +    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
> + }
> +
> +static void spike_v1_10_0_board_init(MachineState *machine)
> +{
> +    const struct MemmapEntry *memmap = spike_memmap;
> +
> +    SpikeState *s = g_new0(SpikeState, 1);
> +    MemoryRegion *system_memory = get_system_memory();
> +    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
> +    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
> +
> +    /* Initialize SOC */
> +    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
> +    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
> +                              &error_abort);
> +    object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU,
> "cpu-type",
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
> +                            &error_abort);
> +    object_property_set_bool(OBJECT(&s->soc), true, "realized",
> +                            &error_abort);
> +
> +    /* register system main memory (actual RAM) */
> +    memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
> +                           machine->ram_size, &error_fatal);
> +    memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
> +        main_mem);
> +
> +    /* create device tree */
> +    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
> +
> +    /* boot rom */
> +    memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
> +                           s->fdt_size + 0x2000, &error_fatal);
> +    memory_region_add_subregion(system_memory, 0x0, boot_rom);
> +
> +    if (machine->kernel_filename) {
> +        load_kernel(machine->kernel_filename);
> +    }
> +
> +    /* reset vector */
> +    uint32_t reset_vec[8] = {
> +        0x00000297,                  /* 1:  auipc  t0, %pcrel_hi(dtb) */
> +        0x02028593,                  /*     addi   a1, t0, %pcrel_lo(1b)
> */
> +        0xf1402573,                  /*     csrr   a0, mhartid  */
> +#if defined(TARGET_RISCV32)
> +        0x0182a283,                  /*     lw     t0, 24(t0) */
> +#elif defined(TARGET_RISCV64)
> +        0x0182b283,                  /*     ld     t0, 24(t0) */
> +#endif
> +        0x00028067,                  /*     jr     t0 */
> +        0x00000000,
> +        memmap[SPIKE_DRAM].base,     /* start: .dword DRAM_BASE */
> +        0x00000000,
> +                                     /* dtb: */
> +    };
> +
> +    /* copy in the reset vector */
> +    copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec,
> sizeof(reset_vec));
> +
> +    /* copy in the device tree */
> +    qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
> +    cpu_physical_memory_write(memmap[SPIKE_MROM].base +
> sizeof(reset_vec),
> +        s->fdt, s->fdt_size);
> +
> +    /* initialize HTIF using symbols found in load_kernel */
> +    htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env,
> serial_hds[0]);
> +
> +    /* Core Local Interruptor (timer and IPI) */
> +    sifive_clint_create(memmap[SPIKE_CLINT].base,
> memmap[SPIKE_CLINT].size,
> +        smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
> +}
> +
> +static void spike_v1_09_1_board_init(MachineState *machine)
> +{
> +    const struct MemmapEntry *memmap = spike_memmap;
> +
> +    SpikeState *s = g_new0(SpikeState, 1);
> +    MemoryRegion *system_memory = get_system_memory();
> +    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
> +    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
> +
> +    /* Initialize SOC */
> +    object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
> +    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
> +                              &error_abort);
> +    object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU,
> "cpu-type",
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
> +                            &error_abort);
> +    object_property_set_bool(OBJECT(&s->soc), true, "realized",
> +                            &error_abort);
> +
> +    /* register system main memory (actual RAM) */
> +    memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
> +                           machine->ram_size, &error_fatal);
> +    memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
> +        main_mem);
> +
> +    /* boot rom */
> +    memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
> +                           0x40000, &error_fatal);
> +    memory_region_add_subregion(system_memory, 0x0, boot_rom);
> +
> +    if (machine->kernel_filename) {
> +        load_kernel(machine->kernel_filename);
> +    }
> +
> +    /* reset vector */
> +    uint32_t reset_vec[8] = {
> +        0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui
> */
> +        0x00028067,                   /* jump to DRAM_BASE */
> +        0x00000000,                   /* reserved */
> +        memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string
> pointer */
> +        0, 0, 0, 0                    /* trap vector */
> +    };
> +
> +    /* part one of config string - before memory size specified */
> +    const char *config_string_tmpl =
> +        "platform {\n"
> +        "  vendor ucb;\n"
> +        "  arch spike;\n"
> +        "};\n"
> +        "rtc {\n"
> +        "  addr 0x%" PRIx64 "x;\n"
> +        "};\n"
> +        "ram {\n"
> +        "  0 {\n"
> +        "    addr 0x%" PRIx64 "x;\n"
> +        "    size 0x%" PRIx64 "x;\n"
> +        "  };\n"
> +        "};\n"
> +        "core {\n"
> +        "  0" " {\n"
> +        "    " "0 {\n"
> +        "      isa %s;\n"
> +        "      timecmp 0x%" PRIx64 "x;\n"
> +        "      ipi 0x%" PRIx64 "x;\n"
> +        "    };\n"
> +        "  };\n"
> +        "};\n";
> +
> +    /* build config string with supplied memory size */
> +    char *isa = riscv_isa_string(&s->soc.harts[0]);
> +    size_t config_string_size = strlen(config_string_tmpl) + 48;
> +    char *config_string = malloc(config_string_size);
> +    snprintf(config_string, config_string_size, config_string_tmpl,
> +        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE,
> +        (uint64_t)memmap[SPIKE_DRAM].base,
> +        (uint64_t)ram_size, isa,
> +        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE,
> +        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE);
> +    g_free(isa);
> +    size_t config_string_len = strlen(config_string);
> +
> +    /* copy in the reset vector */
> +    copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec,
> sizeof(reset_vec));
> +
> +    /* copy in the config string */
> +    cpu_physical_memory_write(memmap[SPIKE_MROM].base +
> sizeof(reset_vec),
> +        config_string, config_string_len);
> +
> +    /* initialize HTIF using symbols found in load_kernel */
> +    htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env,
> serial_hds[0]);
> +
> +    /* Core Local Interruptor (timer and IPI) */
> +    sifive_clint_create(memmap[SPIKE_CLINT].base,
> memmap[SPIKE_CLINT].size,
> +        smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
> +}
> +
> +static const TypeInfo spike_v_1_09_1_device = {
> +    .name          = TYPE_RISCV_SPIKE_V1_09_1_BOARD,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(SpikeState),
> +};
> +
> +static const TypeInfo spike_v_1_10_0_device = {
> +    .name          = TYPE_RISCV_SPIKE_V1_10_0_BOARD,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(SpikeState),
> +};
> +
> +static void spike_v1_09_1_machine_init(MachineClass *mc)
> +{
> +    mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)";
> +    mc->init = spike_v1_09_1_board_init;
> +    mc->max_cpus = 1;
> +}
> +
> +static void spike_v1_10_0_machine_init(MachineClass *mc)
> +{
> +    mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)";
> +    mc->init = spike_v1_10_0_board_init;
> +    mc->max_cpus = 1;
> +    mc->is_default = 1;
> +}
> +
> +DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init)
> +DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init)
> +
> +static void riscv_spike_board_register_types(void)
> +{
> +    type_register_static(&spike_v_1_09_1_device);
> +    type_register_static(&spike_v_1_10_0_device);
> +}
> +
> +type_init(riscv_spike_board_register_types);
> diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h
> new file mode 100644
> index 0000000..53d5eea
> --- /dev/null
> +++ b/include/hw/riscv/spike.h
> @@ -0,0 +1,53 @@
> +/*
> + * Spike machine interface
> + *
> + * Copyright (c) 2017 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef HW_SPIKE_H
> +#define HW_SPIKE_H
> +
> +#define TYPE_RISCV_SPIKE_V1_09_1_BOARD "riscv.spike_v1_9"
> +#define TYPE_RISCV_SPIKE_V1_10_0_BOARD "riscv.spike_v1_10"
> +
> +#define SPIKE(obj) \
> +    OBJECT_CHECK(SpikeState, (obj), TYPE_RISCV_SPIKE_BOARD)
> +
> +typedef struct {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    RISCVHartArrayState soc;
> +    void *fdt;
> +    int fdt_size;
> +} SpikeState;
> +
> +
> +enum {
> +    SPIKE_MROM,
> +    SPIKE_CLINT,
> +    SPIKE_DRAM
> +};
> +
> +#if defined(TARGET_RISCV32)
> +#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV32GCSU_V1_09_1
> +#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0
> +#elif defined(TARGET_RISCV64)
> +#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV64GCSU_V1_09_1
> +#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0
> +#endif
> +
> +#endif
> --
> 2.7.0
>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 11/23] Add symbol table callback interface to load_elf
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 11/23] Add symbol table callback interface to load_elf Michael Clark
@ 2018-03-09 11:34   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 11:34 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> The RISC-V HTIF (Host Target Interface) console device requires access
> to the symbol table to locate the 'tohost' and 'fromhost' symbols.
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Michael Clark <mjc@sifive.com>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  hw/core/loader.c     | 18 ++++++++++++++++--
>  include/hw/elf_ops.h | 34 +++++++++++++++++++++-------------
>  include/hw/loader.h  | 17 ++++++++++++++++-
>  3 files changed, 53 insertions(+), 16 deletions(-)
> 
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index c08f130..1d26c6e 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -450,6 +450,20 @@ int load_elf_ram(const char *filename,
>                   int clear_lsb, int data_swab, AddressSpace *as,
>                   bool load_rom)
>  {
> +    return load_elf_ram_sym(filename, translate_fn, translate_opaque,
> +                            pentry, lowaddr, highaddr, big_endian,
> +                            elf_machine, clear_lsb, data_swab, as,
> +                            load_rom, NULL);
> +}
> +
> +/* return < 0 if error, otherwise the number of bytes loaded in memory */
> +int load_elf_ram_sym(const char *filename,
> +                     uint64_t (*translate_fn)(void *, uint64_t),
> +                     void *translate_opaque, uint64_t *pentry,
> +                     uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
> +                     int elf_machine, int clear_lsb, int data_swab,
> +                     AddressSpace *as, bool load_rom, symbol_fn_t sym_cb)
> +{
>      int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
>      uint8_t e_ident[EI_NIDENT];
>  
> @@ -488,11 +502,11 @@ int load_elf_ram(const char *filename,
>      if (e_ident[EI_CLASS] == ELFCLASS64) {
>          ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
>                           pentry, lowaddr, highaddr, elf_machine, clear_lsb,
> -                         data_swab, as, load_rom);
> +                         data_swab, as, load_rom, sym_cb);
>      } else {
>          ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
>                           pentry, lowaddr, highaddr, elf_machine, clear_lsb,
> -                         data_swab, as, load_rom);
> +                         data_swab, as, load_rom, sym_cb);
>      }
>  
>   fail:
> diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h
> index d192e7e..b6e19e3 100644
> --- a/include/hw/elf_ops.h
> +++ b/include/hw/elf_ops.h
> @@ -105,7 +105,7 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1)
>  }
>  
>  static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
> -                                  int clear_lsb)
> +                                  int clear_lsb, symbol_fn_t sym_cb)
>  {
>      struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
>      struct elf_sym *syms = NULL;
> @@ -133,10 +133,26 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
>  
>      nsyms = symtab->sh_size / sizeof(struct elf_sym);
>  
> +    /* String table */
> +    if (symtab->sh_link >= ehdr->e_shnum) {
> +        goto fail;
> +    }
> +    strtab = &shdr_table[symtab->sh_link];
> +
> +    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
> +    if (!str) {
> +        goto fail;
> +    }
> +
>      i = 0;
>      while (i < nsyms) {
> -        if (must_swab)
> +        if (must_swab) {
>              glue(bswap_sym, SZ)(&syms[i]);
> +        }
> +        if (sym_cb) {
> +            sym_cb(str + syms[i].st_name, syms[i].st_info,
> +                   syms[i].st_value, syms[i].st_size);
> +        }
>          /* We are only interested in function symbols.
>             Throw everything else away.  */
>          if (syms[i].st_shndx == SHN_UNDEF ||
> @@ -163,15 +179,6 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
>          }
>      }
>  
> -    /* String table */
> -    if (symtab->sh_link >= ehdr->e_shnum)
> -        goto fail;
> -    strtab = &shdr_table[symtab->sh_link];
> -
> -    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
> -    if (!str)
> -        goto fail;
> -
>      /* Commit */
>      s = g_malloc0(sizeof(*s));
>      s->lookup_symbol = glue(lookup_symbol, SZ);
> @@ -264,7 +271,8 @@ static int glue(load_elf, SZ)(const char *name, int fd,
>                                int must_swab, uint64_t *pentry,
>                                uint64_t *lowaddr, uint64_t *highaddr,
>                                int elf_machine, int clear_lsb, int data_swab,
> -                              AddressSpace *as, bool load_rom)
> +                              AddressSpace *as, bool load_rom,
> +                              symbol_fn_t sym_cb)
>  {
>      struct elfhdr ehdr;
>      struct elf_phdr *phdr = NULL, *ph;
> @@ -329,7 +337,7 @@ static int glue(load_elf, SZ)(const char *name, int fd,
>      if (pentry)
>     	*pentry = (uint64_t)(elf_sword)ehdr.e_entry;
>  
> -    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb);
> +    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb);
>  
>      size = ehdr.e_phnum * sizeof(phdr[0]);
>      if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) {
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index 5edbe02..7b05e8b 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -64,7 +64,7 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz);
>  #define ELF_LOAD_WRONG_ENDIAN -4
>  const char *load_elf_strerror(int error);
>  
> -/** load_elf_ram:
> +/** load_elf_ram_sym:
>   * @filename: Path of ELF file
>   * @translate_fn: optional function to translate load addresses
>   * @translate_opaque: opaque data passed to @translate_fn
> @@ -81,6 +81,7 @@ const char *load_elf_strerror(int error);
>   * @as: The AddressSpace to load the ELF to. The value of address_space_memory
>   *      is used if nothing is supplied here.
>   * @load_rom : Load ELF binary as ROM
> + * @sym_cb: Callback function for symbol table entries
>   *
>   * Load an ELF file's contents to the emulated system's address space.
>   * Clients may optionally specify a callback to perform address
> @@ -93,6 +94,20 @@ const char *load_elf_strerror(int error);
>   * If @elf_machine is EM_NONE then the machine type will be read from the
>   * ELF header and no checks will be carried out against the machine type.
>   */
> +typedef void (*symbol_fn_t)(const char *st_name, int st_info,
> +                            uint64_t st_value, uint64_t st_size);
> +
> +int load_elf_ram_sym(const char *filename,
> +                     uint64_t (*translate_fn)(void *, uint64_t),
> +                     void *translate_opaque, uint64_t *pentry,
> +                     uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
> +                     int elf_machine, int clear_lsb, int data_swab,
> +                     AddressSpace *as, bool load_rom, symbol_fn_t sym_cb);
> +
> +/** load_elf_ram:
> + * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a
> + * symbol callback function
> + */
>  int load_elf_ram(const char *filename,
>                   uint64_t (*translate_fn)(void *, uint64_t),
>                   void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 12/23] RISC-V HTIF Console
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 12/23] RISC-V HTIF Console Michael Clark
@ 2018-03-09 11:52   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 11:52 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Stefan O'Rear, Sagar Karandikar, Bastian Koppelmann,
	Palmer Dabbelt, RISC-V Patches

Hi Michael,

On 03/02/2018 02:51 PM, Michael Clark wrote:
> HTIF (Host Target Interface) provides console emulation for QEMU. HTIF
> allows identical copies of BBL (Berkeley Boot Loader) and linux to run
> on both Spike and QEMU. BBL provides HTIF console access via the
> SBI (Supervisor Binary Interface) and the linux kernel SBI console.
> 
> The HTIT chardev implements the pre qom legacy interface consistent
> with the 16550a UART in 'hw/char/serial.c'.
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  hw/riscv/riscv_htif.c         | 258 ++++++++++++++++++++++++++++++++++++++++++
>  include/hw/riscv/riscv_htif.h |  61 ++++++++++
>  2 files changed, 319 insertions(+)
>  create mode 100644 hw/riscv/riscv_htif.c
>  create mode 100644 include/hw/riscv/riscv_htif.h
> 
> diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c
> new file mode 100644
> index 0000000..3e17f30
> --- /dev/null
> +++ b/hw/riscv/riscv_htif.c
> @@ -0,0 +1,258 @@
> +/*
> + * QEMU RISC-V Host Target Interface (HTIF) Emulation
> + *
> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
> + * Copyright (c) 2017-2018 SiFive, Inc.
> + *
> + * This provides HTIF device emulation for QEMU. At the moment this allows
> + * for identical copies of bbl/linux to run on both spike and QEMU.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.h"
> +#include "hw/sysbus.h"
> +#include "hw/char/serial.h"
> +#include "chardev/char.h"
> +#include "chardev/char-fe.h"
> +#include "hw/riscv/riscv_htif.h"
> +#include "qemu/timer.h"
> +#include "exec/address-spaces.h"
> +#include "qemu/error-report.h"
> +
> +#define RISCV_DEBUG_HTIF 0
> +#define HTIF_DEBUG(fmt, ...)                                                   \
> +    do {                                                                       \
> +        if (RISCV_DEBUG_HTIF) {                                                \
> +            qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
> +        }                                                                      \
> +    } while (0)
> +
> +static uint64_t fromhost_addr, tohost_addr;
> +
> +void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
> +    uint64_t st_size)

I guess QEMU code style is to align all args to the same column, but
can't find it in the CODING_STYLE.

> +{
> +    if (strcmp("fromhost", st_name) == 0) {
> +        fromhost_addr = st_value;
> +        if (st_size != 8) {
> +            error_report("HTIF fromhost must be 8 bytes");
> +            exit(1);
> +        }
> +    } else if (strcmp("tohost", st_name) == 0) {
> +        tohost_addr = st_value;
> +        if (st_size != 8) {
> +            error_report("HTIF tohost must be 8 bytes");
> +            exit(1);
> +        }
> +    }
> +}
> +
> +/*
> + * Called by the char dev to see if HTIF is ready to accept input.
> + */
> +static int htif_can_recv(void *opaque)
> +{
> +    return 1;
> +}
> +
> +/*
> + * Called by the char dev to supply input to HTIF console.
> + * We assume that we will receive one character at a time.
> + */
> +static void htif_recv(void *opaque, const uint8_t *buf, int size)
> +{
> +    HTIFState *htifstate = opaque;
> +
> +    if (size != 1) {

Maybe worth logging error here.

> +        return;
> +    }
> +
> +    /* TODO - we need to check whether mfromhost is zero which indicates
> +              the device is ready to receive. The current implementation
> +              will drop characters */
> +
> +    uint64_t val_written = htifstate->pending_read;
> +    uint64_t resp = 0x100 | *buf;
> +
> +    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);

I personally find extract64(val_written, 48, 16) easier to read/review.

> +}
> +
> +/*
> + * Called by the char dev to supply special events to the HTIF console.
> + * Not used for HTIF.
> + */
> +static void htif_event(void *opaque, int event)
> +{
> +
> +}
> +
> +static int htif_be_change(void *opaque)
> +{
> +    HTIFState *s = opaque;
> +
> +    qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
> +        htif_be_change, s, NULL, true);
> +
> +    return 0;
> +}
> +
> +static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
> +{
> +    uint8_t device = val_written >> 56;
> +    uint8_t cmd = val_written >> 48;
> +    uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;

Ditto, extract64(val_written, 16, 48).

> +    int resp = 0;
> +
> +    HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64
> +        " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload);
> +
> +    /*
> +     * Currently, there is a fixed mapping of devices:
> +     * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy)
> +     * 1: Console
> +     */
> +    if (unlikely(device == 0x0)) {
> +        /* frontend syscall handler, shutdown and exit code support */
> +        if (cmd == 0x0) {
> +            if (payload & 0x1) {
> +                /* exit code */
> +                int exit_code = payload >> 1;
> +                exit(exit_code);
> +            } else {
> +                qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n");
> +            }
> +        } else {
> +            qemu_log("HTIF device %d: unknown command\n", device);

While here, it would be more useful to also log the command.

> +        }
> +    } else if (likely(device == 0x1)) {
> +        /* HTIF Console */
> +        if (cmd == 0x0) {
> +            /* this should be a queue, but not yet implemented as such */
> +            htifstate->pending_read = val_written;
> +            htifstate->env->mtohost = 0; /* clear to indicate we read */
> +            return;
> +        } else if (cmd == 0x1) {
> +            qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
> +            resp = 0x100 | (uint8_t)payload;
> +        } else {
> +            qemu_log("HTIF device %d: unknown command\n", device);

Ditto.

> +        }
> +    } else {
> +        qemu_log("HTIF unknown device or command\n");
> +        HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64
> +            " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload);

I think we can drop the first line and replace HTIF_DEBUG() ->
qemu_log() in the second.

> +    }
> +    /*
> +     * - latest bbl does not set fromhost to 0 if there is a value in tohost
> +     * - with this code enabled, qemu hangs waiting for fromhost to go to 0
> +     * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10
> +     * - HTIF needs protocol documentation and a more complete state machine
> +
> +        while (!htifstate->fromhost_inprogress &&
> +            htifstate->env->mfromhost != 0x0) {
> +        }
> +    */
> +    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
> +    htifstate->env->mtohost = 0; /* clear to indicate we read */
> +}
> +
> +#define TOHOST_OFFSET1 (htifstate->tohost_offset)
> +#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4)
> +#define FROMHOST_OFFSET1 (htifstate->fromhost_offset)
> +#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4)
> +
> +/* CPU wants to read an HTIF register */
> +static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    HTIFState *htifstate = opaque;
> +    if (addr == TOHOST_OFFSET1) {
> +        return htifstate->env->mtohost & 0xFFFFFFFF;

UINT32_MAX is easier to read/review than count Fs.

> +    } else if (addr == TOHOST_OFFSET2) {
> +        return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF;
> +    } else if (addr == FROMHOST_OFFSET1) {
> +        return htifstate->env->mfromhost & 0xFFFFFFFF;
> +    } else if (addr == FROMHOST_OFFSET2) {
> +        return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF;
> +    } else {
> +        qemu_log("Invalid htif read: address %016" PRIx64 "\n",
> +            (uint64_t)addr);

"exec/hwaddr.h" provides HWADDR_PRIx to avoid such casts.

> +        return 0;
> +    }
> +}
> +
> +/* CPU wrote to an HTIF register */
> +static void htif_mm_write(void *opaque, hwaddr addr,
> +                            uint64_t value, unsigned size)

2 extra spaces for alignment :)

> +{
> +    HTIFState *htifstate = opaque;
> +    if (addr == TOHOST_OFFSET1) {
> +        if (htifstate->env->mtohost == 0x0) {
> +            htifstate->allow_tohost = 1;
> +            htifstate->env->mtohost = value & 0xFFFFFFFF;
> +        } else {
> +            htifstate->allow_tohost = 0;
> +        }
> +    } else if (addr == TOHOST_OFFSET2) {
> +        if (htifstate->allow_tohost) {
> +            htifstate->env->mtohost |= value << 32;
> +            htif_handle_tohost_write(htifstate, htifstate->env->mtohost);
> +        }
> +    } else if (addr == FROMHOST_OFFSET1) {
> +        htifstate->fromhost_inprogress = 1;
> +        htifstate->env->mfromhost = value & 0xFFFFFFFF;
> +    } else if (addr == FROMHOST_OFFSET2) {
> +        htifstate->env->mfromhost |= value << 32;
> +        htifstate->fromhost_inprogress = 0;
> +    } else {
> +        qemu_log("Invalid htif write: address %016" PRIx64 "\n",
> +            (uint64_t)addr);
> +    }
> +}
> +
> +static const MemoryRegionOps htif_mm_ops = {
> +    .read = htif_mm_read,
> +    .write = htif_mm_write,
> +};
> +
> +HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
> +    CPURISCVState *env, Chardev *chr)
> +{
> +    uint64_t base = MIN(tohost_addr, fromhost_addr);
> +    uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
> +    uint64_t tohost_offset = tohost_addr - base;
> +    uint64_t fromhost_offset = fromhost_addr - base;
> +
> +    HTIFState *s = g_malloc0(sizeof(HTIFState));
> +    s->address_space = address_space;
> +    s->main_mem = main_mem;
> +    s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem);
> +    s->env = env;
> +    s->tohost_offset = tohost_offset;
> +    s->fromhost_offset = fromhost_offset;
> +    s->pending_read = 0;
> +    s->allow_tohost = 0;
> +    s->fromhost_inprogress = 0;
> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
> +    qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
> +        htif_be_change, s, NULL, true);
> +    if (base) {
> +        memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
> +                            TYPE_HTIF_UART, size);
> +        memory_region_add_subregion(address_space, base, &s->mmio);
> +    }
> +
> +    return s;
> +}
> diff --git a/include/hw/riscv/riscv_htif.h b/include/hw/riscv/riscv_htif.h
> new file mode 100644
> index 0000000..fb5f881
> --- /dev/null
> +++ b/include/hw/riscv/riscv_htif.h
> @@ -0,0 +1,61 @@
> +/*
> + * QEMU RISCV Host Target Interface (HTIF) Emulation
> + *
> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
> + * Copyright (c) 2017-2018 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef HW_RISCV_HTIF_H
> +#define HW_RISCV_HTIF_H
> +
> +#include "hw/hw.h"
> +#include "chardev/char.h"
> +#include "chardev/char-fe.h"
> +#include "sysemu/sysemu.h"
> +#include "exec/memory.h"
> +#include "target/riscv/cpu.h"
> +
> +#define TYPE_HTIF_UART "riscv.htif.uart"
> +
> +typedef struct HTIFState {
> +    int allow_tohost;
> +    int fromhost_inprogress;
> +
> +    hwaddr tohost_offset;
> +    hwaddr fromhost_offset;
> +    uint64_t tohost_size;
> +    uint64_t fromhost_size;
> +    MemoryRegion mmio;
> +    MemoryRegion *address_space;
> +    MemoryRegion *main_mem;
> +    void *main_mem_ram_ptr;
> +
> +    CPURISCVState *env;
> +    CharBackend chr;
> +    uint64_t pending_read;
> +} HTIFState;
> +
> +extern const VMStateDescription vmstate_htif;
> +extern const MemoryRegionOps htif_io_ops;
> +
> +/* HTIF symbol callback */
> +void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
> +    uint64_t st_size);
> +
> +/* legacy pre qom */
> +HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
> +    CPURISCVState *env, Chardev *chr);
> +
> +#endif
> 

At any rate,
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher Michael Clark
@ 2018-03-09 11:57   ` Philippe Mathieu-Daudé
  2018-03-10  3:01     ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 11:57 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> Test finisher memory mapped device used to exit simulation.
> 
> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  hw/riscv/sifive_test.c         | 93 ++++++++++++++++++++++++++++++++++++++++++
>  include/hw/riscv/sifive_test.h | 42 +++++++++++++++++++
>  2 files changed, 135 insertions(+)
>  create mode 100644 hw/riscv/sifive_test.c
>  create mode 100644 include/hw/riscv/sifive_test.h
> 
> diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c
> new file mode 100644
> index 0000000..8abd2cd
> --- /dev/null
> +++ b/hw/riscv/sifive_test.c
> @@ -0,0 +1,93 @@
> +/*
> + * QEMU SiFive Test Finisher
> + *
> + * Copyright (c) 2018 SiFive, Inc.
> + *
> + * Test finisher memory mapped device used to exit simulation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/sysbus.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/sifive_test.h"
> +
> +static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    return 0;
> +}
> +
> +static void sifive_test_write(void *opaque, hwaddr addr,
> +           uint64_t val64, unsigned int size)
> +{
> +    if (addr == 0) {
> +        int status = val64 & 0xffff;
> +        int code = (val64 >> 16) & 0xffff;
> +        switch (status) {
> +        case FINISHER_FAIL:
> +            exit(code);
> +        case FINISHER_PASS:
> +            exit(0);
> +        default:
> +            break;
> +        }
> +    }
> +    hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
> +        __func__, (int)addr, val64);
> +}
> +
> +static const MemoryRegionOps sifive_test_ops = {
> +    .read = sifive_test_read,
> +    .write = sifive_test_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void sifive_test_init(Object *obj)
> +{
> +    SiFiveTestState *s = SIFIVE_TEST(obj);
> +
> +    memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s,
> +                          TYPE_SIFIVE_TEST, 0x1000);

0x1000? 0x8 is enough :)

> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +}
> +
> +static const TypeInfo sifive_test_info = {
> +    .name          = TYPE_SIFIVE_TEST,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(SiFiveTestState),
> +    .instance_init = sifive_test_init,
> +};
> +
> +static void sifive_test_register_types(void)
> +{
> +    type_register_static(&sifive_test_info);
> +}
> +
> +type_init(sifive_test_register_types)
> +
> +
> +/*
> + * Create Test device.
> + */
> +DeviceState *sifive_test_create(hwaddr addr)
> +{
> +    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST);
> +    qdev_init_nofail(dev);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_test.h
> new file mode 100644
> index 0000000..71d4c9f
> --- /dev/null
> +++ b/include/hw/riscv/sifive_test.h
> @@ -0,0 +1,42 @@
> +/*
> + * QEMU Test Finisher interface
> + *
> + * Copyright (c) 2018 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef HW_SIFIVE_TEST_H
> +#define HW_SIFIVE_TEST_H
> +
> +#define TYPE_SIFIVE_TEST "riscv.sifive.test"
> +
> +#define SIFIVE_TEST(obj) \
> +    OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST)
> +
> +typedef struct SiFiveTestState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion mmio;
> +} SiFiveTestState;
> +
> +enum {
> +    FINISHER_FAIL = 0x3333,
> +    FINISHER_PASS = 0x5555
> +};
> +
> +DeviceState *sifive_test_create(hwaddr addr);
> +
> +#endif
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device Michael Clark
@ 2018-03-09 12:39   ` Philippe Mathieu-Daudé
  2018-03-10  3:02     ` Michael Clark
  2018-04-10  3:21   ` Antony Pavlov
  1 sibling, 1 reply; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 12:39 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Stefan O'Rear, Sagar Karandikar, Bastian Koppelmann,
	Palmer Dabbelt, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> BBL supports the SiFive UART for early console access via the SBI
> (Supervisor Binary Interface) and the linux kernel SBI console.
> 
> The SiFive UART implements the pre qom legacy interface consistent
> with the 16550a UART in 'hw/char/serial.c'.
> 
> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  hw/riscv/sifive_uart.c         | 176 +++++++++++++++++++++++++++++++++++++++++
>  include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
>  2 files changed, 247 insertions(+)
>  create mode 100644 hw/riscv/sifive_uart.c
>  create mode 100644 include/hw/riscv/sifive_uart.h
> 
> diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
> new file mode 100644
> index 0000000..b0c3798
> --- /dev/null
> +++ b/hw/riscv/sifive_uart.c
> @@ -0,0 +1,176 @@
> +/*
> + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> + *
> + * Copyright (c) 2016 Stefan O'Rear
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/sysbus.h"
> +#include "chardev/char.h"
> +#include "chardev/char-fe.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/sifive_uart.h"
> +
> +/*
> + * Not yet implemented:
> + *
> + * Transmit FIFO using "qemu/fifo8.h"
> + * SIFIVE_UART_IE_TXWM interrupts
> + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
> + * Rx FIFO watermark interrupt trigger threshold
> + * Tx FIFO watermark interrupt trigger threshold.
> + */
> +
> +static void update_irq(SiFiveUARTState *s)
> +{
> +    int cond = 0;
> +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
> +        cond = 1;
> +    }
> +    if (cond) {
> +        qemu_irq_raise(s->irq);
> +    } else {
> +        qemu_irq_lower(s->irq);
> +    }

or     qemu_set_irq(s->irq, cond);

> +}
> +
> +static uint64_t
> +uart_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    SiFiveUARTState *s = opaque;
> +    unsigned char r;
> +    switch (addr) {
> +    case SIFIVE_UART_RXFIFO:
> +        if (s->rx_fifo_len) {
> +            r = s->rx_fifo[0];
> +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
> +            s->rx_fifo_len--;
> +            qemu_chr_fe_accept_input(&s->chr);
> +            update_irq(s);
> +            return r;
> +        }
> +        return 0x80000000;

Can you add a #define for this bit?

> +
> +    case SIFIVE_UART_TXFIFO:
> +        return 0; /* Should check tx fifo */
> +    case SIFIVE_UART_IE:
> +        return s->ie;
> +    case SIFIVE_UART_IP:
> +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
> +    case SIFIVE_UART_TXCTRL:
> +        return s->txctrl;
> +    case SIFIVE_UART_RXCTRL:
> +        return s->rxctrl;
> +    case SIFIVE_UART_DIV:
> +        return s->div;
> +    }
> +
> +    hw_error("%s: bad read: addr=0x%x\n",
> +        __func__, (int)addr);

qemu_log(GUEST_ERROR)?

> +    return 0;
> +}
> +
> +static void
> +uart_write(void *opaque, hwaddr addr,
> +           uint64_t val64, unsigned int size)
> +{
> +    SiFiveUARTState *s = opaque;
> +    uint32_t value = val64;
> +    unsigned char ch = value;
> +
> +    switch (addr) {
> +    case SIFIVE_UART_TXFIFO:
> +        qemu_chr_fe_write(&s->chr, &ch, 1);
> +        return;
> +    case SIFIVE_UART_IE:
> +        s->ie = val64;
> +        update_irq(s);
> +        return;
> +    case SIFIVE_UART_TXCTRL:
> +        s->txctrl = val64;
> +        return;
> +    case SIFIVE_UART_RXCTRL:
> +        s->rxctrl = val64;
> +        return;
> +    case SIFIVE_UART_DIV:
> +        s->div = val64;
> +        return;
> +    }
> +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
> +        __func__, (int)addr, (int)value);

Ditto.

> +}
> +
> +static const MemoryRegionOps uart_ops = {
> +    .read = uart_read,
> +    .write = uart_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> +{
> +    SiFiveUARTState *s = opaque;
> +
> +    /* Got a byte.  */
> +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
> +        printf("WARNING: UART dropped char.\n");

replace printf() by warn_report()?

> +        return;
> +    }
> +    s->rx_fifo[s->rx_fifo_len++] = *buf;
> +
> +    update_irq(s);
> +}
> +
> +static int uart_can_rx(void *opaque)
> +{
> +    SiFiveUARTState *s = opaque;
> +
> +    return s->rx_fifo_len < sizeof(s->rx_fifo);
> +}
> +
> +static void uart_event(void *opaque, int event)
> +{
> +}
> +
> +static int uart_be_change(void *opaque)
> +{
> +    SiFiveUARTState *s = opaque;
> +
> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> +        uart_be_change, s, NULL, true);
> +
> +    return 0;
> +}
> +
> +/*
> + * Create UART device.
> + */
> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
> +    Chardev *chr, qemu_irq irq)
> +{
> +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
> +    s->irq = irq;
> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> +        uart_be_change, s, NULL, true);
> +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
> +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
> +    memory_region_add_subregion(address_space, base, &s->mmio);
> +    return s;
> +}
> diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
> new file mode 100644
> index 0000000..504f18a
> --- /dev/null
> +++ b/include/hw/riscv/sifive_uart.h
> @@ -0,0 +1,71 @@
> +/*
> + * SiFive UART interface
> + *
> + * Copyright (c) 2016 Stefan O'Rear
> + * Copyright (c) 2017 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef HW_SIFIVE_UART_H
> +#define HW_SIFIVE_UART_H
> +
> +enum {
> +    SIFIVE_UART_TXFIFO        = 0,
> +    SIFIVE_UART_RXFIFO        = 4,
> +    SIFIVE_UART_TXCTRL        = 8,
> +    SIFIVE_UART_TXMARK        = 10,
> +    SIFIVE_UART_RXCTRL        = 12,
> +    SIFIVE_UART_RXMARK        = 14,
> +    SIFIVE_UART_IE            = 16,
> +    SIFIVE_UART_IP            = 20,
> +    SIFIVE_UART_DIV           = 24,
> +    SIFIVE_UART_MAX           = 32
> +};
> +
> +enum {
> +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt enable */
> +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt enable */
> +};
> +
> +enum {
> +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt pending */
> +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt pending */
> +};

These 2 enums don't need to be exported and can stay in sifive_uart.c.

> +
> +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
> +
> +#define SIFIVE_UART(obj) \
> +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
> +
> +typedef struct SiFiveUARTState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    qemu_irq irq;
> +    MemoryRegion mmio;
> +    CharBackend chr;
> +    uint8_t rx_fifo[8];
> +    unsigned int rx_fifo_len;
> +    uint32_t ie;
> +    uint32_t ip;
> +    uint32_t txctrl;
> +    uint32_t rxctrl;
> +    uint32_t div;
> +} SiFiveUARTState;
> +
> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
> +    Chardev *chr, qemu_irq irq);
> +
> +#endif
> 

removing printf():
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 07/23] RISC-V GDB Stub
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 07/23] RISC-V GDB Stub Michael Clark
@ 2018-03-09 12:46   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 12:46 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> GDB Register read and write routines.
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  target/riscv/gdbstub.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 62 insertions(+)
>  create mode 100644 target/riscv/gdbstub.c
> 
> diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
> new file mode 100644
> index 0000000..4f919b6
> --- /dev/null
> +++ b/target/riscv/gdbstub.c
> @@ -0,0 +1,62 @@
> +/*
> + * RISC-V GDB Server Stub
> + *
> + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "exec/gdbstub.h"
> +#include "cpu.h"
> +
> +int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    CPURISCVState *env = &cpu->env;
> +
> +    if (n < 32) {
> +        return gdb_get_regl(mem_buf, env->gpr[n]);
> +    } else if (n == 32) {
> +        return gdb_get_regl(mem_buf, env->pc);
> +    } else if (n < 65) {
> +        return gdb_get_reg64(mem_buf, env->fpr[n - 33]);
> +    } else if (n < 4096 + 65) {
> +        return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65));
> +    }
> +    return 0;
> +}
> +
> +int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +    CPURISCVState *env = &cpu->env;
> +
> +    if (n == 0) {
> +        /* discard writes to x0 */
> +        return sizeof(target_ulong);
> +    } else if (n < 32) {
> +        env->gpr[n] = ldtul_p(mem_buf);
> +        return sizeof(target_ulong);
> +    } else if (n == 32) {
> +        env->pc = ldtul_p(mem_buf);
> +        return sizeof(target_ulong);
> +    } else if (n < 65) {
> +        env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */
> +        return sizeof(uint64_t);
> +    } else if (n < 4096 + 65) {
> +        csr_write_helper(env, ldtul_p(mem_buf), n - 65);
> +    }
> +    return 0;
> +}
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array Michael Clark
@ 2018-03-09 12:52   ` Philippe Mathieu-Daudé
  2018-03-09 13:48     ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 12:52 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> Holds the state of a heterogenous array of RISC-V hardware threads.

heterogeneous

> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  hw/riscv/riscv_hart.c         | 89 +++++++++++++++++++++++++++++++++++++++++++
>  include/hw/riscv/riscv_hart.h | 39 +++++++++++++++++++
>  2 files changed, 128 insertions(+)
>  create mode 100644 hw/riscv/riscv_hart.c
>  create mode 100644 include/hw/riscv/riscv_hart.h
> 
> diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
> new file mode 100644
> index 0000000..14e3c18
> --- /dev/null
> +++ b/hw/riscv/riscv_hart.c
> @@ -0,0 +1,89 @@
> +/*
> + * QEMU RISCV Hart Array
> + *
> + * Copyright (c) 2017 SiFive, Inc.
> + *
> + * Holds the state of a heterogenous array of RISC-V harts
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/sysbus.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/riscv_hart.h"
> +
> +static Property riscv_harts_props[] = {
> +    DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
> +    DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void riscv_harts_cpu_reset(void *opaque)
> +{
> +    RISCVCPU *cpu = opaque;
> +    cpu_reset(CPU(cpu));
> +}
> +
> +static void riscv_harts_realize(DeviceState *dev, Error **errp)
> +{
> +    RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
> +    Error *err = NULL;
> +    int n;
> +
> +    s->harts = g_new0(RISCVCPU, s->num_harts);
> +
> +    for (n = 0; n < s->num_harts; n++) {
> +
> +        object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type);
> +        s->harts[n].env.mhartid = n;
> +        object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]),
> +                                  &error_abort);
> +        qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
> +        object_property_set_bool(OBJECT(&s->harts[n]), true,
> +                                 "realized", &err);
> +        if (err) {
> +            error_propagate(errp, err);
> +            return;
> +        }
> +    }
> +}
> +
> +static void riscv_harts_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->props = riscv_harts_props;
> +    dc->realize = riscv_harts_realize;
> +}
> +
> +static void riscv_harts_init(Object *obj)
> +{
> +    /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */

This seems an old comment, now this would be RISCV_HART_ARRAY(obj).

Maybe better remove this riscv_harts_init(), ...

> +}
> +
> +static const TypeInfo riscv_harts_info = {
> +    .name          = TYPE_RISCV_HART_ARRAY,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RISCVHartArrayState),
> +    .instance_init = riscv_harts_init,

... and drop this line?

> +    .class_init    = riscv_harts_class_init,
> +};
> +
> +static void riscv_harts_register_types(void)
> +{
> +    type_register_static(&riscv_harts_info);
> +}
> +
> +type_init(riscv_harts_register_types)
> diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
> new file mode 100644
> index 0000000..0671d88
> --- /dev/null
> +++ b/include/hw/riscv/riscv_hart.h
> @@ -0,0 +1,39 @@
> +/*
> + * QEMU RISC-V Hart Array interface
> + *
> + * Copyright (c) 2017 SiFive, Inc.
> + *
> + * Holds the state of a heterogenous array of RISC-V harts
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef HW_RISCV_HART_H
> +#define HW_RISCV_HART_H
> +
> +#define TYPE_RISCV_HART_ARRAY "riscv.hart_array"
> +
> +#define RISCV_HART_ARRAY(obj) \
> +    OBJECT_CHECK(RISCVHartArrayState, (obj), TYPE_RISCV_HART_ARRAY)
> +
> +typedef struct RISCVHartArrayState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    uint32_t num_harts;
> +    char *cpu_type;
> +    RISCVCPU *harts;
> +} RISCVHartArrayState;
> +
> +#endif
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure Michael Clark
  2018-03-02 14:33   ` Eric Blake
@ 2018-03-09 13:03   ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 13:03 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> This adds RISC-V into the build system enabling the following targets:
> 
> - riscv32-softmmu
> - riscv64-softmmu
> - riscv32-linux-user
> - riscv64-linux-user
> 
> This adds defaults configs for RISC-V, enables the build for the RISC-V
> CPU core, hardware, and Linux User Emulation. The 'qemu-binfmt-conf.sh'
> script is updated to add the RISC-V ELF magic.
> 
> Expected checkpatch errors for consistency reasons:
> 
> ERROR: line over 90 characters
> FILE: scripts/qemu-binfmt-conf.sh
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  arch_init.c                            |  2 ++
>  configure                              | 13 +++++++++++++
>  cpus.c                                 |  6 ++++++
>  default-configs/riscv32-linux-user.mak |  1 +
>  default-configs/riscv32-softmmu.mak    |  4 ++++
>  default-configs/riscv64-linux-user.mak |  1 +
>  default-configs/riscv64-softmmu.mak    |  4 ++++
>  hw/riscv/Makefile.objs                 | 11 +++++++++++
>  include/sysemu/arch_init.h             |  1 +
>  qapi-schema.json                       | 17 ++++++++++++++++-
>  scripts/qemu-binfmt-conf.sh            | 13 ++++++++++++-
>  target/riscv/Makefile.objs             |  1 +
>  12 files changed, 72 insertions(+), 2 deletions(-)
>  create mode 100644 default-configs/riscv32-linux-user.mak
>  create mode 100644 default-configs/riscv32-softmmu.mak
>  create mode 100644 default-configs/riscv64-linux-user.mak
>  create mode 100644 default-configs/riscv64-softmmu.mak
>  create mode 100644 hw/riscv/Makefile.objs
>  create mode 100644 target/riscv/Makefile.objs
> 
> diff --git a/arch_init.c b/arch_init.c
> index 4c36f2b..e157619 100644
> --- a/arch_init.c
> +++ b/arch_init.c
> @@ -71,6 +71,8 @@ int graphic_depth = 32;
>  #define QEMU_ARCH QEMU_ARCH_OPENRISC
>  #elif defined(TARGET_PPC)
>  #define QEMU_ARCH QEMU_ARCH_PPC
> +#elif defined(TARGET_RISCV)
> +#define QEMU_ARCH QEMU_ARCH_RISCV
>  #elif defined(TARGET_S390X)
>  #define QEMU_ARCH QEMU_ARCH_S390X
>  #elif defined(TARGET_SH4)
> diff --git a/configure b/configure
> index 00c4b63..a3240a8 100755
> --- a/configure
> +++ b/configure
> @@ -6801,6 +6801,16 @@ case "$target_name" in
>      echo "TARGET_ABI32=y" >> $config_target_mak
>      gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
>    ;;
> +  riscv32)
> +    TARGET_BASE_ARCH=riscv
> +    TARGET_ABI_DIR=riscv
> +    mttcg=yes
> +  ;;
> +  riscv64)
> +    TARGET_BASE_ARCH=riscv
> +    TARGET_ABI_DIR=riscv
> +    mttcg=yes
> +  ;;
>    sh4|sh4eb)
>      TARGET_ARCH=sh4
>      bflt="yes"
> @@ -6970,6 +6980,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
>    ppc*)
>      disas_config "PPC"
>    ;;
> +  riscv)
> +    disas_config "RISCV"
> +  ;;
>    s390*)
>      disas_config "S390"
>    ;;
> diff --git a/cpus.c b/cpus.c
> index af67826..407c3f8 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -2094,6 +2094,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
>  #elif defined(TARGET_SPARC)
>          SPARCCPU *sparc_cpu = SPARC_CPU(cpu);
>          CPUSPARCState *env = &sparc_cpu->env;
> +#elif defined(TARGET_RISCV)
> +        RISCVCPU *riscv_cpu = RISCV_CPU(cpu);
> +        CPURISCVState *env = &riscv_cpu->env;
>  #elif defined(TARGET_MIPS)
>          MIPSCPU *mips_cpu = MIPS_CPU(cpu);
>          CPUMIPSState *env = &mips_cpu->env;
> @@ -2133,6 +2136,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
>  #elif defined(TARGET_S390X)
>          info->value->arch = CPU_INFO_ARCH_S390;
>          info->value->u.s390.cpu_state = env->cpu_state;
> +#elif defined(TARGET_RISCV)
> +        info->value->arch = CPU_INFO_ARCH_RISCV;
> +        info->value->u.riscv.pc = env->pc;
>  #else
>          info->value->arch = CPU_INFO_ARCH_OTHER;
>  #endif
> diff --git a/default-configs/riscv32-linux-user.mak b/default-configs/riscv32-linux-user.mak
> new file mode 100644
> index 0000000..865b362
> --- /dev/null
> +++ b/default-configs/riscv32-linux-user.mak
> @@ -0,0 +1 @@
> +# Default configuration for riscv-linux-user
> diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak
> new file mode 100644
> index 0000000..f9e7421
> --- /dev/null
> +++ b/default-configs/riscv32-softmmu.mak
> @@ -0,0 +1,4 @@
> +# Default configuration for riscv-softmmu
> +
> +CONFIG_SERIAL=y
> +CONFIG_VIRTIO=y
> diff --git a/default-configs/riscv64-linux-user.mak b/default-configs/riscv64-linux-user.mak
> new file mode 100644
> index 0000000..865b362
> --- /dev/null
> +++ b/default-configs/riscv64-linux-user.mak
> @@ -0,0 +1 @@
> +# Default configuration for riscv-linux-user
> diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak
> new file mode 100644
> index 0000000..f9e7421
> --- /dev/null
> +++ b/default-configs/riscv64-softmmu.mak
> @@ -0,0 +1,4 @@
> +# Default configuration for riscv-softmmu
> +
> +CONFIG_SERIAL=y
> +CONFIG_VIRTIO=y
> diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
> new file mode 100644
> index 0000000..1dde01d
> --- /dev/null
> +++ b/hw/riscv/Makefile.objs
> @@ -0,0 +1,11 @@
> +obj-y += riscv_htif.o
> +obj-y += riscv_hart.o
> +obj-y += sifive_e.o
> +obj-y += sifive_clint.o
> +obj-y += sifive_prci.o
> +obj-y += sifive_plic.o
> +obj-y += sifive_test.o
> +obj-y += sifive_u.o
> +obj-y += sifive_uart.o
> +obj-y += spike.o
> +obj-y += virt.o
> diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
> index d40d882..ef52ae0 100644
> --- a/include/sysemu/arch_init.h
> +++ b/include/sysemu/arch_init.h
> @@ -24,6 +24,7 @@ enum {
>      QEMU_ARCH_TRICORE = (1 << 16),
>      QEMU_ARCH_NIOS2 = (1 << 17),
>      QEMU_ARCH_HPPA = (1 << 18),
> +    QEMU_ARCH_RISCV = (1 << 19),
>  };
>  
>  extern const uint32_t arch_type;
> diff --git a/qapi-schema.json b/qapi-schema.json
> index cd98a94..0ff2ed3 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -412,10 +412,12 @@
>  #
>  # @s390: since 2.12
>  #
> +# @riscv: since 2.12
> +#
>  # Since: 2.6
>  ##
>  { 'enum': 'CpuInfoArch',
> -  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] }
> +  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'riscv', 'other' ] }
>  
>  ##
>  # @CpuInfo:
> @@ -455,6 +457,7 @@
>              'mips': 'CpuInfoMIPS',
>              'tricore': 'CpuInfoTricore',
>              's390': 'CpuInfoS390',
> +            'riscv': 'CpuInfoRISCV',
>              'other': 'CpuInfoOther' } }
>  
>  ##
> @@ -515,6 +518,17 @@
>  { 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }
>  
>  ##
> +# @CpuInfoRISCV:
> +#
> +# Additional information about a virtual RISCV CPU
> +#
> +# @pc: the instruction pointer
> +#
> +# Since 2.12
> +##
> +{ 'struct': 'CpuInfoRISCV', 'data': { 'pc': 'int' } }
> +
> +##
>  # @CpuInfoOther:
>  #
>  # No additional information is available about the virtual CPU
> @@ -625,6 +639,7 @@
>              'mips': 'CpuInfoOther',
>              'tricore': 'CpuInfoOther',
>              's390': 'CpuInfoS390',
> +            'riscv': 'CpuInfoRISCV',
>              'other': 'CpuInfoOther' } }
>  
>  ##
> diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
> index ea5a748..bdb21bd 100755
> --- a/scripts/qemu-binfmt-conf.sh
> +++ b/scripts/qemu-binfmt-conf.sh
> @@ -4,7 +4,7 @@
>  
>  qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \
>  mips mipsel mipsn32 mipsn32el mips64 mips64el \
> -sh4 sh4eb s390x aarch64 aarch64_be hppa"
> +sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64"
>  
>  i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00'
>  i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
> @@ -100,6 +100,14 @@ hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
>  hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
>  hppa_family=hppa
>  
> +riscv32_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
> +riscv32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
> +riscv32_family=riscv
> +
> +riscv64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
> +riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
> +riscv64_family=riscv
> +
>  qemu_get_family() {
>      cpu=${HOST_ARCH:-$(uname -m)}
>      case "$cpu" in
> @@ -124,6 +132,9 @@ qemu_get_family() {
>      sparc*)
>          echo "sparc"
>          ;;
> +    riscv*)
> +        echo "riscv"
> +        ;;
>      *)
>          echo "$cpu"
>          ;;
> diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs
> new file mode 100644
> index 0000000..abd0a7c
> --- /dev/null
> +++ b/target/riscv/Makefile.objs
> @@ -0,0 +1 @@
> +obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 02/23] RISC-V ELF Machine Definition
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 02/23] RISC-V ELF Machine Definition Michael Clark
@ 2018-03-09 13:05   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-03-09 13:05 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

On 03/02/2018 02:51 PM, Michael Clark wrote:
> Define RISC-V ELF machine EM_RISCV 243
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  include/elf.h | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/include/elf.h b/include/elf.h
> index 943ee21..c0dc9bb 100644
> --- a/include/elf.h
> +++ b/include/elf.h
> @@ -119,6 +119,8 @@ typedef int64_t  Elf64_Sxword;
>  
>  #define EM_UNICORE32    110     /* UniCore32 */
>  
> +#define EM_RISCV        243     /* RISC-V */
> +
>  /*
>   * This is an interim value that we will use until the committee comes
>   * up with a final number.
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array
  2018-03-09 12:52   ` Philippe Mathieu-Daudé
@ 2018-03-09 13:48     ` Michael Clark
  0 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-09 13:48 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Bastian Koppelmann, Palmer Dabbelt, RISC-V Patches,
	Sagar Karandikar, qemu-devel

Got it. I can add to the post merge cleanup patch series. BTW the code
currently creates a homogeneous array or cores but the intent is that it
supports a heterogeneous array, in the future. It might evolve into an SOC
base class. We need a method to give it an array or cpu_models. For example
the U54-MC has an e51 monitor core and 4 u54 application cores, but they
are part of a heterogeneous core complex. This code is essentially
“scaffolding”. All of the boards currently have a CLINT, so we could move
that in here too, and perhaps rename it.

Evolution... it works as it is at present. Heterogenous signals the future
intent.

On Sat, 10 Mar 2018 at 1:52 AM, Philippe Mathieu-Daudé <f4bug@amsat.org>
wrote:

> On 03/02/2018 02:51 PM, Michael Clark wrote:
> > Holds the state of a heterogenous array of RISC-V hardware threads.
>
> heterogeneous
>
> >
> > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> > Signed-off-by: Michael Clark <mjc@sifive.com>
> > ---
> >  hw/riscv/riscv_hart.c         | 89
> +++++++++++++++++++++++++++++++++++++++++++
> >  include/hw/riscv/riscv_hart.h | 39 +++++++++++++++++++
> >  2 files changed, 128 insertions(+)
> >  create mode 100644 hw/riscv/riscv_hart.c
> >  create mode 100644 include/hw/riscv/riscv_hart.h
> >
> > diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
> > new file mode 100644
> > index 0000000..14e3c18
> > --- /dev/null
> > +++ b/hw/riscv/riscv_hart.c
> > @@ -0,0 +1,89 @@
> > +/*
> > + * QEMU RISCV Hart Array
> > + *
> > + * Copyright (c) 2017 SiFive, Inc.
> > + *
> > + * Holds the state of a heterogenous array of RISC-V harts
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "hw/sysbus.h"
> > +#include "target/riscv/cpu.h"
> > +#include "hw/riscv/riscv_hart.h"
> > +
> > +static Property riscv_harts_props[] = {
> > +    DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
> > +    DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void riscv_harts_cpu_reset(void *opaque)
> > +{
> > +    RISCVCPU *cpu = opaque;
> > +    cpu_reset(CPU(cpu));
> > +}
> > +
> > +static void riscv_harts_realize(DeviceState *dev, Error **errp)
> > +{
> > +    RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
> > +    Error *err = NULL;
> > +    int n;
> > +
> > +    s->harts = g_new0(RISCVCPU, s->num_harts);
> > +
> > +    for (n = 0; n < s->num_harts; n++) {
> > +
> > +        object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type);
> > +        s->harts[n].env.mhartid = n;
> > +        object_property_add_child(OBJECT(s), "harts[*]",
> OBJECT(&s->harts[n]),
> > +                                  &error_abort);
> > +        qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
> > +        object_property_set_bool(OBJECT(&s->harts[n]), true,
> > +                                 "realized", &err);
> > +        if (err) {
> > +            error_propagate(errp, err);
> > +            return;
> > +        }
> > +    }
> > +}
> > +
> > +static void riscv_harts_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->props = riscv_harts_props;
> > +    dc->realize = riscv_harts_realize;
> > +}
> > +
> > +static void riscv_harts_init(Object *obj)
> > +{
> > +    /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */
>
> This seems an old comment, now this would be RISCV_HART_ARRAY(obj).
>
> Maybe better remove this riscv_harts_init(), ...
>
> > +}
> > +
> > +static const TypeInfo riscv_harts_info = {
> > +    .name          = TYPE_RISCV_HART_ARRAY,
> > +    .parent        = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(RISCVHartArrayState),
> > +    .instance_init = riscv_harts_init,
>
> ... and drop this line?
>
> > +    .class_init    = riscv_harts_class_init,
> > +};
> > +
> > +static void riscv_harts_register_types(void)
> > +{
> > +    type_register_static(&riscv_harts_info);
> > +}
> > +
> > +type_init(riscv_harts_register_types)
> > diff --git a/include/hw/riscv/riscv_hart.h
> b/include/hw/riscv/riscv_hart.h
> > new file mode 100644
> > index 0000000..0671d88
> > --- /dev/null
> > +++ b/include/hw/riscv/riscv_hart.h
> > @@ -0,0 +1,39 @@
> > +/*
> > + * QEMU RISC-V Hart Array interface
> > + *
> > + * Copyright (c) 2017 SiFive, Inc.
> > + *
> > + * Holds the state of a heterogenous array of RISC-V harts
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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/>.
> > + */
> > +
> > +#ifndef HW_RISCV_HART_H
> > +#define HW_RISCV_HART_H
> > +
> > +#define TYPE_RISCV_HART_ARRAY "riscv.hart_array"
> > +
> > +#define RISCV_HART_ARRAY(obj) \
> > +    OBJECT_CHECK(RISCVHartArrayState, (obj), TYPE_RISCV_HART_ARRAY)
> > +
> > +typedef struct RISCVHartArrayState {
> > +    /*< private >*/
> > +    SysBusDevice parent_obj;
> > +
> > +    /*< public >*/
> > +    uint32_t num_harts;
> > +    char *cpu_type;
> > +    RISCVCPU *harts;
> > +} RISCVHartArrayState;
> > +
> > +#endif
> >
>
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
  2018-03-05  8:41 ` Richard W.M. Jones
  2018-03-05 10:02   ` Alex Bennée
@ 2018-03-09 15:07   ` Michael Clark
  2018-03-09 16:43   ` Peter Maydell
  2 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-09 15:07 UTC (permalink / raw)
  To: Richard W.M. Jones, Richard Henderson, Peter Maydell
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Mon, Mar 5, 2018 at 9:41 PM, Richard W.M. Jones <rjones@redhat.com>
wrote:

>
> The attached patch is also needed to avoid crashes during various
> math-heavy test suites.
>

Thanks. I missed your email.

I've integrated this and the other outstanding patches into the `riscv-all`
branch in the riscv repo here.

- https://github.com/riscv/riscv-qemu/commits/riscv-all

This also triggered aborts on riscv-tests, which is all the more reason to
get riscv-tests running in the CI so we spot these things early.

Thanks,
Michael.

Rich.
>
> From: Stef O'Rear <address@hidden>
>
> Date: Sat, 3 Mar 2018 03:46:00 -0800
>
> Subject: [PATCH] softfloat: fix crash on int conversion of SNaN
>
>
>> Signed-off-by: Stef O'Rear <address@hidden>
>
>
Signed-off-by: Michael Clark <mjc@sifive.com>


> ---
>
>  fpu/softfloat.c | 4 ++++
>
>  1 file changed, 4 insertions(+)
>
>
>> diff --git a/fpu/softfloat.c b/fpu/softfloat.c
>
> index e7fb0d357a..1da1db377e 100644
>
> --- a/fpu/softfloat.c
>
> +++ b/fpu/softfloat.c
>
> @@ -1342,6 +1342,8 @@ static int64_t round_to_int_and_pack(FloatParts in,
>> int rmode,
>
>      switch (p.cls) {
>
>      case float_class_snan:
>
>      case float_class_qnan:
>
> +    case float_class_dnan:
>
> +    case float_class_msnan:
>
>          return max;
>
>      case float_class_inf:
>
>          return p.sign ? min : max;
>
> @@ -1430,6 +1432,8 @@ static uint64_t round_to_uint_and_pack(FloatParts
>> in, int rmode, uint64_t max,
>
>      switch (p.cls) {
>
>      case float_class_snan:
>
>      case float_class_qnan:
>
> +    case float_class_dnan:
>
> +    case float_class_msnan:
>
>          s->float_exception_flags = orig_flags | float_flag_invalid;
>
>          return max;
>
>      case float_class_inf:
>
> --
>
> 2.15.1
>
>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
  2018-03-05  8:41 ` Richard W.M. Jones
  2018-03-05 10:02   ` Alex Bennée
  2018-03-09 15:07   ` Michael Clark
@ 2018-03-09 16:43   ` Peter Maydell
  2018-03-09 18:27     ` Richard W.M. Jones
  2 siblings, 1 reply; 70+ messages in thread
From: Peter Maydell @ 2018-03-09 16:43 UTC (permalink / raw)
  To: Richard W.M. Jones
  Cc: Michael Clark, Bastian Koppelmann, Palmer Dabbelt,
	QEMU Developers, Sagar Karandikar, RISC-V Patches

On 5 March 2018 at 08:41, Richard W.M. Jones <rjones@redhat.com> wrote:
>
> The attached patch is also needed to avoid crashes during various
> math-heavy test suites.

Applied to master, thanks.

FYI, sending patches as attachments in the middle of a long
thread on something else is a good way to cause them to get
lost. Luckily Michael ran into the same bug again and that
reminded me to fish this one out.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission
  2018-03-09 16:43   ` Peter Maydell
@ 2018-03-09 18:27     ` Richard W.M. Jones
  0 siblings, 0 replies; 70+ messages in thread
From: Richard W.M. Jones @ 2018-03-09 18:27 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Michael Clark, Bastian Koppelmann, Palmer Dabbelt,
	QEMU Developers, Sagar Karandikar, RISC-V Patches

On Fri, Mar 09, 2018 at 04:43:34PM +0000, Peter Maydell wrote:
> On 5 March 2018 at 08:41, Richard W.M. Jones <rjones@redhat.com> wrote:
> >
> > The attached patch is also needed to avoid crashes during various
> > math-heavy test suites.
> 
> Applied to master, thanks.
> 
> FYI, sending patches as attachments in the middle of a long
> thread on something else is a good way to cause them to get
> lost. Luckily Michael ran into the same bug again and that
> reminded me to fish this one out.

Yup sorry I thought that the patch had been separately submitted,
but apparently it never was.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher
  2018-03-09 11:57   ` Philippe Mathieu-Daudé
@ 2018-03-10  3:01     ` Michael Clark
  0 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-03-10  3:01 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Sat, Mar 10, 2018 at 12:57 AM, Philippe Mathieu-Daudé <f4bug@amsat.org>
wrote:

> On 03/02/2018 02:51 PM, Michael Clark wrote:
> > Test finisher memory mapped device used to exit simulation.
> >
> > Acked-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> > Signed-off-by: Michael Clark <mjc@sifive.com>
> > ---
> >  hw/riscv/sifive_test.c         | 93 ++++++++++++++++++++++++++++++
> ++++++++++++
> >  include/hw/riscv/sifive_test.h | 42 +++++++++++++++++++
> >  2 files changed, 135 insertions(+)
> >  create mode 100644 hw/riscv/sifive_test.c
> >  create mode 100644 include/hw/riscv/sifive_test.h
> >
> > diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c
> > new file mode 100644
> > index 0000000..8abd2cd
> > --- /dev/null
> > +++ b/hw/riscv/sifive_test.c
> > @@ -0,0 +1,93 @@
> > +/*
> > + * QEMU SiFive Test Finisher
> > + *
> > + * Copyright (c) 2018 SiFive, Inc.
> > + *
> > + * Test finisher memory mapped device used to exit simulation
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "hw/sysbus.h"
> > +#include "target/riscv/cpu.h"
> > +#include "hw/riscv/sifive_test.h"
> > +
> > +static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned
> int size)
> > +{
> > +    return 0;
> > +}
> > +
> > +static void sifive_test_write(void *opaque, hwaddr addr,
> > +           uint64_t val64, unsigned int size)
> > +{
> > +    if (addr == 0) {
> > +        int status = val64 & 0xffff;
> > +        int code = (val64 >> 16) & 0xffff;
> > +        switch (status) {
> > +        case FINISHER_FAIL:
> > +            exit(code);
> > +        case FINISHER_PASS:
> > +            exit(0);
> > +        default:
> > +            break;
> > +        }
> > +    }
> > +    hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
> > +        __func__, (int)addr, val64);
> > +}
> > +
> > +static const MemoryRegionOps sifive_test_ops = {
> > +    .read = sifive_test_read,
> > +    .write = sifive_test_write,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4
> > +    }
> > +};
> > +
> > +static void sifive_test_init(Object *obj)
> > +{
> > +    SiFiveTestState *s = SIFIVE_TEST(obj);
> > +
> > +    memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s,
> > +                          TYPE_SIFIVE_TEST, 0x1000);
>
> 0x1000? 0x8 is enough :)
>
>
In this case we were following the aperture size of SiFive's test finisher.
See the device tree here. 0x4000 is the offset, 0x1000 is the length.

L20: teststatus@4000 {
compatible = "sifive,test0";
reg = <0x0 0x4000 0x0 0x1000>;
reg-names = "control";
};

I can change it to 8 and it will still work. There should probably be a
SIFIVE_TEST_APERTURE_SIZE constant (perhaps something a little shorter).

BTW We used the test finisher because the firmware we have already knows
how to use it to shutdown a device in the SBI (Supervisor Binary Interface)
sbi_shutdown() method.

Apparently there is a generic device-tree mechanism of passing a reference
to a GPIO node for shutdown and a GPIO node for reset, that is recognised
by Linux Kernel device-tree code. When we have GPIOs we may change the
method to use the standard Linux mechanism.

> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> > +}
> > +
> > +static const TypeInfo sifive_test_info = {
> > +    .name          = TYPE_SIFIVE_TEST,
> > +    .parent        = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(SiFiveTestState),
> > +    .instance_init = sifive_test_init,
> > +};
> > +
> > +static void sifive_test_register_types(void)
> > +{
> > +    type_register_static(&sifive_test_info);
> > +}
> > +
> > +type_init(sifive_test_register_types)
> > +
> > +
> > +/*
> > + * Create Test device.
> > + */
> > +DeviceState *sifive_test_create(hwaddr addr)
> > +{
> > +    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST);
> > +    qdev_init_nofail(dev);
> > +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> > +    return dev;
> > +}
> > diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_
> test.h
> > new file mode 100644
> > index 0000000..71d4c9f
> > --- /dev/null
> > +++ b/include/hw/riscv/sifive_test.h
> > @@ -0,0 +1,42 @@
> > +/*
> > + * QEMU Test Finisher interface
> > + *
> > + * Copyright (c) 2018 SiFive, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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/>.
> > + */
> > +
> > +#ifndef HW_SIFIVE_TEST_H
> > +#define HW_SIFIVE_TEST_H
> > +
> > +#define TYPE_SIFIVE_TEST "riscv.sifive.test"
> > +
> > +#define SIFIVE_TEST(obj) \
> > +    OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST)
> > +
> > +typedef struct SiFiveTestState {
> > +    /*< private >*/
> > +    SysBusDevice parent_obj;
> > +
> > +    /*< public >*/
> > +    MemoryRegion mmio;
> > +} SiFiveTestState;
> > +
> > +enum {
> > +    FINISHER_FAIL = 0x3333,
> > +    FINISHER_PASS = 0x5555
> > +};
> > +
> > +DeviceState *sifive_test_create(hwaddr addr);
> > +
> > +#endif
> >
>
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-09 12:39   ` Philippe Mathieu-Daudé
@ 2018-03-10  3:02     ` Michael Clark
  2018-03-10  9:40       ` Mark Cave-Ayland
  0 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-10  3:02 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: QEMU Developers, Stefan O'Rear, Sagar Karandikar,
	Bastian Koppelmann, Palmer Dabbelt, RISC-V Patches

On Sat, Mar 10, 2018 at 1:39 AM, Philippe Mathieu-Daudé <f4bug@amsat.org>
wrote:

> On 03/02/2018 02:51 PM, Michael Clark wrote:
> > QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> > BBL supports the SiFive UART for early console access via the SBI
> > (Supervisor Binary Interface) and the linux kernel SBI console.
> >
> > The SiFive UART implements the pre qom legacy interface consistent
> > with the 16550a UART in 'hw/char/serial.c'.
> >
> > Acked-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
> > Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> > Signed-off-by: Michael Clark <mjc@sifive.com>
> > ---
> >  hw/riscv/sifive_uart.c         | 176 ++++++++++++++++++++++++++++++
> +++++++++++
> >  include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
> >  2 files changed, 247 insertions(+)
> >  create mode 100644 hw/riscv/sifive_uart.c
> >  create mode 100644 include/hw/riscv/sifive_uart.h
> >
> > diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
> > new file mode 100644
> > index 0000000..b0c3798
> > --- /dev/null
> > +++ b/hw/riscv/sifive_uart.c
> > @@ -0,0 +1,176 @@
> > +/*
> > + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> > + *
> > + * Copyright (c) 2016 Stefan O'Rear
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "hw/sysbus.h"
> > +#include "chardev/char.h"
> > +#include "chardev/char-fe.h"
> > +#include "target/riscv/cpu.h"
> > +#include "hw/riscv/sifive_uart.h"
> > +
> > +/*
> > + * Not yet implemented:
> > + *
> > + * Transmit FIFO using "qemu/fifo8.h"
> > + * SIFIVE_UART_IE_TXWM interrupts
> > + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
> > + * Rx FIFO watermark interrupt trigger threshold
> > + * Tx FIFO watermark interrupt trigger threshold.
> > + */
> > +
> > +static void update_irq(SiFiveUARTState *s)
> > +{
> > +    int cond = 0;
> > +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
> > +        cond = 1;
> > +    }
> > +    if (cond) {
> > +        qemu_irq_raise(s->irq);
> > +    } else {
> > +        qemu_irq_lower(s->irq);
> > +    }
>
> or     qemu_set_irq(s->irq, cond);
>
> > +}
> > +
> > +static uint64_t
> > +uart_read(void *opaque, hwaddr addr, unsigned int size)
> > +{
> > +    SiFiveUARTState *s = opaque;
> > +    unsigned char r;
> > +    switch (addr) {
> > +    case SIFIVE_UART_RXFIFO:
> > +        if (s->rx_fifo_len) {
> > +            r = s->rx_fifo[0];
> > +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
> > +            s->rx_fifo_len--;
> > +            qemu_chr_fe_accept_input(&s->chr);
> > +            update_irq(s);
> > +            return r;
> > +        }
> > +        return 0x80000000;
>
> Can you add a #define for this bit?
>
> > +
> > +    case SIFIVE_UART_TXFIFO:
> > +        return 0; /* Should check tx fifo */
> > +    case SIFIVE_UART_IE:
> > +        return s->ie;
> > +    case SIFIVE_UART_IP:
> > +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
> > +    case SIFIVE_UART_TXCTRL:
> > +        return s->txctrl;
> > +    case SIFIVE_UART_RXCTRL:
> > +        return s->rxctrl;
> > +    case SIFIVE_UART_DIV:
> > +        return s->div;
> > +    }
> > +
> > +    hw_error("%s: bad read: addr=0x%x\n",
> > +        __func__, (int)addr);
>
> qemu_log(GUEST_ERROR)?
>
> > +    return 0;
> > +}
> > +
> > +static void
> > +uart_write(void *opaque, hwaddr addr,
> > +           uint64_t val64, unsigned int size)
> > +{
> > +    SiFiveUARTState *s = opaque;
> > +    uint32_t value = val64;
> > +    unsigned char ch = value;
> > +
> > +    switch (addr) {
> > +    case SIFIVE_UART_TXFIFO:
> > +        qemu_chr_fe_write(&s->chr, &ch, 1);
> > +        return;
> > +    case SIFIVE_UART_IE:
> > +        s->ie = val64;
> > +        update_irq(s);
> > +        return;
> > +    case SIFIVE_UART_TXCTRL:
> > +        s->txctrl = val64;
> > +        return;
> > +    case SIFIVE_UART_RXCTRL:
> > +        s->rxctrl = val64;
> > +        return;
> > +    case SIFIVE_UART_DIV:
> > +        s->div = val64;
> > +        return;
> > +    }
> > +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
> > +        __func__, (int)addr, (int)value);
>
> Ditto.
>
> > +}
> > +
> > +static const MemoryRegionOps uart_ops = {
> > +    .read = uart_read,
> > +    .write = uart_write,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4
> > +    }
> > +};
> > +
> > +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> > +{
> > +    SiFiveUARTState *s = opaque;
> > +
> > +    /* Got a byte.  */
> > +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
> > +        printf("WARNING: UART dropped char.\n");
>
> replace printf() by warn_report()?
>
> > +        return;
> > +    }
> > +    s->rx_fifo[s->rx_fifo_len++] = *buf;
> > +
> > +    update_irq(s);
> > +}
> > +
> > +static int uart_can_rx(void *opaque)
> > +{
> > +    SiFiveUARTState *s = opaque;
> > +
> > +    return s->rx_fifo_len < sizeof(s->rx_fifo);
> > +}
> > +
> > +static void uart_event(void *opaque, int event)
> > +{
> > +}
> > +
> > +static int uart_be_change(void *opaque)
> > +{
> > +    SiFiveUARTState *s = opaque;
> > +
> > +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> > +        uart_be_change, s, NULL, true);
> > +
> > +    return 0;
> > +}
> > +
> > +/*
> > + * Create UART device.
> > + */
> > +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space,
> hwaddr base,
> > +    Chardev *chr, qemu_irq irq)
> > +{
> > +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
> > +    s->irq = irq;
> > +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
> > +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> > +        uart_be_change, s, NULL, true);
> > +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
> > +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
> > +    memory_region_add_subregion(address_space, base, &s->mmio);
> > +    return s;
> > +}
> > diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_
> uart.h
> > new file mode 100644
> > index 0000000..504f18a
> > --- /dev/null
> > +++ b/include/hw/riscv/sifive_uart.h
> > @@ -0,0 +1,71 @@
> > +/*
> > + * SiFive UART interface
> > + *
> > + * Copyright (c) 2016 Stefan O'Rear
> > + * Copyright (c) 2017 SiFive, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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/>.
> > + */
> > +
> > +#ifndef HW_SIFIVE_UART_H
> > +#define HW_SIFIVE_UART_H
> > +
> > +enum {
> > +    SIFIVE_UART_TXFIFO        = 0,
> > +    SIFIVE_UART_RXFIFO        = 4,
> > +    SIFIVE_UART_TXCTRL        = 8,
> > +    SIFIVE_UART_TXMARK        = 10,
> > +    SIFIVE_UART_RXCTRL        = 12,
> > +    SIFIVE_UART_RXMARK        = 14,
> > +    SIFIVE_UART_IE            = 16,
> > +    SIFIVE_UART_IP            = 20,
> > +    SIFIVE_UART_DIV           = 24,
> > +    SIFIVE_UART_MAX           = 32
> > +};
> > +
> > +enum {
> > +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt
> enable */
> > +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt
> enable */
> > +};
> > +
> > +enum {
> > +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt
> pending */
> > +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt
> pending */
> > +};
>
> These 2 enums don't need to be exported and can stay in sifive_uart.c.
>
> > +
> > +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
> > +
> > +#define SIFIVE_UART(obj) \
> > +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
> > +
> > +typedef struct SiFiveUARTState {
> > +    /*< private >*/
> > +    SysBusDevice parent_obj;
> > +
> > +    /*< public >*/
> > +    qemu_irq irq;
> > +    MemoryRegion mmio;
> > +    CharBackend chr;
> > +    uint8_t rx_fifo[8];
> > +    unsigned int rx_fifo_len;
> > +    uint32_t ie;
> > +    uint32_t ip;
> > +    uint32_t txctrl;
> > +    uint32_t rxctrl;
> > +    uint32_t div;
> > +} SiFiveUARTState;
> > +
> > +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space,
> hwaddr base,
> > +    Chardev *chr, qemu_irq irq);
> > +
> > +#endif
> >
>
> removing printf():
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>

I will go through and make these changes and include them in the post-merge
cleanup patch series...

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-10  3:02     ` Michael Clark
@ 2018-03-10  9:40       ` Mark Cave-Ayland
  2018-03-11 11:43         ` Bastian Koppelmann
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Cave-Ayland @ 2018-03-10  9:40 UTC (permalink / raw)
  To: Michael Clark, Philippe Mathieu-Daudé
  Cc: Stefan O'Rear, Sagar Karandikar, Bastian Koppelmann,
	Palmer Dabbelt, QEMU Developers, RISC-V Patches

On 10/03/18 03:02, Michael Clark wrote:

> On Sat, Mar 10, 2018 at 1:39 AM, Philippe Mathieu-Daudé <f4bug@amsat.org>
> wrote:
> 
>> On 03/02/2018 02:51 PM, Michael Clark wrote:
>>> QEMU model of the UART on the SiFive E300 and U500 series SOCs.
>>> BBL supports the SiFive UART for early console access via the SBI
>>> (Supervisor Binary Interface) and the linux kernel SBI console.
>>>
>>> The SiFive UART implements the pre qom legacy interface consistent
>>> with the 16550a UART in 'hw/char/serial.c'.

FWIW I can highly recommend switching over to the QOM/qdev APIs where 
possible since it makes rewiring machines much easier, and passing 
around direct types such as qemu_irq via custom functions means people 
are less likely to touch them.

>>> Acked-by: Richard Henderson <richard.henderson@linaro.org>
>>> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
>>> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
>>> Signed-off-by: Michael Clark <mjc@sifive.com>
>>> ---
>>>   hw/riscv/sifive_uart.c         | 176 ++++++++++++++++++++++++++++++
>> +++++++++++
>>>   include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
>>>   2 files changed, 247 insertions(+)
>>>   create mode 100644 hw/riscv/sifive_uart.c
>>>   create mode 100644 include/hw/riscv/sifive_uart.h
>>>
>>> diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
>>> new file mode 100644
>>> index 0000000..b0c3798
>>> --- /dev/null
>>> +++ b/hw/riscv/sifive_uart.c
>>> @@ -0,0 +1,176 @@
>>> +/*
>>> + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
>>> + *
>>> + * Copyright (c) 2016 Stefan O'Rear
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>> it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2 or later, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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/>.
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qapi/error.h"
>>> +#include "hw/sysbus.h"
>>> +#include "chardev/char.h"
>>> +#include "chardev/char-fe.h"
>>> +#include "target/riscv/cpu.h"
>>> +#include "hw/riscv/sifive_uart.h"
>>> +
>>> +/*
>>> + * Not yet implemented:
>>> + *
>>> + * Transmit FIFO using "qemu/fifo8.h"
>>> + * SIFIVE_UART_IE_TXWM interrupts
>>> + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
>>> + * Rx FIFO watermark interrupt trigger threshold
>>> + * Tx FIFO watermark interrupt trigger threshold.
>>> + */
>>> +
>>> +static void update_irq(SiFiveUARTState *s)
>>> +{
>>> +    int cond = 0;
>>> +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
>>> +        cond = 1;
>>> +    }
>>> +    if (cond) {
>>> +        qemu_irq_raise(s->irq);
>>> +    } else {
>>> +        qemu_irq_lower(s->irq);
>>> +    }
>>
>> or     qemu_set_irq(s->irq, cond);
>>
>>> +}
>>> +
>>> +static uint64_t
>>> +uart_read(void *opaque, hwaddr addr, unsigned int size)
>>> +{
>>> +    SiFiveUARTState *s = opaque;
>>> +    unsigned char r;
>>> +    switch (addr) {
>>> +    case SIFIVE_UART_RXFIFO:
>>> +        if (s->rx_fifo_len) {
>>> +            r = s->rx_fifo[0];
>>> +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
>>> +            s->rx_fifo_len--;
>>> +            qemu_chr_fe_accept_input(&s->chr);
>>> +            update_irq(s);
>>> +            return r;
>>> +        }
>>> +        return 0x80000000;
>>
>> Can you add a #define for this bit?
>>
>>> +
>>> +    case SIFIVE_UART_TXFIFO:
>>> +        return 0; /* Should check tx fifo */
>>> +    case SIFIVE_UART_IE:
>>> +        return s->ie;
>>> +    case SIFIVE_UART_IP:
>>> +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
>>> +    case SIFIVE_UART_TXCTRL:
>>> +        return s->txctrl;
>>> +    case SIFIVE_UART_RXCTRL:
>>> +        return s->rxctrl;
>>> +    case SIFIVE_UART_DIV:
>>> +        return s->div;
>>> +    }
>>> +
>>> +    hw_error("%s: bad read: addr=0x%x\n",
>>> +        __func__, (int)addr);
>>
>> qemu_log(GUEST_ERROR)?
>>
>>> +    return 0;
>>> +}
>>> +
>>> +static void
>>> +uart_write(void *opaque, hwaddr addr,
>>> +           uint64_t val64, unsigned int size)
>>> +{
>>> +    SiFiveUARTState *s = opaque;
>>> +    uint32_t value = val64;
>>> +    unsigned char ch = value;
>>> +
>>> +    switch (addr) {
>>> +    case SIFIVE_UART_TXFIFO:
>>> +        qemu_chr_fe_write(&s->chr, &ch, 1);
>>> +        return;
>>> +    case SIFIVE_UART_IE:
>>> +        s->ie = val64;
>>> +        update_irq(s);
>>> +        return;
>>> +    case SIFIVE_UART_TXCTRL:
>>> +        s->txctrl = val64;
>>> +        return;
>>> +    case SIFIVE_UART_RXCTRL:
>>> +        s->rxctrl = val64;
>>> +        return;
>>> +    case SIFIVE_UART_DIV:
>>> +        s->div = val64;
>>> +        return;
>>> +    }
>>> +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
>>> +        __func__, (int)addr, (int)value);
>>
>> Ditto.
>>
>>> +}
>>> +
>>> +static const MemoryRegionOps uart_ops = {
>>> +    .read = uart_read,
>>> +    .write = uart_write,
>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 4,
>>> +        .max_access_size = 4
>>> +    }
>>> +};
>>> +
>>> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
>>> +{
>>> +    SiFiveUARTState *s = opaque;
>>> +
>>> +    /* Got a byte.  */
>>> +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
>>> +        printf("WARNING: UART dropped char.\n");
>>
>> replace printf() by warn_report()?
>>
>>> +        return;
>>> +    }
>>> +    s->rx_fifo[s->rx_fifo_len++] = *buf;
>>> +
>>> +    update_irq(s);
>>> +}
>>> +
>>> +static int uart_can_rx(void *opaque)
>>> +{
>>> +    SiFiveUARTState *s = opaque;
>>> +
>>> +    return s->rx_fifo_len < sizeof(s->rx_fifo);
>>> +}
>>> +
>>> +static void uart_event(void *opaque, int event)
>>> +{
>>> +}
>>> +
>>> +static int uart_be_change(void *opaque)
>>> +{
>>> +    SiFiveUARTState *s = opaque;
>>> +
>>> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
>>> +        uart_be_change, s, NULL, true);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +/*
>>> + * Create UART device.
>>> + */
>>> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space,
>> hwaddr base,
>>> +    Chardev *chr, qemu_irq irq)
>>> +{
>>> +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
>>> +    s->irq = irq;
>>> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
>>> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
>>> +        uart_be_change, s, NULL, true);
>>> +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
>>> +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
>>> +    memory_region_add_subregion(address_space, base, &s->mmio);
>>> +    return s;
>>> +}
>>> diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_
>> uart.h
>>> new file mode 100644
>>> index 0000000..504f18a
>>> --- /dev/null
>>> +++ b/include/hw/riscv/sifive_uart.h
>>> @@ -0,0 +1,71 @@
>>> +/*
>>> + * SiFive UART interface
>>> + *
>>> + * Copyright (c) 2016 Stefan O'Rear
>>> + * Copyright (c) 2017 SiFive, Inc.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>> it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2 or later, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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/>.
>>> + */
>>> +
>>> +#ifndef HW_SIFIVE_UART_H
>>> +#define HW_SIFIVE_UART_H
>>> +
>>> +enum {
>>> +    SIFIVE_UART_TXFIFO        = 0,
>>> +    SIFIVE_UART_RXFIFO        = 4,
>>> +    SIFIVE_UART_TXCTRL        = 8,
>>> +    SIFIVE_UART_TXMARK        = 10,
>>> +    SIFIVE_UART_RXCTRL        = 12,
>>> +    SIFIVE_UART_RXMARK        = 14,
>>> +    SIFIVE_UART_IE            = 16,
>>> +    SIFIVE_UART_IP            = 20,
>>> +    SIFIVE_UART_DIV           = 24,
>>> +    SIFIVE_UART_MAX           = 32
>>> +};
>>> +
>>> +enum {
>>> +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt
>> enable */
>>> +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt
>> enable */
>>> +};
>>> +
>>> +enum {
>>> +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt
>> pending */
>>> +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt
>> pending */
>>> +};
>>
>> These 2 enums don't need to be exported and can stay in sifive_uart.c.
>>
>>> +
>>> +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
>>> +
>>> +#define SIFIVE_UART(obj) \
>>> +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
>>> +
>>> +typedef struct SiFiveUARTState {
>>> +    /*< private >*/
>>> +    SysBusDevice parent_obj;
>>> +
>>> +    /*< public >*/
>>> +    qemu_irq irq;
>>> +    MemoryRegion mmio;
>>> +    CharBackend chr;
>>> +    uint8_t rx_fifo[8];
>>> +    unsigned int rx_fifo_len;
>>> +    uint32_t ie;
>>> +    uint32_t ip;
>>> +    uint32_t txctrl;
>>> +    uint32_t rxctrl;
>>> +    uint32_t div;
>>> +} SiFiveUARTState;
>>> +
>>> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space,
>> hwaddr base,
>>> +    Chardev *chr, qemu_irq irq);
>>> +
>>> +#endif
>>>
>>
>> removing printf():
>> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>>
> 
> I will go through and make these changes and include them in the post-merge
> cleanup patch series...

Another general note: for each of the main QEMU platforms supported 
there is a home page on the official wiki, so do make sure that you set 
yourself a template for RiscV and add some information to help people 
get started.

As an example here's the SPARC page which I currently maintain: 
https://wiki.qemu.org/Documentation/Platforms/SPARC. I tend to also keep 
a rough changelog there too so people can see which features have been 
added in which QEMU version.


HTH,

Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-10  9:40       ` Mark Cave-Ayland
@ 2018-03-11 11:43         ` Bastian Koppelmann
  2018-03-16 18:30           ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Bastian Koppelmann @ 2018-03-11 11:43 UTC (permalink / raw)
  To: Mark Cave-Ayland, Michael Clark, Philippe Mathieu-Daudé
  Cc: Stefan O'Rear, Sagar Karandikar, Palmer Dabbelt,
	QEMU Developers, RISC-V Patches

Hi Mark,

On 03/10/2018 10:40 AM, Mark Cave-Ayland wrote:
> On 10/03/18 03:02, Michael Clark wrote:
> 
>> On Sat, Mar 10, 2018 at 1:39 AM, Philippe Mathieu-Daudé <f4bug@amsat.org>
>> wrote:
>>
[...]
> Another general note: for each of the main QEMU platforms supported
> there is a home page on the official wiki, so do make sure that you set
> yourself a template for RiscV and add some information to help people
> get started.
>
Thanks for the pointer. I went ahead and created a basic page for RISC-V
with instructions on how to build QEMU and boot Fedora.

Cheers,
Bastian

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-11 11:43         ` Bastian Koppelmann
@ 2018-03-16 18:30           ` Michael Clark
  2018-03-16 18:36             ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-16 18:30 UTC (permalink / raw)
  To: Bastian Koppelmann
  Cc: Mark Cave-Ayland, Philippe Mathieu-Daudé,
	Stefan O'Rear, Sagar Karandikar, Palmer Dabbelt,
	QEMU Developers, RISC-V Patches

On Sun, Mar 11, 2018 at 4:43 AM, Bastian Koppelmann <
kbastian@mail.uni-paderborn.de> wrote:

> Hi Mark,
>
> On 03/10/2018 10:40 AM, Mark Cave-Ayland wrote:
> > On 10/03/18 03:02, Michael Clark wrote:
> >
> >> On Sat, Mar 10, 2018 at 1:39 AM, Philippe Mathieu-Daudé <
> f4bug@amsat.org>
> >> wrote:
> >>
> [...]
> > Another general note: for each of the main QEMU platforms supported
> > there is a home page on the official wiki, so do make sure that you set
> > yourself a template for RiscV and add some information to help people
> > get started.
> >
> Thanks for the pointer. I went ahead and created a basic page for RISC-V
> with instructions on how to build QEMU and boot Fedora.
>

Thanks Bastian! I'll add a link back to the official QEMU RISC-V
Architecture page from the riscv-qemu repo wiki...

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-16 18:30           ` Michael Clark
@ 2018-03-16 18:36             ` Michael Clark
  2018-03-16 20:46               ` Bastian Koppelmann
  0 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-03-16 18:36 UTC (permalink / raw)
  To: Bastian Koppelmann
  Cc: Mark Cave-Ayland, Philippe Mathieu-Daudé,
	Stefan O'Rear, Sagar Karandikar, Palmer Dabbelt,
	QEMU Developers, RISC-V Patches

On Fri, Mar 16, 2018 at 11:30 AM, Michael Clark <mjc@sifive.com> wrote:

>
>
> On Sun, Mar 11, 2018 at 4:43 AM, Bastian Koppelmann <
> kbastian@mail.uni-paderborn.de> wrote:
>
>> Hi Mark,
>>
>> On 03/10/2018 10:40 AM, Mark Cave-Ayland wrote:
>> > On 10/03/18 03:02, Michael Clark wrote:
>> >
>> >> On Sat, Mar 10, 2018 at 1:39 AM, Philippe Mathieu-Daudé <
>> f4bug@amsat.org>
>> >> wrote:
>> >>
>> [...]
>> > Another general note: for each of the main QEMU platforms supported
>> > there is a home page on the official wiki, so do make sure that you set
>> > yourself a template for RiscV and add some information to help people
>> > get started.
>> >
>> Thanks for the pointer. I went ahead and created a basic page for RISC-V
>> with instructions on how to build QEMU and boot Fedora.
>>
>
> Thanks Bastian! I'll add a link back to the official QEMU RISC-V
> Architecture page from the riscv-qemu repo wiki...
>

I noticed there is a spelling mistake in the description: "implented"
instead of "implemented"

Michael.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-16 18:36             ` Michael Clark
@ 2018-03-16 20:46               ` Bastian Koppelmann
  0 siblings, 0 replies; 70+ messages in thread
From: Bastian Koppelmann @ 2018-03-16 20:46 UTC (permalink / raw)
  To: Michael Clark
  Cc: Mark Cave-Ayland, Philippe Mathieu-Daudé,
	Stefan O'Rear, Sagar Karandikar, Palmer Dabbelt,
	QEMU Developers, RISC-V Patches

On 03/16/2018 07:36 PM, Michael Clark wrote:
> On Fri, Mar 16, 2018 at 11:30 AM, Michael Clark <mjc@sifive.com> wrote:
> 
>>
>>
>> On Sun, Mar 11, 2018 at 4:43 AM, Bastian Koppelmann <
>> kbastian@mail.uni-paderborn.de> wrote:
>>
>>> Hi Mark,
>>>
>>> On 03/10/2018 10:40 AM, Mark Cave-Ayland wrote:
>>>> On 10/03/18 03:02, Michael Clark wrote:
>>>>
>>>>> On Sat, Mar 10, 2018 at 1:39 AM, Philippe Mathieu-Daudé <
>>> f4bug@amsat.org>
>>>>> wrote:
>>>>>
>>> [...]
>>>> Another general note: for each of the main QEMU platforms supported
>>>> there is a home page on the official wiki, so do make sure that you set
>>>> yourself a template for RiscV and add some information to help people
>>>> get started.
>>>>
>>> Thanks for the pointer. I went ahead and created a basic page for RISC-V
>>> with instructions on how to build QEMU and boot Fedora.
>>>
>>
>> Thanks Bastian! I'll add a link back to the official QEMU RISC-V
>> Architecture page from the riscv-qemu repo wiki...
>>
> 
> I noticed there is a spelling mistake in the description: "implented"
> instead of "implemented"

Woops, fixed :).

Cheers,
Bastian

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation Michael Clark
@ 2018-04-04 12:44   ` Laurent Vivier
  2018-04-08 20:59     ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Laurent Vivier @ 2018-04-04 12:44 UTC (permalink / raw)
  To: Michael Clark, qemu-devel
  Cc: Bastian Koppelmann, Palmer Dabbelt, Sagar Karandikar, RISC-V Patches

Le 02/03/2018 à 14:51, Michael Clark a écrit :
> Implementation of linux user emulation for RISC-V.
> 
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  linux-user/elfload.c              |  22 +++
>  linux-user/main.c                 |  99 +++++++++++++
>  linux-user/riscv/syscall_nr.h     | 287 ++++++++++++++++++++++++++++++++++++++
>  linux-user/riscv/target_cpu.h     |  18 +++
>  linux-user/riscv/target_elf.h     |  14 ++
>  linux-user/riscv/target_signal.h  |  23 +++
>  linux-user/riscv/target_structs.h |  46 ++++++
>  linux-user/riscv/target_syscall.h |  56 ++++++++
>  linux-user/riscv/termbits.h       | 222 +++++++++++++++++++++++++++++
>  linux-user/signal.c               | 203 ++++++++++++++++++++++++++-
>  linux-user/syscall.c              |   2 +
>  linux-user/syscall_defs.h         |  13 +-
>  target/riscv/cpu_user.h           |  13 ++
>  13 files changed, 1012 insertions(+), 6 deletions(-)
>  create mode 100644 linux-user/riscv/syscall_nr.h
>  create mode 100644 linux-user/riscv/target_cpu.h
>  create mode 100644 linux-user/riscv/target_elf.h
>  create mode 100644 linux-user/riscv/target_signal.h
>  create mode 100644 linux-user/riscv/target_structs.h
>  create mode 100644 linux-user/riscv/target_syscall.h
>  create mode 100644 linux-user/riscv/termbits.h
>  create mode 100644 target/riscv/cpu_user.h
> 
...
> diff --git a/linux-user/signal.c b/linux-user/signal.c
> index 9a380b9..4d3f244 100644
> --- a/linux-user/signal.c
> +++ b/linux-user/signal.c
...
> +static abi_ulong get_sigframe(struct target_sigaction *ka,
> +                              CPURISCVState *regs, size_t framesize)
> +{
> +    abi_ulong sp = regs->gpr[xSP];
> +    int onsigstack = on_sig_stack(sp);
> +
> +    /* redzone */
> +    /* This is the X/Open sanctioned signal stack switching.  */
> +    if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) {
> +        sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
> +    }
> +
> +    sp -= framesize;
> +    sp &= ~3UL; /* align sp on 4-byte boundary */

kernel aligns using 0xf. Why do you use a different alignment?

> +
> +    /* If we are on the alternate signal stack and would overflow it, don't.
> +       Return an always-bogus address instead so we will die with SIGSEGV. */
> +    if (onsigstack && !likely(on_sig_stack(sp))) {
> +        return -1L;
> +    }
> +
> +    return sp;
> +}
Other question why don't you use the same logic as in kernel?

1- check for signal stack overflow
2- check for X/Open sanctioned signal stack switching

static inline void __user *get_sigframe(struct ksignal *ksig,
        struct pt_regs *regs, size_t framesize)
{
        unsigned long sp;
        /* Default to using normal stack */
        sp = regs->sp;

        /*
         * If we are on the alternate signal stack and would overflow
it, don't.
         * Return an always-bogus address instead so we will die with
SIGSEGV.
         */
        if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
                return (void __user __force *)(-1UL);

        /* This is the X/Open sanctioned signal stack switching. */
        sp = sigsp(sp, ksig) - framesize;

        /* Align the stack frame. */
        sp &= ~0xfUL;

        return (void __user *)sp;
}

Thanks,
Laurent

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation
  2018-04-04 12:44   ` Laurent Vivier
@ 2018-04-08 20:59     ` Michael Clark
  0 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-04-08 20:59 UTC (permalink / raw)
  To: Laurent Vivier
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Thu, Apr 5, 2018 at 12:44 AM, Laurent Vivier <laurent@vivier.eu> wrote:

> Le 02/03/2018 à 14:51, Michael Clark a écrit :
> > Implementation of linux user emulation for RISC-V.
> >
> > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> > Signed-off-by: Michael Clark <mjc@sifive.com>
> > ---
> >  linux-user/elfload.c              |  22 +++
> >  linux-user/main.c                 |  99 +++++++++++++
> >  linux-user/riscv/syscall_nr.h     | 287 ++++++++++++++++++++++++++++++
> ++++++++
> >  linux-user/riscv/target_cpu.h     |  18 +++
> >  linux-user/riscv/target_elf.h     |  14 ++
> >  linux-user/riscv/target_signal.h  |  23 +++
> >  linux-user/riscv/target_structs.h |  46 ++++++
> >  linux-user/riscv/target_syscall.h |  56 ++++++++
> >  linux-user/riscv/termbits.h       | 222 +++++++++++++++++++++++++++++
> >  linux-user/signal.c               | 203 ++++++++++++++++++++++++++-
> >  linux-user/syscall.c              |   2 +
> >  linux-user/syscall_defs.h         |  13 +-
> >  target/riscv/cpu_user.h           |  13 ++
> >  13 files changed, 1012 insertions(+), 6 deletions(-)
> >  create mode 100644 linux-user/riscv/syscall_nr.h
> >  create mode 100644 linux-user/riscv/target_cpu.h
> >  create mode 100644 linux-user/riscv/target_elf.h
> >  create mode 100644 linux-user/riscv/target_signal.h
> >  create mode 100644 linux-user/riscv/target_structs.h
> >  create mode 100644 linux-user/riscv/target_syscall.h
> >  create mode 100644 linux-user/riscv/termbits.h
> >  create mode 100644 target/riscv/cpu_user.h
> >
> ...
> > diff --git a/linux-user/signal.c b/linux-user/signal.c
> > index 9a380b9..4d3f244 100644
> > --- a/linux-user/signal.c
> > +++ b/linux-user/signal.c
> ...
> > +static abi_ulong get_sigframe(struct target_sigaction *ka,
> > +                              CPURISCVState *regs, size_t framesize)
> > +{
> > +    abi_ulong sp = regs->gpr[xSP];
> > +    int onsigstack = on_sig_stack(sp);
> > +
> > +    /* redzone */
> > +    /* This is the X/Open sanctioned signal stack switching.  */
> > +    if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) {
> > +        sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_
> size;
> > +    }
> > +
> > +    sp -= framesize;
> > +    sp &= ~3UL; /* align sp on 4-byte boundary */
>
> kernel aligns using 0xf. Why do you use a different alignment?


Thanks for reviewing this! This looks like a bug!

I'm raising this as an issue on the riscv-qemu tracker so that its easier
than searching through the mail archive:

- https://github.com/riscv/riscv-qemu/issues/129

> +
> > +    /* If we are on the alternate signal stack and would overflow it,
> don't.
> > +       Return an always-bogus address instead so we will die with
> SIGSEGV. */
> > +    if (onsigstack && !likely(on_sig_stack(sp))) {
> > +        return -1L;
> > +    }
> > +
> > +    return sp;
> > +}
> Other question why don't you use the same logic as in kernel?
>
> 1- check for signal stack overflow
> 2- check for X/Open sanctioned signal stack switching
>
> static inline void __user *get_sigframe(struct ksignal *ksig,
>         struct pt_regs *regs, size_t framesize)
> {
>         unsigned long sp;
>         /* Default to using normal stack */
>         sp = regs->sp;
>
>         /*
>          * If we are on the alternate signal stack and would overflow
> it, don't.
>          * Return an always-bogus address instead so we will die with
> SIGSEGV.
>          */
>         if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
>                 return (void __user __force *)(-1UL);
>
>         /* This is the X/Open sanctioned signal stack switching. */
>         sp = sigsp(sp, ksig) - framesize;
>
>         /* Align the stack frame. */
>         sp &= ~0xfUL;
>
>         return (void __user *)sp;
> }
>
> Thanks,
> Laurent
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device Michael Clark
  2018-03-09 12:39   ` Philippe Mathieu-Daudé
@ 2018-04-10  3:21   ` Antony Pavlov
  2018-04-10  6:17     ` Thomas Huth
  1 sibling, 1 reply; 70+ messages in thread
From: Antony Pavlov @ 2018-04-10  3:21 UTC (permalink / raw)
  To: Michael Clark
  Cc: qemu-devel, Stefan O'Rear, Sagar Karandikar,
	Bastian Koppelmann, Palmer Dabbelt, RISC-V Patches

On Sat,  3 Mar 2018 02:51:47 +1300
Michael Clark <mjc@sifive.com> wrote:

> QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> BBL supports the SiFive UART for early console access via the SBI
> (Supervisor Binary Interface) and the linux kernel SBI console.
> 
> The SiFive UART implements the pre qom legacy interface consistent
> with the 16550a UART in 'hw/char/serial.c'.
> 
> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>  hw/riscv/sifive_uart.c         | 176 +++++++++++++++++++++++++++++++++++++++++
>  include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
>  2 files changed, 247 insertions(+)
>  create mode 100644 hw/riscv/sifive_uart.c
>  create mode 100644 include/hw/riscv/sifive_uart.h
> 
> diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
> new file mode 100644
> index 0000000..b0c3798
> --- /dev/null
> +++ b/hw/riscv/sifive_uart.c
> @@ -0,0 +1,176 @@
> +/*
> + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> + *
> + * Copyright (c) 2016 Stefan O'Rear
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/sysbus.h"
> +#include "chardev/char.h"
> +#include "chardev/char-fe.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/sifive_uart.h"
>
> +/*
> + * Not yet implemented:
> + *
> + * Transmit FIFO using "qemu/fifo8.h"
> + * SIFIVE_UART_IE_TXWM interrupts
> + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
> + * Rx FIFO watermark interrupt trigger threshold
> + * Tx FIFO watermark interrupt trigger threshold.
> + */
> +
> +static void update_irq(SiFiveUARTState *s)
> +{
> +    int cond = 0;
> +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
> +        cond = 1;
> +    }
> +    if (cond) {
> +        qemu_irq_raise(s->irq);
> +    } else {
> +        qemu_irq_lower(s->irq);
> +    }
> +}
> +
> +static uint64_t
> +uart_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    SiFiveUARTState *s = opaque;
> +    unsigned char r;
> +    switch (addr) {
> +    case SIFIVE_UART_RXFIFO:
> +        if (s->rx_fifo_len) {
> +            r = s->rx_fifo[0];
> +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
> +            s->rx_fifo_len--;
> +            qemu_chr_fe_accept_input(&s->chr);
> +            update_irq(s);
> +            return r;
> +        }
> +        return 0x80000000;
> +
> +    case SIFIVE_UART_TXFIFO:
> +        return 0; /* Should check tx fifo */
> +    case SIFIVE_UART_IE:
> +        return s->ie;
> +    case SIFIVE_UART_IP:
> +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
> +    case SIFIVE_UART_TXCTRL:
> +        return s->txctrl;
> +    case SIFIVE_UART_RXCTRL:
> +        return s->rxctrl;
> +    case SIFIVE_UART_DIV:
> +        return s->div;
> +    }
> +
> +    hw_error("%s: bad read: addr=0x%x\n",
> +        __func__, (int)addr);
> +    return 0;
> +}
> +
> +static void
> +uart_write(void *opaque, hwaddr addr,
> +           uint64_t val64, unsigned int size)
> +{
> +    SiFiveUARTState *s = opaque;
> +    uint32_t value = val64;
> +    unsigned char ch = value;
> +
> +    switch (addr) {
> +    case SIFIVE_UART_TXFIFO:
> +        qemu_chr_fe_write(&s->chr, &ch, 1);
> +        return;
> +    case SIFIVE_UART_IE:
> +        s->ie = val64;
> +        update_irq(s);
> +        return;
> +    case SIFIVE_UART_TXCTRL:
> +        s->txctrl = val64;
> +        return;
> +    case SIFIVE_UART_RXCTRL:
> +        s->rxctrl = val64;
> +        return;
> +    case SIFIVE_UART_DIV:
> +        s->div = val64;
> +        return;
> +    }
> +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
> +        __func__, (int)addr, (int)value);
> +}
> +
> +static const MemoryRegionOps uart_ops = {
> +    .read = uart_read,
> +    .write = uart_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> +{
> +    SiFiveUARTState *s = opaque;
> +
> +    /* Got a byte.  */
> +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
> +        printf("WARNING: UART dropped char.\n");
> +        return;
> +    }
> +    s->rx_fifo[s->rx_fifo_len++] = *buf;
> +
> +    update_irq(s);
> +}
> +
> +static int uart_can_rx(void *opaque)
> +{
> +    SiFiveUARTState *s = opaque;
> +
> +    return s->rx_fifo_len < sizeof(s->rx_fifo);
> +}
> +
> +static void uart_event(void *opaque, int event)
> +{
> +}
> +
> +static int uart_be_change(void *opaque)
> +{
> +    SiFiveUARTState *s = opaque;
> +
> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> +        uart_be_change, s, NULL, true);
> +
> +    return 0;
> +}
> +
> +/*
> + * Create UART device.
> + */
> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
> +    Chardev *chr, qemu_irq irq)
> +{
> +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
> +    s->irq = irq;
> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> +        uart_be_change, s, NULL, true);
> +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
> +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
> +    memory_region_add_subregion(address_space, base, &s->mmio);
> +    return s;
> +}
> diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
> new file mode 100644
> index 0000000..504f18a
> --- /dev/null
> +++ b/include/hw/riscv/sifive_uart.h
> @@ -0,0 +1,71 @@
> +/*
> + * SiFive UART interface
> + *
> + * Copyright (c) 2016 Stefan O'Rear
> + * Copyright (c) 2017 SiFive, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/>.
> + */
> +
> +#ifndef HW_SIFIVE_UART_H
> +#define HW_SIFIVE_UART_H
> +
> +enum {
> +    SIFIVE_UART_TXFIFO        = 0,
> +    SIFIVE_UART_RXFIFO        = 4,
> +    SIFIVE_UART_TXCTRL        = 8,
> +    SIFIVE_UART_TXMARK        = 10,
> +    SIFIVE_UART_RXCTRL        = 12,
> +    SIFIVE_UART_RXMARK        = 14,
> +    SIFIVE_UART_IE            = 16,
> +    SIFIVE_UART_IP            = 20,
> +    SIFIVE_UART_DIV           = 24,
> +    SIFIVE_UART_MAX           = 32
> +};
> +
> +enum {
> +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt enable */
> +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt enable */
> +};
> +
> +enum {
> +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt pending */
> +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt pending */
> +};
> +
> +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
> +
> +#define SIFIVE_UART(obj) \
> +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
> +
> +typedef struct SiFiveUARTState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;


You use SysBusDevive in this header file but there is no 'include "hw/sysbus.h"' in the header file itself.

Please see my comment https://github.com/riscv/riscv-qemu/pull/130#issuecomment-379640538


> +    /*< public >*/
> +    qemu_irq irq;
> +    MemoryRegion mmio;
> +    CharBackend chr;

Just the same thing. CharBackend is defined in "chardev/char-fe.h" please include it.

> +    uint8_t rx_fifo[8];
> +    unsigned int rx_fifo_len;
> +    uint32_t ie;
> +    uint32_t ip;
> +    uint32_t txctrl;
> +    uint32_t rxctrl;
> +    uint32_t div;
> +} SiFiveUARTState;
> +
> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
> +    Chardev *chr, qemu_irq irq);
> +
> +#endif
> -- 
> 2.7.0
> 
> 


-- 
Best regards,
  Antony Pavlov

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-04-10  3:21   ` Antony Pavlov
@ 2018-04-10  6:17     ` Thomas Huth
  2018-04-10  8:04       ` Antony Pavlov
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Huth @ 2018-04-10  6:17 UTC (permalink / raw)
  To: Antony Pavlov, Michael Clark
  Cc: Stefan O'Rear, Sagar Karandikar, Bastian Koppelmann,
	Palmer Dabbelt, qemu-devel, RISC-V Patches

On 10.04.2018 05:21, Antony Pavlov wrote:
> On Sat,  3 Mar 2018 02:51:47 +1300
> Michael Clark <mjc@sifive.com> wrote:
> 
>> QEMU model of the UART on the SiFive E300 and U500 series SOCs.
>> BBL supports the SiFive UART for early console access via the SBI
>> (Supervisor Binary Interface) and the linux kernel SBI console.
>>
>> The SiFive UART implements the pre qom legacy interface consistent
>> with the 16550a UART in 'hw/char/serial.c'.
>>
>> Acked-by: Richard Henderson <richard.henderson@linaro.org>
>> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
>> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
>> Signed-off-by: Michael Clark <mjc@sifive.com>
>> ---
>>  hw/riscv/sifive_uart.c         | 176 +++++++++++++++++++++++++++++++++++++++++
>>  include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
>>  2 files changed, 247 insertions(+)
>>  create mode 100644 hw/riscv/sifive_uart.c
>>  create mode 100644 include/hw/riscv/sifive_uart.h
>>
>> diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
>> new file mode 100644
>> index 0000000..b0c3798
>> --- /dev/null
>> +++ b/hw/riscv/sifive_uart.c
>> @@ -0,0 +1,176 @@
>> +/*
>> + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
>> + *
>> + * Copyright (c) 2016 Stefan O'Rear
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2 or later, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "hw/sysbus.h"
>> +#include "chardev/char.h"
>> +#include "chardev/char-fe.h"
>> +#include "target/riscv/cpu.h"
>> +#include "hw/riscv/sifive_uart.h"
>>
>> +/*
>> + * Not yet implemented:
>> + *
>> + * Transmit FIFO using "qemu/fifo8.h"
>> + * SIFIVE_UART_IE_TXWM interrupts
>> + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
>> + * Rx FIFO watermark interrupt trigger threshold
>> + * Tx FIFO watermark interrupt trigger threshold.
>> + */
>> +
>> +static void update_irq(SiFiveUARTState *s)
>> +{
>> +    int cond = 0;
>> +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
>> +        cond = 1;
>> +    }
>> +    if (cond) {
>> +        qemu_irq_raise(s->irq);
>> +    } else {
>> +        qemu_irq_lower(s->irq);
>> +    }
>> +}
>> +
>> +static uint64_t
>> +uart_read(void *opaque, hwaddr addr, unsigned int size)
>> +{
>> +    SiFiveUARTState *s = opaque;
>> +    unsigned char r;
>> +    switch (addr) {
>> +    case SIFIVE_UART_RXFIFO:
>> +        if (s->rx_fifo_len) {
>> +            r = s->rx_fifo[0];
>> +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
>> +            s->rx_fifo_len--;
>> +            qemu_chr_fe_accept_input(&s->chr);
>> +            update_irq(s);
>> +            return r;
>> +        }
>> +        return 0x80000000;
>> +
>> +    case SIFIVE_UART_TXFIFO:
>> +        return 0; /* Should check tx fifo */
>> +    case SIFIVE_UART_IE:
>> +        return s->ie;
>> +    case SIFIVE_UART_IP:
>> +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
>> +    case SIFIVE_UART_TXCTRL:
>> +        return s->txctrl;
>> +    case SIFIVE_UART_RXCTRL:
>> +        return s->rxctrl;
>> +    case SIFIVE_UART_DIV:
>> +        return s->div;
>> +    }
>> +
>> +    hw_error("%s: bad read: addr=0x%x\n",
>> +        __func__, (int)addr);
>> +    return 0;
>> +}
>> +
>> +static void
>> +uart_write(void *opaque, hwaddr addr,
>> +           uint64_t val64, unsigned int size)
>> +{
>> +    SiFiveUARTState *s = opaque;
>> +    uint32_t value = val64;
>> +    unsigned char ch = value;
>> +
>> +    switch (addr) {
>> +    case SIFIVE_UART_TXFIFO:
>> +        qemu_chr_fe_write(&s->chr, &ch, 1);
>> +        return;
>> +    case SIFIVE_UART_IE:
>> +        s->ie = val64;
>> +        update_irq(s);
>> +        return;
>> +    case SIFIVE_UART_TXCTRL:
>> +        s->txctrl = val64;
>> +        return;
>> +    case SIFIVE_UART_RXCTRL:
>> +        s->rxctrl = val64;
>> +        return;
>> +    case SIFIVE_UART_DIV:
>> +        s->div = val64;
>> +        return;
>> +    }
>> +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
>> +        __func__, (int)addr, (int)value);
>> +}
>> +
>> +static const MemoryRegionOps uart_ops = {
>> +    .read = uart_read,
>> +    .write = uart_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4
>> +    }
>> +};
>> +
>> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
>> +{
>> +    SiFiveUARTState *s = opaque;
>> +
>> +    /* Got a byte.  */
>> +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
>> +        printf("WARNING: UART dropped char.\n");
>> +        return;
>> +    }
>> +    s->rx_fifo[s->rx_fifo_len++] = *buf;
>> +
>> +    update_irq(s);
>> +}
>> +
>> +static int uart_can_rx(void *opaque)
>> +{
>> +    SiFiveUARTState *s = opaque;
>> +
>> +    return s->rx_fifo_len < sizeof(s->rx_fifo);
>> +}
>> +
>> +static void uart_event(void *opaque, int event)
>> +{
>> +}
>> +
>> +static int uart_be_change(void *opaque)
>> +{
>> +    SiFiveUARTState *s = opaque;
>> +
>> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
>> +        uart_be_change, s, NULL, true);
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Create UART device.
>> + */
>> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
>> +    Chardev *chr, qemu_irq irq)
>> +{
>> +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
>> +    s->irq = irq;
>> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
>> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
>> +        uart_be_change, s, NULL, true);
>> +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
>> +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
>> +    memory_region_add_subregion(address_space, base, &s->mmio);
>> +    return s;
>> +}
>> diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
>> new file mode 100644
>> index 0000000..504f18a
>> --- /dev/null
>> +++ b/include/hw/riscv/sifive_uart.h
>> @@ -0,0 +1,71 @@
>> +/*
>> + * SiFive UART interface
>> + *
>> + * Copyright (c) 2016 Stefan O'Rear
>> + * Copyright (c) 2017 SiFive, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2 or later, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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/>.
>> + */
>> +
>> +#ifndef HW_SIFIVE_UART_H
>> +#define HW_SIFIVE_UART_H
>> +
>> +enum {
>> +    SIFIVE_UART_TXFIFO        = 0,
>> +    SIFIVE_UART_RXFIFO        = 4,
>> +    SIFIVE_UART_TXCTRL        = 8,
>> +    SIFIVE_UART_TXMARK        = 10,
>> +    SIFIVE_UART_RXCTRL        = 12,
>> +    SIFIVE_UART_RXMARK        = 14,
>> +    SIFIVE_UART_IE            = 16,
>> +    SIFIVE_UART_IP            = 20,
>> +    SIFIVE_UART_DIV           = 24,
>> +    SIFIVE_UART_MAX           = 32
>> +};
>> +
>> +enum {
>> +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt enable */
>> +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt enable */
>> +};
>> +
>> +enum {
>> +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt pending */
>> +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt pending */
>> +};
>> +
>> +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
>> +
>> +#define SIFIVE_UART(obj) \
>> +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
>> +
>> +typedef struct SiFiveUARTState {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
> 
> 
> You use SysBusDevive in this header file but there is no 'include "hw/sysbus.h"' in the header file itself.
> 
> Please see my comment https://github.com/riscv/riscv-qemu/pull/130#issuecomment-379640538
> 
> 
>> +    /*< public >*/
>> +    qemu_irq irq;
>> +    MemoryRegion mmio;
>> +    CharBackend chr;
> 
> Just the same thing. CharBackend is defined in "chardev/char-fe.h" please include it.

Honestly, I rather prefer to *not* add more includes to header files
than we already have. We already have got lots of "touch this header and
you have to recompile almost the whole QEMU" conditions, so to avoid
that this situation gets worse, we should rather avoid including headers
from headers if it is not necessary. Thus if the current sources build
fine, no need to change anything here. Just my 0.02 €.

 Thomas

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-04-10  6:17     ` Thomas Huth
@ 2018-04-10  8:04       ` Antony Pavlov
  2018-04-11 21:12         ` Michael Clark
  2018-04-11 22:25         ` Eric Blake
  0 siblings, 2 replies; 70+ messages in thread
From: Antony Pavlov @ 2018-04-10  8:04 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Michael Clark, Stefan O'Rear, Sagar Karandikar,
	Bastian Koppelmann, Palmer Dabbelt, qemu-devel, RISC-V Patches,
	Eric Blake

On Tue, 10 Apr 2018 08:17:32 +0200
Thomas Huth <thuth@redhat.com> wrote:

> On 10.04.2018 05:21, Antony Pavlov wrote:
> > On Sat,  3 Mar 2018 02:51:47 +1300
> > Michael Clark <mjc@sifive.com> wrote:
> > 
> >> QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> >> BBL supports the SiFive UART for early console access via the SBI
> >> (Supervisor Binary Interface) and the linux kernel SBI console.
> >>
> >> The SiFive UART implements the pre qom legacy interface consistent
> >> with the 16550a UART in 'hw/char/serial.c'.
> >>
> >> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> >> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
> >> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> >> Signed-off-by: Michael Clark <mjc@sifive.com>
> >> ---
> >>  hw/riscv/sifive_uart.c         | 176 +++++++++++++++++++++++++++++++++++++++++
> >>  include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
> >>  2 files changed, 247 insertions(+)
> >>  create mode 100644 hw/riscv/sifive_uart.c
> >>  create mode 100644 include/hw/riscv/sifive_uart.h
> >>
> >> diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
> >> new file mode 100644
> >> index 0000000..b0c3798
> >> --- /dev/null
> >> +++ b/hw/riscv/sifive_uart.c
> >> @@ -0,0 +1,176 @@
> >> +/*
> >> + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> >> + *
> >> + * Copyright (c) 2016 Stefan O'Rear
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify it
> >> + * under the terms and conditions of the GNU General Public License,
> >> + * version 2 or later, as published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope 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/>.
> >> + */
> >> +
> >> +#include "qemu/osdep.h"
> >> +#include "qapi/error.h"
> >> +#include "hw/sysbus.h"
> >> +#include "chardev/char.h"
> >> +#include "chardev/char-fe.h"
> >> +#include "target/riscv/cpu.h"
> >> +#include "hw/riscv/sifive_uart.h"
> >>
> >> +/*
> >> + * Not yet implemented:
> >> + *
> >> + * Transmit FIFO using "qemu/fifo8.h"
> >> + * SIFIVE_UART_IE_TXWM interrupts
> >> + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
> >> + * Rx FIFO watermark interrupt trigger threshold
> >> + * Tx FIFO watermark interrupt trigger threshold.
> >> + */
> >> +
> >> +static void update_irq(SiFiveUARTState *s)
> >> +{
> >> +    int cond = 0;
> >> +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
> >> +        cond = 1;
> >> +    }
> >> +    if (cond) {
> >> +        qemu_irq_raise(s->irq);
> >> +    } else {
> >> +        qemu_irq_lower(s->irq);
> >> +    }
> >> +}
> >> +
> >> +static uint64_t
> >> +uart_read(void *opaque, hwaddr addr, unsigned int size)
> >> +{
> >> +    SiFiveUARTState *s = opaque;
> >> +    unsigned char r;
> >> +    switch (addr) {
> >> +    case SIFIVE_UART_RXFIFO:
> >> +        if (s->rx_fifo_len) {
> >> +            r = s->rx_fifo[0];
> >> +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
> >> +            s->rx_fifo_len--;
> >> +            qemu_chr_fe_accept_input(&s->chr);
> >> +            update_irq(s);
> >> +            return r;
> >> +        }
> >> +        return 0x80000000;
> >> +
> >> +    case SIFIVE_UART_TXFIFO:
> >> +        return 0; /* Should check tx fifo */
> >> +    case SIFIVE_UART_IE:
> >> +        return s->ie;
> >> +    case SIFIVE_UART_IP:
> >> +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
> >> +    case SIFIVE_UART_TXCTRL:
> >> +        return s->txctrl;
> >> +    case SIFIVE_UART_RXCTRL:
> >> +        return s->rxctrl;
> >> +    case SIFIVE_UART_DIV:
> >> +        return s->div;
> >> +    }
> >> +
> >> +    hw_error("%s: bad read: addr=0x%x\n",
> >> +        __func__, (int)addr);
> >> +    return 0;
> >> +}
> >> +
> >> +static void
> >> +uart_write(void *opaque, hwaddr addr,
> >> +           uint64_t val64, unsigned int size)
> >> +{
> >> +    SiFiveUARTState *s = opaque;
> >> +    uint32_t value = val64;
> >> +    unsigned char ch = value;
> >> +
> >> +    switch (addr) {
> >> +    case SIFIVE_UART_TXFIFO:
> >> +        qemu_chr_fe_write(&s->chr, &ch, 1);
> >> +        return;
> >> +    case SIFIVE_UART_IE:
> >> +        s->ie = val64;
> >> +        update_irq(s);
> >> +        return;
> >> +    case SIFIVE_UART_TXCTRL:
> >> +        s->txctrl = val64;
> >> +        return;
> >> +    case SIFIVE_UART_RXCTRL:
> >> +        s->rxctrl = val64;
> >> +        return;
> >> +    case SIFIVE_UART_DIV:
> >> +        s->div = val64;
> >> +        return;
> >> +    }
> >> +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
> >> +        __func__, (int)addr, (int)value);
> >> +}
> >> +
> >> +static const MemoryRegionOps uart_ops = {
> >> +    .read = uart_read,
> >> +    .write = uart_write,
> >> +    .endianness = DEVICE_NATIVE_ENDIAN,
> >> +    .valid = {
> >> +        .min_access_size = 4,
> >> +        .max_access_size = 4
> >> +    }
> >> +};
> >> +
> >> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> >> +{
> >> +    SiFiveUARTState *s = opaque;
> >> +
> >> +    /* Got a byte.  */
> >> +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
> >> +        printf("WARNING: UART dropped char.\n");
> >> +        return;
> >> +    }
> >> +    s->rx_fifo[s->rx_fifo_len++] = *buf;
> >> +
> >> +    update_irq(s);
> >> +}
> >> +
> >> +static int uart_can_rx(void *opaque)
> >> +{
> >> +    SiFiveUARTState *s = opaque;
> >> +
> >> +    return s->rx_fifo_len < sizeof(s->rx_fifo);
> >> +}
> >> +
> >> +static void uart_event(void *opaque, int event)
> >> +{
> >> +}
> >> +
> >> +static int uart_be_change(void *opaque)
> >> +{
> >> +    SiFiveUARTState *s = opaque;
> >> +
> >> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> >> +        uart_be_change, s, NULL, true);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +/*
> >> + * Create UART device.
> >> + */
> >> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
> >> +    Chardev *chr, qemu_irq irq)
> >> +{
> >> +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
> >> +    s->irq = irq;
> >> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
> >> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
> >> +        uart_be_change, s, NULL, true);
> >> +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
> >> +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
> >> +    memory_region_add_subregion(address_space, base, &s->mmio);
> >> +    return s;
> >> +}
> >> diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
> >> new file mode 100644
> >> index 0000000..504f18a
> >> --- /dev/null
> >> +++ b/include/hw/riscv/sifive_uart.h
> >> @@ -0,0 +1,71 @@
> >> +/*
> >> + * SiFive UART interface
> >> + *
> >> + * Copyright (c) 2016 Stefan O'Rear
> >> + * Copyright (c) 2017 SiFive, Inc.
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify it
> >> + * under the terms and conditions of the GNU General Public License,
> >> + * version 2 or later, as published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope 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/>.
> >> + */
> >> +
> >> +#ifndef HW_SIFIVE_UART_H
> >> +#define HW_SIFIVE_UART_H
> >> +
> >> +enum {
> >> +    SIFIVE_UART_TXFIFO        = 0,
> >> +    SIFIVE_UART_RXFIFO        = 4,
> >> +    SIFIVE_UART_TXCTRL        = 8,
> >> +    SIFIVE_UART_TXMARK        = 10,
> >> +    SIFIVE_UART_RXCTRL        = 12,
> >> +    SIFIVE_UART_RXMARK        = 14,
> >> +    SIFIVE_UART_IE            = 16,
> >> +    SIFIVE_UART_IP            = 20,
> >> +    SIFIVE_UART_DIV           = 24,
> >> +    SIFIVE_UART_MAX           = 32
> >> +};
> >> +
> >> +enum {
> >> +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt enable */
> >> +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt enable */
> >> +};
> >> +
> >> +enum {
> >> +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt pending */
> >> +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt pending */
> >> +};
> >> +
> >> +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
> >> +
> >> +#define SIFIVE_UART(obj) \
> >> +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
> >> +
> >> +typedef struct SiFiveUARTState {
> >> +    /*< private >*/
> >> +    SysBusDevice parent_obj;
> > 
> > 
> > You use SysBusDevive in this header file but there is no 'include "hw/sysbus.h"' in the header file itself.
> > 
> > Please see my comment https://github.com/riscv/riscv-qemu/pull/130#issuecomment-379640538
> > 
> > 
> >> +    /*< public >*/
> >> +    qemu_irq irq;
> >> +    MemoryRegion mmio;
> >> +    CharBackend chr;
> > 
> > Just the same thing. CharBackend is defined in "chardev/char-fe.h" please include it.
> 
> Honestly, I rather prefer to *not* add more includes to header files
> than we already have. We already have got lots of "touch this header and
> you have to recompile almost the whole QEMU" conditions, so to avoid
> that this situation gets worse, we should rather avoid including headers
> from headers if it is not necessary. Thus if the current sources build
> fine, no need to change anything here. Just my 0.02 €.

Adding ONLY NECESSARY header files inclusions to header file __can't produce__
additional recompile efforts.
Moreover this can decrease number of include directives in c-files.

I __rebased__ my RISC-V board in my out-of tree qemu branch (https://github.com/miet-riscv-workgroup/riscv-qemu/tree/20180409.erizo). I faced with problem: I have to track dependencies of
header files from include/hw/riscv/ which I use.

So your "not-add-more-includes-to-header-files" approach has an disadvantage:
if a header file required header list changes, each c-file that includes that header file
must be edited to update the #include statement list.

RISC-V is a very promising architecture. It is possible that we will have many RISC-V-related
c-files in the future in qemu. It will be very painful to change these c-files on every RISC-V header
files requirements change.

To Eric Blake:
I have added you to CC because of your comment in the conversation https://patchwork.kernel.org/patch/10147099/

-- 
Best regards,
  Antony Pavlov

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-04-10  8:04       ` Antony Pavlov
@ 2018-04-11 21:12         ` Michael Clark
  2018-04-11 22:25         ` Eric Blake
  1 sibling, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-04-11 21:12 UTC (permalink / raw)
  To: Antony Pavlov
  Cc: Thomas Huth, Stefan O'Rear, Sagar Karandikar,
	Bastian Koppelmann, Palmer Dabbelt, QEMU Developers,
	RISC-V Patches, Eric Blake

On Tue, Apr 10, 2018 at 8:04 PM, Antony Pavlov <antonynpavlov@gmail.com>
wrote:

> On Tue, 10 Apr 2018 08:17:32 +0200
> Thomas Huth <thuth@redhat.com> wrote:
>
> > On 10.04.2018 05:21, Antony Pavlov wrote:
> > > On Sat,  3 Mar 2018 02:51:47 +1300
> > > Michael Clark <mjc@sifive.com> wrote:
> > >
> > >> QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> > >> BBL supports the SiFive UART for early console access via the SBI
> > >> (Supervisor Binary Interface) and the linux kernel SBI console.
> > >>
> > >> The SiFive UART implements the pre qom legacy interface consistent
> > >> with the 16550a UART in 'hw/char/serial.c'.
> > >>
> > >> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> > >> Signed-off-by: Stefan O'Rear <sorear2@gmail.com>
> > >> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> > >> Signed-off-by: Michael Clark <mjc@sifive.com>
> > >> ---
> > >>  hw/riscv/sifive_uart.c         | 176 ++++++++++++++++++++++++++++++
> +++++++++++
> > >>  include/hw/riscv/sifive_uart.h |  71 +++++++++++++++++
> > >>  2 files changed, 247 insertions(+)
> > >>  create mode 100644 hw/riscv/sifive_uart.c
> > >>  create mode 100644 include/hw/riscv/sifive_uart.h
> > >>
> > >> diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
> > >> new file mode 100644
> > >> index 0000000..b0c3798
> > >> --- /dev/null
> > >> +++ b/hw/riscv/sifive_uart.c
> > >> @@ -0,0 +1,176 @@
> > >> +/*
> > >> + * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
> > >> + *
> > >> + * Copyright (c) 2016 Stefan O'Rear
> > >> + *
> > >> + * This program is free software; you can redistribute it and/or
> modify it
> > >> + * under the terms and conditions of the GNU General Public License,
> > >> + * version 2 or later, as published by the Free Software Foundation.
> > >> + *
> > >> + * This program is distributed in the hope 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/>.
> > >> + */
> > >> +
> > >> +#include "qemu/osdep.h"
> > >> +#include "qapi/error.h"
> > >> +#include "hw/sysbus.h"
> > >> +#include "chardev/char.h"
> > >> +#include "chardev/char-fe.h"
> > >> +#include "target/riscv/cpu.h"
> > >> +#include "hw/riscv/sifive_uart.h"
> > >>
> > >> +/*
> > >> + * Not yet implemented:
> > >> + *
> > >> + * Transmit FIFO using "qemu/fifo8.h"
> > >> + * SIFIVE_UART_IE_TXWM interrupts
> > >> + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
> > >> + * Rx FIFO watermark interrupt trigger threshold
> > >> + * Tx FIFO watermark interrupt trigger threshold.
> > >> + */
> > >> +
> > >> +static void update_irq(SiFiveUARTState *s)
> > >> +{
> > >> +    int cond = 0;
> > >> +    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
> > >> +        cond = 1;
> > >> +    }
> > >> +    if (cond) {
> > >> +        qemu_irq_raise(s->irq);
> > >> +    } else {
> > >> +        qemu_irq_lower(s->irq);
> > >> +    }
> > >> +}
> > >> +
> > >> +static uint64_t
> > >> +uart_read(void *opaque, hwaddr addr, unsigned int size)
> > >> +{
> > >> +    SiFiveUARTState *s = opaque;
> > >> +    unsigned char r;
> > >> +    switch (addr) {
> > >> +    case SIFIVE_UART_RXFIFO:
> > >> +        if (s->rx_fifo_len) {
> > >> +            r = s->rx_fifo[0];
> > >> +            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
> > >> +            s->rx_fifo_len--;
> > >> +            qemu_chr_fe_accept_input(&s->chr);
> > >> +            update_irq(s);
> > >> +            return r;
> > >> +        }
> > >> +        return 0x80000000;
> > >> +
> > >> +    case SIFIVE_UART_TXFIFO:
> > >> +        return 0; /* Should check tx fifo */
> > >> +    case SIFIVE_UART_IE:
> > >> +        return s->ie;
> > >> +    case SIFIVE_UART_IP:
> > >> +        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
> > >> +    case SIFIVE_UART_TXCTRL:
> > >> +        return s->txctrl;
> > >> +    case SIFIVE_UART_RXCTRL:
> > >> +        return s->rxctrl;
> > >> +    case SIFIVE_UART_DIV:
> > >> +        return s->div;
> > >> +    }
> > >> +
> > >> +    hw_error("%s: bad read: addr=0x%x\n",
> > >> +        __func__, (int)addr);
> > >> +    return 0;
> > >> +}
> > >> +
> > >> +static void
> > >> +uart_write(void *opaque, hwaddr addr,
> > >> +           uint64_t val64, unsigned int size)
> > >> +{
> > >> +    SiFiveUARTState *s = opaque;
> > >> +    uint32_t value = val64;
> > >> +    unsigned char ch = value;
> > >> +
> > >> +    switch (addr) {
> > >> +    case SIFIVE_UART_TXFIFO:
> > >> +        qemu_chr_fe_write(&s->chr, &ch, 1);
> > >> +        return;
> > >> +    case SIFIVE_UART_IE:
> > >> +        s->ie = val64;
> > >> +        update_irq(s);
> > >> +        return;
> > >> +    case SIFIVE_UART_TXCTRL:
> > >> +        s->txctrl = val64;
> > >> +        return;
> > >> +    case SIFIVE_UART_RXCTRL:
> > >> +        s->rxctrl = val64;
> > >> +        return;
> > >> +    case SIFIVE_UART_DIV:
> > >> +        s->div = val64;
> > >> +        return;
> > >> +    }
> > >> +    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
> > >> +        __func__, (int)addr, (int)value);
> > >> +}
> > >> +
> > >> +static const MemoryRegionOps uart_ops = {
> > >> +    .read = uart_read,
> > >> +    .write = uart_write,
> > >> +    .endianness = DEVICE_NATIVE_ENDIAN,
> > >> +    .valid = {
> > >> +        .min_access_size = 4,
> > >> +        .max_access_size = 4
> > >> +    }
> > >> +};
> > >> +
> > >> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> > >> +{
> > >> +    SiFiveUARTState *s = opaque;
> > >> +
> > >> +    /* Got a byte.  */
> > >> +    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
> > >> +        printf("WARNING: UART dropped char.\n");
> > >> +        return;
> > >> +    }
> > >> +    s->rx_fifo[s->rx_fifo_len++] = *buf;
> > >> +
> > >> +    update_irq(s);
> > >> +}
> > >> +
> > >> +static int uart_can_rx(void *opaque)
> > >> +{
> > >> +    SiFiveUARTState *s = opaque;
> > >> +
> > >> +    return s->rx_fifo_len < sizeof(s->rx_fifo);
> > >> +}
> > >> +
> > >> +static void uart_event(void *opaque, int event)
> > >> +{
> > >> +}
> > >> +
> > >> +static int uart_be_change(void *opaque)
> > >> +{
> > >> +    SiFiveUARTState *s = opaque;
> > >> +
> > >> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
> uart_event,
> > >> +        uart_be_change, s, NULL, true);
> > >> +
> > >> +    return 0;
> > >> +}
> > >> +
> > >> +/*
> > >> + * Create UART device.
> > >> + */
> > >> +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space,
> hwaddr base,
> > >> +    Chardev *chr, qemu_irq irq)
> > >> +{
> > >> +    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
> > >> +    s->irq = irq;
> > >> +    qemu_chr_fe_init(&s->chr, chr, &error_abort);
> > >> +    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
> uart_event,
> > >> +        uart_be_change, s, NULL, true);
> > >> +    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
> > >> +                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
> > >> +    memory_region_add_subregion(address_space, base, &s->mmio);
> > >> +    return s;
> > >> +}
> > >> diff --git a/include/hw/riscv/sifive_uart.h
> b/include/hw/riscv/sifive_uart.h
> > >> new file mode 100644
> > >> index 0000000..504f18a
> > >> --- /dev/null
> > >> +++ b/include/hw/riscv/sifive_uart.h
> > >> @@ -0,0 +1,71 @@
> > >> +/*
> > >> + * SiFive UART interface
> > >> + *
> > >> + * Copyright (c) 2016 Stefan O'Rear
> > >> + * Copyright (c) 2017 SiFive, Inc.
> > >> + *
> > >> + * This program is free software; you can redistribute it and/or
> modify it
> > >> + * under the terms and conditions of the GNU General Public License,
> > >> + * version 2 or later, as published by the Free Software Foundation.
> > >> + *
> > >> + * This program is distributed in the hope 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/>.
> > >> + */
> > >> +
> > >> +#ifndef HW_SIFIVE_UART_H
> > >> +#define HW_SIFIVE_UART_H
> > >> +
> > >> +enum {
> > >> +    SIFIVE_UART_TXFIFO        = 0,
> > >> +    SIFIVE_UART_RXFIFO        = 4,
> > >> +    SIFIVE_UART_TXCTRL        = 8,
> > >> +    SIFIVE_UART_TXMARK        = 10,
> > >> +    SIFIVE_UART_RXCTRL        = 12,
> > >> +    SIFIVE_UART_RXMARK        = 14,
> > >> +    SIFIVE_UART_IE            = 16,
> > >> +    SIFIVE_UART_IP            = 20,
> > >> +    SIFIVE_UART_DIV           = 24,
> > >> +    SIFIVE_UART_MAX           = 32
> > >> +};
> > >> +
> > >> +enum {
> > >> +    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt
> enable */
> > >> +    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt
> enable */
> > >> +};
> > >> +
> > >> +enum {
> > >> +    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt
> pending */
> > >> +    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt
> pending */
> > >> +};
> > >> +
> > >> +#define TYPE_SIFIVE_UART "riscv.sifive.uart"
> > >> +
> > >> +#define SIFIVE_UART(obj) \
> > >> +    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
> > >> +
> > >> +typedef struct SiFiveUARTState {
> > >> +    /*< private >*/
> > >> +    SysBusDevice parent_obj;
> > >
> > >
> > > You use SysBusDevive in this header file but there is no 'include
> "hw/sysbus.h"' in the header file itself.
> > >
> > > Please see my comment https://github.com/riscv/riscv-qemu/pull/130#
> issuecomment-379640538
> > >
> > >
> > >> +    /*< public >*/
> > >> +    qemu_irq irq;
> > >> +    MemoryRegion mmio;
> > >> +    CharBackend chr;
> > >
> > > Just the same thing. CharBackend is defined in "chardev/char-fe.h"
> please include it.
> >
> > Honestly, I rather prefer to *not* add more includes to header files
> > than we already have. We already have got lots of "touch this header and
> > you have to recompile almost the whole QEMU" conditions, so to avoid
> > that this situation gets worse, we should rather avoid including headers
> > from headers if it is not necessary. Thus if the current sources build
> > fine, no need to change anything here. Just my 0.02 €.
>
> Adding ONLY NECESSARY header files inclusions to header file __can't
> produce__
> additional recompile efforts.
> Moreover this can decrease number of include directives in c-files.
>
> I __rebased__ my RISC-V board in my out-of tree qemu branch (
> https://github.com/miet-riscv-workgroup/riscv-qemu/tree/20180409.erizo).
> I faced with problem: I have to track dependencies of
> header files from include/hw/riscv/ which I use.
>
> So your "not-add-more-includes-to-header-files" approach has an
> disadvantage:
> if a header file required header list changes, each c-file that includes
> that header file
> must be edited to update the #include statement list.
>
> RISC-V is a very promising architecture. It is possible that we will have
> many RISC-V-related
> c-files in the future in qemu. It will be very painful to change these
> c-files on every RISC-V header
> files requirements change.
>
> To Eric Blake:
> I have added you to CC because of your comment in the conversation
> https://patchwork.kernel.org/patch/10147099/


Currently no headers in include/hw/riscv/*.h include any other headers and
i'd like to keep it that way if possible.

It's not painful if no headers include any other headers. On the contrary
it keeps things simple. There are no implicit dependencies. Core QEMU
header dependencies should not be brought in by riscv specific headers.
That would lead to source modules that do not include their QEMU
dependencies making the situation for managing source modules more opaque.

We have followed the best practice that source files should explicitly
include their header dependencies.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device
  2018-04-10  8:04       ` Antony Pavlov
  2018-04-11 21:12         ` Michael Clark
@ 2018-04-11 22:25         ` Eric Blake
  1 sibling, 0 replies; 70+ messages in thread
From: Eric Blake @ 2018-04-11 22:25 UTC (permalink / raw)
  To: Antony Pavlov, Thomas Huth
  Cc: Michael Clark, Stefan O'Rear, Sagar Karandikar,
	Bastian Koppelmann, Palmer Dabbelt, qemu-devel, RISC-V Patches

[-- Attachment #1: Type: text/plain, Size: 3695 bytes --]

On 04/10/2018 03:04 AM, Antony Pavlov wrote:

>>>> +++ b/include/hw/riscv/sifive_uart.h

>>>> +
>>>> +typedef struct SiFiveUARTState {
>>>> +    /*< private >*/
>>>> +    SysBusDevice parent_obj;
>>>
>>>
>>> You use SysBusDevive in this header file but there is no 'include "hw/sysbus.h"' in the header file itself.

That is, this header is not standalone; a .c file can't use the header
unless it first includes hw/sysbus.h prior to sifive_uart.h.

>>>
>>> Please see my comment https://github.com/riscv/riscv-qemu/pull/130#issuecomment-379640538
>>>
>>>
>>>> +    /*< public >*/
>>>> +    qemu_irq irq;
>>>> +    MemoryRegion mmio;
>>>> +    CharBackend chr;
>>>
>>> Just the same thing. CharBackend is defined in "chardev/char-fe.h" please include it.

If you were to use a CharBackend*, you could get by with just the
typedef.  Since all .c files include osdeps.h, which in turn includes
typedefs.h, you wouldn't have to include anything if you only refer to
the type via a pointer.  But here, you are including a full object, so
the compiler has to know the size of the type, which means this header
DOES depend on "chardev/char-fe.h" being included first (either in this
.h to keep it standalone, or in all .c files prior to the point where
they include sifive_uart.h).

>>
>> Honestly, I rather prefer to *not* add more includes to header files
>> than we already have. We already have got lots of "touch this header and
>> you have to recompile almost the whole QEMU" conditions, so to avoid
>> that this situation gets worse, we should rather avoid including headers
>> from headers if it is not necessary. Thus if the current sources build
>> fine, no need to change anything here. Just my 0.02 €.
> 
> Adding ONLY NECESSARY header files inclusions to header file __can't produce__
> additional recompile efforts.
> Moreover this can decrease number of include directives in c-files.

My personal preference: if your header only refers to a type via a
pointer where the header is still standalone with just the appropriate
typedefs, then DON'T include another .h from your header.  But if your
header has a hard dependency on something not already included by
osdeps.h, where failing to include that other header first creates a
compile error, then including the .h in your header is appropriate, as
it is less work for all .c clients that use your header.

The art of reducing compile-time dependencies is figuring out which
structs must be included inline (requiring .h in headers), and where you
can use pointers, or opaque types that live in .c, or whatever other
solutions, so that the headers become lighter-weight.  But it is NOT
designed to break standalone use of a header (other than the one
exception that headers DON'T include osdeps.h because that had to
already be included first by all .c files).

> 
> I __rebased__ my RISC-V board in my out-of tree qemu branch (https://github.com/miet-riscv-workgroup/riscv-qemu/tree/20180409.erizo). I faced with problem: I have to track dependencies of
> header files from include/hw/riscv/ which I use.
> 
> So your "not-add-more-includes-to-header-files" approach has an disadvantage:
> if a header file required header list changes, each c-file that includes that header file
> must be edited to update the #include statement list.

Indeed, that's why I argue that include statements in .h files that are
necessary for standalone compilation of that header is a good idea, and
not something to burden every .c file with.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 619 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler Michael Clark
@ 2018-04-27 12:26   ` Peter Maydell
  2018-04-29 23:27     ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Peter Maydell @ 2018-04-27 12:26 UTC (permalink / raw)
  To: Michael Clark
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On 2 March 2018 at 13:51, Michael Clark <mjc@sifive.com> wrote:
> The RISC-V disassembler has no dependencies outside of the 'disas'
> directory so it can be applied independently. The majority of the
> disassembler is machine-generated from instruction set metadata:
>
> - https://github.com/michaeljclark/riscv-meta

> +        case 5:
> +            if (isa == rv128) {
> +                op = rv_op_c_sqsp;
> +            } else {
> +                op = rv_op_c_fsdsp; break;
> +            }
> +        case 6: op = rv_op_c_swsp; break;

Coverity (CID1390575) points out that for the
case 5 / isa == rv128 codepath, we set op, and
then fall through into case 6 which overwrites it.

Is there a missing "break" statement here? (If the
fallthrough is deliberate it should be marked with a
/* fallthrough */ comment.)

thanks
-- PMM

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine Michael Clark
@ 2018-04-27 14:17   ` Peter Maydell
  2018-04-30  0:18     ` Michael Clark
  0 siblings, 1 reply; 70+ messages in thread
From: Peter Maydell @ 2018-04-27 14:17 UTC (permalink / raw)
  To: Michael Clark
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On 2 March 2018 at 13:51, Michael Clark <mjc@sifive.com> wrote:
> RISC-V machine with device-tree, 16550a UART and VirtIO MMIO.
> The following machine is implemented:
>
> - 'virt'; CLINT, PLIC, 16550A UART, VirtIO MMIO, device-tree
>
> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> Signed-off-by: Michael Clark <mjc@sifive.com>

Hi; Coverity spots (CID 1390606) that in this function you
leak a little bit of memory:

> +static void riscv_virt_board_init(MachineState *machine)
> +{

> +    /* create PLIC hart topology configuration string */
> +    plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus;
> +    plic_hart_config = g_malloc0(plic_hart_config_len);

Here we allocate memory into plic_hart_config...

> +    for (i = 0; i < smp_cpus; i++) {
> +        if (i != 0) {
> +            strncat(plic_hart_config, ",", plic_hart_config_len);
> +        }
> +        strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len);
> +        plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1);
> +    }
> +
> +    /* MMIO */
> +    s->plic = sifive_plic_create(memmap[VIRT_PLIC].base,
> +        plic_hart_config,

(and this function doesn't take ownership of the string)

> +        VIRT_PLIC_NUM_SOURCES,
> +        VIRT_PLIC_NUM_PRIORITIES,
> +        VIRT_PLIC_PRIORITY_BASE,
> +        VIRT_PLIC_PENDING_BASE,
> +        VIRT_PLIC_ENABLE_BASE,
> +        VIRT_PLIC_ENABLE_STRIDE,
> +        VIRT_PLIC_CONTEXT_BASE,
> +        VIRT_PLIC_CONTEXT_STRIDE,
> +        memmap[VIRT_PLIC].size);
> +    sifive_clint_create(memmap[VIRT_CLINT].base,
> +        memmap[VIRT_CLINT].size, smp_cpus,
> +        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
> +    sifive_test_create(memmap[VIRT_TEST].base);
> +
> +    for (i = 0; i < VIRTIO_COUNT; i++) {
> +        sysbus_create_simple("virtio-mmio",
> +            memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
> +            SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
> +    }
> +
> +    serial_mm_init(system_memory, memmap[VIRT_UART0].base,
> +        0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
> +        serial_hds[0], DEVICE_LITTLE_ENDIAN);

...but we don't free the memory before leaving.

> +}

Not a big deal since this function is only run once, but adding
in the necessary g_free(plic_hart_config) will placate Coverity.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler
  2018-04-27 12:26   ` Peter Maydell
@ 2018-04-29 23:27     ` Michael Clark
  0 siblings, 0 replies; 70+ messages in thread
From: Michael Clark @ 2018-04-29 23:27 UTC (permalink / raw)
  To: Peter Maydell
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On Sat, Apr 28, 2018 at 12:26 AM, Peter Maydell <peter.maydell@linaro.org>
wrote:

> On 2 March 2018 at 13:51, Michael Clark <mjc@sifive.com> wrote:
> > The RISC-V disassembler has no dependencies outside of the 'disas'
> > directory so it can be applied independently. The majority of the
> > disassembler is machine-generated from instruction set metadata:
> >
> > - https://github.com/michaeljclark/riscv-meta
>
> > +        case 5:
> > +            if (isa == rv128) {
> > +                op = rv_op_c_sqsp;
> > +            } else {
> > +                op = rv_op_c_fsdsp; break;
> > +            }
> > +        case 6: op = rv_op_c_swsp; break;
>
> Coverity (CID1390575) points out that for the
> case 5 / isa == rv128 codepath, we set op, and
> then fall through into case 6 which overwrites it.
>
> Is there a missing "break" statement here? (If the
> fallthrough is deliberate it should be marked with a
> /* fallthrough */ comment.)
>

Thanks!

The disassembler is largely machine generated but the generator has an
issue with ISAs > 2 on a single set of encodings i.e. RVC. The generator
was originally coded for 1 or 2 encodings but I later added rv128 to the
opcodes metadata. I manually edited the generated code to add rv128
disassembly support for compressed instructions, and obviously made a
mistake in hand transcription (the reason I went to the effort of making
the generator). The compressed instructions overload the same encodings
with different meanings for the 3 ISAs, and this is where the generator
fell down, so it was only rv128 compressed instructions that I hand edited.
My own propensity to make mistakes is what attracts me to machine generated
code and of course lots of testing.

128-bit support would be interesting. Fabrice's RISCVEMU supports RV128
however it is an interpreter. If the large guest code is factored
correctly, then we could support RV128 on 64-bit hosts much like we can
support 64-bit targets on 32-bit hosts.

I've just spun and sent a patch for review. Given it's only broken on rv128
I'll just add it to the existing series.

Regards,
Michael.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine
  2018-04-27 14:17   ` Peter Maydell
@ 2018-04-30  0:18     ` Michael Clark
  2018-04-30  7:49       ` Peter Maydell
  0 siblings, 1 reply; 70+ messages in thread
From: Michael Clark @ 2018-04-30  0:18 UTC (permalink / raw)
  To: Peter Maydell, Alistair Francis, Palmer Dabbelt
  Cc: QEMU Developers, Bastian Koppelmann, Sagar Karandikar, RISC-V Patches

On Sat, Apr 28, 2018 at 2:17 AM, Peter Maydell <peter.maydell@linaro.org>
wrote:

> On 2 March 2018 at 13:51, Michael Clark <mjc@sifive.com> wrote:
> > RISC-V machine with device-tree, 16550a UART and VirtIO MMIO.
> > The following machine is implemented:
> >
> > - 'virt'; CLINT, PLIC, 16550A UART, VirtIO MMIO, device-tree
> >
> > Acked-by: Richard Henderson <richard.henderson@linaro.org>
> > Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
> > Signed-off-by: Michael Clark <mjc@sifive.com>
>
> Hi; Coverity spots (CID 1390606) that in this function you
> leak a little bit of memory:
>
> > +static void riscv_virt_board_init(MachineState *machine)
> > +{
>
> > +    /* create PLIC hart topology configuration string */
> > +    plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) *
> smp_cpus;
> > +    plic_hart_config = g_malloc0(plic_hart_config_len);
>
> Here we allocate memory into plic_hart_config...
>
> > +    for (i = 0; i < smp_cpus; i++) {
> > +        if (i != 0) {
> > +            strncat(plic_hart_config, ",", plic_hart_config_len);
> > +        }
> > +        strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG,
> plic_hart_config_len);
> > +        plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1);
> > +    }
> > +
> > +    /* MMIO */
> > +    s->plic = sifive_plic_create(memmap[VIRT_PLIC].base,
> > +        plic_hart_config,
>
> (and this function doesn't take ownership of the string)
>
> > +        VIRT_PLIC_NUM_SOURCES,
> > +        VIRT_PLIC_NUM_PRIORITIES,
> > +        VIRT_PLIC_PRIORITY_BASE,
> > +        VIRT_PLIC_PENDING_BASE,
> > +        VIRT_PLIC_ENABLE_BASE,
> > +        VIRT_PLIC_ENABLE_STRIDE,
> > +        VIRT_PLIC_CONTEXT_BASE,
> > +        VIRT_PLIC_CONTEXT_STRIDE,
> > +        memmap[VIRT_PLIC].size);
> > +    sifive_clint_create(memmap[VIRT_CLINT].base,
> > +        memmap[VIRT_CLINT].size, smp_cpus,
> > +        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
> > +    sifive_test_create(memmap[VIRT_TEST].base);
> > +
> > +    for (i = 0; i < VIRTIO_COUNT; i++) {
> > +        sysbus_create_simple("virtio-mmio",
> > +            memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
> > +            SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
> > +    }
> > +
> > +    serial_mm_init(system_memory, memmap[VIRT_UART0].base,
> > +        0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
> > +        serial_hds[0], DEVICE_LITTLE_ENDIAN);
>
> ...but we don't free the memory before leaving.
>
> > +}
>
> Not a big deal since this function is only run once, but adding
> in the necessary g_free(plic_hart_config) will placate Coverity.
>
>
Didn't mean to go off list. I'm adding Alastair as he is looking at
refactoring the machines to use QOM for an SOC holding the devices,
separate from the machine state structure.

Quite a bit of our initialization code in several QOM classes allocate
memory that is not freed. e.g. the PLIC. Usually these functions are only
run once, but ideally all of the code should be memory clean. i.e. exit
without leaks. Many programs don't bother with this but I think it is a
good practice.

Should we use dc->unrealize to point to an unrelalize function that calls
g_free? Is unrealize the QOM destructor?

As an aside, for Alastair's sake. We intend to implement and generalize ISA
string parsing so that RISCVHartArray is indeed heterogeneous and the PLIC
can figure out its dimensions from RISCVHartArray. Once the CPUs are
realized, we can derive the modes that a CPU/hart supports from 'misa'. The
RTL designers for the SiFive PLIC made a decision to save address space
versus using 2-bits in the interrupt configuration address space to encode
mode (with reserved areas for unsupported modes). With the current compact
address space layout, we need to know which modes a hart (hardware thread)
supports to see how much address space it uses in the PLIC configuration
aperture, and each CPU can have a variable sized aperture depending on the
modes it supports. This complicates dynamic address decode as we can't
simply use ranges of bits to get mode and hart. The RTL generates the
address decode statically from the core complex configuration so it's not
an issue in hardware, although one would wonder whether it might use more
comparators in its address decode logic. Also of note, is that we may add
an option to the PLIC that inverts the dimensions from { hart, mode } to {
mode, hart } so that M mode can use PMP to protect its interrupt routing
configuration. Presently the modes are interleaved instead of having per
per mode apertures (which is required for virtualization of the PLIC). That
requirement (dimension order) would dictate a more regular address space
layout.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine
  2018-04-30  0:18     ` Michael Clark
@ 2018-04-30  7:49       ` Peter Maydell
  0 siblings, 0 replies; 70+ messages in thread
From: Peter Maydell @ 2018-04-30  7:49 UTC (permalink / raw)
  To: Michael Clark
  Cc: Alistair Francis, Palmer Dabbelt, QEMU Developers,
	Bastian Koppelmann, Sagar Karandikar, RISC-V Patches

On 30 April 2018 at 01:18, Michael Clark <mjc@sifive.com> wrote:
> Quite a bit of our initialization code in several QOM classes allocate
> memory that is not freed. e.g. the PLIC. Usually these functions are only
> run once, but ideally all of the code should be memory clean. i.e. exit
> without leaks. Many programs don't bother with this but I think it is a good
> practice.
>
> Should we use dc->unrealize to point to an unrelalize function that calls
> g_free? Is unrealize the QOM destructor?

unrealize is the destructor, yes, but we currently only use it for
devices that are hot-pluggable (and thus unpluggable). For non-pluggable
devices we currently generally don't worry about giving the objects
a destructor, since it can never be called.

This is a bit weird though, and I think there's a thread on-list
somewhere where there's a proposal that we should (a) document
better the best way to divide tasks between init/realize and
(b) perhaps suggest that devices should init unrealize too.

Board model code is also rather prone to "allocate and never free",
because historically boards didn't have a QOM object to own the
memory they allocated. This is why board code often does
g_new0(MemoryRegion, 1) to allocate MemoryRegions, where device
code would just have a MemoryRegion field in its device struct.
Now that boards have real MachineState objects associated with
them, it is possible to write them the way you write a device --
hw/arm/mps2.c has an example of this. Since again it doesn't have
a destructor there's not much difference in practice, though:
we still rely on the memory being freed on program exit.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines
  2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines Michael Clark
  2018-03-09  4:50   ` Michael Clark
@ 2018-05-14 16:49   ` Peter Maydell
  1 sibling, 0 replies; 70+ messages in thread
From: Peter Maydell @ 2018-05-14 16:49 UTC (permalink / raw)
  To: Michael Clark
  Cc: QEMU Developers, Bastian Koppelmann, Palmer Dabbelt,
	Sagar Karandikar, RISC-V Patches

On 2 March 2018 at 13:51, Michael Clark <mjc@sifive.com> wrote:
> RISC-V machines compatble with Spike aka riscv-isa-sim, the RISC-V
> Instruction Set Simulator. The following machines are implemented:
>
> - 'spike_v1.9.1'; HTIF console, config-string, Privileged ISA Version 1.9.1
> - 'spike_v1.10'; HTIF console, device-tree, Privileged ISA Version 1.10
>
> Acked-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Signed-off-by: Michael Clark <mjc@sifive.com>

Hi; Coverity (CID 1391015) thinks there's a memory leak here:

> +    /* part one of config string - before memory size specified */
> +    const char *config_string_tmpl =
> +        "platform {\n"
> +        "  vendor ucb;\n"
> +        "  arch spike;\n"
> +        "};\n"
> +        "rtc {\n"
> +        "  addr 0x%" PRIx64 "x;\n"
> +        "};\n"
> +        "ram {\n"
> +        "  0 {\n"
> +        "    addr 0x%" PRIx64 "x;\n"
> +        "    size 0x%" PRIx64 "x;\n"
> +        "  };\n"
> +        "};\n"
> +        "core {\n"
> +        "  0" " {\n"
> +        "    " "0 {\n"
> +        "      isa %s;\n"
> +        "      timecmp 0x%" PRIx64 "x;\n"
> +        "      ipi 0x%" PRIx64 "x;\n"
> +        "    };\n"
> +        "  };\n"
> +        "};\n";
> +
> +    /* build config string with supplied memory size */
> +    char *isa = riscv_isa_string(&s->soc.harts[0]);
> +    size_t config_string_size = strlen(config_string_tmpl) + 48;
> +    char *config_string = malloc(config_string_size);

We malloc() config_string here...

> +    snprintf(config_string, config_string_size, config_string_tmpl,
> +        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE,
> +        (uint64_t)memmap[SPIKE_DRAM].base,
> +        (uint64_t)ram_size, isa,
> +        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE,
> +        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE);
> +    g_free(isa);
> +    size_t config_string_len = strlen(config_string);
> +
> +    /* copy in the reset vector */
> +    copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
> +
> +    /* copy in the config string */
> +    cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
> +        config_string, config_string_len);

...and finish using it here, but we never free it.

We also don't check that malloc() succeeded, so we'll crash if it
returns NULL. The recommended fix for this is to use g_malloc()
instead.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2018-05-14 16:50 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-02 13:51 [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 01/23] RISC-V Maintainers Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 02/23] RISC-V ELF Machine Definition Michael Clark
2018-03-09 13:05   ` Philippe Mathieu-Daudé
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 03/23] RISC-V CPU Core Definition Michael Clark
2018-03-03  2:23   ` Michael Clark
2018-03-03  2:34     ` Michael Clark
2018-03-05  9:44   ` Igor Mammedov
2018-03-05 22:24     ` Michael Clark
2018-03-06  8:58       ` Igor Mammedov
2018-03-06 10:41         ` Igor Mammedov
2018-03-07  3:23         ` Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 04/23] RISC-V Disassembler Michael Clark
2018-04-27 12:26   ` Peter Maydell
2018-04-29 23:27     ` Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 05/23] RISC-V CPU Helpers Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 06/23] RISC-V FPU Support Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 07/23] RISC-V GDB Stub Michael Clark
2018-03-09 12:46   ` Philippe Mathieu-Daudé
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 08/23] RISC-V TCG Code Generation Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 09/23] RISC-V Physical Memory Protection Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 10/23] RISC-V Linux User Emulation Michael Clark
2018-04-04 12:44   ` Laurent Vivier
2018-04-08 20:59     ` Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 11/23] Add symbol table callback interface to load_elf Michael Clark
2018-03-09 11:34   ` Philippe Mathieu-Daudé
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 12/23] RISC-V HTIF Console Michael Clark
2018-03-09 11:52   ` Philippe Mathieu-Daudé
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 13/23] RISC-V HART Array Michael Clark
2018-03-09 12:52   ` Philippe Mathieu-Daudé
2018-03-09 13:48     ` Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 14/23] SiFive RISC-V CLINT Block Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 15/23] SiFive RISC-V PLIC Block Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 16/23] RISC-V Spike Machines Michael Clark
2018-03-09  4:50   ` Michael Clark
2018-05-14 16:49   ` Peter Maydell
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 17/23] SiFive RISC-V Test Finisher Michael Clark
2018-03-09 11:57   ` Philippe Mathieu-Daudé
2018-03-10  3:01     ` Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 18/23] RISC-V VirtIO Machine Michael Clark
2018-04-27 14:17   ` Peter Maydell
2018-04-30  0:18     ` Michael Clark
2018-04-30  7:49       ` Peter Maydell
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 19/23] SiFive RISC-V UART Device Michael Clark
2018-03-09 12:39   ` Philippe Mathieu-Daudé
2018-03-10  3:02     ` Michael Clark
2018-03-10  9:40       ` Mark Cave-Ayland
2018-03-11 11:43         ` Bastian Koppelmann
2018-03-16 18:30           ` Michael Clark
2018-03-16 18:36             ` Michael Clark
2018-03-16 20:46               ` Bastian Koppelmann
2018-04-10  3:21   ` Antony Pavlov
2018-04-10  6:17     ` Thomas Huth
2018-04-10  8:04       ` Antony Pavlov
2018-04-11 21:12         ` Michael Clark
2018-04-11 22:25         ` Eric Blake
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 20/23] SiFive RISC-V PRCI Block Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 21/23] SiFive Freedom E Series RISC-V Machine Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 22/23] SiFive Freedom U " Michael Clark
2018-03-02 13:51 ` [Qemu-devel] [PATCH v8 23/23] RISC-V Build Infrastructure Michael Clark
2018-03-02 14:33   ` Eric Blake
2018-03-03  2:37     ` Michael Clark
2018-03-05 15:59       ` Eric Blake
2018-03-09 13:03   ` Philippe Mathieu-Daudé
2018-03-02 14:17 ` [Qemu-devel] [PATCH v8 00/23] RISC-V QEMU Port Submission no-reply
2018-03-05  8:41 ` Richard W.M. Jones
2018-03-05 10:02   ` Alex Bennée
2018-03-09 15:07   ` Michael Clark
2018-03-09 16:43   ` Peter Maydell
2018-03-09 18:27     ` Richard W.M. Jones

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.