diff mbox series

[RFC,v4,13/15] target/riscv: Introduce dynamic time frequency for virt machine

Message ID 20201203124703.168-14-jiangyifei@huawei.com (mailing list archive)
State New, archived
Headers show
Series Add riscv kvm accel support | expand

Commit Message

Yifei Jiang Dec. 3, 2020, 12:47 p.m. UTC
Currently, time base frequency was fixed as SIFIVE_CLINT_TIMEBASE_FREQ.
Here introduce "time-frequency" property to set time base frequency dynamically
of which default value is still SIFIVE_CLINT_TIMEBASE_FREQ. The virt machine
uses frequency of the first cpu to create clint and fdt.

Signed-off-by: Yifei Jiang <jiangyifei@huawei.com>
Signed-off-by: Yipeng Yin <yinyipeng1@huawei.com>
---
 hw/riscv/virt.c    | 18 ++++++++++++++----
 target/riscv/cpu.c |  3 +++
 target/riscv/cpu.h |  2 ++
 3 files changed, 19 insertions(+), 4 deletions(-)

Comments

Alistair Francis Dec. 8, 2020, 10:25 p.m. UTC | #1
On Thu, Dec 3, 2020 at 4:57 AM Yifei Jiang <jiangyifei@huawei.com> wrote:
>
> Currently, time base frequency was fixed as SIFIVE_CLINT_TIMEBASE_FREQ.
> Here introduce "time-frequency" property to set time base frequency dynamically
> of which default value is still SIFIVE_CLINT_TIMEBASE_FREQ. The virt machine
> uses frequency of the first cpu to create clint and fdt.
>
> Signed-off-by: Yifei Jiang <jiangyifei@huawei.com>
> Signed-off-by: Yipeng Yin <yinyipeng1@huawei.com>
> ---
>  hw/riscv/virt.c    | 18 ++++++++++++++----
>  target/riscv/cpu.c |  3 +++
>  target/riscv/cpu.h |  2 ++
>  3 files changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> index 47b7018193..788a7237b6 100644
> --- a/hw/riscv/virt.c
> +++ b/hw/riscv/virt.c
> @@ -178,7 +178,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
>  }
>
>  static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
> -    uint64_t mem_size, const char *cmdline)
> +    uint64_t mem_size, const char *cmdline, uint64_t timebase_frequency)
>  {
>      void *fdt;
>      int i, cpu, socket;
> @@ -225,7 +225,7 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
>
>      qemu_fdt_add_subnode(fdt, "/cpus");
>      qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> -                          SIFIVE_CLINT_TIMEBASE_FREQ);
> +                          timebase_frequency);
>      qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
>      qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
>      qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
> @@ -510,6 +510,7 @@ static void virt_machine_init(MachineState *machine)
>      target_ulong firmware_end_addr, kernel_start_addr;
>      uint32_t fdt_load_addr;
>      uint64_t kernel_entry;
> +    uint64_t timebase_frequency = 0;
>      DeviceState *mmio_plic, *virtio_plic, *pcie_plic;
>      int i, j, base_hartid, hart_count;
>      CPUState *cs;
> @@ -553,12 +554,20 @@ static void virt_machine_init(MachineState *machine)
>                                  hart_count, &error_abort);
>          sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
>
> +        if (!timebase_frequency) {
> +            timebase_frequency = RISCV_CPU(first_cpu)->env.frequency;
> +        }
> +        /* If vcpu's time frequency is not specified, we use default frequency */
> +        if (!timebase_frequency) {
> +            timebase_frequency = SIFIVE_CLINT_TIMEBASE_FREQ;
> +        }
> +
>          /* Per-socket CLINT */
>          sifive_clint_create(
>              memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
>              memmap[VIRT_CLINT].size, base_hartid, hart_count,
>              SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
> -            SIFIVE_CLINT_TIMEBASE_FREQ, true);
> +            timebase_frequency, true);
>
>          /* Per-socket PLIC hart topology configuration string */
>          plic_hart_config_len =
> @@ -610,7 +619,8 @@ static void virt_machine_init(MachineState *machine)
>          main_mem);
>
>      /* create device tree */
> -    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
> +    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline,
> +               timebase_frequency);
>
>      /* boot rom */
>      memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom",
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 439dc89ee7..66f35bcbbf 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -494,6 +494,8 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>
>      riscv_cpu_register_gdb_regs_for_features(cs);
>
> +    env->user_frequency = env->frequency;
> +
>      qemu_init_vcpu(cs);
>      cpu_reset(cs);
>
> @@ -531,6 +533,7 @@ static Property riscv_cpu_properties[] = {
>      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
>      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
>      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
> +    DEFINE_PROP_UINT64("time-frequency", RISCVCPU, env.frequency, 0),

Why not set the default to SIFIVE_CLINT_TIMEBASE_FREQ?

Also, QEMU now has a clock API, is using that instead a better option?

Alistair

>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 16d6050ead..f5b6c34176 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -243,6 +243,8 @@ struct CPURISCVState {
>      uint64_t kvm_timer_time;
>      uint64_t kvm_timer_compare;
>      uint64_t kvm_timer_state;
> +    uint64_t user_frequency;
> +    uint64_t frequency;
>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> --
> 2.19.1
>
>
Yifei Jiang Dec. 15, 2020, 7:31 a.m. UTC | #2
> -----Original Message-----
> From: Alistair Francis [mailto:alistair23@gmail.com]
> Sent: Wednesday, December 9, 2020 6:26 AM
> To: Jiangyifei <jiangyifei@huawei.com>
> Cc: qemu-devel@nongnu.org Developers <qemu-devel@nongnu.org>; open
> list:RISC-V <qemu-riscv@nongnu.org>; Zhangxiaofeng (F)
> <victor.zhangxiaofeng@huawei.com>; Sagar Karandikar
> <sagark@eecs.berkeley.edu>; open list:Overall <kvm@vger.kernel.org>;
> libvir-list@redhat.com; Bastian Koppelmann
> <kbastian@mail.uni-paderborn.de>; Anup Patel <anup.patel@wdc.com>;
> yinyipeng <yinyipeng1@huawei.com>; Alistair Francis
> <Alistair.Francis@wdc.com>; kvm-riscv@lists.infradead.org; Palmer Dabbelt
> <palmer@dabbelt.com>; dengkai (A) <dengkai1@huawei.com>; Wubin (H)
> <wu.wubin@huawei.com>; Zhanghailiang <zhang.zhanghailiang@huawei.com>
> Subject: Re: [PATCH RFC v4 13/15] target/riscv: Introduce dynamic time
> frequency for virt machine
> 
> On Thu, Dec 3, 2020 at 4:57 AM Yifei Jiang <jiangyifei@huawei.com> wrote:
> >
> > Currently, time base frequency was fixed as SIFIVE_CLINT_TIMEBASE_FREQ.
> > Here introduce "time-frequency" property to set time base frequency
> > dynamically of which default value is still
> > SIFIVE_CLINT_TIMEBASE_FREQ. The virt machine uses frequency of the first
> cpu to create clint and fdt.
> >
> > Signed-off-by: Yifei Jiang <jiangyifei@huawei.com>
> > Signed-off-by: Yipeng Yin <yinyipeng1@huawei.com>
> > ---
> >  hw/riscv/virt.c    | 18 ++++++++++++++----
> >  target/riscv/cpu.c |  3 +++
> >  target/riscv/cpu.h |  2 ++
> >  3 files changed, 19 insertions(+), 4 deletions(-)
> >
> > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index
> > 47b7018193..788a7237b6 100644
> > --- a/hw/riscv/virt.c
> > +++ b/hw/riscv/virt.c
> > @@ -178,7 +178,7 @@ static void create_pcie_irq_map(void *fdt, char
> > *nodename,  }
> >
> >  static void create_fdt(RISCVVirtState *s, const struct MemmapEntry
> *memmap,
> > -    uint64_t mem_size, const char *cmdline)
> > +    uint64_t mem_size, const char *cmdline, uint64_t
> > + timebase_frequency)
> >  {
> >      void *fdt;
> >      int i, cpu, socket;
> > @@ -225,7 +225,7 @@ static void create_fdt(RISCVVirtState *s, const
> > struct MemmapEntry *memmap,
> >
> >      qemu_fdt_add_subnode(fdt, "/cpus");
> >      qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> > -                          SIFIVE_CLINT_TIMEBASE_FREQ);
> > +                          timebase_frequency);
> >      qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> >      qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> >      qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); @@ -510,6 +510,7
> @@
> > static void virt_machine_init(MachineState *machine)
> >      target_ulong firmware_end_addr, kernel_start_addr;
> >      uint32_t fdt_load_addr;
> >      uint64_t kernel_entry;
> > +    uint64_t timebase_frequency = 0;
> >      DeviceState *mmio_plic, *virtio_plic, *pcie_plic;
> >      int i, j, base_hartid, hart_count;
> >      CPUState *cs;
> > @@ -553,12 +554,20 @@ static void virt_machine_init(MachineState
> *machine)
> >                                  hart_count, &error_abort);
> >          sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
> >
> > +        if (!timebase_frequency) {
> > +            timebase_frequency = RISCV_CPU(first_cpu)->env.frequency;
> > +        }
> > +        /* If vcpu's time frequency is not specified, we use default
> frequency */
> > +        if (!timebase_frequency) {
> > +            timebase_frequency = SIFIVE_CLINT_TIMEBASE_FREQ;
> > +        }
> > +
> >          /* Per-socket CLINT */
> >          sifive_clint_create(
> >              memmap[VIRT_CLINT].base + i *
> memmap[VIRT_CLINT].size,
> >              memmap[VIRT_CLINT].size, base_hartid, hart_count,
> >              SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE,
> SIFIVE_TIME_BASE,
> > -            SIFIVE_CLINT_TIMEBASE_FREQ, true);
> > +            timebase_frequency, true);
> >
> >          /* Per-socket PLIC hart topology configuration string */
> >          plic_hart_config_len =
> > @@ -610,7 +619,8 @@ static void virt_machine_init(MachineState
> *machine)
> >          main_mem);
> >
> >      /* create device tree */
> > -    create_fdt(s, memmap, machine->ram_size,
> machine->kernel_cmdline);
> > +    create_fdt(s, memmap, machine->ram_size,
> machine->kernel_cmdline,
> > +               timebase_frequency);
> >
> >      /* boot rom */
> >      memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom",
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index
> > 439dc89ee7..66f35bcbbf 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -494,6 +494,8 @@ static void riscv_cpu_realize(DeviceState *dev,
> > Error **errp)
> >
> >      riscv_cpu_register_gdb_regs_for_features(cs);
> >
> > +    env->user_frequency = env->frequency;
> > +
> >      qemu_init_vcpu(cs);
> >      cpu_reset(cs);
> >
> > @@ -531,6 +533,7 @@ static Property riscv_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
> >      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
> >      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec,
> > DEFAULT_RSTVEC),
> > +    DEFINE_PROP_UINT64("time-frequency", RISCVCPU, env.frequency, 0),
> 
> Why not set the default to SIFIVE_CLINT_TIMEBASE_FREQ?
> 

When the time frequency is not specified, it will follow the host or the migration
source. And we define 0 as equivalent to not specified time frequency.

> Also, QEMU now has a clock API, is using that instead a better option?
> 

Sorry, I didn't find the clock API. Could you tell me what the API is.
I think that the time frequency is option of KVM VCPU. So it is appropriate to put this
option in the CPU.

Yifei

> Alistair
> 
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index
> > 16d6050ead..f5b6c34176 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -243,6 +243,8 @@ struct CPURISCVState {
> >      uint64_t kvm_timer_time;
> >      uint64_t kvm_timer_compare;
> >      uint64_t kvm_timer_state;
> > +    uint64_t user_frequency;
> > +    uint64_t frequency;
> >  };
> >
> >  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> > --
> > 2.19.1
> >
> >
Alistair Francis Jan. 5, 2021, 5:36 p.m. UTC | #3
On Mon, Dec 14, 2020 at 11:31 PM Jiangyifei <jiangyifei@huawei.com> wrote:
>
>
> > -----Original Message-----
> > From: Alistair Francis [mailto:alistair23@gmail.com]
> > Sent: Wednesday, December 9, 2020 6:26 AM
> > To: Jiangyifei <jiangyifei@huawei.com>
> > Cc: qemu-devel@nongnu.org Developers <qemu-devel@nongnu.org>; open
> > list:RISC-V <qemu-riscv@nongnu.org>; Zhangxiaofeng (F)
> > <victor.zhangxiaofeng@huawei.com>; Sagar Karandikar
> > <sagark@eecs.berkeley.edu>; open list:Overall <kvm@vger.kernel.org>;
> > libvir-list@redhat.com; Bastian Koppelmann
> > <kbastian@mail.uni-paderborn.de>; Anup Patel <anup.patel@wdc.com>;
> > yinyipeng <yinyipeng1@huawei.com>; Alistair Francis
> > <Alistair.Francis@wdc.com>; kvm-riscv@lists.infradead.org; Palmer Dabbelt
> > <palmer@dabbelt.com>; dengkai (A) <dengkai1@huawei.com>; Wubin (H)
> > <wu.wubin@huawei.com>; Zhanghailiang <zhang.zhanghailiang@huawei.com>
> > Subject: Re: [PATCH RFC v4 13/15] target/riscv: Introduce dynamic time
> > frequency for virt machine
> >
> > On Thu, Dec 3, 2020 at 4:57 AM Yifei Jiang <jiangyifei@huawei.com> wrote:
> > >
> > > Currently, time base frequency was fixed as SIFIVE_CLINT_TIMEBASE_FREQ.
> > > Here introduce "time-frequency" property to set time base frequency
> > > dynamically of which default value is still
> > > SIFIVE_CLINT_TIMEBASE_FREQ. The virt machine uses frequency of the first
> > cpu to create clint and fdt.
> > >
> > > Signed-off-by: Yifei Jiang <jiangyifei@huawei.com>
> > > Signed-off-by: Yipeng Yin <yinyipeng1@huawei.com>
> > > ---
> > >  hw/riscv/virt.c    | 18 ++++++++++++++----
> > >  target/riscv/cpu.c |  3 +++
> > >  target/riscv/cpu.h |  2 ++
> > >  3 files changed, 19 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index
> > > 47b7018193..788a7237b6 100644
> > > --- a/hw/riscv/virt.c
> > > +++ b/hw/riscv/virt.c
> > > @@ -178,7 +178,7 @@ static void create_pcie_irq_map(void *fdt, char
> > > *nodename,  }
> > >
> > >  static void create_fdt(RISCVVirtState *s, const struct MemmapEntry
> > *memmap,
> > > -    uint64_t mem_size, const char *cmdline)
> > > +    uint64_t mem_size, const char *cmdline, uint64_t
> > > + timebase_frequency)
> > >  {
> > >      void *fdt;
> > >      int i, cpu, socket;
> > > @@ -225,7 +225,7 @@ static void create_fdt(RISCVVirtState *s, const
> > > struct MemmapEntry *memmap,
> > >
> > >      qemu_fdt_add_subnode(fdt, "/cpus");
> > >      qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> > > -                          SIFIVE_CLINT_TIMEBASE_FREQ);
> > > +                          timebase_frequency);
> > >      qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> > >      qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> > >      qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); @@ -510,6 +510,7
> > @@
> > > static void virt_machine_init(MachineState *machine)
> > >      target_ulong firmware_end_addr, kernel_start_addr;
> > >      uint32_t fdt_load_addr;
> > >      uint64_t kernel_entry;
> > > +    uint64_t timebase_frequency = 0;
> > >      DeviceState *mmio_plic, *virtio_plic, *pcie_plic;
> > >      int i, j, base_hartid, hart_count;
> > >      CPUState *cs;
> > > @@ -553,12 +554,20 @@ static void virt_machine_init(MachineState
> > *machine)
> > >                                  hart_count, &error_abort);
> > >          sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
> > >
> > > +        if (!timebase_frequency) {
> > > +            timebase_frequency = RISCV_CPU(first_cpu)->env.frequency;
> > > +        }
> > > +        /* If vcpu's time frequency is not specified, we use default
> > frequency */
> > > +        if (!timebase_frequency) {
> > > +            timebase_frequency = SIFIVE_CLINT_TIMEBASE_FREQ;
> > > +        }
> > > +
> > >          /* Per-socket CLINT */
> > >          sifive_clint_create(
> > >              memmap[VIRT_CLINT].base + i *
> > memmap[VIRT_CLINT].size,
> > >              memmap[VIRT_CLINT].size, base_hartid, hart_count,
> > >              SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE,
> > SIFIVE_TIME_BASE,
> > > -            SIFIVE_CLINT_TIMEBASE_FREQ, true);
> > > +            timebase_frequency, true);
> > >
> > >          /* Per-socket PLIC hart topology configuration string */
> > >          plic_hart_config_len =
> > > @@ -610,7 +619,8 @@ static void virt_machine_init(MachineState
> > *machine)
> > >          main_mem);
> > >
> > >      /* create device tree */
> > > -    create_fdt(s, memmap, machine->ram_size,
> > machine->kernel_cmdline);
> > > +    create_fdt(s, memmap, machine->ram_size,
> > machine->kernel_cmdline,
> > > +               timebase_frequency);
> > >
> > >      /* boot rom */
> > >      memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom",
> > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index
> > > 439dc89ee7..66f35bcbbf 100644
> > > --- a/target/riscv/cpu.c
> > > +++ b/target/riscv/cpu.c
> > > @@ -494,6 +494,8 @@ static void riscv_cpu_realize(DeviceState *dev,
> > > Error **errp)
> > >
> > >      riscv_cpu_register_gdb_regs_for_features(cs);
> > >
> > > +    env->user_frequency = env->frequency;
> > > +
> > >      qemu_init_vcpu(cs);
> > >      cpu_reset(cs);
> > >
> > > @@ -531,6 +533,7 @@ static Property riscv_cpu_properties[] = {
> > >      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
> > >      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
> > >      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec,
> > > DEFAULT_RSTVEC),
> > > +    DEFINE_PROP_UINT64("time-frequency", RISCVCPU, env.frequency, 0),
> >
> > Why not set the default to SIFIVE_CLINT_TIMEBASE_FREQ?
> >
>
> When the time frequency is not specified, it will follow the host or the migration
> source. And we define 0 as equivalent to not specified time frequency.
>
> > Also, QEMU now has a clock API, is using that instead a better option?
> >
>
> Sorry, I didn't find the clock API. Could you tell me what the API is.
> I think that the time frequency is option of KVM VCPU. So it is appropriate to put this
> option in the CPU.

The clock API is documented here:
https://gitlab.com/qemu-project/qemu/-/blob/master/docs/devel/clocks.rst

I'm not sure if it applies to KVM, but it is at least worth considering.

Alistair

>
> Yifei
>
> > Alistair
> >
> > >      DEFINE_PROP_END_OF_LIST(),
> > >  };
> > >
> > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index
> > > 16d6050ead..f5b6c34176 100644
> > > --- a/target/riscv/cpu.h
> > > +++ b/target/riscv/cpu.h
> > > @@ -243,6 +243,8 @@ struct CPURISCVState {
> > >      uint64_t kvm_timer_time;
> > >      uint64_t kvm_timer_compare;
> > >      uint64_t kvm_timer_state;
> > > +    uint64_t user_frequency;
> > > +    uint64_t frequency;
> > >  };
> > >
> > >  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> > > --
> > > 2.19.1
> > >
> > >
diff mbox series

Patch

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 47b7018193..788a7237b6 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -178,7 +178,7 @@  static void create_pcie_irq_map(void *fdt, char *nodename,
 }
 
 static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
-    uint64_t mem_size, const char *cmdline)
+    uint64_t mem_size, const char *cmdline, uint64_t timebase_frequency)
 {
     void *fdt;
     int i, cpu, socket;
@@ -225,7 +225,7 @@  static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
 
     qemu_fdt_add_subnode(fdt, "/cpus");
     qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
-                          SIFIVE_CLINT_TIMEBASE_FREQ);
+                          timebase_frequency);
     qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
     qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
     qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
@@ -510,6 +510,7 @@  static void virt_machine_init(MachineState *machine)
     target_ulong firmware_end_addr, kernel_start_addr;
     uint32_t fdt_load_addr;
     uint64_t kernel_entry;
+    uint64_t timebase_frequency = 0;
     DeviceState *mmio_plic, *virtio_plic, *pcie_plic;
     int i, j, base_hartid, hart_count;
     CPUState *cs;
@@ -553,12 +554,20 @@  static void virt_machine_init(MachineState *machine)
                                 hart_count, &error_abort);
         sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
 
+        if (!timebase_frequency) {
+            timebase_frequency = RISCV_CPU(first_cpu)->env.frequency;
+        }
+        /* If vcpu's time frequency is not specified, we use default frequency */
+        if (!timebase_frequency) {
+            timebase_frequency = SIFIVE_CLINT_TIMEBASE_FREQ;
+        }
+
         /* Per-socket CLINT */
         sifive_clint_create(
             memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
             memmap[VIRT_CLINT].size, base_hartid, hart_count,
             SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
-            SIFIVE_CLINT_TIMEBASE_FREQ, true);
+            timebase_frequency, true);
 
         /* Per-socket PLIC hart topology configuration string */
         plic_hart_config_len =
@@ -610,7 +619,8 @@  static void virt_machine_init(MachineState *machine)
         main_mem);
 
     /* create device tree */
-    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+    create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline,
+               timebase_frequency);
 
     /* boot rom */
     memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom",
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 439dc89ee7..66f35bcbbf 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -494,6 +494,8 @@  static void riscv_cpu_realize(DeviceState *dev, Error **errp)
 
     riscv_cpu_register_gdb_regs_for_features(cs);
 
+    env->user_frequency = env->frequency;
+
     qemu_init_vcpu(cs);
     cpu_reset(cs);
 
@@ -531,6 +533,7 @@  static Property riscv_cpu_properties[] = {
     DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
     DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
     DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
+    DEFINE_PROP_UINT64("time-frequency", RISCVCPU, env.frequency, 0),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 16d6050ead..f5b6c34176 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -243,6 +243,8 @@  struct CPURISCVState {
     uint64_t kvm_timer_time;
     uint64_t kvm_timer_compare;
     uint64_t kvm_timer_state;
+    uint64_t user_frequency;
+    uint64_t frequency;
 };
 
 OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,