On Mon, Aug 24, 2026 at 5:40 AM Tina Zhang <[email protected]> wrote:
>
> Add get_intercept_linear_addr() to compute the linear address for
> intercepts that report one, and pass the result through
> x86_instruction_info.
>
> Handle INVLPG as the initial user. INVLPG's memory operand is decoded
> with NoAccess, and thus src_val does not contain the operand address.
> Compute the address from the decoded segment base and effective address
> in the emulator, where the operand state is available.
>
> This allows intercept handlers to use the linear address directly
> without duplicating the emulator's address calculation.
>
> Signed-off-by: Tina Zhang <[email protected]>
> ---
> arch/x86/kvm/emulate.c | 29 +++++++++++++++++++++--------
> arch/x86/kvm/kvm_emulate.h | 1 +
> 2 files changed, 22 insertions(+), 8 deletions(-)
>
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index 8071b372d233..a0e57b64cadd 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -410,6 +410,26 @@ static int em_salc(struct x86_emulate_ctxt *ctxt)
> _fault ? X86EMUL_UNHANDLEABLE : X86EMUL_CONTINUE; \
> })
>
> +static unsigned long seg_base(struct x86_emulate_ctxt *ctxt, int seg)
> +{
> + if (ctxt->mode == X86EMUL_MODE_PROT64 && seg < VCPU_SREG_FS)
> + return 0;
> +
> + return ctxt->ops->get_cached_segment_base(ctxt, seg);
> +}
> +
> +static u64 get_intercept_linear_addr(struct x86_emulate_ctxt *ctxt,
> + enum x86_intercept intercept)
> +{
> + u64 la;
> +
> + if (intercept != x86_intercept_invlpg)
> + return 0;
> +
> + la = seg_base(ctxt, ctxt->src.addr.mem.seg) + ctxt->src.addr.mem.ea;
> + return ctxt->mode == X86EMUL_MODE_PROT64 ? la : (u32)la;
> +}
The linear address calculation here is replicated from __linearize().
Perhaps something like:
static u64 get_invlpg_linear_addr(struct x86_emulate_ctxt *ctxt)
{
unsigned int max_size;
ulong linear;
if (intercept != x86_intercept_invlpg)
return 0;
__linearize(ctxt, ctxt->src.addr.mem, &max_size, 1,
ctxt->mode, &linear, X86EMUL_F_INVLPG);
return linear;
}
> static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt,
> enum x86_intercept intercept,
> enum x86_intercept_stage stage)
> @@ -427,6 +447,7 @@ static int emulator_check_intercept(struct
> x86_emulate_ctxt *ctxt,
> .src_type = ctxt->src.type,
> .dst_type = ctxt->dst.type,
> .ad_bytes = ctxt->ad_bytes,
> + .intercept_linear_addr = get_intercept_linear_addr(ctxt,
> intercept),
Maybe:
+ .invlpg_linear_addr = get_invlpg_linear_addr(ctxt, intercept),
> .rip = ctxt->eip,
> .next_rip = ctxt->_eip,
> };
> @@ -520,14 +541,6 @@ static u32 desc_limit_scaled(struct desc_struct *desc)
> return desc->g ? (limit << 12) | 0xfff : limit;
> }
>
> -static unsigned long seg_base(struct x86_emulate_ctxt *ctxt, int seg)
> -{
> - if (ctxt->mode == X86EMUL_MODE_PROT64 && seg < VCPU_SREG_FS)
> - return 0;
> -
> - return ctxt->ops->get_cached_segment_base(ctxt, seg);
> -}
> -
> static int emulate_exception(struct x86_emulate_ctxt *ctxt, int vec,
> u32 error, bool valid)
> {
> diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
> index 3e375af15c03..9eda648a1314 100644
> --- a/arch/x86/kvm/kvm_emulate.h
> +++ b/arch/x86/kvm/kvm_emulate.h
> @@ -51,6 +51,7 @@ struct x86_instruction_info {
> u8 src_type; /* type of source operand */
> u8 dst_type; /* type of destination operand */
> u8 ad_bytes; /* size of src/dst address */
> + u64 intercept_linear_addr; /* arch-reported linear address, if
> any */
+ u64 invlpg_linear_addr; /* linear address, if invlpg */
> u64 rip; /* rip of the instruction */
> u64 next_rip; /* rip following the instruction */
> };
>
> base-commit: 1b731e5ded480bd1e5546aed35584238661ce72e
> --
> 2.43.7
>
I don't really like special-casing INVLPG, but I don't see an easy alternative.