Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-13 Thread Richard Henderson

On 6/12/22 20:50, gaosong wrote:

Thank you for your advice.
like this:

  void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk)
  {
+    CPUState *cs = env_cpu(env);
+
  if (rj > rk) {
-    env->badaddr = env->pc;
-    do_raise_exception(env, EXCCODE_BCE, env->badaddr);
+    cpu_restore_state(cs, GETPC(), true);
+    cs->exception_index = EXCCODE_BCE;
+    cpu_loop_exit(cs);
  }
  }


This is not required -- better to continue using do_raise_exception.



cpu.c


  case EXCCODE_ADEM:
+    case EXCCODE_BCE:
  case EXCCODE_SYS:
  case EXCCODE_BRK:
+    case EXCCODE_INE:
+    case EXCCODE_IPE:
+    case EXCCODE_FPE:
+    env->badvaddr = env->pc;
+    QEMU_FALLTHROUGH;
  case EXCCODE_PIL:
  case EXCCODE_PIS:
  case EXCCODE_PME:
  case EXCCODE_PNR:
  case EXCCODE_PNX:
  case EXCCODE_PPI:
-    case EXCCODE_INE:
-    case EXCCODE_IPE:
-    case EXCCODE_FPE:
  cause = cs->exception_index;
  break;


But this looks correct, and sufficient to solve the problem.


r~



Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-12 Thread gaosong


On 2022/6/12 上午12:06, Richard Henderson wrote:
void helper_asrtle_d(CPULoongArchState *env,  target_ulong rj, 
target_ulong  rk)

{
  if (rj > rk) {
     env->badvaddr = env->pc;
     do_raise_exception(env, EXCCODE_BCE, env->badvaddr);
  }
}


Well, not quite.  The value of env->pc is not current; it is too 
expensive to update all of the time.  We need to recover that value by 
using TCG unwinding, e.g.:


    if (rj > rk) {
    cpu_restore_state(env_cpu(env), GETPC(), true);
    env->badvaddr = env->pc;

However,

    do_raise_exception(env, EXCCODE_ADEM, GETPC());

expects to do it's own cpu_restore_state via cpu_loop_exit_restore(), 
and we should not do that twice.


Therefore, since the value of badvaddr is something that we can more 
easily recover later than earlier, we should move the setting of 
badvaddr for ADEM to loongarch_cpu_do_interrupt():


    case EXCCODE_ADEM:

    env->badvaddr = env->pc;
    cause = cs->exception_index;
    break;

It is probably worthwhile to check how many other exceptions should be 
having the same effect.



Thank you for your advice.
like this:

 void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, 
target_ulong rk)

 {
+    CPUState *cs = env_cpu(env);
+
 if (rj > rk) {
-    env->badaddr = env->pc;
-    do_raise_exception(env, EXCCODE_BCE, env->badaddr);
+    cpu_restore_state(cs, GETPC(), true);
+    cs->exception_index = EXCCODE_BCE;
+    cpu_loop_exit(cs);
 }
 }

cpu.c


 case EXCCODE_ADEM:
+    case EXCCODE_BCE:
 case EXCCODE_SYS:
 case EXCCODE_BRK:
+    case EXCCODE_INE:
+    case EXCCODE_IPE:
+    case EXCCODE_FPE:
+    env->badvaddr = env->pc;
+    QEMU_FALLTHROUGH;
 case EXCCODE_PIL:
 case EXCCODE_PIS:
 case EXCCODE_PME:
 case EXCCODE_PNR:
 case EXCCODE_PNX:
 case EXCCODE_PPI:
-    case EXCCODE_INE:
-    case EXCCODE_IPE:
-    case EXCCODE_FPE:
 cause = cs->exception_index;
 break;

Thanks
Song Gao


Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-11 Thread Richard Henderson

On 6/10/22 20:10, gaosong wrote:

pc 0x12638 0x12638 
badvaddr   0x12638 0x12638 

...

So badvaddr is the PC,


Yes.


void helper_asrtle_d(CPULoongArchState *env,  target_ulong rj, target_ulong  rk)
{
  if (rj > rk) {
         env->badvaddr = env->pc;
         do_raise_exception(env, EXCCODE_BCE,  env->badvaddr);
  }
}


Well, not quite.  The value of env->pc is not current; it is too expensive to update all 
of the time.  We need to recover that value by using TCG unwinding, e.g.:


if (rj > rk) {
cpu_restore_state(env_cpu(env), GETPC(), true);
env->badvaddr = env->pc;

However,

do_raise_exception(env, EXCCODE_ADEM, GETPC());

expects to do it's own cpu_restore_state via cpu_loop_exit_restore(), and we should not do 
that twice.


Therefore, since the value of badvaddr is something that we can more easily recover later 
than earlier, we should move the setting of badvaddr for ADEM to loongarch_cpu_do_interrupt():


case EXCCODE_ADEM:

env->badvaddr = env->pc;
cause = cs->exception_index;
break;

It is probably worthwhile to check how many other exceptions should be having 
the same effect.


r~



Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-10 Thread gaosong


On 2022/6/11 上午6:45, Richard Henderson wrote:

On 6/9/22 23:53, gaosong wrote:

Hi Richard,

On 2022/6/10 上午2:42, Richard Henderson wrote:
  void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, 
target_ulong rk)

  {
  if (rj > rk) {
+#ifdef CONFIG_USER_ONLY
+    cpu_loop_exit_sigsegv(env_cpu(env), GETPC(),
+  MMU_DATA_LOAD, true, GETPC());
+#else
  do_raise_exception(env, EXCCODE_ADEM, GETPC());
+#endif


This change is wrong.  First, the kernel's do_ade raises SIGBUS. 
Second, GETPC() is a host address, not a guest address.  Third, this 
highlights the fact that the existing system code is wrong, and 
should be setting badvaddr.


You need to
(1) set badvaddr here, and then
(2) handle EXCCODE_ADEM in linux-user/loongarch/cpu_loop.c to 
force_fix_fault(TARGET_SIGBUS, TARGET_BUS_ADRERR, env->badvaddr). 


badvaddr is env->pc or base->pc_next?


I don't know.  The documentation for the ASRT{LE,GD}.D instructions is 
incomplete.


However, from the kernel code,

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/loongarch/kernel/traps.c#n360 



I can see that the kernel expects *some* value to be set there.  Given 
that this is the same trap used by the bound check memory accesses, I 
presume that badvaddr is related to the memory access not the PC.  So 
I would expect badvaddr to be RJ.


But that is a guess.  Please check with your hardware engineers.


Thanks you,


I had tested asrtgt.d on hardware.  the log is :

the gdb log

(gdb) info registers
  zero   ra tp   sp
R0    00fff7e7c774 00fff7ffef20 00ff6940
    a0   a1 a2   a3
R4   0001 00ff6a88 00ff6a98 00fff7fbc4b0
    a4   a5 a6   a7
R8    00fff7fe6f70 00ff6a80 0080
    t0   t1 t2   t3
R12     00fff7fbeeb8
    t4   t5 t6   t7
R16  00fff7fbdd40 00fff7fbdd40 7f7f7f7f7f7f7f7f 
    t8    x fp   s0
R20  ff00  00ff6960 
    s1   s2 s3   s4
R24  00012658 00bd9c60  00be9b50
    s5   s6 s7   s8
R28  00bd9c60   00bcfa08
pc 0x12638 0x12638 
badvaddr   0xfff7e935c8    0xfff7e935c8 <__cxa_atexit>
(gdb) stepi

Program received signal SIGSYS, Bad system call.
0x00012638 in main () at asrtle.c:8
8        asm volatile("asrtgt.d  %0,%1\n\t"
(gdb) info registers
  zero   ra tp   sp
R0    00fff7e7c774 00fff7ffef20 00ff6940
    a0   a1 a2   a3
R4   0001 00ff6a88 00ff6a98 00fff7fbc4b0
    a4   a5 a6   a7
R8    00fff7fe6f70 00ff6a80 0080
    t0   t1 t2   t3
R12     00fff7fbeeb8
    t4   t5 t6   t7
R16  00fff7fbdd40 00fff7fbdd40 7f7f7f7f7f7f7f7f 
    t8    x fp   s0
R20  ff00  00ff6960 
    s1   s2 s3   s4
R24  00012658 00bd9c60  00be9b50
    s5   s6 s7   s8
R28  00bd9c60   00bcfa08
pc 0x12638 0x12638 
badvaddr   0x12638 0x12638 
(gdb) disas
Dump of assembler code for function main:
   0x00012618 <+0>:    addi.d    $r3,$r3,-32(0xfe0)
   0x0001261c <+4>:    st.d    $r22,$r3,24(0x18)
   0x00012620 <+8>:    addi.d    $r22,$r3,32(0x20)
   0x00012624 <+12>:    addi.w    $r12,$r0,23(0x17)
   0x00012628 <+16>:    st.d    $r12,$r22,-24(0xfe8)
   0x0001262c <+20>:    st.d    $r0,$r22,-32(0xfe0)
   0x00012630 <+24>:    ld.d    $r12,$r22,-32(0xfe0)
   0x00012634 <+28>:    ld.d    $r13,$r22,-32(0xfe0)
=> 0x00012638 <+32>:    asrtgt.d    $r12,$r12
   0x0001263c <+36>:    st.d    $r12,$r22,-24(0xfe8)
   0x00012640 <+40>:    move    $r12,$r0
   0x00012644 <+44>:    move    $r4,$r12
   0x00012648 <+48>:    ld.d    $r22,$r3,24(0x18)
   0x0001264c <+52>:    addi.d    $r3,$r3,32(0x20)
   0x00012650 <+56>:    jirl    $r0,$r1,0
End of assembler dump.


Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-10 Thread Richard Henderson

On 6/9/22 23:53, gaosong wrote:

Hi Richard,

On 2022/6/10 上午2:42, Richard Henderson wrote:

  void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk)
  {
  if (rj > rk) {
+#ifdef CONFIG_USER_ONLY
+    cpu_loop_exit_sigsegv(env_cpu(env), GETPC(),
+  MMU_DATA_LOAD, true, GETPC());
+#else
  do_raise_exception(env, EXCCODE_ADEM, GETPC());
+#endif


This change is wrong.  First, the kernel's do_ade raises SIGBUS. Second, GETPC() is a 
host address, not a guest address.  Third, this highlights the fact that the existing 
system code is wrong, and should be setting badvaddr.


You need to
(1) set badvaddr here, and then
(2) handle EXCCODE_ADEM in linux-user/loongarch/cpu_loop.c to 
force_fix_fault(TARGET_SIGBUS, TARGET_BUS_ADRERR, env->badvaddr). 


badvaddr is env->pc or base->pc_next?


I don't know.  The documentation for the ASRT{LE,GD}.D instructions is 
incomplete.

However, from the kernel code,

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/loongarch/kernel/traps.c#n360

I can see that the kernel expects *some* value to be set there.  Given that this is the 
same trap used by the bound check memory accesses, I presume that badvaddr is related to 
the memory access not the PC.  So I would expect badvaddr to be RJ.


But that is a guess.  Please check with your hardware engineers.


r~



Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-10 Thread gaosong

Hi Richard,

On 2022/6/10 上午2:42, Richard Henderson wrote:
  void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, 
target_ulong rk)

  {
  if (rj > rk) {
+#ifdef CONFIG_USER_ONLY
+    cpu_loop_exit_sigsegv(env_cpu(env), GETPC(),
+  MMU_DATA_LOAD, true, GETPC());
+#else
  do_raise_exception(env, EXCCODE_ADEM, GETPC());
+#endif


This change is wrong.  First, the kernel's do_ade raises SIGBUS. 
Second, GETPC() is a host address, not a guest address.  Third, this 
highlights the fact that the existing system code is wrong, and should 
be setting badvaddr.


You need to
(1) set badvaddr here, and then
(2) handle EXCCODE_ADEM in linux-user/loongarch/cpu_loop.c to 
force_fix_fault(TARGET_SIGBUS, TARGET_BUS_ADRERR, env->badvaddr). 


badvaddr is env->pc or base->pc_next?   and we just raise exception on 
use-mode,


like this

  void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, 
target_ulong rk)

  {
  if (rj > rk) {

     env->badvaddr = env->pc;

#ifdef CONFIG_USER_ONLY
  do_raise_exception(env, EXCCODE_ADEM, GETPC());
#endif

  }

}

cpu_loop.c

    case EXCCODE_ADEM:
    force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRERR, 
env->badvaddr);

    break;

Thanks.
Song Gao


Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-09 Thread Richard Henderson

On 6/8/22 19:42, Song Gao wrote:

diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index 85c11a60d4..ee42707868 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -93,8 +93,7 @@ DEF_HELPER_2(frint_d, i64, env, i64)
  
  DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32)
  
-DEF_HELPER_1(rdtime_d, i64, env)

-
+#ifndef CONFIG_USER_ONLY
  /* CSRs helper */
  DEF_HELPER_1(csrrd_pgd, i64, env)
  DEF_HELPER_1(csrrd_tval, i64, env)
@@ -128,3 +127,5 @@ DEF_HELPER_4(lddir, tl, env, tl, tl, i32)
  DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
  DEF_HELPER_1(ertn, void, env)
  DEF_HELPER_1(idle, void, env)
+DEF_HELPER_1(rdtime_d, i64, env)
+#endif


This is wrong.


  static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a)
  {
+#ifdef CONFIG_USER_ONLY
+return cpu_get_host_ticks();


This is very wrong.  You're calling cpu_get_host_ticks at translation time.
There are no changes required during translation.

You should in fact be calling cpu_get_host_ticks in helper_rdtime_d.


  void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk)
  {
  if (rj > rk) {
+#ifdef CONFIG_USER_ONLY
+cpu_loop_exit_sigsegv(env_cpu(env), GETPC(),
+  MMU_DATA_LOAD, true, GETPC());
+#else
  do_raise_exception(env, EXCCODE_ADEM, GETPC());
+#endif


This change is wrong.  First, the kernel's do_ade raises SIGBUS.  Second, GETPC() is a 
host address, not a guest address.  Third, this highlights the fact that the existing 
system code is wrong, and should be setting badvaddr.


You need to
(1) set badvaddr here, and then
(2) handle EXCCODE_ADEM in linux-user/loongarch/cpu_loop.c to 
force_fix_fault(TARGET_SIGBUS, TARGET_BUS_ADRERR, env->badvaddr).



  void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk)
  {
  if (rj <= rk) {
+#ifdef CONFIG_USER_ONLY
+cpu_loop_exit_sigsegv(env_cpu(env), GETPC(),
+  MMU_DATA_LOAD, true, GETPC());
+#else
  do_raise_exception(env, EXCCODE_ADEM, GETPC());
+#endif
  }
  }


Likewise.


r~



Re: [PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-09 Thread WANG Xuerui

On 2022/6/9 10:42, Song Gao wrote:

Some functions and member of the structure are different with softmmu-mode
So we need adjust them to support user-mode.

Signed-off-by: Song Gao 
Signed-off-by: Xiaojuan Yang 
---
  target/loongarch/cpu.c| 22 ++--
  target/loongarch/cpu.h|  6 
  target/loongarch/helper.h |  5 +--
  target/loongarch/insn_trans/trans_extra.c.inc | 14 
  .../insn_trans/trans_privileged.c.inc | 36 +++
  target/loongarch/internals.h  |  2 ++
  target/loongarch/op_helper.c  | 12 +++
  7 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 4c8f96bc3a..472e258f68 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -18,7 +18,6 @@
  #include "fpu/softfloat-helpers.h"
  #include "cpu-csr.h"
  #include "sysemu/reset.h"
-#include "hw/loader.h"
  
  const char * const regnames[32] = {

  "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
@@ -82,6 +81,7 @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
  env->pc = value;
  }
  
+#ifndef CONFIG_USER_ONLY

  #include "hw/loongarch/virt.h"
  
  void loongarch_cpu_set_irq(void *opaque, int irq, int level)

@@ -292,6 +292,7 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int 
interrupt_request)
  }
  return false;
  }
+#endif
  
  #ifdef CONFIG_TCG

  static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
@@ -306,6 +307,9 @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
  
  static bool loongarch_cpu_has_work(CPUState *cs)

  {
+#ifdef CONFIG_USER_ONLY
+return true;
+#else
  LoongArchCPU *cpu = LOONGARCH_CPU(cs);
  CPULoongArchState *env = >env;
  bool has_work = false;
@@ -316,6 +320,7 @@ static bool loongarch_cpu_has_work(CPUState *cs)
  }
  
  return has_work;

+#endif
  }
  
  static void loongarch_la464_initfn(Object *obj)

@@ -464,7 +469,9 @@ static void loongarch_cpu_reset(DeviceState *dev)
  env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
  }
  
+#ifndef CONFIG_USER_ONLY

  env->pc = 0x1c00;
+#endif
  
  restore_fp_status(env);

  cs->exception_index = -1;
@@ -495,6 +502,7 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error 
**errp)
  lacc->parent_realize(dev, errp);
  }
  
+#ifndef CONFIG_USER_ONLY

  static void loongarch_qemu_write(void *opaque, hwaddr addr,
   uint64_t val, unsigned size)
  {
@@ -529,13 +537,16 @@ static const MemoryRegionOps loongarch_qemu_ops = {
  .max_access_size = 8,
  },
  };
+#endif
  
  static void loongarch_cpu_init(Object *obj)

  {
  LoongArchCPU *cpu = LOONGARCH_CPU(obj);
-CPULoongArchState *env = >env;
  
+#ifdef CONFIG_USER_ONLY

  cpu_set_cpustate_pointers(cpu);
+#else
+CPULoongArchState *env = >env;
  qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS);
  timer_init_ns(>timer, QEMU_CLOCK_VIRTUAL,
_constant_timer_cb, cpu);
@@ -545,6 +556,7 @@ static void loongarch_cpu_init(Object *obj)
  memory_region_init_io(>iocsr_mem, OBJECT(cpu), _qemu_ops,
NULL, "iocsr_misc", 0x428);
  memory_region_add_subregion(>system_iocsr, 0, >iocsr_mem);
+#endif
  }
  
  static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)

@@ -612,18 +624,22 @@ static struct TCGCPUOps loongarch_tcg_ops = {
  .initialize = loongarch_translate_init,
  .synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
  
+#ifndef CONFIG_USER_ONLY

  .tlb_fill = loongarch_cpu_tlb_fill,
  .cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
  .do_interrupt = loongarch_cpu_do_interrupt,
  .do_transaction_failed = loongarch_cpu_do_transaction_failed,
+#endif
  };
  #endif /* CONFIG_TCG */
  
+#ifndef CONFIG_USER_ONLY

  #include "hw/core/sysemu-cpu-ops.h"
  
  static const struct SysemuCPUOps loongarch_sysemu_ops = {

  .get_phys_page_debug = loongarch_cpu_get_phys_page_debug,
  };
+#endif
  
  static void loongarch_cpu_class_init(ObjectClass *c, void *data)

  {
@@ -639,8 +655,10 @@ static void loongarch_cpu_class_init(ObjectClass *c, void 
*data)
  cc->has_work = loongarch_cpu_has_work;
  cc->dump_state = loongarch_cpu_dump_state;
  cc->set_pc = loongarch_cpu_set_pc;
+#ifndef CONFIG_USER_ONLY
  dc->vmsd = _loongarch_cpu;
  cc->sysemu_ops = _sysemu_ops;
+#endif
  cc->disas_set_info = loongarch_cpu_disas_set_info;
  cc->gdb_read_register = loongarch_cpu_gdb_read_register;
  cc->gdb_write_register = loongarch_cpu_gdb_write_register;
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 71a5036c3c..19eed2f0c1 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -303,6 +303,7 @@ typedef struct CPUArchState {
  uint64_t CSR_DERA;
  uint64_t CSR_DSAVE;
  
+#ifndef 

[PATCH v15 8/9] target/loongarch: Adjust functions and structure to support user-mode

2022-06-08 Thread Song Gao
Some functions and member of the structure are different with softmmu-mode
So we need adjust them to support user-mode.

Signed-off-by: Song Gao 
Signed-off-by: Xiaojuan Yang 
---
 target/loongarch/cpu.c| 22 ++--
 target/loongarch/cpu.h|  6 
 target/loongarch/helper.h |  5 +--
 target/loongarch/insn_trans/trans_extra.c.inc | 14 
 .../insn_trans/trans_privileged.c.inc | 36 +++
 target/loongarch/internals.h  |  2 ++
 target/loongarch/op_helper.c  | 12 +++
 7 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 4c8f96bc3a..472e258f68 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -18,7 +18,6 @@
 #include "fpu/softfloat-helpers.h"
 #include "cpu-csr.h"
 #include "sysemu/reset.h"
-#include "hw/loader.h"
 
 const char * const regnames[32] = {
 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
@@ -82,6 +81,7 @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
 env->pc = value;
 }
 
+#ifndef CONFIG_USER_ONLY
 #include "hw/loongarch/virt.h"
 
 void loongarch_cpu_set_irq(void *opaque, int irq, int level)
@@ -292,6 +292,7 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int 
interrupt_request)
 }
 return false;
 }
+#endif
 
 #ifdef CONFIG_TCG
 static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
@@ -306,6 +307,9 @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
 
 static bool loongarch_cpu_has_work(CPUState *cs)
 {
+#ifdef CONFIG_USER_ONLY
+return true;
+#else
 LoongArchCPU *cpu = LOONGARCH_CPU(cs);
 CPULoongArchState *env = >env;
 bool has_work = false;
@@ -316,6 +320,7 @@ static bool loongarch_cpu_has_work(CPUState *cs)
 }
 
 return has_work;
+#endif
 }
 
 static void loongarch_la464_initfn(Object *obj)
@@ -464,7 +469,9 @@ static void loongarch_cpu_reset(DeviceState *dev)
 env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
 }
 
+#ifndef CONFIG_USER_ONLY
 env->pc = 0x1c00;
+#endif
 
 restore_fp_status(env);
 cs->exception_index = -1;
@@ -495,6 +502,7 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error 
**errp)
 lacc->parent_realize(dev, errp);
 }
 
+#ifndef CONFIG_USER_ONLY
 static void loongarch_qemu_write(void *opaque, hwaddr addr,
  uint64_t val, unsigned size)
 {
@@ -529,13 +537,16 @@ static const MemoryRegionOps loongarch_qemu_ops = {
 .max_access_size = 8,
 },
 };
+#endif
 
 static void loongarch_cpu_init(Object *obj)
 {
 LoongArchCPU *cpu = LOONGARCH_CPU(obj);
-CPULoongArchState *env = >env;
 
+#ifdef CONFIG_USER_ONLY
 cpu_set_cpustate_pointers(cpu);
+#else
+CPULoongArchState *env = >env;
 qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS);
 timer_init_ns(>timer, QEMU_CLOCK_VIRTUAL,
   _constant_timer_cb, cpu);
@@ -545,6 +556,7 @@ static void loongarch_cpu_init(Object *obj)
 memory_region_init_io(>iocsr_mem, OBJECT(cpu), _qemu_ops,
   NULL, "iocsr_misc", 0x428);
 memory_region_add_subregion(>system_iocsr, 0, >iocsr_mem);
+#endif
 }
 
 static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
@@ -612,18 +624,22 @@ static struct TCGCPUOps loongarch_tcg_ops = {
 .initialize = loongarch_translate_init,
 .synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
 
+#ifndef CONFIG_USER_ONLY
 .tlb_fill = loongarch_cpu_tlb_fill,
 .cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
 .do_interrupt = loongarch_cpu_do_interrupt,
 .do_transaction_failed = loongarch_cpu_do_transaction_failed,
+#endif
 };
 #endif /* CONFIG_TCG */
 
+#ifndef CONFIG_USER_ONLY
 #include "hw/core/sysemu-cpu-ops.h"
 
 static const struct SysemuCPUOps loongarch_sysemu_ops = {
 .get_phys_page_debug = loongarch_cpu_get_phys_page_debug,
 };
+#endif
 
 static void loongarch_cpu_class_init(ObjectClass *c, void *data)
 {
@@ -639,8 +655,10 @@ static void loongarch_cpu_class_init(ObjectClass *c, void 
*data)
 cc->has_work = loongarch_cpu_has_work;
 cc->dump_state = loongarch_cpu_dump_state;
 cc->set_pc = loongarch_cpu_set_pc;
+#ifndef CONFIG_USER_ONLY
 dc->vmsd = _loongarch_cpu;
 cc->sysemu_ops = _sysemu_ops;
+#endif
 cc->disas_set_info = loongarch_cpu_disas_set_info;
 cc->gdb_read_register = loongarch_cpu_gdb_read_register;
 cc->gdb_write_register = loongarch_cpu_gdb_write_register;
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 71a5036c3c..19eed2f0c1 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -303,6 +303,7 @@ typedef struct CPUArchState {
 uint64_t CSR_DERA;
 uint64_t CSR_DSAVE;
 
+#ifndef CONFIG_USER_ONLY
 LoongArchTLB  tlb[LOONGARCH_TLB_MAX];
 
 AddressSpace address_space_iocsr;
@@ -310,6 +311,7 @@ typedef struct CPUArchState {