Skip to content

Commit

Permalink
RISC-V: Init and Halt Code
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
palmer-dabbelt committed Sep 26, 2017
1 parent 8caea50 commit 76d2a04
Show file tree
Hide file tree
Showing 15 changed files with 1,524 additions and 0 deletions.
88 changes: 88 additions & 0 deletions arch/riscv/include/asm/bug.h
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions arch/riscv/include/asm/cache.h
Original file line number Diff line number Diff line change
@@ -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 changes: 52 additions & 0 deletions arch/riscv/include/asm/smp.h
Original file line number Diff line number Diff line change
@@ -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 changes: 105 additions & 0 deletions arch/riscv/kernel/cacheinfo.c
Original file line number Diff line number Diff line change
@@ -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 changes: 108 additions & 0 deletions arch/riscv/kernel/cpu.c
Original file line number Diff line number Diff line change
@@ -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 */
Loading

0 comments on commit 76d2a04

Please sign in to comment.