[PATCH 08/18] arm/arm64: add per thread user_mode flag

Andrew Jones
2015-02-01T18:35:05Z

While current_mode() == USR_MODE works on armv7 from PL0 to check
if we're in user mode, current_mode() would require reading a
privileged register on armv8. To work around this, on arm64 we
introduced a 'user_mode' variable. This variable needs to be per
thread now. Rather than starting to pollute thread_info with a
bunch of bools, create a flags field and a TIF_USER_MODE flag to
replace it. Use it on armv7 too for consistency. Also, now that
we need to create a thread_info initializer, add mpidr utilities
for setting thread_info->cpu.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
arm/cstart64.S | 16 +---------------
arm/selftest.c | 2 +-
lib/arm/asm/processor.h | 11 +++++++++++
lib/arm/asm/thread_info.h | 13 +++++++++++--
lib/arm/processor.c | 14 ++++++++++++++
lib/arm/setup.c | 3 +++
lib/arm64/asm/processor.h | 14 +++++++++++++-
lib/arm64/processor.c | 15 +++++++++++++--
8 files changed, 67 insertions(+), 21 deletions(-)

diff --git a/arm/cstart64.S b/arm/cstart64.S
index 2fe15eb1d3972..58e4040cfb40f 100644
--- a/arm/cstart64.S
+++ b/arm/cstart64.S
@@ -156,13 +156,7 @@ asm_mmu_enable:
mrs x2, spsr_el1
stp x1, x2, [sp, #S_PC]

- and x2, x2, #PSR_MODE_MASK
- cmp x2, #PSR_MODE_EL0t
- b.ne 1f
- adr x2, user_mode
- str xzr, [x2] /* we're in kernel mode now */
-
-1: mov x0, \vec
+ mov x0, \vec
mov x1, sp
mrs x2, esr_el1
bl do_handle_exception
@@ -171,14 +165,6 @@ asm_mmu_enable:
msr spsr_el1, x2
msr elr_el1, x1

- and x2, x2, #PSR_MODE_MASK
- cmp x2, #PSR_MODE_EL0t
- b.ne 1f
- adr x2, user_mode
- mov x1, #1
- str x1, [x2] /* we're going back to user mode */
-
-1:
.if \vec >= 8
ldr x1, [sp, #S_SP]
msr sp_el0, x1
diff --git a/arm/selftest.c b/arm/selftest.c
index 05ca7efe95f83..d77495747b08a 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -240,7 +240,7 @@ static enum vector check_vector_prep(void)
{
unsigned long daif;

- if (user_mode)
+ if (is_user())
return EL0_SYNC_64;

asm volatile("mrs %0, daif" : "=r" (daif) ::);
diff --git a/lib/arm/asm/processor.h b/lib/arm/asm/processor.h
index 9c37db66640e8..f25e7eee3666c 100644
--- a/lib/arm/asm/processor.h
+++ b/lib/arm/asm/processor.h
@@ -33,6 +33,17 @@ static inline unsigned long current_cpsr(void)

#define current_mode() (current_cpsr() & MODE_MASK)

+static inline unsigned int get_mpidr(void)
+{
+ unsigned int mpidr;
+ asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
+ return mpidr;
+}
+
+/* Only support Aff0 for now, up to 4 cpus */
+#define mpidr_to_cpu(mpidr) ((int)((mpidr) & 0xff))
+
extern void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr);
+extern bool is_user(void);

#endif /* _ASMARM_PROCESSOR_H_ */
diff --git a/lib/arm/asm/thread_info.h b/lib/arm/asm/thread_info.h
index ea86f142a7d93..17997e21d1274 100644
--- a/lib/arm/asm/thread_info.h
+++ b/lib/arm/asm/thread_info.h
@@ -11,17 +11,26 @@
#define THREAD_SIZE 16384
#define THREAD_START_SP (THREAD_SIZE - 16)

+#define TIF_USER_MODE (1U << 0)
+
struct thread_info {
int cpu;
+ unsigned int flags;
char ext[0]; /* allow unit tests to add extended info */
};

+static inline struct thread_info *thread_info_sp(unsigned long sp)
+{
+ return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
+}
+
register unsigned long current_stack_pointer asm("sp");

static inline struct thread_info *current_thread_info(void)
{
- return (struct thread_info *)
- (current_stack_pointer & ~(THREAD_SIZE - 1));
+ return thread_info_sp(current_stack_pointer);
}

+extern void thread_info_init(struct thread_info *ti, unsigned int flags);
+
#endif /* _ASMARM_THREAD_INFO_H_ */
diff --git a/lib/arm/processor.c b/lib/arm/processor.c
index d2fd597fcd139..8a514a29c063b 100644
--- a/lib/arm/processor.c
+++ b/lib/arm/processor.c
@@ -100,10 +100,19 @@ void do_handle_exception(enum vector v, struct pt_regs *regs)
abort();
}

+void thread_info_init(struct thread_info *ti, unsigned int flags)
+{
+ memset(ti, 0, sizeof(struct thread_info));
+ ti->cpu = mpidr_to_cpu(get_mpidr());
+ ti->flags = flags;
+}
+
void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr)
{
sp_usr &= (~7UL); /* stack ptr needs 8-byte alignment */

+ thread_info_init(thread_info_sp(sp_usr), TIF_USER_MODE);
+
asm volatile(
"mrs r0, cpsr\n"
"bic r0, #" xstr(MODE_MASK) "\n"
@@ -115,3 +124,8 @@ void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr)
"mov pc, %2\n"
:: "r" (arg), "r" (sp_usr), "r" (func) : "r0");
}
+
+bool is_user(void)
+{
+ return current_thread_info()->flags & TIF_USER_MODE;
+}
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index 8f58802e958ac..b30c8696f6539 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -14,6 +14,7 @@
#include <libfdt/libfdt.h>
#include <devicetree.h>
#include <alloc.h>
+#include <asm/thread_info.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/mmu.h>
@@ -79,6 +80,8 @@ void setup(const void *fdt)
io_init();
cpu_init();

+ thread_info_init(current_thread_info(), 0);
+
assert(dt_get_bootargs(&bootargs) == 0);
setup_args(bootargs);
}
diff --git a/lib/arm64/asm/processor.h b/lib/arm64/asm/processor.h
index d287f55b8dac6..c1326351d201f 100644
--- a/lib/arm64/asm/processor.h
+++ b/lib/arm64/asm/processor.h
@@ -60,8 +60,20 @@ static inline unsigned long current_level(void)
return el & 0xc;
}

-extern bool user_mode;
+#define DEFINE_GET_SYSREG32(reg) \
+static inline unsigned int get_##reg(void) \
+{ \
+ unsigned int reg; \
+ asm volatile("mrs %0, " #reg "_el1" : "=r" (reg)); \
+ return reg; \
+}
+DEFINE_GET_SYSREG32(mpidr)
+
+/* Only support Aff0 for now, gicv2 only */
+#define mpidr_to_cpu(mpidr) ((int)((mpidr) & 0xff))
+
extern void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr);
+extern bool is_user(void);

#endif /* !__ASSEMBLY__ */
#endif /* _ASMARM64_PROCESSOR_H_ */
diff --git a/lib/arm64/processor.c b/lib/arm64/processor.c
index 7f61b3fff281f..152767eecf062 100644
--- a/lib/arm64/processor.c
+++ b/lib/arm64/processor.c
@@ -168,12 +168,18 @@ void install_vector_handler(enum vector v, vector_fn fn)
vector_handlers[v] = fn;
}

-bool user_mode;
+void thread_info_init(struct thread_info *ti, unsigned int flags)
+{
+ memset(ti, 0, sizeof(struct thread_info));
+ ti->cpu = mpidr_to_cpu(get_mpidr());
+ ti->flags = flags;
+}
+
void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr)
{
sp_usr &= (~15UL); /* stack ptr needs 16-byte alignment */

- user_mode = true;
+ thread_info_init(thread_info_sp(sp_usr), TIF_USER_MODE);

asm volatile(
"mov x0, %0\n"
@@ -184,3 +190,8 @@ void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr)
"eret\n"
:: "r" (arg), "r" (sp_usr), "r" (func) : "x0", "x3");
}
+
+bool is_user(void)
+{
+ return current_thread_info()->flags & TIF_USER_MODE;
+}
--
1.9.3