mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00: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