Skip to content

Commit b0c3f6d

Browse files
committed
hv: Implement ECV on M2 and later chips
This lets us eliminate HV timer ticks on secondary CPUs, which should significantly improve the scalability of this whole thing. (We leave them at 1Hz just in case, since that much won't hurt) Signed-off-by: Hector Martin <[email protected]>
1 parent a75afff commit b0c3f6d

4 files changed

Lines changed: 55 additions & 12 deletions

File tree

src/arm_cpu_regs.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,28 @@
2525
#define CNTHCTL_EL0PCTEN BIT(0)
2626

2727
#define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1)
28+
#define SYS_CNTV_CTL_EL02 sys_reg(3, 5, 14, 3, 1)
2829
#define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1)
30+
#define SYS_CNTP_CTL_EL02 sys_reg(3, 5, 14, 2, 1)
2931
#define SYS_CNTHV_CTL_EL2 sys_reg(3, 4, 14, 3, 1)
3032
#define SYS_CNTHP_CTL_EL2 sys_reg(3, 4, 14, 2, 1)
3133
#define CNTx_CTL_ISTATUS BIT(2)
3234
#define CNTx_CTL_IMASK BIT(1)
3335
#define CNTx_CTL_ENABLE BIT(0)
3436

37+
#define SYS_CNTV_TVAL_EL0 sys_reg(3, 3, 14, 3, 0)
38+
#define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1)
39+
#define SYS_CNTV_CVAL_EL0 sys_reg(3, 3, 14, 3, 2)
40+
#define SYS_CNTV_TVAL_EL02 sys_reg(3, 5, 14, 3, 0)
41+
#define SYS_CNTV_CTL_EL02 sys_reg(3, 5, 14, 3, 1)
42+
#define SYS_CNTV_CVAL_EL02 sys_reg(3, 5, 14, 3, 2)
43+
#define SYS_CNTP_TVAL_EL0 sys_reg(3, 3, 14, 2, 0)
44+
#define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1)
45+
#define SYS_CNTP_CVAL_EL0 sys_reg(3, 3, 14, 2, 2)
46+
#define SYS_CNTP_TVAL_EL02 sys_reg(3, 5, 14, 2, 0)
47+
#define SYS_CNTP_CTL_EL02 sys_reg(3, 5, 14, 2, 1)
48+
#define SYS_CNTP_CVAL_EL02 sys_reg(3, 5, 14, 2, 2)
49+
3550
#define SYS_ESR_EL2 sys_reg(3, 4, 5, 2, 0)
3651
#define ESR_ISS2 GENMASK(36, 32)
3752
#define ESR_EC GENMASK(31, 26)

src/hv.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
#include "usb.h"
1313
#include "utils.h"
1414

15-
#define HV_TICK_RATE 1000
15+
#define HV_TICK_RATE 1000
16+
#define HV_SLOW_TICK_RATE 1
1617

1718
DECLARE_SPINLOCK(bhl);
1819

@@ -22,10 +23,12 @@ void hv_exit_guest(void) __attribute__((noreturn));
2223
extern char _hv_vectors_start[0];
2324

2425
u64 hv_tick_interval;
26+
u64 hv_secondary_tick_interval;
2527

2628
int hv_pinned_cpu;
2729
int hv_want_cpu;
2830

31+
static bool hv_has_ecv;
2932
static bool hv_should_exit;
3033
bool hv_started_cpus[MAX_CPUS];
3134
u32 hv_cpus_in_guest;
@@ -60,9 +63,6 @@ void hv_init(void)
6063
smp_set_wfe_mode(true);
6164
hv_wdt_init();
6265

63-
// Enable physical timer for EL1
64-
msr(CNTHCTL_EL2, CNTHCTL_EL1PTEN | CNTHCTL_EL1PCTEN);
65-
6666
hv_pt_init();
6767

6868
// Configure hypervisor defaults
@@ -80,6 +80,21 @@ void hv_init(void)
8080
// Compute tick interval
8181
hv_tick_interval = mrs(CNTFRQ_EL0) / HV_TICK_RATE;
8282

83+
hv_has_ecv = mrs(ID_AA64MMFR0_EL1) & (0xfULL << 60);
84+
85+
if (hv_has_ecv) {
86+
printf("HV: ECV enabled\n");
87+
reg_set(CNTHCTL_EL2,
88+
CNTHCTL_EL1NVVCT | CNTHCTL_EL1NVPCT | CNTHCTL_EL1TVT | CNTHCTL_EL1PCTEN);
89+
hv_secondary_tick_interval = mrs(CNTFRQ_EL0) / HV_SLOW_TICK_RATE;
90+
} else {
91+
printf("HV: No ECV supported\n");
92+
// Enable physical timer for EL1
93+
msr(CNTHCTL_EL2, CNTHCTL_EL1PTEN | CNTHCTL_EL1PCTEN);
94+
95+
hv_secondary_tick_interval = hv_tick_interval;
96+
}
97+
8398
// Set deep WFI back to defaults
8499
reg_mask(SYS_IMP_APL_CYC_OVRD, CYC_OVRD_WFI_MODE_MASK, CYC_OVRD_WFI_MODE(0));
85100

@@ -121,7 +136,7 @@ void hv_start(void *entry, u64 regs[4])
121136
hv_secondary_info.sprr_config = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1);
122137
hv_secondary_info.gxf_config = mrs(SYS_IMP_APL_GXF_CONFIG_EL1);
123138

124-
hv_arm_tick();
139+
hv_arm_tick(false);
125140
hv_pinned_cpu = -1;
126141
hv_want_cpu = -1;
127142
hv_cpus_in_guest = 1;
@@ -177,7 +192,7 @@ static void hv_init_secondary(struct hv_secondary_info_t *info)
177192
if (gxf_enabled())
178193
gl2_call(hv_set_gxf_vbar, 0, 0, 0, 0);
179194

180-
hv_arm_tick();
195+
hv_arm_tick(true);
181196
}
182197

183198
static void hv_enter_secondary(void *entry, u64 regs[4])
@@ -309,9 +324,12 @@ void hv_set_elr(u64 val)
309324
return msr(ELR_EL2, val);
310325
}
311326

312-
void hv_arm_tick(void)
327+
void hv_arm_tick(bool secondary)
313328
{
314-
msr(CNTP_TVAL_EL0, hv_tick_interval);
329+
if (secondary)
330+
msr(CNTP_TVAL_EL0, hv_secondary_tick_interval);
331+
else
332+
msr(CNTP_TVAL_EL0, hv_tick_interval);
315333
msr(CNTP_CTL_EL0, CNTx_CTL_ENABLE);
316334
}
317335

src/hv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ void hv_start_secondary(int cpu, void *entry, u64 regs[4]);
104104
void hv_rendezvous(void);
105105
bool hv_switch_cpu(int cpu);
106106
void hv_pin_cpu(int cpu);
107-
void hv_arm_tick(void);
107+
void hv_arm_tick(bool secondary);
108108
void hv_rearm(void);
109109
void hv_maybe_exit(void);
110110
void hv_tick(struct exc_info *ctx);

src/hv_exc.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,13 @@ static bool hv_handle_msr(struct exc_info *ctx, u64 iss)
207207
/* Some kind of timer */
208208
SYSREG_PASS(sys_reg(3, 7, 15, 1, 1));
209209
SYSREG_PASS(sys_reg(3, 7, 15, 3, 1));
210+
/* Architectural timer, for ECV */
211+
SYSREG_MAP(SYS_CNTV_CTL_EL0, SYS_CNTV_CTL_EL02)
212+
SYSREG_MAP(SYS_CNTV_CVAL_EL0, SYS_CNTV_CVAL_EL02)
213+
SYSREG_MAP(SYS_CNTV_TVAL_EL0, SYS_CNTV_TVAL_EL02)
214+
SYSREG_MAP(SYS_CNTP_CTL_EL0, SYS_CNTP_CTL_EL02)
215+
SYSREG_MAP(SYS_CNTP_CVAL_EL0, SYS_CNTP_CVAL_EL02)
216+
SYSREG_MAP(SYS_CNTP_TVAL_EL0, SYS_CNTP_TVAL_EL02)
210217
/* Spammy stuff seen on t600x p-cores */
211218
SYSREG_PASS(sys_reg(3, 2, 15, 12, 0));
212219
SYSREG_PASS(sys_reg(3, 2, 15, 13, 0));
@@ -451,7 +458,7 @@ void hv_exc_fiq(struct exc_info *ctx)
451458
if (smp_id() != interruptible_cpu && !(mrs(ISR_EL1) & 0x40) && hv_want_cpu == -1) {
452459
// Non-interruptible CPU and it was just a timer tick (or spurious), so just update FIQs
453460
hv_update_fiq();
454-
hv_arm_tick();
461+
hv_arm_tick(true);
455462
return;
456463
}
457464

@@ -461,9 +468,12 @@ void hv_exc_fiq(struct exc_info *ctx)
461468

462469
// Only poll for HV events in the interruptible CPU
463470
if (tick) {
464-
if (smp_id() == interruptible_cpu)
471+
if (smp_id() == interruptible_cpu) {
465472
hv_tick(ctx);
466-
hv_arm_tick();
473+
hv_arm_tick(false);
474+
} else {
475+
hv_arm_tick(true);
476+
}
467477
}
468478

469479
if (mrs(CNTV_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {

0 commit comments

Comments
 (0)