mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	RISC-V: Init and Halt Code
This contains the various __init C functions, the initial assembly kernel entry point, and the code to reset the system. When a file was init-related this patch contains the entire file. Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com>
This commit is contained in:
		
							parent
							
								
									8caea50236
								
							
						
					
					
						commit
						76d2a0493a
					
				
							
								
								
									
										88
									
								
								arch/riscv/include/asm/bug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								arch/riscv/include/asm/bug.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _ASM_RISCV_BUG_H | ||||||
|  | #define _ASM_RISCV_BUG_H | ||||||
|  | 
 | ||||||
|  | #include <linux/compiler.h> | ||||||
|  | #include <linux/const.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/asm.h> | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_GENERIC_BUG | ||||||
|  | #define __BUG_INSN	_AC(0x00100073, UL) /* ebreak */ | ||||||
|  | 
 | ||||||
|  | #ifndef __ASSEMBLY__ | ||||||
|  | typedef u32 bug_insn_t; | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS | ||||||
|  | #define __BUG_ENTRY_ADDR	INT " 1b - 2b" | ||||||
|  | #define __BUG_ENTRY_FILE	INT " %0 - 2b" | ||||||
|  | #else | ||||||
|  | #define __BUG_ENTRY_ADDR	RISCV_PTR " 1b" | ||||||
|  | #define __BUG_ENTRY_FILE	RISCV_PTR " %0" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_DEBUG_BUGVERBOSE | ||||||
|  | #define __BUG_ENTRY			\ | ||||||
|  | 	__BUG_ENTRY_ADDR "\n\t"		\ | ||||||
|  | 	__BUG_ENTRY_FILE "\n\t"		\ | ||||||
|  | 	SHORT " %1" | ||||||
|  | #else | ||||||
|  | #define __BUG_ENTRY			\ | ||||||
|  | 	__BUG_ENTRY_ADDR | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define BUG()							\ | ||||||
|  | do {								\ | ||||||
|  | 	__asm__ __volatile__ (					\ | ||||||
|  | 		"1:\n\t"					\ | ||||||
|  | 			"ebreak\n"				\ | ||||||
|  | 			".pushsection __bug_table,\"a\"\n\t"	\ | ||||||
|  | 		"2:\n\t"					\ | ||||||
|  | 			__BUG_ENTRY "\n\t"			\ | ||||||
|  | 			".org 2b + %2\n\t"			\ | ||||||
|  | 			".popsection"				\ | ||||||
|  | 		:						\ | ||||||
|  | 		: "i" (__FILE__), "i" (__LINE__),		\ | ||||||
|  | 		  "i" (sizeof(struct bug_entry)));		\ | ||||||
|  | 	unreachable();						\ | ||||||
|  | } while (0) | ||||||
|  | #endif /* !__ASSEMBLY__ */ | ||||||
|  | #else /* CONFIG_GENERIC_BUG */ | ||||||
|  | #ifndef __ASSEMBLY__ | ||||||
|  | #define BUG()							\ | ||||||
|  | do {								\ | ||||||
|  | 	__asm__ __volatile__ ("ebreak\n");			\ | ||||||
|  | 	unreachable();						\ | ||||||
|  | } while (0) | ||||||
|  | #endif /* !__ASSEMBLY__ */ | ||||||
|  | #endif /* CONFIG_GENERIC_BUG */ | ||||||
|  | 
 | ||||||
|  | #define HAVE_ARCH_BUG | ||||||
|  | 
 | ||||||
|  | #include <asm-generic/bug.h> | ||||||
|  | 
 | ||||||
|  | #ifndef __ASSEMBLY__ | ||||||
|  | 
 | ||||||
|  | struct pt_regs; | ||||||
|  | struct task_struct; | ||||||
|  | 
 | ||||||
|  | extern void die(struct pt_regs *regs, const char *str); | ||||||
|  | extern void do_trap(struct pt_regs *regs, int signo, int code, | ||||||
|  | 	unsigned long addr, struct task_struct *tsk); | ||||||
|  | 
 | ||||||
|  | #endif /* !__ASSEMBLY__ */ | ||||||
|  | 
 | ||||||
|  | #endif /* _ASM_RISCV_BUG_H */ | ||||||
							
								
								
									
										22
									
								
								arch/riscv/include/asm/cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								arch/riscv/include/asm/cache.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2017 Chen Liqin <liqin.chen@sunplusct.com> | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _ASM_RISCV_CACHE_H | ||||||
|  | #define _ASM_RISCV_CACHE_H | ||||||
|  | 
 | ||||||
|  | #define L1_CACHE_SHIFT		6 | ||||||
|  | 
 | ||||||
|  | #define L1_CACHE_BYTES		(1 << L1_CACHE_SHIFT) | ||||||
|  | 
 | ||||||
|  | #endif /* _ASM_RISCV_CACHE_H */ | ||||||
							
								
								
									
										52
									
								
								arch/riscv/include/asm/smp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								arch/riscv/include/asm/smp.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _ASM_RISCV_SMP_H | ||||||
|  | #define _ASM_RISCV_SMP_H | ||||||
|  | 
 | ||||||
|  | /* This both needs asm-offsets.h and is used when generating it. */ | ||||||
|  | #ifndef GENERATING_ASM_OFFSETS | ||||||
|  | #include <asm/asm-offsets.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <linux/cpumask.h> | ||||||
|  | #include <linux/irqreturn.h> | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_SMP | ||||||
|  | 
 | ||||||
|  | /* SMP initialization hook for setup_arch */ | ||||||
|  | void __init init_clockevent(void); | ||||||
|  | 
 | ||||||
|  | /* SMP initialization hook for setup_arch */ | ||||||
|  | void __init setup_smp(void); | ||||||
|  | 
 | ||||||
|  | /* Hook for the generic smp_call_function_many() routine. */ | ||||||
|  | void arch_send_call_function_ipi_mask(struct cpumask *mask); | ||||||
|  | 
 | ||||||
|  | /* Hook for the generic smp_call_function_single() routine. */ | ||||||
|  | void arch_send_call_function_single_ipi(int cpu); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This is particularly ugly: it appears we can't actually get the definition | ||||||
|  |  * of task_struct here, but we need access to the CPU this task is running on. | ||||||
|  |  * Instead of using C we're using asm-offsets.h to get the current processor | ||||||
|  |  * ID. | ||||||
|  |  */ | ||||||
|  | #define raw_smp_processor_id() (*((int*)((char*)get_current() + TASK_TI_CPU))) | ||||||
|  | 
 | ||||||
|  | /* Interprocessor interrupt handler */ | ||||||
|  | irqreturn_t handle_ipi(void); | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_SMP */ | ||||||
|  | 
 | ||||||
|  | #endif /* _ASM_RISCV_SMP_H */ | ||||||
							
								
								
									
										105
									
								
								arch/riscv/kernel/cacheinfo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								arch/riscv/kernel/cacheinfo.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2017 SiFive | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/cacheinfo.h> | ||||||
|  | #include <linux/cpu.h> | ||||||
|  | #include <linux/of.h> | ||||||
|  | #include <linux/of_device.h> | ||||||
|  | 
 | ||||||
|  | static void ci_leaf_init(struct cacheinfo *this_leaf, | ||||||
|  | 			 struct device_node *node, | ||||||
|  | 			 enum cache_type type, unsigned int level) | ||||||
|  | { | ||||||
|  | 	this_leaf->of_node = node; | ||||||
|  | 	this_leaf->level = level; | ||||||
|  | 	this_leaf->type = type; | ||||||
|  | 	/* not a sector cache */ | ||||||
|  | 	this_leaf->physical_line_partition = 1; | ||||||
|  | 	/* TODO: Add to DTS */ | ||||||
|  | 	this_leaf->attributes = | ||||||
|  | 		CACHE_WRITE_BACK | ||||||
|  | 		| CACHE_READ_ALLOCATE | ||||||
|  | 		| CACHE_WRITE_ALLOCATE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int __init_cache_level(unsigned int cpu) | ||||||
|  | { | ||||||
|  | 	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||||||
|  | 	struct device_node *np = of_cpu_device_node_get(cpu); | ||||||
|  | 	int levels = 0, leaves = 0, level; | ||||||
|  | 
 | ||||||
|  | 	if (of_property_read_bool(np, "cache-size")) | ||||||
|  | 		++leaves; | ||||||
|  | 	if (of_property_read_bool(np, "i-cache-size")) | ||||||
|  | 		++leaves; | ||||||
|  | 	if (of_property_read_bool(np, "d-cache-size")) | ||||||
|  | 		++leaves; | ||||||
|  | 	if (leaves > 0) | ||||||
|  | 		levels = 1; | ||||||
|  | 
 | ||||||
|  | 	while ((np = of_find_next_cache_node(np))) { | ||||||
|  | 		if (!of_device_is_compatible(np, "cache")) | ||||||
|  | 			break; | ||||||
|  | 		if (of_property_read_u32(np, "cache-level", &level)) | ||||||
|  | 			break; | ||||||
|  | 		if (level <= levels) | ||||||
|  | 			break; | ||||||
|  | 		if (of_property_read_bool(np, "cache-size")) | ||||||
|  | 			++leaves; | ||||||
|  | 		if (of_property_read_bool(np, "i-cache-size")) | ||||||
|  | 			++leaves; | ||||||
|  | 		if (of_property_read_bool(np, "d-cache-size")) | ||||||
|  | 			++leaves; | ||||||
|  | 		levels = level; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	this_cpu_ci->num_levels = levels; | ||||||
|  | 	this_cpu_ci->num_leaves = leaves; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int __populate_cache_leaves(unsigned int cpu) | ||||||
|  | { | ||||||
|  | 	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||||||
|  | 	struct cacheinfo *this_leaf = this_cpu_ci->info_list; | ||||||
|  | 	struct device_node *np = of_cpu_device_node_get(cpu); | ||||||
|  | 	int levels = 1, level = 1; | ||||||
|  | 
 | ||||||
|  | 	if (of_property_read_bool(np, "cache-size")) | ||||||
|  | 		ci_leaf_init(this_leaf++, np, CACHE_TYPE_UNIFIED, level); | ||||||
|  | 	if (of_property_read_bool(np, "i-cache-size")) | ||||||
|  | 		ci_leaf_init(this_leaf++, np, CACHE_TYPE_INST, level); | ||||||
|  | 	if (of_property_read_bool(np, "d-cache-size")) | ||||||
|  | 		ci_leaf_init(this_leaf++, np, CACHE_TYPE_DATA, level); | ||||||
|  | 
 | ||||||
|  | 	while ((np = of_find_next_cache_node(np))) { | ||||||
|  | 		if (!of_device_is_compatible(np, "cache")) | ||||||
|  | 			break; | ||||||
|  | 		if (of_property_read_u32(np, "cache-level", &level)) | ||||||
|  | 			break; | ||||||
|  | 		if (level <= levels) | ||||||
|  | 			break; | ||||||
|  | 		if (of_property_read_bool(np, "cache-size")) | ||||||
|  | 			ci_leaf_init(this_leaf++, np, CACHE_TYPE_UNIFIED, level); | ||||||
|  | 		if (of_property_read_bool(np, "i-cache-size")) | ||||||
|  | 			ci_leaf_init(this_leaf++, np, CACHE_TYPE_INST, level); | ||||||
|  | 		if (of_property_read_bool(np, "d-cache-size")) | ||||||
|  | 			ci_leaf_init(this_leaf++, np, CACHE_TYPE_DATA, level); | ||||||
|  | 		levels = level; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level) | ||||||
|  | DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves) | ||||||
							
								
								
									
										108
									
								
								arch/riscv/kernel/cpu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								arch/riscv/kernel/cpu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/seq_file.h> | ||||||
|  | #include <linux/of.h> | ||||||
|  | 
 | ||||||
|  | /* Return -1 if not a valid hart */ | ||||||
|  | int riscv_of_processor_hart(struct device_node *node) | ||||||
|  | { | ||||||
|  | 	const char *isa, *status; | ||||||
|  | 	u32 hart; | ||||||
|  | 
 | ||||||
|  | 	if (!of_device_is_compatible(node, "riscv")) { | ||||||
|  | 		pr_warn("Found incompatible CPU\n"); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (of_property_read_u32(node, "reg", &hart)) { | ||||||
|  | 		pr_warn("Found CPU without hart ID\n"); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 	if (hart >= NR_CPUS) { | ||||||
|  | 		pr_info("Found hart ID %d, which is above NR_CPUs.  Disabling this hart\n", hart); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (of_property_read_string(node, "status", &status)) { | ||||||
|  | 		pr_warn("CPU with hartid=%d has no \"status\" property\n", hart); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 	if (strcmp(status, "okay")) { | ||||||
|  | 		pr_info("CPU with hartid=%d has a non-okay status of \"%s\"\n", hart, status); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (of_property_read_string(node, "riscv,isa", &isa)) { | ||||||
|  | 		pr_warn("CPU with hartid=%d has no \"riscv,isa\" property\n", hart); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 	if (isa[0] != 'r' || isa[1] != 'v') { | ||||||
|  | 		pr_warn("CPU with hartid=%d has an invalid ISA of \"%s\"\n", hart, isa); | ||||||
|  | 		return -(ENODEV); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return hart; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_PROC_FS | ||||||
|  | 
 | ||||||
|  | static void *c_start(struct seq_file *m, loff_t *pos) | ||||||
|  | { | ||||||
|  | 	*pos = cpumask_next(*pos - 1, cpu_online_mask); | ||||||
|  | 	if ((*pos) < nr_cpu_ids) | ||||||
|  | 		return (void *)(uintptr_t)(1 + *pos); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||||||
|  | { | ||||||
|  | 	(*pos)++; | ||||||
|  | 	return c_start(m, pos); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void c_stop(struct seq_file *m, void *v) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int c_show(struct seq_file *m, void *v) | ||||||
|  | { | ||||||
|  | 	unsigned long hart_id = (unsigned long)v - 1; | ||||||
|  | 	struct device_node *node = of_get_cpu_node(hart_id, NULL); | ||||||
|  | 	const char *compat, *isa, *mmu; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(m, "hart\t: %lu\n", hart_id); | ||||||
|  | 	if (!of_property_read_string(node, "riscv,isa", &isa) | ||||||
|  | 	    && isa[0] == 'r' | ||||||
|  | 	    && isa[1] == 'v') | ||||||
|  | 		seq_printf(m, "isa\t: %s\n", isa); | ||||||
|  | 	if (!of_property_read_string(node, "mmu-type", &mmu) | ||||||
|  | 	    && !strncmp(mmu, "riscv,", 6)) | ||||||
|  | 		seq_printf(m, "mmu\t: %s\n", mmu+6); | ||||||
|  | 	if (!of_property_read_string(node, "compatible", &compat) | ||||||
|  | 	    && strcmp(compat, "riscv")) | ||||||
|  | 		seq_printf(m, "uarch\t: %s\n", compat); | ||||||
|  | 	seq_puts(m, "\n"); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct seq_operations cpuinfo_op = { | ||||||
|  | 	.start	= c_start, | ||||||
|  | 	.next	= c_next, | ||||||
|  | 	.stop	= c_stop, | ||||||
|  | 	.show	= c_show | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_PROC_FS */ | ||||||
							
								
								
									
										157
									
								
								arch/riscv/kernel/head.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								arch/riscv/kernel/head.S
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,157 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or
 | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <asm/thread_info.h> | ||||||
|  | #include <asm/asm-offsets.h> | ||||||
|  | #include <asm/asm.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/linkage.h> | ||||||
|  | #include <asm/thread_info.h> | ||||||
|  | #include <asm/page.h> | ||||||
|  | #include <asm/csr.h> | ||||||
|  | 
 | ||||||
|  | __INIT | ||||||
|  | ENTRY(_start) | ||||||
|  | 	/* Mask all interrupts */ | ||||||
|  | 	csrw sie, zero | ||||||
|  | 
 | ||||||
|  | 	/* Load the global pointer */ | ||||||
|  | .option push
 | ||||||
|  | .option norelax
 | ||||||
|  | 	la gp, __global_pointer$ | ||||||
|  | .option pop
 | ||||||
|  | 
 | ||||||
|  | 	/* | ||||||
|  | 	 * Disable FPU to detect illegal usage of | ||||||
|  | 	 * floating point in kernel space | ||||||
|  | 	 */ | ||||||
|  | 	li t0, SR_FS | ||||||
|  | 	csrc sstatus, t0 | ||||||
|  | 
 | ||||||
|  | 	/* Pick one hart to run the main boot sequence */ | ||||||
|  | 	la a3, hart_lottery | ||||||
|  | 	li a2, 1 | ||||||
|  | 	amoadd.w a3, a2, (a3) | ||||||
|  | 	bnez a3, .Lsecondary_start | ||||||
|  | 
 | ||||||
|  | 	/* Save hart ID and DTB physical address */ | ||||||
|  | 	mv s0, a0 | ||||||
|  | 	mv s1, a1 | ||||||
|  | 
 | ||||||
|  | 	/* Initialize page tables and relocate to virtual addresses */ | ||||||
|  | 	la sp, init_thread_union + THREAD_SIZE | ||||||
|  | 	call setup_vm | ||||||
|  | 	call relocate | ||||||
|  | 
 | ||||||
|  | 	/* Restore C environment */ | ||||||
|  | 	la tp, init_task | ||||||
|  | 	sw s0, TASK_TI_CPU(tp) | ||||||
|  | 
 | ||||||
|  | 	la sp, init_thread_union | ||||||
|  | 	li a0, ASM_THREAD_SIZE | ||||||
|  | 	add sp, sp, a0 | ||||||
|  | 
 | ||||||
|  | 	/* Start the kernel */ | ||||||
|  | 	mv a0, s0 | ||||||
|  | 	mv a1, s1 | ||||||
|  | 	call sbi_save | ||||||
|  | 	tail start_kernel | ||||||
|  | 
 | ||||||
|  | relocate: | ||||||
|  | 	/* Relocate return address */ | ||||||
|  | 	li a1, PAGE_OFFSET | ||||||
|  | 	la a0, _start | ||||||
|  | 	sub a1, a1, a0 | ||||||
|  | 	add ra, ra, a1 | ||||||
|  | 
 | ||||||
|  | 	/* Point stvec to virtual address of intruction after sptbr write */ | ||||||
|  | 	la a0, 1f | ||||||
|  | 	add a0, a0, a1 | ||||||
|  | 	csrw stvec, a0 | ||||||
|  | 
 | ||||||
|  | 	/* Compute sptbr for kernel page tables, but don't load it yet */ | ||||||
|  | 	la a2, swapper_pg_dir | ||||||
|  | 	srl a2, a2, PAGE_SHIFT | ||||||
|  | 	li a1, SPTBR_MODE | ||||||
|  | 	or a2, a2, a1 | ||||||
|  | 
 | ||||||
|  | 	/* | ||||||
|  | 	 * Load trampoline page directory, which will cause us to trap to | ||||||
|  | 	 * stvec if VA != PA, or simply fall through if VA == PA | ||||||
|  | 	 */ | ||||||
|  | 	la a0, trampoline_pg_dir | ||||||
|  | 	srl a0, a0, PAGE_SHIFT | ||||||
|  | 	or a0, a0, a1 | ||||||
|  | 	sfence.vma | ||||||
|  | 	csrw sptbr, a0 | ||||||
|  | 1: | ||||||
|  | 	/* Set trap vector to spin forever to help debug */ | ||||||
|  | 	la a0, .Lsecondary_park | ||||||
|  | 	csrw stvec, a0 | ||||||
|  | 
 | ||||||
|  | 	/* Reload the global pointer */ | ||||||
|  | .option push
 | ||||||
|  | .option norelax
 | ||||||
|  | 	la gp, __global_pointer$ | ||||||
|  | .option pop
 | ||||||
|  | 
 | ||||||
|  | 	/* Switch to kernel page tables */ | ||||||
|  | 	csrw sptbr, a2 | ||||||
|  | 
 | ||||||
|  | 	ret | ||||||
|  | 
 | ||||||
|  | .Lsecondary_start: | ||||||
|  | #ifdef CONFIG_SMP | ||||||
|  | 	li a1, CONFIG_NR_CPUS | ||||||
|  | 	bgeu a0, a1, .Lsecondary_park | ||||||
|  | 
 | ||||||
|  | 	/* Set trap vector to spin forever to help debug */ | ||||||
|  | 	la a3, .Lsecondary_park | ||||||
|  | 	csrw stvec, a3 | ||||||
|  | 
 | ||||||
|  | 	slli a3, a0, LGREG | ||||||
|  | 	la a1, __cpu_up_stack_pointer | ||||||
|  | 	la a2, __cpu_up_task_pointer | ||||||
|  | 	add a1, a3, a1 | ||||||
|  | 	add a2, a3, a2 | ||||||
|  | 
 | ||||||
|  | 	/* | ||||||
|  | 	 * This hart didn't win the lottery, so we wait for the winning hart to | ||||||
|  | 	 * get far enough along the boot process that it should continue. | ||||||
|  | 	 */ | ||||||
|  | .Lwait_for_cpu_up: | ||||||
|  | 	/* FIXME: We should WFI to save some energy here. */ | ||||||
|  | 	REG_L sp, (a1) | ||||||
|  | 	REG_L tp, (a2) | ||||||
|  | 	beqz sp, .Lwait_for_cpu_up | ||||||
|  | 	beqz tp, .Lwait_for_cpu_up | ||||||
|  | 	fence | ||||||
|  | 
 | ||||||
|  | 	/* Enable virtual memory and relocate to virtual address */ | ||||||
|  | 	call relocate | ||||||
|  | 
 | ||||||
|  | 	tail smp_callin | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | .Lsecondary_park: | ||||||
|  | 	/* We lack SMP support or have too many harts, so park this hart */ | ||||||
|  | 	wfi | ||||||
|  | 	j .Lsecondary_park | ||||||
|  | END(_start) | ||||||
|  | 
 | ||||||
|  | __PAGE_ALIGNED_BSS | ||||||
|  | 	/* Empty zero page */ | ||||||
|  | 	.balign PAGE_SIZE
 | ||||||
|  | ENTRY(empty_zero_page) | ||||||
|  | 	.fill (empty_zero_page + PAGE_SIZE) - ., 1, 0x00 | ||||||
|  | END(empty_zero_page) | ||||||
							
								
								
									
										39
									
								
								arch/riscv/kernel/irq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								arch/riscv/kernel/irq.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * Copyright (C) 2017 SiFive | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/interrupt.h> | ||||||
|  | #include <linux/irqchip.h> | ||||||
|  | #include <linux/irqdomain.h> | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_RISCV_INTC | ||||||
|  | #include <linux/irqchip/irq-riscv-intc.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void __init init_IRQ(void) | ||||||
|  | { | ||||||
|  | 	irqchip_init(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | asmlinkage void __irq_entry do_IRQ(unsigned int cause, struct pt_regs *regs) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_RISCV_INTC | ||||||
|  | 	/*
 | ||||||
|  | 	 * FIXME: We don't want a direct call to riscv_intc_irq here.  The plan | ||||||
|  | 	 * is to put an IRQ domain here and let the interrupt controller | ||||||
|  | 	 * register with that, but I poked around the arm64 code a bit and | ||||||
|  | 	 * there might be a better way to do it (ie, something fully generic). | ||||||
|  | 	 */ | ||||||
|  | 	riscv_intc_irq(cause, regs); | ||||||
|  | #endif | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								arch/riscv/kernel/reset.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								arch/riscv/kernel/reset.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/reboot.h> | ||||||
|  | #include <linux/export.h> | ||||||
|  | #include <asm/sbi.h> | ||||||
|  | 
 | ||||||
|  | void (*pm_power_off)(void) = machine_power_off; | ||||||
|  | EXPORT_SYMBOL(pm_power_off); | ||||||
|  | 
 | ||||||
|  | void machine_restart(char *cmd) | ||||||
|  | { | ||||||
|  | 	do_kernel_restart(cmd); | ||||||
|  | 	while (1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void machine_halt(void) | ||||||
|  | { | ||||||
|  | 	machine_power_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void machine_power_off(void) | ||||||
|  | { | ||||||
|  | 	sbi_shutdown(); | ||||||
|  | 	while (1); | ||||||
|  | } | ||||||
							
								
								
									
										257
									
								
								arch/riscv/kernel/setup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								arch/riscv/kernel/setup.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,257 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2009 Sunplus Core Technology Co., Ltd. | ||||||
|  |  *  Chen Liqin <liqin.chen@sunplusct.com> | ||||||
|  |  *  Lennox Wu <lennox.wu@sunplusct.com> | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * 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 the file COPYING, or write | ||||||
|  |  * to the Free Software Foundation, Inc., | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/mm.h> | ||||||
|  | #include <linux/memblock.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/initrd.h> | ||||||
|  | #include <linux/console.h> | ||||||
|  | #include <linux/screen_info.h> | ||||||
|  | #include <linux/of_fdt.h> | ||||||
|  | #include <linux/of_platform.h> | ||||||
|  | #include <linux/sched/task.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/setup.h> | ||||||
|  | #include <asm/sections.h> | ||||||
|  | #include <asm/pgtable.h> | ||||||
|  | #include <asm/smp.h> | ||||||
|  | #include <asm/sbi.h> | ||||||
|  | #include <asm/tlbflush.h> | ||||||
|  | #include <asm/thread_info.h> | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_HVC_RISCV_SBI | ||||||
|  | #include <asm/hvc_riscv_sbi.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_DUMMY_CONSOLE | ||||||
|  | struct screen_info screen_info = { | ||||||
|  | 	.orig_video_lines	= 30, | ||||||
|  | 	.orig_video_cols	= 80, | ||||||
|  | 	.orig_video_mode	= 0, | ||||||
|  | 	.orig_video_ega_bx	= 0, | ||||||
|  | 	.orig_video_isVGA	= 1, | ||||||
|  | 	.orig_video_points	= 8 | ||||||
|  | }; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_CMDLINE_BOOL | ||||||
|  | static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; | ||||||
|  | #endif /* CONFIG_CMDLINE_BOOL */ | ||||||
|  | 
 | ||||||
|  | unsigned long va_pa_offset; | ||||||
|  | unsigned long pfn_base; | ||||||
|  | 
 | ||||||
|  | /* The lucky hart to first increment this variable will boot the other cores */ | ||||||
|  | atomic_t hart_lottery; | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_BLK_DEV_INITRD | ||||||
|  | static void __init setup_initrd(void) | ||||||
|  | { | ||||||
|  | 	extern char __initramfs_start[]; | ||||||
|  | 	extern unsigned long __initramfs_size; | ||||||
|  | 	unsigned long size; | ||||||
|  | 
 | ||||||
|  | 	if (__initramfs_size > 0) { | ||||||
|  | 		initrd_start = (unsigned long)(&__initramfs_start); | ||||||
|  | 		initrd_end = initrd_start + __initramfs_size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (initrd_start >= initrd_end) { | ||||||
|  | 		printk(KERN_INFO "initrd not found or empty"); | ||||||
|  | 		goto disable; | ||||||
|  | 	} | ||||||
|  | 	if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) { | ||||||
|  | 		printk(KERN_ERR "initrd extends beyond end of memory"); | ||||||
|  | 		goto disable; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	size =  initrd_end - initrd_start; | ||||||
|  | 	memblock_reserve(__pa(initrd_start), size); | ||||||
|  | 	initrd_below_start_ok = 1; | ||||||
|  | 
 | ||||||
|  | 	printk(KERN_INFO "Initial ramdisk at: 0x%p (%lu bytes)\n", | ||||||
|  | 		(void *)(initrd_start), size); | ||||||
|  | 	return; | ||||||
|  | disable: | ||||||
|  | 	pr_cont(" - disabling initrd\n"); | ||||||
|  | 	initrd_start = 0; | ||||||
|  | 	initrd_end = 0; | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_BLK_DEV_INITRD */ | ||||||
|  | 
 | ||||||
|  | pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned_bss; | ||||||
|  | pgd_t trampoline_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); | ||||||
|  | 
 | ||||||
|  | #ifndef __PAGETABLE_PMD_FOLDED | ||||||
|  | #define NUM_SWAPPER_PMDS ((uintptr_t)-PAGE_OFFSET >> PGDIR_SHIFT) | ||||||
|  | pmd_t swapper_pmd[PTRS_PER_PMD*((-PAGE_OFFSET)/PGDIR_SIZE)] __page_aligned_bss; | ||||||
|  | pmd_t trampoline_pmd[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | asmlinkage void __init setup_vm(void) | ||||||
|  | { | ||||||
|  | 	extern char _start; | ||||||
|  | 	uintptr_t i; | ||||||
|  | 	uintptr_t pa = (uintptr_t) &_start; | ||||||
|  | 	pgprot_t prot = __pgprot(pgprot_val(PAGE_KERNEL) | _PAGE_EXEC); | ||||||
|  | 
 | ||||||
|  | 	va_pa_offset = PAGE_OFFSET - pa; | ||||||
|  | 	pfn_base = PFN_DOWN(pa); | ||||||
|  | 
 | ||||||
|  | 	/* Sanity check alignment and size */ | ||||||
|  | 	BUG_ON((PAGE_OFFSET % PGDIR_SIZE) != 0); | ||||||
|  | 	BUG_ON((pa % (PAGE_SIZE * PTRS_PER_PTE)) != 0); | ||||||
|  | 
 | ||||||
|  | #ifndef __PAGETABLE_PMD_FOLDED | ||||||
|  | 	trampoline_pg_dir[(PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD] = | ||||||
|  | 		pfn_pgd(PFN_DOWN((uintptr_t)trampoline_pmd), | ||||||
|  | 			__pgprot(_PAGE_TABLE)); | ||||||
|  | 	trampoline_pmd[0] = pfn_pmd(PFN_DOWN(pa), prot); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < (-PAGE_OFFSET)/PGDIR_SIZE; ++i) { | ||||||
|  | 		size_t o = (PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD + i; | ||||||
|  | 		swapper_pg_dir[o] = | ||||||
|  | 			pfn_pgd(PFN_DOWN((uintptr_t)swapper_pmd) + i, | ||||||
|  | 				__pgprot(_PAGE_TABLE)); | ||||||
|  | 	} | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(swapper_pmd); i++) | ||||||
|  | 		swapper_pmd[i] = pfn_pmd(PFN_DOWN(pa + i * PMD_SIZE), prot); | ||||||
|  | #else | ||||||
|  | 	trampoline_pg_dir[(PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD] = | ||||||
|  | 		pfn_pgd(PFN_DOWN(pa), prot); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < (-PAGE_OFFSET)/PGDIR_SIZE; ++i) { | ||||||
|  | 		size_t o = (PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD + i; | ||||||
|  | 		swapper_pg_dir[o] = | ||||||
|  | 			pfn_pgd(PFN_DOWN(pa + i * PGDIR_SIZE), prot); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init sbi_save(unsigned int hartid, void *dtb) | ||||||
|  | { | ||||||
|  | 	early_init_dt_scan(__va(dtb)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Allow the user to manually add a memory region (in case DTS is broken); | ||||||
|  |  * "mem_end=nn[KkMmGg]" | ||||||
|  |  */ | ||||||
|  | static int __init mem_end_override(char *p) | ||||||
|  | { | ||||||
|  | 	resource_size_t base, end; | ||||||
|  | 
 | ||||||
|  | 	if (!p) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	base = (uintptr_t) __pa(PAGE_OFFSET); | ||||||
|  | 	end = memparse(p, &p) & PMD_MASK; | ||||||
|  | 	if (end == 0) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	memblock_add(base, end - base); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | early_param("mem_end", mem_end_override); | ||||||
|  | 
 | ||||||
|  | static void __init setup_bootmem(void) | ||||||
|  | { | ||||||
|  | 	struct memblock_region *reg; | ||||||
|  | 	phys_addr_t mem_size = 0; | ||||||
|  | 
 | ||||||
|  | 	/* Find the memory region containing the kernel */ | ||||||
|  | 	for_each_memblock(memory, reg) { | ||||||
|  | 		phys_addr_t vmlinux_end = __pa(_end); | ||||||
|  | 		phys_addr_t end = reg->base + reg->size; | ||||||
|  | 
 | ||||||
|  | 		if (reg->base <= vmlinux_end && vmlinux_end <= end) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * Reserve from the start of the region to the end of | ||||||
|  | 			 * the kernel | ||||||
|  | 			 */ | ||||||
|  | 			memblock_reserve(reg->base, vmlinux_end - reg->base); | ||||||
|  | 			mem_size = min(reg->size, (phys_addr_t)-PAGE_OFFSET); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	BUG_ON(mem_size == 0); | ||||||
|  | 
 | ||||||
|  | 	set_max_mapnr(PFN_DOWN(mem_size)); | ||||||
|  | 	max_low_pfn = pfn_base + PFN_DOWN(mem_size); | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_BLK_DEV_INITRD | ||||||
|  | 	setup_initrd(); | ||||||
|  | #endif /* CONFIG_BLK_DEV_INITRD */ | ||||||
|  | 
 | ||||||
|  | 	early_init_fdt_reserve_self(); | ||||||
|  | 	early_init_fdt_scan_reserved_mem(); | ||||||
|  | 	memblock_allow_resize(); | ||||||
|  | 	memblock_dump_all(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init setup_arch(char **cmdline_p) | ||||||
|  | { | ||||||
|  | #if defined(CONFIG_HVC_RISCV_SBI) | ||||||
|  | 	if (likely(early_console == NULL)) { | ||||||
|  | 		early_console = &riscv_sbi_early_console_dev; | ||||||
|  | 		register_console(early_console); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_CMDLINE_BOOL | ||||||
|  | #ifdef CONFIG_CMDLINE_OVERRIDE | ||||||
|  | 	strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); | ||||||
|  | #else | ||||||
|  | 	if (builtin_cmdline[0] != '\0') { | ||||||
|  | 		/* Append bootloader command line to built-in */ | ||||||
|  | 		strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE); | ||||||
|  | 		strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE); | ||||||
|  | 		strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); | ||||||
|  | 	} | ||||||
|  | #endif /* CONFIG_CMDLINE_OVERRIDE */ | ||||||
|  | #endif /* CONFIG_CMDLINE_BOOL */ | ||||||
|  | 	*cmdline_p = boot_command_line; | ||||||
|  | 
 | ||||||
|  | 	parse_early_param(); | ||||||
|  | 
 | ||||||
|  | 	init_mm.start_code = (unsigned long) _stext; | ||||||
|  | 	init_mm.end_code   = (unsigned long) _etext; | ||||||
|  | 	init_mm.end_data   = (unsigned long) _edata; | ||||||
|  | 	init_mm.brk        = (unsigned long) _end; | ||||||
|  | 
 | ||||||
|  | 	setup_bootmem(); | ||||||
|  | 	paging_init(); | ||||||
|  | 	unflatten_device_tree(); | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_SMP | ||||||
|  | 	setup_smp(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_DUMMY_CONSOLE | ||||||
|  | 	conswitchp = &dummy_con; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	riscv_fill_hwcap(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int __init riscv_device_init(void) | ||||||
|  | { | ||||||
|  | 	return of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | ||||||
|  | } | ||||||
|  | subsys_initcall_sync(riscv_device_init); | ||||||
							
								
								
									
										110
									
								
								arch/riscv/kernel/smp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								arch/riscv/kernel/smp.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | /*
 | ||||||
|  |  * SMP initialisation and IPI support | ||||||
|  |  * Based on arch/arm64/kernel/smp.c | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2012 ARM Ltd. | ||||||
|  |  * Copyright (C) 2015 Regents of the University of California | ||||||
|  |  * Copyright (C) 2017 SiFive | ||||||
|  |  * | ||||||
|  |  * 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/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/interrupt.h> | ||||||
|  | #include <linux/smp.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/sbi.h> | ||||||
|  | #include <asm/tlbflush.h> | ||||||
|  | #include <asm/cacheflush.h> | ||||||
|  | 
 | ||||||
|  | /* A collection of single bit ipi messages.  */ | ||||||
|  | static struct { | ||||||
|  | 	unsigned long bits ____cacheline_aligned; | ||||||
|  | } ipi_data[NR_CPUS] __cacheline_aligned; | ||||||
|  | 
 | ||||||
|  | enum ipi_message_type { | ||||||
|  | 	IPI_RESCHEDULE, | ||||||
|  | 	IPI_CALL_FUNC, | ||||||
|  | 	IPI_MAX | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | irqreturn_t handle_ipi(void) | ||||||
|  | { | ||||||
|  | 	unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits; | ||||||
|  | 
 | ||||||
|  | 	/* Clear pending IPI */ | ||||||
|  | 	csr_clear(sip, SIE_SSIE); | ||||||
|  | 
 | ||||||
|  | 	while (true) { | ||||||
|  | 		unsigned long ops; | ||||||
|  | 
 | ||||||
|  | 		/* Order bit clearing and data access. */ | ||||||
|  | 		mb(); | ||||||
|  | 
 | ||||||
|  | 		ops = xchg(pending_ipis, 0); | ||||||
|  | 		if (ops == 0) | ||||||
|  | 			return IRQ_HANDLED; | ||||||
|  | 
 | ||||||
|  | 		if (ops & (1 << IPI_RESCHEDULE)) | ||||||
|  | 			scheduler_ipi(); | ||||||
|  | 
 | ||||||
|  | 		if (ops & (1 << IPI_CALL_FUNC)) | ||||||
|  | 			generic_smp_call_function_interrupt(); | ||||||
|  | 
 | ||||||
|  | 		BUG_ON((ops >> IPI_MAX) != 0); | ||||||
|  | 
 | ||||||
|  | 		/* Order data access and bit testing. */ | ||||||
|  | 		mb(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return IRQ_HANDLED; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | send_ipi_message(const struct cpumask *to_whom, enum ipi_message_type operation) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	mb(); | ||||||
|  | 	for_each_cpu(i, to_whom) | ||||||
|  | 		set_bit(operation, &ipi_data[i].bits); | ||||||
|  | 
 | ||||||
|  | 	mb(); | ||||||
|  | 	sbi_send_ipi(cpumask_bits(to_whom)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void arch_send_call_function_ipi_mask(struct cpumask *mask) | ||||||
|  | { | ||||||
|  | 	send_ipi_message(mask, IPI_CALL_FUNC); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void arch_send_call_function_single_ipi(int cpu) | ||||||
|  | { | ||||||
|  | 	send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ipi_stop(void *unused) | ||||||
|  | { | ||||||
|  | 	while (1) | ||||||
|  | 		wait_for_interrupt(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void smp_send_stop(void) | ||||||
|  | { | ||||||
|  | 	on_each_cpu(ipi_stop, NULL, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void smp_send_reschedule(int cpu) | ||||||
|  | { | ||||||
|  | 	send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); | ||||||
|  | } | ||||||
							
								
								
									
										114
									
								
								arch/riscv/kernel/smpboot.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								arch/riscv/kernel/smpboot.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | |||||||
|  | /*
 | ||||||
|  |  * SMP initialisation and IPI support | ||||||
|  |  * Based on arch/arm64/kernel/smp.c | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2012 ARM Ltd. | ||||||
|  |  * Copyright (C) 2015 Regents of the University of California | ||||||
|  |  * Copyright (C) 2017 SiFive | ||||||
|  |  * | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/mm.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/kernel_stat.h> | ||||||
|  | #include <linux/notifier.h> | ||||||
|  | #include <linux/cpu.h> | ||||||
|  | #include <linux/percpu.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/err.h> | ||||||
|  | #include <linux/irq.h> | ||||||
|  | #include <linux/of.h> | ||||||
|  | #include <linux/sched/task_stack.h> | ||||||
|  | #include <asm/irq.h> | ||||||
|  | #include <asm/mmu_context.h> | ||||||
|  | #include <asm/tlbflush.h> | ||||||
|  | #include <asm/sections.h> | ||||||
|  | #include <asm/sbi.h> | ||||||
|  | 
 | ||||||
|  | void *__cpu_up_stack_pointer[NR_CPUS]; | ||||||
|  | void *__cpu_up_task_pointer[NR_CPUS]; | ||||||
|  | 
 | ||||||
|  | void __init smp_prepare_boot_cpu(void) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init smp_prepare_cpus(unsigned int max_cpus) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init setup_smp(void) | ||||||
|  | { | ||||||
|  | 	struct device_node *dn = NULL; | ||||||
|  | 	int hart, im_okay_therefore_i_am = 0; | ||||||
|  | 
 | ||||||
|  | 	while ((dn = of_find_node_by_type(dn, "cpu"))) { | ||||||
|  | 		hart = riscv_of_processor_hart(dn); | ||||||
|  | 		if (hart >= 0) { | ||||||
|  | 			set_cpu_possible(hart, true); | ||||||
|  | 			set_cpu_present(hart, true); | ||||||
|  | 			if (hart == smp_processor_id()) { | ||||||
|  | 				BUG_ON(im_okay_therefore_i_am); | ||||||
|  | 				im_okay_therefore_i_am = 1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BUG_ON(!im_okay_therefore_i_am); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int __cpu_up(unsigned int cpu, struct task_struct *tidle) | ||||||
|  | { | ||||||
|  | 	tidle->thread_info.cpu = cpu; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * On RISC-V systems, all harts boot on their own accord.  Our _start | ||||||
|  | 	 * selects the first hart to boot the kernel and causes the remainder | ||||||
|  | 	 * of the harts to spin in a loop waiting for their stack pointer to be | ||||||
|  | 	 * setup by that main hart.  Writing __cpu_up_stack_pointer signals to | ||||||
|  | 	 * the spinning harts that they can continue the boot process. | ||||||
|  | 	 */ | ||||||
|  | 	smp_mb(); | ||||||
|  | 	__cpu_up_stack_pointer[cpu] = task_stack_page(tidle) + THREAD_SIZE; | ||||||
|  | 	__cpu_up_task_pointer[cpu] = tidle; | ||||||
|  | 
 | ||||||
|  | 	while (!cpu_online(cpu)) | ||||||
|  | 		cpu_relax(); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init smp_cpus_done(unsigned int max_cpus) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * C entry point for a secondary processor. | ||||||
|  |  */ | ||||||
|  | asmlinkage void __init smp_callin(void) | ||||||
|  | { | ||||||
|  | 	struct mm_struct *mm = &init_mm; | ||||||
|  | 
 | ||||||
|  | 	/* All kernel threads share the same mm context.  */ | ||||||
|  | 	atomic_inc(&mm->mm_count); | ||||||
|  | 	current->active_mm = mm; | ||||||
|  | 
 | ||||||
|  | 	trap_init(); | ||||||
|  | 	init_clockevent(); | ||||||
|  | 	notify_cpu_starting(smp_processor_id()); | ||||||
|  | 	set_cpu_online(smp_processor_id(), 1); | ||||||
|  | 	local_flush_tlb_all(); | ||||||
|  | 	local_irq_enable(); | ||||||
|  | 	preempt_disable(); | ||||||
|  | 	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								arch/riscv/kernel/time.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								arch/riscv/kernel/time.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * Copyright (C) 2017 SiFive | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/clocksource.h> | ||||||
|  | #include <linux/clockchips.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_RISCV_TIMER | ||||||
|  | #include <linux/timer_riscv.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <asm/sbi.h> | ||||||
|  | 
 | ||||||
|  | unsigned long riscv_timebase; | ||||||
|  | 
 | ||||||
|  | DECLARE_PER_CPU(struct clock_event_device, riscv_clock_event); | ||||||
|  | 
 | ||||||
|  | void riscv_timer_interrupt(void) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_RISCV_TIMER | ||||||
|  | 	/*
 | ||||||
|  | 	 * FIXME: This needs to be cleaned up along with the rest of the IRQ | ||||||
|  | 	 * handling cleanup.  See irq.c for more details. | ||||||
|  | 	 */ | ||||||
|  | 	struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event); | ||||||
|  | 
 | ||||||
|  | 	evdev->event_handler(evdev); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init init_clockevent(void) | ||||||
|  | { | ||||||
|  | 	timer_probe(); | ||||||
|  | 	csr_set(sie, SIE_STIE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init time_init(void) | ||||||
|  | { | ||||||
|  | 	struct device_node *cpu; | ||||||
|  | 	u32 prop; | ||||||
|  | 
 | ||||||
|  | 	cpu = of_find_node_by_path("/cpus"); | ||||||
|  | 	if (!cpu || of_property_read_u32(cpu, "timebase-frequency", &prop)) | ||||||
|  | 		panic(KERN_WARNING "RISC-V system with no 'timebase-frequency' in DTS\n"); | ||||||
|  | 	riscv_timebase = prop; | ||||||
|  | 
 | ||||||
|  | 	lpj_fine = riscv_timebase / HZ; | ||||||
|  | 
 | ||||||
|  | 	init_clockevent(); | ||||||
|  | } | ||||||
							
								
								
									
										180
									
								
								arch/riscv/kernel/traps.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								arch/riscv/kernel/traps.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,180 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/sched/debug.h> | ||||||
|  | #include <linux/sched/signal.h> | ||||||
|  | #include <linux/signal.h> | ||||||
|  | #include <linux/kdebug.h> | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | #include <linux/mm.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/irq.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/processor.h> | ||||||
|  | #include <asm/ptrace.h> | ||||||
|  | #include <asm/csr.h> | ||||||
|  | 
 | ||||||
|  | int show_unhandled_signals = 1; | ||||||
|  | 
 | ||||||
|  | extern asmlinkage void handle_exception(void); | ||||||
|  | 
 | ||||||
|  | static DEFINE_SPINLOCK(die_lock); | ||||||
|  | 
 | ||||||
|  | void die(struct pt_regs *regs, const char *str) | ||||||
|  | { | ||||||
|  | 	static int die_counter; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	oops_enter(); | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irq(&die_lock); | ||||||
|  | 	console_verbose(); | ||||||
|  | 	bust_spinlocks(1); | ||||||
|  | 
 | ||||||
|  | 	pr_emerg("%s [#%d]\n", str, ++die_counter); | ||||||
|  | 	print_modules(); | ||||||
|  | 	show_regs(regs); | ||||||
|  | 
 | ||||||
|  | 	ret = notify_die(DIE_OOPS, str, regs, 0, regs->scause, SIGSEGV); | ||||||
|  | 
 | ||||||
|  | 	bust_spinlocks(0); | ||||||
|  | 	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); | ||||||
|  | 	spin_unlock_irq(&die_lock); | ||||||
|  | 	oops_exit(); | ||||||
|  | 
 | ||||||
|  | 	if (in_interrupt()) | ||||||
|  | 		panic("Fatal exception in interrupt"); | ||||||
|  | 	if (panic_on_oops) | ||||||
|  | 		panic("Fatal exception"); | ||||||
|  | 	if (ret != NOTIFY_STOP) | ||||||
|  | 		do_exit(SIGSEGV); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void do_trap_siginfo(int signo, int code, | ||||||
|  | 	unsigned long addr, struct task_struct *tsk) | ||||||
|  | { | ||||||
|  | 	siginfo_t info; | ||||||
|  | 
 | ||||||
|  | 	info.si_signo = signo; | ||||||
|  | 	info.si_errno = 0; | ||||||
|  | 	info.si_code = code; | ||||||
|  | 	info.si_addr = (void __user *)addr; | ||||||
|  | 	force_sig_info(signo, &info, tsk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void do_trap(struct pt_regs *regs, int signo, int code, | ||||||
|  | 	unsigned long addr, struct task_struct *tsk) | ||||||
|  | { | ||||||
|  | 	if (show_unhandled_signals && unhandled_signal(tsk, signo) | ||||||
|  | 	    && printk_ratelimit()) { | ||||||
|  | 		pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x" REG_FMT, | ||||||
|  | 			tsk->comm, task_pid_nr(tsk), signo, code, addr); | ||||||
|  | 		print_vma_addr(KERN_CONT " in ", GET_IP(regs)); | ||||||
|  | 		pr_cont("\n"); | ||||||
|  | 		show_regs(regs); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	do_trap_siginfo(signo, code, addr, tsk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void do_trap_error(struct pt_regs *regs, int signo, int code, | ||||||
|  | 	unsigned long addr, const char *str) | ||||||
|  | { | ||||||
|  | 	if (user_mode(regs)) { | ||||||
|  | 		do_trap(regs, signo, code, addr, current); | ||||||
|  | 	} else { | ||||||
|  | 		if (!fixup_exception(regs)) | ||||||
|  | 			die(regs, str); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define DO_ERROR_INFO(name, signo, code, str)				\ | ||||||
|  | asmlinkage void name(struct pt_regs *regs)				\ | ||||||
|  | {									\ | ||||||
|  | 	do_trap_error(regs, signo, code, regs->sepc, "Oops - " str);	\ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DO_ERROR_INFO(do_trap_unknown, | ||||||
|  | 	SIGILL, ILL_ILLTRP, "unknown exception"); | ||||||
|  | DO_ERROR_INFO(do_trap_insn_misaligned, | ||||||
|  | 	SIGBUS, BUS_ADRALN, "instruction address misaligned"); | ||||||
|  | DO_ERROR_INFO(do_trap_insn_fault, | ||||||
|  | 	SIGSEGV, SEGV_ACCERR, "instruction access fault"); | ||||||
|  | DO_ERROR_INFO(do_trap_insn_illegal, | ||||||
|  | 	SIGILL, ILL_ILLOPC, "illegal instruction"); | ||||||
|  | DO_ERROR_INFO(do_trap_load_misaligned, | ||||||
|  | 	SIGBUS, BUS_ADRALN, "load address misaligned"); | ||||||
|  | DO_ERROR_INFO(do_trap_load_fault, | ||||||
|  | 	SIGSEGV, SEGV_ACCERR, "load access fault"); | ||||||
|  | DO_ERROR_INFO(do_trap_store_misaligned, | ||||||
|  | 	SIGBUS, BUS_ADRALN, "store (or AMO) address misaligned"); | ||||||
|  | DO_ERROR_INFO(do_trap_store_fault, | ||||||
|  | 	SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault"); | ||||||
|  | DO_ERROR_INFO(do_trap_ecall_u, | ||||||
|  | 	SIGILL, ILL_ILLTRP, "environment call from U-mode"); | ||||||
|  | DO_ERROR_INFO(do_trap_ecall_s, | ||||||
|  | 	SIGILL, ILL_ILLTRP, "environment call from S-mode"); | ||||||
|  | DO_ERROR_INFO(do_trap_ecall_m, | ||||||
|  | 	SIGILL, ILL_ILLTRP, "environment call from M-mode"); | ||||||
|  | 
 | ||||||
|  | asmlinkage void do_trap_break(struct pt_regs *regs) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_GENERIC_BUG | ||||||
|  | 	if (!user_mode(regs)) { | ||||||
|  | 		enum bug_trap_type type; | ||||||
|  | 
 | ||||||
|  | 		type = report_bug(regs->sepc, regs); | ||||||
|  | 		switch (type) { | ||||||
|  | 		case BUG_TRAP_TYPE_NONE: | ||||||
|  | 			break; | ||||||
|  | 		case BUG_TRAP_TYPE_WARN: | ||||||
|  | 			regs->sepc += sizeof(bug_insn_t); | ||||||
|  | 			return; | ||||||
|  | 		case BUG_TRAP_TYPE_BUG: | ||||||
|  | 			die(regs, "Kernel BUG"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif /* CONFIG_GENERIC_BUG */ | ||||||
|  | 
 | ||||||
|  | 	do_trap_siginfo(SIGTRAP, TRAP_BRKPT, regs->sepc, current); | ||||||
|  | 	regs->sepc += 0x4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_GENERIC_BUG | ||||||
|  | int is_valid_bugaddr(unsigned long pc) | ||||||
|  | { | ||||||
|  | 	bug_insn_t insn; | ||||||
|  | 
 | ||||||
|  | 	if (pc < PAGE_OFFSET) | ||||||
|  | 		return 0; | ||||||
|  | 	if (probe_kernel_address((bug_insn_t __user *)pc, insn)) | ||||||
|  | 		return 0; | ||||||
|  | 	return (insn == __BUG_INSN); | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_GENERIC_BUG */ | ||||||
|  | 
 | ||||||
|  | void __init trap_init(void) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * Set sup0 scratch register to 0, indicating to exception vector | ||||||
|  | 	 * that we are presently executing in the kernel | ||||||
|  | 	 */ | ||||||
|  | 	csr_write(sscratch, 0); | ||||||
|  | 	/* Set the exception vector address */ | ||||||
|  | 	csr_write(stvec, &handle_exception); | ||||||
|  | 	/* Enable all interrupts */ | ||||||
|  | 	csr_write(sie, -1); | ||||||
|  | } | ||||||
							
								
								
									
										125
									
								
								arch/riscv/kernel/vdso.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								arch/riscv/kernel/vdso.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. | ||||||
|  |  *                    <benh@kernel.crashing.org> | ||||||
|  |  * Copyright (C) 2012 ARM Limited | ||||||
|  |  * Copyright (C) 2015 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  * 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/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/mm.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/binfmts.h> | ||||||
|  | #include <linux/err.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/vdso.h> | ||||||
|  | 
 | ||||||
|  | extern char vdso_start[], vdso_end[]; | ||||||
|  | 
 | ||||||
|  | static unsigned int vdso_pages; | ||||||
|  | static struct page **vdso_pagelist; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * The vDSO data page. | ||||||
|  |  */ | ||||||
|  | static union { | ||||||
|  | 	struct vdso_data	data; | ||||||
|  | 	u8			page[PAGE_SIZE]; | ||||||
|  | } vdso_data_store __page_aligned_data; | ||||||
|  | struct vdso_data *vdso_data = &vdso_data_store.data; | ||||||
|  | 
 | ||||||
|  | static int __init vdso_init(void) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; | ||||||
|  | 	vdso_pagelist = | ||||||
|  | 		kcalloc(vdso_pages + 1, sizeof(struct page *), GFP_KERNEL); | ||||||
|  | 	if (unlikely(vdso_pagelist == NULL)) { | ||||||
|  | 		pr_err("vdso: pagelist allocation failed\n"); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < vdso_pages; i++) { | ||||||
|  | 		struct page *pg; | ||||||
|  | 
 | ||||||
|  | 		pg = virt_to_page(vdso_start + (i << PAGE_SHIFT)); | ||||||
|  | 		ClearPageReserved(pg); | ||||||
|  | 		vdso_pagelist[i] = pg; | ||||||
|  | 	} | ||||||
|  | 	vdso_pagelist[i] = virt_to_page(vdso_data); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | arch_initcall(vdso_init); | ||||||
|  | 
 | ||||||
|  | int arch_setup_additional_pages(struct linux_binprm *bprm, | ||||||
|  | 	int uses_interp) | ||||||
|  | { | ||||||
|  | 	struct mm_struct *mm = current->mm; | ||||||
|  | 	unsigned long vdso_base, vdso_len; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	vdso_len = (vdso_pages + 1) << PAGE_SHIFT; | ||||||
|  | 
 | ||||||
|  | 	down_write(&mm->mmap_sem); | ||||||
|  | 	vdso_base = get_unmapped_area(NULL, 0, vdso_len, 0, 0); | ||||||
|  | 	if (unlikely(IS_ERR_VALUE(vdso_base))) { | ||||||
|  | 		ret = vdso_base; | ||||||
|  | 		goto end; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Put vDSO base into mm struct. We need to do this before calling | ||||||
|  | 	 * install_special_mapping or the perf counter mmap tracking code | ||||||
|  | 	 * will fail to recognise it as a vDSO (since arch_vma_name fails). | ||||||
|  | 	 */ | ||||||
|  | 	mm->context.vdso = (void *)vdso_base; | ||||||
|  | 
 | ||||||
|  | 	ret = install_special_mapping(mm, vdso_base, vdso_len, | ||||||
|  | 		(VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC), | ||||||
|  | 		vdso_pagelist); | ||||||
|  | 
 | ||||||
|  | 	if (unlikely(ret)) | ||||||
|  | 		mm->context.vdso = NULL; | ||||||
|  | 
 | ||||||
|  | end: | ||||||
|  | 	up_write(&mm->mmap_sem); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char *arch_vma_name(struct vm_area_struct *vma) | ||||||
|  | { | ||||||
|  | 	if (vma->vm_mm && (vma->vm_start == (long)vma->vm_mm->context.vdso)) | ||||||
|  | 		return "[vdso]"; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Function stubs to prevent linker errors when AT_SYSINFO_EHDR is defined | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | int in_gate_area_no_mm(unsigned long addr) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int in_gate_area(struct mm_struct *mm, unsigned long addr) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct vm_area_struct *get_gate_vma(struct mm_struct *mm) | ||||||
|  | { | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								arch/riscv/mm/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								arch/riscv/mm/init.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 Regents of the University of California | ||||||
|  |  * | ||||||
|  |  *   This program is free software; you can redistribute it and/or | ||||||
|  |  *   modify it under the terms of the GNU General Public License | ||||||
|  |  *   as published by the Free Software Foundation, version 2. | ||||||
|  |  * | ||||||
|  |  *   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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/mm.h> | ||||||
|  | #include <linux/bootmem.h> | ||||||
|  | #include <linux/initrd.h> | ||||||
|  | #include <linux/memblock.h> | ||||||
|  | #include <linux/swap.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/tlbflush.h> | ||||||
|  | #include <asm/sections.h> | ||||||
|  | #include <asm/pgtable.h> | ||||||
|  | #include <asm/io.h> | ||||||
|  | 
 | ||||||
|  | static void __init zone_sizes_init(void) | ||||||
|  | { | ||||||
|  | 	unsigned long zones_size[MAX_NR_ZONES]; | ||||||
|  | 
 | ||||||
|  | 	memset(zones_size, 0, sizeof(zones_size)); | ||||||
|  | 	zones_size[ZONE_NORMAL] = max_mapnr; | ||||||
|  | 	free_area_init_node(0, zones_size, pfn_base, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void setup_zero_page(void) | ||||||
|  | { | ||||||
|  | 	memset((void *)empty_zero_page, 0, PAGE_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init paging_init(void) | ||||||
|  | { | ||||||
|  | 	init_mm.pgd = (pgd_t *)pfn_to_virt(csr_read(sptbr)); | ||||||
|  | 
 | ||||||
|  | 	setup_zero_page(); | ||||||
|  | 	local_flush_tlb_all(); | ||||||
|  | 	zone_sizes_init(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init mem_init(void) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_FLATMEM | ||||||
|  | 	BUG_ON(!mem_map); | ||||||
|  | #endif /* CONFIG_FLATMEM */ | ||||||
|  | 
 | ||||||
|  | 	high_memory = (void *)(__va(PFN_PHYS(max_low_pfn))); | ||||||
|  | 	free_all_bootmem(); | ||||||
|  | 
 | ||||||
|  | 	mem_init_print_info(NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void free_initmem(void) | ||||||
|  | { | ||||||
|  | 	free_initmem_default(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_BLK_DEV_INITRD | ||||||
|  | void free_initrd_mem(unsigned long start, unsigned long end) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_BLK_DEV_INITRD */ | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Palmer Dabbelt
						Palmer Dabbelt