target/riscv: vxrm write preserves upper bits instead of zeroing vxrm[XLEN-1:2]
## Host environment
- Operating system:
Linux
- OS/kernel version:
Linux DESKTOP-PL0JDQL 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
- Architecture:
x86_64
- QEMU flavor:
qemu-system-riscv64
- QEMU version:
QEMU emulator version 10.2.50 (v10.2.0-2311-gfff352b9b6)
## Description of problem
When writing a value with nonzero upper bits to the vxrm CSR on RV64, QEMU preserves the upper bits on readback instead of zeroing vxrm\[XLEN-1:2\].
The RISC-V vector specification says:
> “The vector fixed-point rounding-mode register holds a two-bit read-write rounding-mode field in the least-significant bits (vxrm\[1:0\]). The upper bits, vxrm\[XLEN-1:2\], should be written as zeros.”
With the test case below, writing -1 to vxrm and then reading it back produces 0xffffffffffffffff in QEMU. By contrast, Spike sanitizes the value and reads back 0x3.
This looks like a model/spec-deviation issue in target/riscv/csr.c. The current write path stores the full value:
`env->vxrm = val;`
and does not mask it down to the low 2 bits.
This does not appear to cause a trap or fault, but it creates non-portable behavior across emulators/implementations.
## Steps to reproduce
1. Build this bare-metal test case:
`li a0, -1`
`csrw vxrm, a0`
`csrr a1, vxrm`
`j .`
2. Build command used:
`riscv64-unknown-elf-gcc -march=rv64imafdch_zicfiss_zicbom_zicboz_v_zicsr_zca_zimop_zcmop_zbb_zbs_zkne_zbkb_zabha_zacas_zawrs_zkr_smepmp_zcb_zicond_zba_zknd_zbc_zbkc_zfh_zfbfmin_zfhmin_zfa_zifencei_zvfbfmin_zbkx_zvksed_zvksh_zvknha_zvknhb_zvkg_zvfbfwma_zvbc_zvbb_zvkned_zksed_zksh_zknh_zvkb_zicbop_zicfilp_svinval_zve32f -mabi=lp64 -mcmodel=medany -nostdlib -nostartfiles -T linker.ld code.S machine_to_supervisor.S machine_to_user.S -o code.elf`
3. Run QEMU with:
`qemu-system-riscv64 -machine virt -m 256M -bios none -kernel code.elf -serial null -display none -S -s -cpu rv64,v=on,smstateen=on,sscofpmf=on,smcsrind=on,sscsrind=on,smaia=on,ssaia=on,ssccfg=on,smcdeleg=on,zicfiss=on,zimop=on,zcmop=on,zaamo=on,zca=on,zbb=on,zbs=on,zkne=on,zbkb=on,zabha=on,zacas=on,zawrs=on,smdbltrp=on,zkr=on,smepmp=on,zcb=on,zicond=on,i=on,m=on,a=on,f=on,d=on,c=on,h=on,zicsr=on,zicbom=on,zicboz=on,zba=on,zknd=on,zbc=on,zbkc=on,zfh=on,zfbfmin=on,zfhmin=on,zfa=on,zifencei=on,zvfbfmin=on,zbkx=on,zvksed=on,zvksh=on,zvknha=on,zvknhb=on,zvkg=on,zvfbfwma=on,zvbc=on,zvbb=on,zvkned=on,zksed=on,zksh=on,zknh=on,zvkb=on,zicbop=on,zicfilp=on,svinval=on,zve32f=on`
4. Connect with GDB:
`riscv64-unknown-elf-gdb code.elf`
`set pagination off`
`target remote :1234`
`c`
`Ctrl-C`
`info registers a1`
5. Observe that QEMU reports:
`a1 = 0xffffffffffffffff`
Expected result:
`a1 = 0x0000000000000003`
## Additional information
Specification reference:
> 30.3.8. Vector Fixed-Point Rounding Mode (vxrm) Register
>
> “The vector fixed-point rounding-mode register holds a two-bit read-write rounding-mode field in the least-significant bits (vxrm\[1:0\]). The upper bits, vxrm\[XLEN-1:2\], should be written as zeros.”
Observed behavior:
\- QEMU readback after writing -1 to vxrm: `0xffffffffffffffff`
\- Spike readback after writing -1 to vxrm: `0x3`
Suspected source location:
\- `target/riscv/csr.c`
\- `write_vxrm()`
Current code stores the full written value:
`env->vxrm = val;`
A possible fix would be to mask the value to the low 2 bits:
`env->vxrm = val & 0x3;`
This appears to be a low-impact spec-deviation / portability issue rather than a crash or security issue.
<!--The line below ensures that proper tags are added to the issue.
Please do not remove it.-->
issue