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;
+}
