-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The patch adds support for thread creation and context switching. The context switching CPU specific code is introduced with the CPU support patch (part of the arch/arm64/mm/proc.S file). AArch64 supports ASID-tagged TLBs and the ASID can be either 8 or 16-bit wide (detectable via the ID_AA64AFR0_EL1 register). Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Tony Lindgren <tony@atomide.com> Acked-by: Nicolas Pitre <nico@linaro.org> Acked-by: Olof Johansson <olof@lixom.net> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
- Loading branch information
Catalin Marinas
committed
Sep 17, 2012
1 parent
1d18c47
commit b3901d5
Showing
4 changed files
with
846 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* Based on arch/arm/include/asm/mmu_context.h | ||
* | ||
* Copyright (C) 1996 Russell King. | ||
* Copyright (C) 2012 ARM Ltd. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
#ifndef __ASM_MMU_CONTEXT_H | ||
#define __ASM_MMU_CONTEXT_H | ||
|
||
#include <linux/compiler.h> | ||
#include <linux/sched.h> | ||
|
||
#include <asm/cacheflush.h> | ||
#include <asm/proc-fns.h> | ||
#include <asm-generic/mm_hooks.h> | ||
#include <asm/cputype.h> | ||
#include <asm/pgtable.h> | ||
|
||
#define MAX_ASID_BITS 16 | ||
|
||
extern unsigned int cpu_last_asid; | ||
|
||
void __init_new_context(struct task_struct *tsk, struct mm_struct *mm); | ||
void __new_context(struct mm_struct *mm); | ||
|
||
/* | ||
* Set TTBR0 to empty_zero_page. No translations will be possible via TTBR0. | ||
*/ | ||
static inline void cpu_set_reserved_ttbr0(void) | ||
{ | ||
unsigned long ttbr = page_to_phys(empty_zero_page); | ||
|
||
asm( | ||
" msr ttbr0_el1, %0 // set TTBR0\n" | ||
" isb" | ||
: | ||
: "r" (ttbr)); | ||
} | ||
|
||
static inline void switch_new_context(struct mm_struct *mm) | ||
{ | ||
unsigned long flags; | ||
|
||
__new_context(mm); | ||
|
||
local_irq_save(flags); | ||
cpu_switch_mm(mm->pgd, mm); | ||
local_irq_restore(flags); | ||
} | ||
|
||
static inline void check_and_switch_context(struct mm_struct *mm, | ||
struct task_struct *tsk) | ||
{ | ||
/* | ||
* Required during context switch to avoid speculative page table | ||
* walking with the wrong TTBR. | ||
*/ | ||
cpu_set_reserved_ttbr0(); | ||
|
||
if (!((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS)) | ||
/* | ||
* The ASID is from the current generation, just switch to the | ||
* new pgd. This condition is only true for calls from | ||
* context_switch() and interrupts are already disabled. | ||
*/ | ||
cpu_switch_mm(mm->pgd, mm); | ||
else if (irqs_disabled()) | ||
/* | ||
* Defer the new ASID allocation until after the context | ||
* switch critical region since __new_context() cannot be | ||
* called with interrupts disabled. | ||
*/ | ||
set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM); | ||
else | ||
/* | ||
* That is a direct call to switch_mm() or activate_mm() with | ||
* interrupts enabled and a new context. | ||
*/ | ||
switch_new_context(mm); | ||
} | ||
|
||
#define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0) | ||
#define destroy_context(mm) do { } while(0) | ||
|
||
#define finish_arch_post_lock_switch \ | ||
finish_arch_post_lock_switch | ||
static inline void finish_arch_post_lock_switch(void) | ||
{ | ||
if (test_and_clear_thread_flag(TIF_SWITCH_MM)) { | ||
struct mm_struct *mm = current->mm; | ||
unsigned long flags; | ||
|
||
__new_context(mm); | ||
|
||
local_irq_save(flags); | ||
cpu_switch_mm(mm->pgd, mm); | ||
local_irq_restore(flags); | ||
} | ||
} | ||
|
||
/* | ||
* This is called when "tsk" is about to enter lazy TLB mode. | ||
* | ||
* mm: describes the currently active mm context | ||
* tsk: task which is entering lazy tlb | ||
* cpu: cpu number which is entering lazy tlb | ||
* | ||
* tsk->mm will be NULL | ||
*/ | ||
static inline void | ||
enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) | ||
{ | ||
} | ||
|
||
/* | ||
* This is the actual mm switch as far as the scheduler | ||
* is concerned. No registers are touched. We avoid | ||
* calling the CPU specific function when the mm hasn't | ||
* actually changed. | ||
*/ | ||
static inline void | ||
switch_mm(struct mm_struct *prev, struct mm_struct *next, | ||
struct task_struct *tsk) | ||
{ | ||
unsigned int cpu = smp_processor_id(); | ||
|
||
#ifdef CONFIG_SMP | ||
/* check for possible thread migration */ | ||
if (!cpumask_empty(mm_cpumask(next)) && | ||
!cpumask_test_cpu(cpu, mm_cpumask(next))) | ||
__flush_icache_all(); | ||
#endif | ||
if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) | ||
check_and_switch_context(next, tsk); | ||
} | ||
|
||
#define deactivate_mm(tsk,mm) do { } while (0) | ||
#define activate_mm(prev,next) switch_mm(prev, next, NULL) | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Based on arch/arm/include/asm/thread_info.h | ||
* | ||
* Copyright (C) 2002 Russell King. | ||
* Copyright (C) 2012 ARM Ltd. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
#ifndef __ASM_THREAD_INFO_H | ||
#define __ASM_THREAD_INFO_H | ||
|
||
#ifdef __KERNEL__ | ||
|
||
#include <linux/compiler.h> | ||
|
||
#ifndef CONFIG_ARM64_64K_PAGES | ||
#define THREAD_SIZE_ORDER 1 | ||
#endif | ||
|
||
#define THREAD_SIZE 8192 | ||
#define THREAD_START_SP (THREAD_SIZE - 16) | ||
|
||
#ifndef __ASSEMBLY__ | ||
|
||
struct task_struct; | ||
struct exec_domain; | ||
|
||
#include <asm/types.h> | ||
|
||
typedef unsigned long mm_segment_t; | ||
|
||
/* | ||
* low level task data that entry.S needs immediate access to. | ||
* __switch_to() assumes cpu_context follows immediately after cpu_domain. | ||
*/ | ||
struct thread_info { | ||
unsigned long flags; /* low level flags */ | ||
mm_segment_t addr_limit; /* address limit */ | ||
struct task_struct *task; /* main task structure */ | ||
struct exec_domain *exec_domain; /* execution domain */ | ||
struct restart_block restart_block; | ||
int preempt_count; /* 0 => preemptable, <0 => bug */ | ||
int cpu; /* cpu */ | ||
}; | ||
|
||
#define INIT_THREAD_INFO(tsk) \ | ||
{ \ | ||
.task = &tsk, \ | ||
.exec_domain = &default_exec_domain, \ | ||
.flags = 0, \ | ||
.preempt_count = INIT_PREEMPT_COUNT, \ | ||
.addr_limit = KERNEL_DS, \ | ||
.restart_block = { \ | ||
.fn = do_no_restart_syscall, \ | ||
}, \ | ||
} | ||
|
||
#define init_thread_info (init_thread_union.thread_info) | ||
#define init_stack (init_thread_union.stack) | ||
|
||
/* | ||
* how to get the thread information struct from C | ||
*/ | ||
static inline struct thread_info *current_thread_info(void) __attribute_const__; | ||
|
||
static inline struct thread_info *current_thread_info(void) | ||
{ | ||
register unsigned long sp asm ("sp"); | ||
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); | ||
} | ||
|
||
#define thread_saved_pc(tsk) \ | ||
((unsigned long)(tsk->thread.cpu_context.pc)) | ||
#define thread_saved_sp(tsk) \ | ||
((unsigned long)(tsk->thread.cpu_context.sp)) | ||
#define thread_saved_fp(tsk) \ | ||
((unsigned long)(tsk->thread.cpu_context.fp)) | ||
|
||
#endif | ||
|
||
/* | ||
* We use bit 30 of the preempt_count to indicate that kernel | ||
* preemption is occurring. See <asm/hardirq.h>. | ||
*/ | ||
#define PREEMPT_ACTIVE 0x40000000 | ||
|
||
/* | ||
* thread information flags: | ||
* TIF_SYSCALL_TRACE - syscall trace active | ||
* TIF_SIGPENDING - signal pending | ||
* TIF_NEED_RESCHED - rescheduling necessary | ||
* TIF_NOTIFY_RESUME - callback before returning to user | ||
* TIF_USEDFPU - FPU was used by this task this quantum (SMP) | ||
* TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED | ||
*/ | ||
#define TIF_SIGPENDING 0 | ||
#define TIF_NEED_RESCHED 1 | ||
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ | ||
#define TIF_SYSCALL_TRACE 8 | ||
#define TIF_POLLING_NRFLAG 16 | ||
#define TIF_MEMDIE 18 /* is terminating due to OOM killer */ | ||
#define TIF_FREEZE 19 | ||
#define TIF_RESTORE_SIGMASK 20 | ||
#define TIF_SINGLESTEP 21 | ||
#define TIF_32BIT 22 /* 32bit process */ | ||
#define TIF_SWITCH_MM 23 /* deferred switch_mm */ | ||
|
||
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) | ||
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) | ||
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) | ||
#define _TIF_32BIT (1 << TIF_32BIT) | ||
|
||
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ | ||
_TIF_NOTIFY_RESUME) | ||
|
||
#endif /* __KERNEL__ */ | ||
#endif /* __ASM_THREAD_INFO_H */ |
Oops, something went wrong.