From 20a485d66e41411f81adb9e75cb3bfaa528ddc74 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Fri, 27 May 2016 08:53:53 +0100 Subject: [PATCH] Helpers to retrieve %ss and %esp from cpu_regs In 32bit, if not stack switch occurs, this information isn't present in an exception frame. As a result, regs->sp and regs->ss may actually alias the interrupted stack frame. To avoid accidental incorrect use, prefix the names in cpu_regs with an underscore. Signed-off-by: Andrew Cooper --- arch/x86/traps.c | 28 ++++++++++++++++++++++++++++ include/arch/x86/regs.h | 12 ++++++++---- include/arch/x86/traps.h | 7 +++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/arch/x86/traps.c b/arch/x86/traps.c index cad7ea6..74d5c54 100644 --- a/arch/x86/traps.c +++ b/arch/x86/traps.c @@ -55,6 +55,34 @@ static bool is_trap_or_interrupt(const struct cpu_regs *regs) return true; } +unsigned long cpu_regs_sp(const struct cpu_regs *regs) +{ +#ifdef __x86_64__ + return regs->_sp; +#else + unsigned int cs = read_cs(); + + if ( (regs->cs & 3) > (cs & 3) ) + return regs->_sp; + + return (unsigned long)regs + offsetof(struct cpu_regs, _sp); +#endif +} + +unsigned int cpu_regs_ss(const struct cpu_regs *regs) +{ +#ifdef __x86_64__ + return regs->_ss; +#else + unsigned int cs = read_cs(); + + if ( (regs->cs & 3) > (cs & 3) ) + return regs->_ss; + + return read_ss(); +#endif +} + /* * C entry-point for exceptions, after the per-environment stubs have suitably * adjusted the stack. diff --git a/include/arch/x86/regs.h b/include/arch/x86/regs.h index 1fb5340..618b562 100644 --- a/include/arch/x86/regs.h +++ b/include/arch/x86/regs.h @@ -7,6 +7,8 @@ #define DECL_REG(n) \ union { uint32_t e ## n; unsigned long n; } +#define _DECL_REG(n) \ + union { uint32_t _e ## n; unsigned long _ ## n; } struct cpu_regs { DECL_REG(bp); @@ -24,8 +26,8 @@ struct cpu_regs { DECL_REG(ip); uint16_t cs, _pad1[1]; DECL_REG(flags); - DECL_REG(sp); /* Won't be valid if stack */ - uint16_t ss, _pad0[1]; /* switch didn't occur. */ + _DECL_REG(sp); /* Won't be valid if stack */ + uint16_t _ss, _pad0[1]; /* switch didn't occur. */ /* Top of stack. */ }; @@ -33,6 +35,8 @@ struct cpu_regs { #define DECL_REG(n) \ union { uint64_t r ## n; uint32_t e ## n; unsigned long n; } +#define _DECL_REG(n) \ + union { uint64_t _r ## n; uint32_t _e ## n; unsigned long _ ## n; } struct cpu_regs { uint64_t r15; @@ -58,8 +62,8 @@ struct cpu_regs { DECL_REG(ip); uint16_t cs, _pad1[3]; DECL_REG(flags); - DECL_REG(sp); - uint16_t ss, _pad0[3]; + _DECL_REG(sp); + uint16_t _ss, _pad0[3]; /* Top of stack. */ }; diff --git a/include/arch/x86/traps.h b/include/arch/x86/traps.h index 600aa76..48687f6 100644 --- a/include/arch/x86/traps.h +++ b/include/arch/x86/traps.h @@ -16,6 +16,13 @@ void arch_init_traps(void); */ void __noreturn arch_crash_hard(void); +/* + * Return the correct %ss/%esp from an exception. In 32bit if no stack switch + * occurs, an exception frame doesn't contain this information. + */ +unsigned long cpu_regs_sp(const struct cpu_regs *regs); +unsigned int cpu_regs_ss(const struct cpu_regs *regs); + extern uint8_t boot_stack[2 * PAGE_SIZE]; #if defined(CONFIG_PV) -- 2.39.5