Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
vmlinux.lds
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#
always-$(KBUILD_BUILTIN) := vmlinux.lds
obj-y := head.o setup.o or32_ksyms.o process.o dma.o \
traps.o time.o irq.o entry.o ptrace.o signal.o \
sys_call_table.o unwinder.o cacheinfo.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_SMP) += smp.o sync-timer.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_OF) += prom.o
obj-y += patching.o
clean:
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
syscall_abis_32 += or1k time32 stat64 rlimit renameat
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC asm-offsets.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* This program is used to generate definitions needed by
* assembly language modules.
*
* We use the technique used in the OSF Mach kernel code:
* generate asm statements containing #defines,
* compile this file to assembler, and then extract the
* #defines from the assembly-language output.
*/
#define COMPILE_OFFSETS
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/thread_info.h>
#include <linux/kbuild.h>
#include <asm/page.h>
#include <asm/processor.h>
int main(void)
{
/* offsets into the task_struct */
DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace));
DEFINE(TASK_THREAD, offsetof(struct task_struct, thread));
DEFINE(TASK_MM, offsetof(struct task_struct, mm));
DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
/* offsets into thread_info */
DEFINE(TI_TASK, offsetof(struct thread_info, task));
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_KSP, offsetof(struct thread_info, ksp));
DEFINE(PT_SIZE, sizeof(struct pt_regs));
/* Interrupt register frame */
DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD);
DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs));
DEFINE(NUM_USER_SEGMENTS, TASK_SIZE >> 28);
return 0;
}
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC cacheinfo support
*
* Based on work done for MIPS and LoongArch. All original copyrights
* apply as per the original source declaration.
*
* OpenRISC implementation:
* Copyright (C) 2025 Sahil Siddiq <sahilcdq@proton.me>
*/
#include <linux/cacheinfo.h>
#include <asm/cpuinfo.h>
#include <asm/spr.h>
#include <asm/spr_defs.h>
static inline void ci_leaf_init(struct cacheinfo *this_leaf, enum cache_type type,
unsigned int level, struct cache_desc *cache, int cpu)
{
this_leaf->type = type;
this_leaf->level = level;
this_leaf->coherency_line_size = cache->block_size;
this_leaf->number_of_sets = cache->sets;
this_leaf->ways_of_associativity = cache->ways;
this_leaf->size = cache->size;
cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
}
int init_cache_level(unsigned int cpu)
{
struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[smp_processor_id()];
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
int leaves = 0, levels = 0;
unsigned long upr = mfspr(SPR_UPR);
unsigned long iccfgr, dccfgr;
if (!(upr & SPR_UPR_UP)) {
printk(KERN_INFO
"-- no UPR register... unable to detect configuration\n");
return -ENOENT;
}
if (cpu_cache_is_present(SPR_UPR_DCP)) {
dccfgr = mfspr(SPR_DCCFGR);
cpuinfo->dcache.ways = 1 << (dccfgr & SPR_DCCFGR_NCW);
cpuinfo->dcache.sets = 1 << ((dccfgr & SPR_DCCFGR_NCS) >> 3);
cpuinfo->dcache.block_size = 16 << ((dccfgr & SPR_DCCFGR_CBS) >> 7);
cpuinfo->dcache.size =
cpuinfo->dcache.sets * cpuinfo->dcache.ways * cpuinfo->dcache.block_size;
leaves += 1;
printk(KERN_INFO
"-- dcache: %d bytes total, %d bytes/line, %d set(s), %d way(s)\n",
cpuinfo->dcache.size, cpuinfo->dcache.block_size,
cpuinfo->dcache.sets, cpuinfo->dcache.ways);
} else
printk(KERN_INFO "-- dcache disabled\n");
if (cpu_cache_is_present(SPR_UPR_ICP)) {
iccfgr = mfspr(SPR_ICCFGR);
cpuinfo->icache.ways = 1 << (iccfgr & SPR_ICCFGR_NCW);
cpuinfo->icache.sets = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3);
cpuinfo->icache.block_size = 16 << ((iccfgr & SPR_ICCFGR_CBS) >> 7);
cpuinfo->icache.size =
cpuinfo->icache.sets * cpuinfo->icache.ways * cpuinfo->icache.block_size;
leaves += 1;
printk(KERN_INFO
"-- icache: %d bytes total, %d bytes/line, %d set(s), %d way(s)\n",
cpuinfo->icache.size, cpuinfo->icache.block_size,
cpuinfo->icache.sets, cpuinfo->icache.ways);
} else
printk(KERN_INFO "-- icache disabled\n");
if (!leaves)
return -ENOENT;
levels = 1;
this_cpu_ci->num_leaves = leaves;
this_cpu_ci->num_levels = levels;
return 0;
}
int populate_cache_leaves(unsigned int cpu)
{
struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[smp_processor_id()];
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
struct cacheinfo *this_leaf = this_cpu_ci->info_list;
int level = 1;
if (cpu_cache_is_present(SPR_UPR_DCP)) {
ci_leaf_init(this_leaf, CACHE_TYPE_DATA, level, &cpuinfo->dcache, cpu);
this_leaf->attributes = ((mfspr(SPR_DCCFGR) & SPR_DCCFGR_CWS) >> 8) ?
CACHE_WRITE_BACK : CACHE_WRITE_THROUGH;
this_leaf++;
}
if (cpu_cache_is_present(SPR_UPR_ICP))
ci_leaf_init(this_leaf, CACHE_TYPE_INST, level, &cpuinfo->icache, cpu);
this_cpu_ci->cpu_map_populated = true;
return 0;
}
+115
View File
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC Linux
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* DMA mapping callbacks...
*/
#include <linux/dma-map-ops.h>
#include <linux/pagewalk.h>
#include <asm/cpuinfo.h>
#include <asm/cacheflush.h>
#include <asm/spr_defs.h>
#include <asm/tlbflush.h>
static int
page_set_nocache(pte_t *pte, unsigned long addr,
unsigned long next, struct mm_walk *walk)
{
pte_val(*pte) |= _PAGE_CI;
/*
* Flush the page out of the TLB so that the new page flags get
* picked up next time there's an access
*/
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
/* Flush page out of dcache */
local_dcache_range_flush(__pa(addr), __pa(next));
return 0;
}
static const struct mm_walk_ops set_nocache_walk_ops = {
.pte_entry = page_set_nocache,
};
static int
page_clear_nocache(pte_t *pte, unsigned long addr,
unsigned long next, struct mm_walk *walk)
{
pte_val(*pte) &= ~_PAGE_CI;
/*
* Flush the page out of the TLB so that the new page flags get
* picked up next time there's an access
*/
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
return 0;
}
static const struct mm_walk_ops clear_nocache_walk_ops = {
.pte_entry = page_clear_nocache,
};
void *arch_dma_set_uncached(void *cpu_addr, size_t size)
{
unsigned long va = (unsigned long)cpu_addr;
int error;
/*
* We need to iterate through the pages, clearing the dcache for
* them and setting the cache-inhibit bit.
*/
mmap_write_lock(&init_mm);
error = walk_kernel_page_table_range(va, va + size,
&set_nocache_walk_ops, NULL, NULL);
mmap_write_unlock(&init_mm);
if (error)
return ERR_PTR(error);
return cpu_addr;
}
void arch_dma_clear_uncached(void *cpu_addr, size_t size)
{
unsigned long va = (unsigned long)cpu_addr;
mmap_write_lock(&init_mm);
/* walk_page_range shouldn't be able to fail here */
WARN_ON(walk_kernel_page_table_range(va, va + size,
&clear_nocache_walk_ops, NULL, NULL));
mmap_write_unlock(&init_mm);
}
void arch_sync_dma_for_device(phys_addr_t addr, size_t size,
enum dma_data_direction dir)
{
switch (dir) {
case DMA_TO_DEVICE:
/* Flush the dcache for the requested range */
local_dcache_range_flush(addr, addr + size);
break;
case DMA_FROM_DEVICE:
/* Invalidate the dcache for the requested range */
local_dcache_range_inv(addr, addr + size);
break;
default:
/*
* NOTE: If dir == DMA_BIDIRECTIONAL then there's no need to
* flush nor invalidate the cache here as the area will need
* to be manually synced anyway.
*/
break;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC irq.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/ftrace.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/export.h>
#include <linux/irqflags.h>
/* read interrupt enabled status */
unsigned long arch_local_save_flags(void)
{
return mfspr(SPR_SR) & (SPR_SR_IEE|SPR_SR_TEE);
}
EXPORT_SYMBOL(arch_local_save_flags);
/* set interrupt enabled status */
void arch_local_irq_restore(unsigned long flags)
{
mtspr(SPR_SR, ((mfspr(SPR_SR) & ~(SPR_SR_IEE|SPR_SR_TEE)) | flags));
}
EXPORT_SYMBOL(arch_local_irq_restore);
void __init init_IRQ(void)
{
irqchip_init();
}
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 Chen Miao
*
* Based on arch/arm/kernel/jump_label.c
*/
#include <linux/jump_label.h>
#include <linux/kernel.h>
#include <linux/memory.h>
#include <asm/bug.h>
#include <asm/cacheflush.h>
#include <asm/text-patching.h>
bool arch_jump_label_transform_queue(struct jump_entry *entry,
enum jump_label_type type)
{
void *addr = (void *)jump_entry_code(entry);
u32 insn;
if (type == JUMP_LABEL_JMP) {
long offset;
offset = jump_entry_target(entry) - jump_entry_code(entry);
/*
* The actual maximum range of the l.j instruction's offset is -134,217,728
* ~ 134,217,724 (sign 26-bit imm).
* For the original jump range, we need to right-shift N by 2 to obtain the
* instruction's offset.
*/
WARN_ON_ONCE(offset < -134217728 || offset > 134217724);
/* 26bit imm mask */
offset = (offset >> 2) & 0x03ffffff;
insn = offset;
} else {
insn = OPENRISC_INSN_NOP;
}
if (early_boot_irqs_disabled)
copy_to_kernel_nofault(addr, &insn, sizeof(insn));
else
patch_insn_write(addr, insn);
return true;
}
void arch_jump_label_transform_apply(void)
{
icache_all_inv();
}
+80
View File
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC module.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/moduleloader.h>
#include <linux/elf.h>
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
unsigned int i;
Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
Elf32_Sym *sym;
uint32_t *location;
uint32_t value;
pr_debug("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rel[i].r_offset;
/* This is the symbol it is referring to. Note that all
undefined symbols have been resolved. */
sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ ELF32_R_SYM(rel[i].r_info);
value = sym->st_value + rel[i].r_addend;
switch (ELF32_R_TYPE(rel[i].r_info)) {
case R_OR1K_32:
*location = value;
break;
case R_OR1K_LO_16_IN_INSN:
*((uint16_t *)location + 1) = value;
break;
case R_OR1K_HI_16_IN_INSN:
*((uint16_t *)location + 1) = value >> 16;
break;
case R_OR1K_INSN_REL_26:
value -= (uint32_t)location;
value >>= 2;
value &= 0x03ffffff;
value |= *location & 0xfc000000;
*location = value;
break;
case R_OR1K_32_PCREL:
value -= (uint32_t)location;
*location = value;
break;
case R_OR1K_AHI16:
/* Adjust the operand to match with a signed LO16. */
value += 0x8000;
*((uint16_t *)location + 1) = value >> 16;
break;
case R_OR1K_SLO16:
/* Split value lower 16-bits. */
value = ((value & 0xf800) << 10) | (value & 0x7ff);
*location = (*location & ~0x3e007ff) | value;
break;
default:
pr_err("module %s: Unknown relocation: %u\n",
me->name, ELF32_R_TYPE(rel[i].r_info));
break;
}
}
return 0;
}
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC or32_ksyms.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/export.h>
#include <linux/elfcore.h>
#include <linux/sched.h>
#include <linux/in6.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/semaphore.h>
#include <linux/pgtable.h>
#include <asm/processor.h>
#include <linux/uaccess.h>
#include <asm/checksum.h>
#include <asm/io.h>
#include <asm/hardirq.h>
#include <asm/delay.h>
#define DECLARE_EXPORT(name) extern void name(void); EXPORT_SYMBOL(name)
/* compiler generated symbols */
DECLARE_EXPORT(__udivsi3);
DECLARE_EXPORT(__divsi3);
DECLARE_EXPORT(__umodsi3);
DECLARE_EXPORT(__modsi3);
DECLARE_EXPORT(__muldi3);
DECLARE_EXPORT(__ashrdi3);
DECLARE_EXPORT(__ashldi3);
DECLARE_EXPORT(__lshrdi3);
DECLARE_EXPORT(__ucmpdi2);
EXPORT_SYMBOL(__copy_tofrom_user);
EXPORT_SYMBOL(__clear_user);
EXPORT_SYMBOL(memset);
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 SiFive
* Copyright (C) 2025 Chen Miao
*/
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <asm/insn-def.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
#include <asm/fixmap.h>
#include <asm/text-patching.h>
#include <asm/sections.h>
static DEFINE_RAW_SPINLOCK(patch_lock);
static __always_inline void *patch_map(void *addr, int fixmap)
{
uintptr_t uaddr = (uintptr_t) addr;
phys_addr_t phys;
if (core_kernel_text(uaddr)) {
phys = __pa_symbol(addr);
} else {
struct page *page = vmalloc_to_page(addr);
BUG_ON(!page);
phys = page_to_phys(page) + offset_in_page(addr);
}
return (void *)set_fixmap_offset(fixmap, phys);
}
static void patch_unmap(int fixmap)
{
clear_fixmap(fixmap);
}
static int __patch_insn_write(void *addr, u32 insn)
{
void *waddr = addr;
unsigned long flags = 0;
int ret;
raw_spin_lock_irqsave(&patch_lock, flags);
waddr = patch_map(addr, FIX_TEXT_POKE0);
ret = copy_to_kernel_nofault(waddr, &insn, OPENRISC_INSN_SIZE);
if (!IS_ENABLED(CONFIG_DCACHE_WRITETHROUGH))
local_dcache_range_flush((unsigned long)waddr,
(unsigned long)waddr + OPENRISC_INSN_SIZE);
local_icache_range_inv((unsigned long)waddr,
(unsigned long)waddr + OPENRISC_INSN_SIZE);
patch_unmap(FIX_TEXT_POKE0);
raw_spin_unlock_irqrestore(&patch_lock, flags);
return ret;
}
/*
* patch_insn_write - Write a single instruction to a specified memory location
* This API provides a single-instruction patching, primarily used for runtime
* code modification.
* By the way, the insn size must be 4 bytes.
*/
int patch_insn_write(void *addr, u32 insn)
{
u32 *tp = addr;
int ret;
if ((uintptr_t) tp & 0x3)
return -EINVAL;
ret = __patch_insn_write(tp, insn);
return ret;
}
+288
View File
@@ -0,0 +1,288 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC process.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* This file handles the architecture-dependent parts of process handling...
*/
#define __KERNEL_SYSCALLS__
#include <linux/cpu.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/elfcore.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/init_task.h>
#include <linux/mqueue.h>
#include <linux/fs.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <asm/fpu.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/spr_defs.h>
#include <asm/switch_to.h>
#include <linux/smp.h>
/*
* Pointer to Current thread info structure.
*
* Used at user space -> kernel transitions.
*/
struct thread_info *current_thread_info_set[NR_CPUS] = { &init_thread_info, };
void machine_restart(char *cmd)
{
do_kernel_restart(cmd);
__asm__("l.nop 13");
/* Give a grace period for failure to restart of 1s */
mdelay(1000);
/* Whoops - the platform was unable to reboot. Tell the user! */
pr_emerg("Reboot failed -- System halted\n");
while (1);
}
/*
* This is used if a sys-off handler was not set by a power management
* driver, in this case we can assume we are on a simulator. On
* OpenRISC simulators l.nop 1 will trigger the simulator exit.
*/
static void default_power_off(void)
{
__asm__("l.nop 1");
}
/*
* Similar to machine_power_off, but don't shut off power. Add code
* here to freeze the system for e.g. post-mortem debug purpose when
* possible. This halt has nothing to do with the idle halt.
*/
void machine_halt(void)
{
printk(KERN_INFO "*** MACHINE HALT ***\n");
__asm__("l.nop 1");
}
/* If or when software power-off is implemented, add code here. */
void machine_power_off(void)
{
printk(KERN_INFO "*** MACHINE POWER OFF ***\n");
do_kernel_power_off();
default_power_off();
}
/*
* Send the doze signal to the cpu if available.
* Make sure, that all interrupts are enabled
*/
void arch_cpu_idle(void)
{
raw_local_irq_enable();
if (mfspr(SPR_UPR) & SPR_UPR_PMP)
mtspr(SPR_PMR, mfspr(SPR_PMR) | SPR_PMR_DME);
raw_local_irq_disable();
}
void (*pm_power_off)(void) = NULL;
EXPORT_SYMBOL(pm_power_off);
/*
* When a process does an "exec", machine state like FPU and debug
* registers need to be reset. This is a hook function for that.
* Currently we don't have any such state to reset, so this is empty.
*/
void flush_thread(void)
{
}
void show_regs(struct pt_regs *regs)
{
show_regs_print_info(KERN_DEFAULT);
/* __PHX__ cleanup this mess */
show_registers(regs);
}
/*
* Copy the thread-specific (arch specific) info from the current
* process to the new one p
*/
extern asmlinkage void ret_from_fork(void);
/*
* copy_thread
* @clone_flags: flags
* @usp: user stack pointer or fn for kernel thread
* @arg: arg to fn for kernel thread; always NULL for userspace thread
* @p: the newly created task
* @tls: the Thread Local Storage pointer for the new process
*
* At the top of a newly initialized kernel stack are two stacked pt_reg
* structures. The first (topmost) is the userspace context of the thread.
* The second is the kernelspace context of the thread.
*
* A kernel thread will not be returning to userspace, so the topmost pt_regs
* struct can be uninitialized; it _does_ need to exist, though, because
* a kernel thread can become a userspace thread by doing a kernel_execve, in
* which case the topmost context will be initialized and used for 'returning'
* to userspace.
*
* The second pt_reg struct needs to be initialized to 'return' to
* ret_from_fork. A kernel thread will need to set r20 to the address of
* a function to call into (with arg in r22); userspace threads need to set
* r20 to NULL in which case ret_from_fork will just continue a return to
* userspace.
*
* A kernel thread 'fn' may return; this is effectively what happens when
* kernel_execve is called. In that case, the userspace pt_regs must have
* been initialized (which kernel_execve takes care of, see start_thread
* below); ret_from_fork will then continue its execution causing the
* 'kernel thread' to return to userspace as a userspace thread.
*/
int
copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
{
u64 clone_flags = args->flags;
unsigned long usp = args->stack;
unsigned long tls = args->tls;
struct pt_regs *userregs;
struct pt_regs *kregs;
unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
unsigned long top_of_kernel_stack;
top_of_kernel_stack = sp;
/* Locate userspace context on stack... */
sp -= STACK_FRAME_OVERHEAD; /* redzone */
sp -= sizeof(struct pt_regs);
userregs = (struct pt_regs *) sp;
/* ...and kernel context */
sp -= STACK_FRAME_OVERHEAD; /* redzone */
sp -= sizeof(struct pt_regs);
kregs = (struct pt_regs *)sp;
if (unlikely(args->fn)) {
memset(kregs, 0, sizeof(struct pt_regs));
kregs->gpr[20] = (unsigned long)args->fn;
kregs->gpr[22] = (unsigned long)args->fn_arg;
} else {
*userregs = *current_pt_regs();
if (usp)
userregs->sp = usp;
/*
* For CLONE_SETTLS set "tp" (r10) to the TLS pointer.
*/
if (clone_flags & CLONE_SETTLS)
userregs->gpr[10] = tls;
userregs->gpr[11] = 0; /* Result from fork() */
kregs->gpr[20] = 0; /* Userspace thread */
}
/*
* _switch wants the kernel stack page in pt_regs->sp so that it
* can restore it to thread_info->ksp... see _switch for details.
*/
kregs->sp = top_of_kernel_stack;
kregs->gpr[9] = (unsigned long)ret_from_fork;
task_thread_info(p)->ksp = (unsigned long)kregs;
return 0;
}
/*
* Set up a thread for executing a new program
*/
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
{
unsigned long sr = mfspr(SPR_SR) & ~SPR_SR_SM;
memset(regs, 0, sizeof(struct pt_regs));
regs->pc = pc;
regs->sr = sr;
regs->sp = sp;
}
extern struct thread_info *_switch(struct thread_info *old_ti,
struct thread_info *new_ti);
extern int lwa_flag;
struct task_struct *__switch_to(struct task_struct *old,
struct task_struct *new)
{
struct task_struct *last;
struct thread_info *new_ti, *old_ti;
unsigned long flags;
local_irq_save(flags);
save_fpu(current);
/* current_set is an array of saved current pointers
* (one for each cpu). we need them at user->kernel transition,
* while we save them at kernel->user transition
*/
new_ti = new->stack;
old_ti = old->stack;
lwa_flag = 0;
current_thread_info_set[smp_processor_id()] = new_ti;
last = (_switch(old_ti, new_ti))->task;
restore_fpu(current);
local_irq_restore(flags);
return last;
}
/*
* Write out registers in core dump format, as defined by the
* struct user_regs_struct
*/
void dump_elf_thread(elf_greg_t *dest, struct pt_regs* regs)
{
dest[0] = 0; /* r0 */
memcpy(dest+1, regs->gpr+1, 31*sizeof(unsigned long));
dest[32] = regs->pc;
dest[33] = regs->sr;
dest[34] = 0;
dest[35] = 0;
}
unsigned long __get_wchan(struct task_struct *p)
{
/* TODO */
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC prom.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* Architecture specific procedures for creating, accessing and
* interpreting the device tree.
*/
#include <linux/init.h>
#include <linux/types.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <asm/page.h>
void __init early_init_devtree(void *params)
{
early_init_dt_scan(params, __pa(params));
memblock_allow_resize();
}
+319
View File
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC ptrace.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2005 Gyorgy Jeney <nog@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/audit.h>
#include <linux/regset.h>
#include <linux/elf.h>
#include <asm/thread_info.h>
#include <asm/page.h>
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs);
asmlinkage void do_syscall_trace_leave(struct pt_regs *regs);
/*
* Copy the thread state to a regset that can be interpreted by userspace.
*
* It doesn't matter what our internal pt_regs structure looks like. The
* important thing is that we export a consistent view of the thread state
* to userspace. As such, we need to make sure that the regset remains
* ABI compatible as defined by the struct user_regs_struct:
*
* (Each item is a 32-bit word)
* r0 = 0 (exported for clarity)
* 31 GPRS r1-r31
* PC (Program counter)
* SR (Supervision register)
*/
static int genregs_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
const struct pt_regs *regs = task_pt_regs(target);
/* r0 */
membuf_zero(&to, 4);
membuf_write(&to, regs->gpr + 1, 31 * 4);
membuf_store(&to, regs->pc);
return membuf_store(&to, regs->sr);
}
/*
* Set the thread state from a regset passed in via ptrace
*/
static int genregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user * ubuf)
{
struct pt_regs *regs = task_pt_regs(target);
int ret;
/* ignore r0 */
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, 4);
/* r1 - r31 */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
regs->gpr+1, 4, 4*32);
/* PC */
if (!ret)
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&regs->pc, 4*32, 4*33);
/*
* Skip SR and padding... userspace isn't allowed to changes bits in
* the Supervision register
*/
if (!ret)
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 4*33, -1);
return ret;
}
#ifdef CONFIG_FPU
/*
* As OpenRISC shares GPRs and floating point registers we don't need to export
* the floating point registers again. So here we only export the fpcsr special
* purpose register.
*/
static int fpregs_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
return membuf_store(&to, target->thread.fpcsr);
}
static int fpregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
/* FPCSR */
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fpcsr, 0, 4);
}
#endif
/*
* Define the register sets available on OpenRISC under Linux
*/
enum or1k_regset {
REGSET_GENERAL,
#ifdef CONFIG_FPU
REGSET_FPU,
#endif
};
static const struct user_regset or1k_regsets[] = {
[REGSET_GENERAL] = {
USER_REGSET_NOTE_TYPE(PRSTATUS),
.n = ELF_NGREG,
.size = sizeof(long),
.align = sizeof(long),
.regset_get = genregs_get,
.set = genregs_set,
},
#ifdef CONFIG_FPU
[REGSET_FPU] = {
USER_REGSET_NOTE_TYPE(PRFPREG),
.n = sizeof(struct __or1k_fpu_state) / sizeof(long),
.size = sizeof(long),
.align = sizeof(long),
.regset_get = fpregs_get,
.set = fpregs_set,
},
#endif
};
static const struct user_regset_view user_or1k_native_view = {
.name = "or1k",
.e_machine = EM_OPENRISC,
.regsets = or1k_regsets,
.n = ARRAY_SIZE(or1k_regsets),
};
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
return &user_or1k_native_view;
}
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
*/
struct pt_regs_offset {
const char *name;
int offset;
};
#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
#define REG_OFFSET_END {.name = NULL, .offset = 0}
static const struct pt_regs_offset regoffset_table[] = {
REG_OFFSET_NAME(sr),
REG_OFFSET_NAME(sp),
REG_OFFSET_NAME(gpr2),
REG_OFFSET_NAME(gpr3),
REG_OFFSET_NAME(gpr4),
REG_OFFSET_NAME(gpr5),
REG_OFFSET_NAME(gpr6),
REG_OFFSET_NAME(gpr7),
REG_OFFSET_NAME(gpr8),
REG_OFFSET_NAME(gpr9),
REG_OFFSET_NAME(gpr10),
REG_OFFSET_NAME(gpr11),
REG_OFFSET_NAME(gpr12),
REG_OFFSET_NAME(gpr13),
REG_OFFSET_NAME(gpr14),
REG_OFFSET_NAME(gpr15),
REG_OFFSET_NAME(gpr16),
REG_OFFSET_NAME(gpr17),
REG_OFFSET_NAME(gpr18),
REG_OFFSET_NAME(gpr19),
REG_OFFSET_NAME(gpr20),
REG_OFFSET_NAME(gpr21),
REG_OFFSET_NAME(gpr22),
REG_OFFSET_NAME(gpr23),
REG_OFFSET_NAME(gpr24),
REG_OFFSET_NAME(gpr25),
REG_OFFSET_NAME(gpr26),
REG_OFFSET_NAME(gpr27),
REG_OFFSET_NAME(gpr28),
REG_OFFSET_NAME(gpr29),
REG_OFFSET_NAME(gpr30),
REG_OFFSET_NAME(gpr31),
REG_OFFSET_NAME(pc),
REG_OFFSET_NAME(orig_gpr11),
REG_OFFSET_END,
};
/**
* regs_query_register_offset() - query register offset from its name
* @name: the name of a register
*
* regs_query_register_offset() returns the offset of a register in struct
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
*/
int regs_query_register_offset(const char *name)
{
const struct pt_regs_offset *roff;
for (roff = regoffset_table; roff->name != NULL; roff++)
if (!strcmp(roff->name, name))
return roff->offset;
return -EINVAL;
}
/**
* regs_within_kernel_stack() - check the address in the stack
* @regs: pt_regs which contains kernel stack pointer.
* @addr: address which is checked.
*
* regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
* If @addr is within the kernel stack, it returns true. If not, returns false.
*/
static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
{
return (addr & ~(THREAD_SIZE - 1)) ==
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1));
}
/**
* regs_get_kernel_stack_nth() - get Nth entry of the stack
* @regs: pt_regs which contains kernel stack pointer.
* @n: stack entry number.
*
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
* is specified by @regs. If the @n th entry is NOT in the kernel stack,
* this returns 0.
*/
unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
{
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
addr += n;
if (regs_within_kernel_stack(regs, (unsigned long)addr))
return *addr;
else
return 0;
}
/*
* Called by kernel/ptrace.c when detaching..
*
* Make sure the single step bit is not set.
*/
void ptrace_disable(struct task_struct *child)
{
pr_debug("ptrace_disable(): TODO\n");
user_disable_single_step(child);
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
long arch_ptrace(struct task_struct *child, long request, unsigned long addr,
unsigned long data)
{
int ret;
switch (request) {
default:
ret = ptrace_request(child, request, addr, data);
break;
}
return ret;
}
/*
* Notification of system call entry/exit
* - triggered by current->work.syscall_trace
*/
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
{
long ret = 0;
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
ptrace_report_syscall_entry(regs))
/*
* Tracing decided this syscall should not happen.
* We'll return a bogus call number to get an ENOSYS
* error, but leave the original number in <something>.
*/
ret = -1L;
audit_syscall_entry(regs->gpr[11], regs->gpr[3], regs->gpr[4],
regs->gpr[5], regs->gpr[6]);
return ret ? : regs->gpr[11];
}
asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
{
int step;
audit_syscall_exit(regs);
step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
ptrace_report_syscall_exit(regs, step);
}
+340
View File
@@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC setup.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* This file handles the architecture-dependent parts of initialization
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/seq_file.h>
#include <linux/serial.h>
#include <linux/initrd.h>
#include <linux/of_fdt.h>
#include <linux/of.h>
#include <linux/device.h>
#include <asm/sections.h>
#include <asm/types.h>
#include <asm/setup.h>
#include <asm/io.h>
#include <asm/cpuinfo.h>
#include <asm/delay.h>
#include "vmlinux.h"
static void __init setup_memory(void)
{
unsigned long ram_start_pfn;
unsigned long ram_end_pfn;
phys_addr_t memory_start, memory_end;
memory_end = memory_start = 0;
/* Find main memory where is the kernel, we assume its the only one */
memory_start = memblock_start_of_DRAM();
memory_end = memblock_end_of_DRAM();
if (!memory_end) {
panic("No memory!");
}
ram_start_pfn = PFN_UP(memory_start);
ram_end_pfn = PFN_DOWN(memblock_end_of_DRAM());
/* setup bootmem globals (we use no_bootmem, but mm still depends on this) */
min_low_pfn = ram_start_pfn;
max_low_pfn = ram_end_pfn;
max_pfn = ram_end_pfn;
/*
* initialize the boot-time allocator (with low memory only).
*
* This makes the memory from the end of the kernel to the end of
* RAM usable.
*/
memblock_reserve(__pa(_stext), _end - _stext);
#ifdef CONFIG_BLK_DEV_INITRD
/* Then reserve the initrd, if any */
if (initrd_start && (initrd_end > initrd_start)) {
unsigned long aligned_start = ALIGN_DOWN(initrd_start, PAGE_SIZE);
unsigned long aligned_end = ALIGN(initrd_end, PAGE_SIZE);
memblock_reserve(__pa(aligned_start), aligned_end - aligned_start);
}
#endif /* CONFIG_BLK_DEV_INITRD */
early_init_fdt_reserve_self();
early_init_fdt_scan_reserved_mem();
memblock_dump_all();
}
struct cpuinfo_or1k cpuinfo_or1k[NR_CPUS];
static void print_cpuinfo(void)
{
unsigned long upr = mfspr(SPR_UPR);
unsigned long vr = mfspr(SPR_VR);
unsigned int version;
unsigned int revision;
struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[smp_processor_id()];
version = (vr & SPR_VR_VER) >> 24;
revision = (vr & SPR_VR_REV);
printk(KERN_INFO "CPU: OpenRISC-%x (revision %d) @%d MHz\n",
version, revision, cpuinfo->clock_frequency / 1000000);
if (!(upr & SPR_UPR_UP)) {
printk(KERN_INFO
"-- no UPR register... unable to detect configuration\n");
return;
}
if (upr & SPR_UPR_DMP)
printk(KERN_INFO "-- dmmu: %4d entries, %lu way(s)\n",
1 << ((mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTS) >> 2),
1 + (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTW));
if (upr & SPR_UPR_IMP)
printk(KERN_INFO "-- immu: %4d entries, %lu way(s)\n",
1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> 2),
1 + (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTW));
printk(KERN_INFO "-- additional features:\n");
if (upr & SPR_UPR_DUP)
printk(KERN_INFO "-- debug unit\n");
if (upr & SPR_UPR_PCUP)
printk(KERN_INFO "-- performance counters\n");
if (upr & SPR_UPR_PMP)
printk(KERN_INFO "-- power management\n");
if (upr & SPR_UPR_PICP)
printk(KERN_INFO "-- PIC\n");
if (upr & SPR_UPR_TTP)
printk(KERN_INFO "-- timer\n");
if (upr & SPR_UPR_CUP)
printk(KERN_INFO "-- custom unit(s)\n");
}
void __init setup_cpuinfo(void)
{
struct device_node *cpu;
int cpu_id = smp_processor_id();
struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[cpu_id];
cpu = of_get_cpu_node(cpu_id, NULL);
if (!cpu)
panic("Couldn't find CPU%d in device tree...\n", cpu_id);
if (of_property_read_u32(cpu, "clock-frequency",
&cpuinfo->clock_frequency)) {
printk(KERN_WARNING
"Device tree missing CPU 'clock-frequency' parameter."
"Assuming frequency 25MHZ"
"This is probably not what you want.");
}
cpuinfo->coreid = mfspr(SPR_COREID);
of_node_put(cpu);
print_cpuinfo();
}
/**
* or1k_early_setup
* @fdt: pointer to the start of the device tree in memory or NULL
*
* Handles the pointer to the device tree that this kernel is to use
* for establishing the available platform devices.
*
* Falls back on built-in device tree in case null pointer is passed.
*/
void __init or1k_early_setup(void *fdt)
{
if (fdt)
pr_info("FDT at %p\n", fdt);
else {
fdt = __dtb_start;
pr_info("Compiled-in FDT at %p\n", fdt);
}
early_init_devtree(fdt);
}
static inline unsigned long extract_value_bits(unsigned long reg,
short bit_nr, short width)
{
return (reg >> bit_nr) & (0 << width);
}
static inline unsigned long extract_value(unsigned long reg, unsigned long mask)
{
while (!(mask & 0x1)) {
reg = reg >> 1;
mask = mask >> 1;
}
return mask & reg;
}
/*
* calibrate_delay
*
* Lightweight calibrate_delay implementation that calculates loops_per_jiffy
* from the clock frequency passed in via the device tree
*
*/
void calibrate_delay(void)
{
const int *val;
struct device_node *cpu = of_get_cpu_node(smp_processor_id(), NULL);
val = of_get_property(cpu, "clock-frequency", NULL);
if (!val)
panic("no cpu 'clock-frequency' parameter in device tree");
loops_per_jiffy = *val / HZ;
pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",
loops_per_jiffy / (500000 / HZ),
(loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy);
of_node_put(cpu);
}
void __init setup_arch(char **cmdline_p)
{
/* setup memblock allocator */
setup_memory();
unflatten_and_copy_device_tree();
setup_cpuinfo();
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
/* process 1's initial memory region is the kernel code/data */
setup_initial_init_mm(_stext, _etext, _edata, _end);
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start == initrd_end) {
printk(KERN_INFO "Initial ramdisk not found\n");
initrd_start = 0;
initrd_end = 0;
} else {
printk(KERN_INFO "Initial ramdisk at: 0x%p (%lu bytes)\n",
(void *)(initrd_start), initrd_end - initrd_start);
initrd_below_start_ok = 1;
}
#endif
/* perform jump_table sorting before paging_init locks down read only memory */
jump_label_init();
/* paging_init() sets up the MMU and marks all pages as reserved */
paging_init();
*cmdline_p = boot_command_line;
printk(KERN_INFO "OpenRISC Linux -- http://openrisc.io\n");
}
static int show_cpuinfo(struct seq_file *m, void *v)
{
unsigned int vr, cpucfgr;
unsigned int avr;
unsigned int version;
#ifdef CONFIG_SMP
struct cpuinfo_or1k *cpuinfo = v;
seq_printf(m, "processor\t\t: %d\n", cpuinfo->coreid);
#endif
vr = mfspr(SPR_VR);
cpucfgr = mfspr(SPR_CPUCFGR);
if (vr & SPR_VR_UVRP) {
vr = mfspr(SPR_VR2);
version = vr & SPR_VR2_VER;
avr = mfspr(SPR_AVR);
seq_printf(m, "cpu architecture\t: "
"OpenRISC 1000 (%d.%d-rev%d)\n",
(avr >> 24) & 0xff,
(avr >> 16) & 0xff,
(avr >> 8) & 0xff);
seq_printf(m, "cpu implementation id\t: 0x%x\n",
(vr & SPR_VR2_CPUID) >> 24);
seq_printf(m, "cpu version\t\t: 0x%x\n", version);
} else {
version = (vr & SPR_VR_VER) >> 24;
seq_printf(m, "cpu\t\t\t: OpenRISC-%x\n", version);
seq_printf(m, "revision\t\t: %d\n", vr & SPR_VR_REV);
}
seq_printf(m, "frequency\t\t: %ld\n", loops_per_jiffy * HZ);
seq_printf(m, "immu\t\t\t: %d entries, %lu ways\n",
1 << ((mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTS) >> 2),
1 + (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_NTW));
seq_printf(m, "dmmu\t\t\t: %d entries, %lu ways\n",
1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> 2),
1 + (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTW));
seq_printf(m, "bogomips\t\t: %lu.%02lu\n",
(loops_per_jiffy * HZ) / 500000,
((loops_per_jiffy * HZ) / 5000) % 100);
seq_puts(m, "features\t\t: ");
seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OB32S ? "orbis32" : "");
seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OB64S ? "orbis64" : "");
seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OF32S ? "orfpx32" : "");
seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OF64S ? "orfpx64" : "");
seq_printf(m, "%s ", cpucfgr & SPR_CPUCFGR_OV64S ? "orvdx64" : "");
seq_puts(m, "\n");
seq_puts(m, "\n");
return 0;
}
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 &cpuinfo_or1k[*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)
{
}
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = show_cpuinfo,
};
+361
View File
@@ -0,0 +1,361 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC signal.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/resume_user_mode.h>
#include <asm/fpu.h>
#include <asm/processor.h>
#include <asm/syscall.h>
#include <asm/ucontext.h>
#include <linux/uaccess.h>
struct rt_sigframe {
struct siginfo info;
struct ucontext uc;
unsigned char retcode[16]; /* trampoline code */
};
asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs);
asmlinkage int do_work_pending(struct pt_regs *regs, unsigned int thread_flags,
int syscall);
#ifdef CONFIG_FPU
static long restore_fp_state(struct sigcontext __user *sc)
{
long err;
err = __copy_from_user(&current->thread.fpcsr, &sc->fpcsr, sizeof(unsigned long));
if (unlikely(err))
return err;
/* Restore the FPU state */
restore_fpu(current);
return 0;
}
static long save_fp_state(struct sigcontext __user *sc)
{
long err;
/* Sync the user FPU state so we can copy to sigcontext */
save_fpu(current);
err = __copy_to_user(&sc->fpcsr, &current->thread.fpcsr, sizeof(unsigned long));
return err;
}
#else
#define save_fp_state(sc) (0)
#define restore_fp_state(sc) (0)
#endif
static int restore_sigcontext(struct pt_regs *regs,
struct sigcontext __user *sc)
{
unsigned long old_sr = regs->sr;
int err = 0;
/* Always make any pending restarted system calls return -EINTR */
current->restart_block.fn = do_no_restart_syscall;
/*
* Restore the regs from &sc->regs.
* (sc is already checked since the sigframe was
* checked in sys_sigreturn previously)
*/
err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long));
err |= __copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long));
err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
err |= restore_fp_state(sc);
/* keep the privileged SR bits kernel owned, restore only user flags */
regs->sr = (old_sr & ~SPR_SR_USER_MASK) | (regs->sr & SPR_SR_USER_MASK);
regs->orig_gpr11 = -1; /* Avoid syscall restart checks */
/* TODO: the other ports use regs->orig_XX to disable syscall checks
* after this completes, but we don't use that mechanism. maybe we can
* use it now ?
*/
return err;
}
asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
{
struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->sp;
sigset_t set;
/*
* Since we stacked the signal on a dword boundary,
* then frame should be dword aligned here. If it's
* not, then the user is trying to mess with us.
*/
if (((unsigned long)frame) & 3)
goto badframe;
if (!access_ok(frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
if (restore_altstack(&frame->uc.uc_stack))
goto badframe;
return regs->gpr[11];
badframe:
force_sig(SIGSEGV);
return 0;
}
/*
* Set up a signal frame.
*/
static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
int err = 0;
/* copy the regs */
/* There should be no need to save callee-saved registers here...
* ...but we save them anyway. Revisit this
*/
err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long));
err |= save_fp_state(sc);
return err;
}
static inline unsigned long align_sigframe(unsigned long sp)
{
return sp & ~3UL;
}
/*
* Work out where the signal frame should go. It's either on the user stack
* or the alternate stack.
*/
static inline void __user *get_sigframe(struct ksignal *ksig,
struct pt_regs *regs, size_t frame_size)
{
unsigned long sp = regs->sp;
/* redzone */
sp -= STACK_FRAME_OVERHEAD;
sp = sigsp(sp, ksig);
sp = align_sigframe(sp - frame_size);
return (void __user *)sp;
}
/* grab and setup a signal frame.
*
* basically we stack a lot of state info, and arrange for the
* user-mode program to return to the kernel using either a
* trampoline which performs the syscall sigreturn, or a provided
* user-mode trampoline.
*/
static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
unsigned long return_ip;
int err = 0;
frame = get_sigframe(ksig, regs, sizeof(*frame));
if (!access_ok(frame, sizeof(*frame)))
return -EFAULT;
/* Create siginfo. */
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
/* Create the ucontext. */
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(NULL, &frame->uc.uc_link);
err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
err |= setup_sigcontext(regs, &frame->uc.uc_mcontext);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err)
return -EFAULT;
/* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode;
/* This is:
l.ori r11,r0,__NR_sigreturn
l.sys 1
*/
err |= __put_user(0xa960, (short __user *)(frame->retcode + 0));
err |= __put_user(__NR_rt_sigreturn, (short __user *)(frame->retcode + 2));
err |= __put_user(0x20000001, (unsigned long __user *)(frame->retcode + 4));
err |= __put_user(0x15000000, (unsigned long __user *)(frame->retcode + 8));
if (err)
return -EFAULT;
/* Set up registers for signal handler */
regs->pc = (unsigned long)ksig->ka.sa.sa_handler; /* what we enter NOW */
regs->gpr[9] = (unsigned long)return_ip; /* what we enter LATER */
regs->gpr[3] = (unsigned long)ksig->sig; /* arg 1: signo */
regs->gpr[4] = (unsigned long)&frame->info; /* arg 2: (siginfo_t*) */
regs->gpr[5] = (unsigned long)&frame->uc; /* arg 3: ucontext */
/* actually move the usp to reflect the stacked frame */
regs->sp = (unsigned long)frame;
return 0;
}
static inline void
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
int ret;
rseq_signal_deliver(ksig, regs);
ret = setup_rt_frame(ksig, sigmask_to_save(), regs);
signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP));
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Also note that the regs structure given here as an argument, is the latest
* pushed pt_regs. It may or may not be the same as the first pushed registers
* when the initial usermode->kernelmode transition took place. Therefore
* we can use user_mode(regs) to see if we came directly from kernel or user
* mode below.
*/
static int do_signal(struct pt_regs *regs, int syscall)
{
struct ksignal ksig;
unsigned long continue_addr = 0;
unsigned long restart_addr = 0;
unsigned long retval = 0;
int restart = 0;
if (syscall) {
continue_addr = regs->pc;
restart_addr = continue_addr - 4;
retval = regs->gpr[11];
/*
* Setup syscall restart here so that a debugger will
* see the already changed PC.
*/
switch (retval) {
case -ERESTART_RESTARTBLOCK:
restart = -2;
fallthrough;
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
restart++;
regs->gpr[11] = regs->orig_gpr11;
regs->pc = restart_addr;
break;
}
}
/*
* Get the signal to deliver. During the call to get_signal the
* debugger may change all our registers so we may need to revert
* the decision to restart the syscall; specifically, if the PC is
* changed, don't restart the syscall.
*/
if (get_signal(&ksig)) {
if (unlikely(restart) && regs->pc == restart_addr) {
if (retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK
|| (retval == -ERESTARTSYS
&& !(ksig.ka.sa.sa_flags & SA_RESTART))) {
/* No automatic restart */
regs->gpr[11] = -EINTR;
regs->pc = continue_addr;
}
}
handle_signal(&ksig, regs);
} else {
/* no handler */
restore_saved_sigmask();
/*
* Restore pt_regs PC as syscall restart will be handled by
* kernel without return to userspace
*/
if (unlikely(restart) && regs->pc == restart_addr) {
regs->pc = continue_addr;
return restart;
}
}
return 0;
}
asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{
do {
if (likely(thread_flags & _TIF_NEED_RESCHED)) {
schedule();
} else {
if (unlikely(!user_mode(regs)))
return 0;
local_irq_enable();
if (thread_flags & (_TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL)) {
int restart = do_signal(regs, syscall);
if (unlikely(restart)) {
/*
* Restart without handlers.
* Deal with it without leaving
* the kernel space.
*/
return restart;
}
syscall = 0;
} else {
resume_user_mode_work(regs);
}
}
local_irq_disable();
thread_flags = read_thread_flags();
} while (thread_flags & _TIF_WORK_MASK);
return 0;
}
+369
View File
@@ -0,0 +1,369 @@
/*
* Copyright (C) 2014 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
* Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
*
* Based on arm64 and arc implementations
* Copyright (C) 2013 ARM Ltd.
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <asm/cpuinfo.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
#include <asm/time.h>
asmlinkage __init void secondary_start_kernel(void);
static unsigned int ipi_irq __ro_after_init;
static void (*smp_cross_call)(const struct cpumask *, unsigned int);
unsigned long secondary_release = -1;
struct thread_info *secondary_thread_info;
enum ipi_msg_type {
IPI_WAKEUP,
IPI_RESCHEDULE,
IPI_CALL_FUNC,
IPI_CALL_FUNC_SINGLE,
};
static DEFINE_SPINLOCK(boot_lock);
static void or1k_ipi_enable(void)
{
if (WARN_ON_ONCE(!ipi_irq))
return;
enable_percpu_irq(ipi_irq, 0);
}
static void boot_secondary(unsigned int cpu, struct task_struct *idle)
{
/*
* set synchronisation state between this boot processor
* and the secondary one
*/
spin_lock(&boot_lock);
secondary_release = cpu;
smp_cross_call(cpumask_of(cpu), IPI_WAKEUP);
/*
* now the secondary core is starting up let it run its
* calibrations, then wait for it to finish
*/
spin_unlock(&boot_lock);
}
void __init smp_init_cpus(void)
{
struct device_node *cpu;
u32 cpu_id;
for_each_of_cpu_node(cpu) {
cpu_id = of_get_cpu_hwid(cpu, 0);
if (cpu_id < NR_CPUS)
set_cpu_possible(cpu_id, true);
}
}
void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned int cpu;
/*
* Initialise the present map, which describes the set of CPUs
* actually populated at the present time.
*/
for_each_possible_cpu(cpu) {
if (cpu < max_cpus)
set_cpu_present(cpu, true);
}
}
void __init smp_cpus_done(unsigned int max_cpus)
{
}
static DECLARE_COMPLETION(cpu_running);
int __cpu_up(unsigned int cpu, struct task_struct *idle)
{
if (smp_cross_call == NULL) {
pr_warn("CPU%u: failed to start, IPI controller missing",
cpu);
return -EIO;
}
secondary_thread_info = task_thread_info(idle);
current_pgd[cpu] = init_mm.pgd;
boot_secondary(cpu, idle);
if (!wait_for_completion_timeout(&cpu_running,
msecs_to_jiffies(1000))) {
pr_crit("CPU%u: failed to start\n", cpu);
return -EIO;
}
synchronise_count_master(cpu);
return 0;
}
asmlinkage __init void secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
unsigned int cpu = smp_processor_id();
/*
* All kernel threads share the same mm context; grab a
* reference and switch to it.
*/
mmgrab(mm);
current->active_mm = mm;
cpumask_set_cpu(cpu, mm_cpumask(mm));
pr_info("CPU%u: Booted secondary processor\n", cpu);
setup_cpuinfo();
openrisc_clockevent_init();
notify_cpu_starting(cpu);
/*
* OK, now it's safe to let the boot CPU continue
*/
complete(&cpu_running);
synchronise_count_slave(cpu);
or1k_ipi_enable();
set_cpu_online(cpu, true);
local_irq_enable();
/*
* OK, it's off to the idle thread for us
*/
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
}
void handle_IPI(unsigned int ipi_msg)
{
unsigned int cpu = smp_processor_id();
switch (ipi_msg) {
case IPI_WAKEUP:
break;
case IPI_RESCHEDULE:
scheduler_ipi();
break;
case IPI_CALL_FUNC:
generic_smp_call_function_interrupt();
break;
case IPI_CALL_FUNC_SINGLE:
generic_smp_call_function_single_interrupt();
break;
default:
WARN(1, "CPU%u: Unknown IPI message 0x%x\n", cpu, ipi_msg);
break;
}
}
void arch_smp_send_reschedule(int cpu)
{
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
}
static void stop_this_cpu(void *dummy)
{
/* Remove this CPU */
set_cpu_online(smp_processor_id(), false);
local_irq_disable();
/* CPU Doze */
if (mfspr(SPR_UPR) & SPR_UPR_PMP)
mtspr(SPR_PMR, mfspr(SPR_PMR) | SPR_PMR_DME);
/* If that didn't work, infinite loop */
while (1)
;
}
void smp_send_stop(void)
{
smp_call_function(stop_this_cpu, NULL, 0);
}
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int),
unsigned int irq)
{
if (WARN_ON(ipi_irq))
return;
smp_cross_call = fn;
ipi_irq = irq;
/* Enabled IPIs for boot CPU immediately */
or1k_ipi_enable();
}
void arch_send_call_function_single_ipi(int cpu)
{
smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
smp_cross_call(mask, IPI_CALL_FUNC);
}
/* TLB flush operations - Performed on each CPU*/
static inline void ipi_flush_tlb_all(void *ignored)
{
local_flush_tlb_all();
}
static inline void ipi_flush_tlb_mm(void *info)
{
struct mm_struct *mm = (struct mm_struct *)info;
local_flush_tlb_mm(mm);
}
static void smp_flush_tlb_mm(struct cpumask *cmask, struct mm_struct *mm)
{
unsigned int cpuid;
if (cpumask_empty(cmask))
return;
cpuid = get_cpu();
if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
/* local cpu is the only cpu present in cpumask */
local_flush_tlb_mm(mm);
} else {
on_each_cpu_mask(cmask, ipi_flush_tlb_mm, mm, 1);
}
put_cpu();
}
struct flush_tlb_data {
unsigned long addr1;
unsigned long addr2;
};
static inline void ipi_flush_tlb_page(void *info)
{
struct flush_tlb_data *fd = (struct flush_tlb_data *)info;
local_flush_tlb_page(NULL, fd->addr1);
}
static inline void ipi_flush_tlb_range(void *info)
{
struct flush_tlb_data *fd = (struct flush_tlb_data *)info;
local_flush_tlb_range(NULL, fd->addr1, fd->addr2);
}
static void smp_flush_tlb_range(const struct cpumask *cmask, unsigned long start,
unsigned long end)
{
unsigned int cpuid;
if (cpumask_empty(cmask))
return;
cpuid = get_cpu();
if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
/* local cpu is the only cpu present in cpumask */
if ((end - start) <= PAGE_SIZE)
local_flush_tlb_page(NULL, start);
else
local_flush_tlb_range(NULL, start, end);
} else {
struct flush_tlb_data fd;
fd.addr1 = start;
fd.addr2 = end;
if ((end - start) <= PAGE_SIZE)
on_each_cpu_mask(cmask, ipi_flush_tlb_page, &fd, 1);
else
on_each_cpu_mask(cmask, ipi_flush_tlb_range, &fd, 1);
}
put_cpu();
}
void flush_tlb_all(void)
{
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
}
void flush_tlb_mm(struct mm_struct *mm)
{
smp_flush_tlb_mm(mm_cpumask(mm), mm);
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
{
smp_flush_tlb_range(mm_cpumask(vma->vm_mm), uaddr, uaddr + PAGE_SIZE);
}
void flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
const struct cpumask *cmask = vma ? mm_cpumask(vma->vm_mm)
: cpu_online_mask;
smp_flush_tlb_range(cmask, start, end);
}
/* Instruction cache invalidate - performed on each cpu */
static void ipi_icache_page_inv(void *arg)
{
struct page *page = arg;
local_icache_page_inv(page);
}
void smp_icache_page_inv(struct page *page)
{
on_each_cpu(ipi_icache_page_inv, page, 1);
}
EXPORT_SYMBOL(smp_icache_page_inv);
static void ipi_icache_all_inv(void *arg)
{
local_icache_all_inv();
}
void smp_icache_all_inv(void)
{
if (num_online_cpus() < 2) {
local_icache_all_inv();
return;
}
/*
* Ensure stores complete before we request remote icaches
* to invalidate.
*/
mb();
on_each_cpu(ipi_icache_all_inv, NULL, 1);
}
@@ -0,0 +1,100 @@
/*
* Stack trace utility for OpenRISC
*
* Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
* Losely based on work from sh and powerpc.
*/
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>
#include <linux/stacktrace.h>
#include <asm/processor.h>
#include <asm/unwinder.h>
/*
* Save stack-backtrace addresses into a stack_trace buffer.
*/
static void
save_stack_address(void *data, unsigned long addr, int reliable)
{
struct stack_trace *trace = data;
if (!reliable)
return;
if (trace->skip > 0) {
trace->skip--;
return;
}
if (trace->nr_entries < trace->max_entries)
trace->entries[trace->nr_entries++] = addr;
}
void save_stack_trace(struct stack_trace *trace)
{
unwind_stack(trace, (unsigned long *) &trace, save_stack_address);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
static void
save_stack_address_nosched(void *data, unsigned long addr, int reliable)
{
struct stack_trace *trace = (struct stack_trace *)data;
if (!reliable)
return;
if (in_sched_functions(addr))
return;
if (trace->skip > 0) {
trace->skip--;
return;
}
if (trace->nr_entries < trace->max_entries)
trace->entries[trace->nr_entries++] = addr;
}
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
unsigned long *sp = NULL;
if (!try_get_task_stack(tsk))
return;
if (tsk == current)
sp = (unsigned long *) &sp;
else {
unsigned long ksp;
/* Locate stack from kernel context */
ksp = task_thread_info(tsk)->ksp;
ksp += STACK_FRAME_OVERHEAD; /* redzone */
ksp += sizeof(struct pt_regs);
sp = (unsigned long *) ksp;
}
unwind_stack(trace, sp, save_stack_address_nosched);
put_task_stack(tsk);
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
void
save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
{
unwind_stack(trace, (unsigned long *) regs->sp,
save_stack_address_nosched);
}
EXPORT_SYMBOL_GPL(save_stack_trace_regs);
@@ -0,0 +1,120 @@
/*
* OR1K timer synchronisation
*
* Based on work from MIPS implementation.
*
* All CPUs will have their count registers synchronised to the CPU0 next time
* value. This can cause a small timewarp for CPU0. All other CPU's should
* not have done anything significant (but they may have had interrupts
* enabled briefly - prom_smp_finish() should not be responsible for enabling
* interrupts...)
*/
#include <linux/kernel.h>
#include <linux/irqflags.h>
#include <linux/cpumask.h>
#include <asm/time.h>
#include <asm/timex.h>
#include <linux/atomic.h>
#include <asm/barrier.h>
#include <asm/spr.h>
static unsigned int initcount;
static atomic_t count_count_start = ATOMIC_INIT(0);
static atomic_t count_count_stop = ATOMIC_INIT(0);
#define COUNTON 100
#define NR_LOOPS 3
void synchronise_count_master(int cpu)
{
int i;
unsigned long flags;
pr_info("Synchronize counters for CPU %u: ", cpu);
local_irq_save(flags);
/*
* We loop a few times to get a primed instruction cache,
* then the last pass is more or less synchronised and
* the master and slaves each set their cycle counters to a known
* value all at once. This reduces the chance of having random offsets
* between the processors, and guarantees that the maximum
* delay between the cycle counters is never bigger than
* the latency of information-passing (cachelines) between
* two CPUs.
*/
for (i = 0; i < NR_LOOPS; i++) {
/* slaves loop on '!= 2' */
while (atomic_read(&count_count_start) != 1)
mb();
atomic_set(&count_count_stop, 0);
smp_wmb();
/* Let the slave writes its count register */
atomic_inc(&count_count_start);
/* Count will be initialised to current timer */
if (i == 1)
initcount = get_cycles();
/*
* Everyone initialises count in the last loop:
*/
if (i == NR_LOOPS-1)
openrisc_timer_set(initcount);
/*
* Wait for slave to leave the synchronization point:
*/
while (atomic_read(&count_count_stop) != 1)
mb();
atomic_set(&count_count_start, 0);
smp_wmb();
atomic_inc(&count_count_stop);
}
/* Arrange for an interrupt in a short while */
openrisc_timer_set_next(COUNTON);
local_irq_restore(flags);
/*
* i386 code reported the skew here, but the
* count registers were almost certainly out of sync
* so no point in alarming people
*/
pr_cont("done.\n");
}
void synchronise_count_slave(int cpu)
{
int i;
/*
* Not every cpu is online at the time this gets called,
* so we first wait for the master to say everyone is ready
*/
for (i = 0; i < NR_LOOPS; i++) {
atomic_inc(&count_count_start);
while (atomic_read(&count_count_start) != 2)
mb();
/*
* Everyone initialises count in the last loop:
*/
if (i == NR_LOOPS-1)
openrisc_timer_set(initcount);
atomic_inc(&count_count_stop);
while (atomic_read(&count_count_stop) != 2)
mb();
}
/* Arrange for an interrupt in a short while */
openrisc_timer_set_next(COUNTON);
}
#undef NR_LOOPS
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC sys_call_table.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/syscalls.h>
#include <linux/signal.h>
#include <linux/unistd.h>
#include <asm/syscalls.h>
#define __SYSCALL(nr, call) [nr] = (call),
#define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native)
#define sys_mmap2 sys_mmap_pgoff
#define sys_clone __sys_clone
#define sys_clone3 __sys_clone3
#define sys_fork __sys_fork
void *sys_call_table[__NR_syscalls] = {
#include <asm/syscall_table_32.h>
};
+179
View File
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC time.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*/
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/interrupt.h>
#include <linux/ftrace.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of_clk.h>
#include <asm/cpuinfo.h>
#include <asm/time.h>
irqreturn_t __irq_entry timer_interrupt(struct pt_regs *regs);
/* Test the timer ticks to count, used in sync routine */
inline void openrisc_timer_set(unsigned long count)
{
mtspr(SPR_TTCR, count);
}
/* Set the timer to trigger in delta cycles */
inline void openrisc_timer_set_next(unsigned long delta)
{
u32 c;
/* Read 32-bit counter value, add delta, mask off the low 28 bits.
* We're guaranteed delta won't be bigger than 28 bits because the
* generic timekeeping code ensures that for us.
*/
c = mfspr(SPR_TTCR);
c += delta;
c &= SPR_TTMR_TP;
/* Set counter and enable interrupt.
* Keep timer in continuous mode always.
*/
mtspr(SPR_TTMR, SPR_TTMR_CR | SPR_TTMR_IE | c);
}
static int openrisc_timer_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
openrisc_timer_set_next(delta);
return 0;
}
/* This is the clock event device based on the OR1K tick timer.
* As the timer is being used as a continuous clock-source (required for HR
* timers) we cannot enable the PERIODIC feature. The tick timer can run using
* one-shot events, so no problem.
*/
static DEFINE_PER_CPU(struct clock_event_device, clockevent_openrisc_timer);
void openrisc_clockevent_init(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt =
&per_cpu(clockevent_openrisc_timer, cpu);
struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[cpu];
mtspr(SPR_TTMR, SPR_TTMR_CR);
#ifdef CONFIG_SMP
evt->broadcast = tick_broadcast;
#endif
evt->name = "openrisc_timer_clockevent",
evt->features = CLOCK_EVT_FEAT_ONESHOT,
evt->rating = 300,
evt->set_next_event = openrisc_timer_set_next_event,
evt->cpumask = cpumask_of(cpu);
/* We only have 28 bits */
clockevents_config_and_register(evt, cpuinfo->clock_frequency,
100, 0x0fffffff);
}
static inline void timer_ack(void)
{
/* Clear the IP bit and disable further interrupts */
/* This can be done very simply... we just need to keep the timer
running, so just maintain the CR bits while clearing the rest
of the register
*/
mtspr(SPR_TTMR, SPR_TTMR_CR);
}
/*
* The timer interrupt is mostly handled in generic code nowadays... this
* function just acknowledges the interrupt and fires the event handler that
* has been set on the clockevent device by the generic time management code.
*
* This function needs to be called by the timer exception handler and that's
* all the exception handler needs to do.
*/
irqreturn_t __irq_entry timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt =
&per_cpu(clockevent_openrisc_timer, cpu);
timer_ack();
/*
* update_process_times() expects us to have called irq_enter().
*/
irq_enter();
evt->event_handler(evt);
irq_exit();
set_irq_regs(old_regs);
return IRQ_HANDLED;
}
/*
* Clocksource: Based on OpenRISC timer/counter
*
* This sets up the OpenRISC Tick Timer as a clock source. The tick timer
* is 32 bits wide and runs at the CPU clock frequency.
*/
static u64 openrisc_timer_read(struct clocksource *cs)
{
return (u64) mfspr(SPR_TTCR);
}
static struct clocksource openrisc_timer = {
.name = "openrisc_timer",
.rating = 200,
.read = openrisc_timer_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init openrisc_timer_init(void)
{
struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[smp_processor_id()];
if (clocksource_register_hz(&openrisc_timer, cpuinfo->clock_frequency))
panic("failed to register clocksource");
/* Enable the incrementer: 'continuous' mode with interrupt disabled */
mtspr(SPR_TTMR, SPR_TTMR_CR);
return 0;
}
void __init time_init(void)
{
u32 upr;
upr = mfspr(SPR_UPR);
if (!(upr & SPR_UPR_TTP))
panic("Linux not supported on devices without tick timer");
openrisc_timer_init();
openrisc_clockevent_init();
of_clk_init(NULL);
timer_probe();
}
+439
View File
@@ -0,0 +1,439 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OpenRISC traps.c
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* Here we handle the break vectors not used by the system call
* mechanism, as well as some general stack/register dumping
* things.
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>
#include <linux/kernel.h>
#include <linux/extable.h>
#include <linux/kmod.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
#include <asm/bug.h>
#include <asm/fpu.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/unwinder.h>
#include <asm/sections.h>
int lwa_flag;
static unsigned long __user *lwa_addr;
asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector);
asmlinkage void do_trap(struct pt_regs *regs, unsigned long address);
asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address);
asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address);
asmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address);
asmlinkage void do_illegal_instruction(struct pt_regs *regs,
unsigned long address);
static void print_trace(void *data, unsigned long addr, int reliable)
{
const char *loglvl = data;
pr_info("%s[<%p>] %s%pS\n", loglvl, (void *) addr, reliable ? "" : "? ",
(void *) addr);
}
static void print_data(unsigned long base_addr, unsigned long word, int i)
{
if (i == 0)
pr_info("(%08lx:)\t%08lx", base_addr + (i * 4), word);
else
pr_info(" %08lx:\t%08lx", base_addr + (i * 4), word);
}
/* displays a short stack trace */
void show_stack(struct task_struct *task, unsigned long *esp, const char *loglvl)
{
if (esp == NULL)
esp = (unsigned long *)&esp;
pr_info("%sCall trace:\n", loglvl);
unwind_stack((void *)loglvl, esp, print_trace);
}
void show_registers(struct pt_regs *regs)
{
int i;
int in_kernel = 1;
unsigned long esp;
esp = (unsigned long)(regs->sp);
if (user_mode(regs))
in_kernel = 0;
pr_info("CPU #: %d\n"
" PC: %08lx SR: %08lx SP: %08lx\n",
smp_processor_id(), regs->pc, regs->sr, regs->sp);
pr_info("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n",
0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]);
pr_info("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n",
regs->gpr[4], regs->gpr[5], regs->gpr[6], regs->gpr[7]);
pr_info("GPR08: %08lx GPR09: %08lx GPR10: %08lx GPR11: %08lx\n",
regs->gpr[8], regs->gpr[9], regs->gpr[10], regs->gpr[11]);
pr_info("GPR12: %08lx GPR13: %08lx GPR14: %08lx GPR15: %08lx\n",
regs->gpr[12], regs->gpr[13], regs->gpr[14], regs->gpr[15]);
pr_info("GPR16: %08lx GPR17: %08lx GPR18: %08lx GPR19: %08lx\n",
regs->gpr[16], regs->gpr[17], regs->gpr[18], regs->gpr[19]);
pr_info("GPR20: %08lx GPR21: %08lx GPR22: %08lx GPR23: %08lx\n",
regs->gpr[20], regs->gpr[21], regs->gpr[22], regs->gpr[23]);
pr_info("GPR24: %08lx GPR25: %08lx GPR26: %08lx GPR27: %08lx\n",
regs->gpr[24], regs->gpr[25], regs->gpr[26], regs->gpr[27]);
pr_info("GPR28: %08lx GPR29: %08lx GPR30: %08lx GPR31: %08lx\n",
regs->gpr[28], regs->gpr[29], regs->gpr[30], regs->gpr[31]);
pr_info(" RES: %08lx oGPR11: %08lx\n",
regs->gpr[11], regs->orig_gpr11);
pr_info("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, (unsigned long)current);
/*
* When in-kernel, we also print out the stack and code at the
* time of the fault..
*/
if (in_kernel) {
pr_info("\nStack: ");
show_stack(NULL, (unsigned long *)esp, KERN_EMERG);
if (esp < PAGE_OFFSET)
goto bad_stack;
pr_info("\n");
for (i = -8; i < 24; i += 1) {
unsigned long word;
if (__get_user(word, &((unsigned long *)esp)[i])) {
bad_stack:
pr_info(" Bad Stack value.");
break;
}
print_data(esp, word, i);
}
pr_info("\nCode: ");
if (regs->pc < PAGE_OFFSET)
goto bad;
for (i = -6; i < 6; i += 1) {
unsigned long word;
if (__get_user(word, &((unsigned long *)regs->pc)[i])) {
bad:
pr_info(" Bad PC value.");
break;
}
print_data(regs->pc, word, i);
}
}
pr_info("\n");
}
/* This is normally the 'Oops' routine */
void __noreturn die(const char *str, struct pt_regs *regs, long err)
{
console_verbose();
pr_emerg("\n%s#: %04lx\n", str, err & 0xffff);
show_registers(regs);
#ifdef CONFIG_JUMP_UPON_UNHANDLED_EXCEPTION
pr_emerg("\n\nUNHANDLED_EXCEPTION: entering infinite loop\n");
/* shut down interrupts */
local_irq_disable();
__asm__ __volatile__("l.nop 1");
do {} while (1);
#endif
make_task_dead(SIGSEGV);
}
asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector)
{
pr_emerg("Unable to handle exception at EA =0x%x, vector 0x%x",
ea, vector);
die("Oops", regs, 9);
}
asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address)
{
if (user_mode(regs)) {
int code = FPE_FLTUNK;
#ifdef CONFIG_FPU
unsigned long fpcsr;
save_fpu(current);
fpcsr = current->thread.fpcsr;
if (fpcsr & SPR_FPCSR_IVF)
code = FPE_FLTINV;
else if (fpcsr & SPR_FPCSR_OVF)
code = FPE_FLTOVF;
else if (fpcsr & SPR_FPCSR_UNF)
code = FPE_FLTUND;
else if (fpcsr & SPR_FPCSR_DZF)
code = FPE_FLTDIV;
else if (fpcsr & SPR_FPCSR_IXF)
code = FPE_FLTRES;
/* Clear all flags */
current->thread.fpcsr &= ~SPR_FPCSR_ALLF;
restore_fpu(current);
#endif
force_sig_fault(SIGFPE, code, (void __user *)regs->pc);
} else {
pr_emerg("KERNEL: Illegal fpe exception 0x%.8lx\n", regs->pc);
die("Die:", regs, SIGFPE);
}
}
asmlinkage void do_trap(struct pt_regs *regs, unsigned long address)
{
if (user_mode(regs)) {
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc);
} else {
pr_emerg("KERNEL: Illegal trap exception 0x%.8lx\n", regs->pc);
die("Die:", regs, SIGILL);
}
}
asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address)
{
if (user_mode(regs)) {
/* Send a SIGBUS */
force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)address);
} else {
pr_emerg("KERNEL: Unaligned Access 0x%.8lx\n", address);
die("Die:", regs, address);
}
}
asmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address)
{
if (user_mode(regs)) {
/* Send a SIGBUS */
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address);
} else { /* Kernel mode */
pr_emerg("KERNEL: Bus error (SIGBUS) 0x%.8lx\n", address);
die("Die:", regs, address);
}
}
static inline int in_delay_slot(struct pt_regs *regs)
{
#ifdef CONFIG_OPENRISC_NO_SPR_SR_DSX
/* No delay slot flag, do the old way */
unsigned int op, insn;
insn = *((unsigned int *)regs->pc);
op = insn >> 26;
switch (op) {
case 0x00: /* l.j */
case 0x01: /* l.jal */
case 0x03: /* l.bnf */
case 0x04: /* l.bf */
case 0x11: /* l.jr */
case 0x12: /* l.jalr */
return 1;
default:
return 0;
}
#else
return mfspr(SPR_SR) & SPR_SR_DSX;
#endif
}
static inline void adjust_pc(struct pt_regs *regs, unsigned long address)
{
int displacement;
unsigned int rb, op, jmp;
if (unlikely(in_delay_slot(regs))) {
/* In delay slot, instruction at pc is a branch, simulate it */
jmp = *((unsigned int *)regs->pc);
displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27);
rb = (jmp & 0x0000ffff) >> 11;
op = jmp >> 26;
switch (op) {
case 0x00: /* l.j */
regs->pc += displacement;
return;
case 0x01: /* l.jal */
regs->pc += displacement;
regs->gpr[9] = regs->pc + 8;
return;
case 0x03: /* l.bnf */
if (regs->sr & SPR_SR_F)
regs->pc += 8;
else
regs->pc += displacement;
return;
case 0x04: /* l.bf */
if (regs->sr & SPR_SR_F)
regs->pc += displacement;
else
regs->pc += 8;
return;
case 0x11: /* l.jr */
regs->pc = regs->gpr[rb];
return;
case 0x12: /* l.jalr */
regs->pc = regs->gpr[rb];
regs->gpr[9] = regs->pc + 8;
return;
default:
break;
}
} else {
regs->pc += 4;
}
}
static inline void simulate_lwa(struct pt_regs *regs, unsigned long address,
unsigned int insn)
{
unsigned int ra, rd;
unsigned long value;
unsigned long orig_pc;
long imm;
const struct exception_table_entry *entry;
orig_pc = regs->pc;
adjust_pc(regs, address);
ra = (insn >> 16) & 0x1f;
rd = (insn >> 21) & 0x1f;
imm = (short)insn;
lwa_addr = (unsigned long __user *)(regs->gpr[ra] + imm);
if ((unsigned long)lwa_addr & 0x3) {
do_unaligned_access(regs, address);
return;
}
if (get_user(value, lwa_addr)) {
if (user_mode(regs)) {
force_sig(SIGSEGV);
return;
}
if ((entry = search_exception_tables(orig_pc))) {
regs->pc = entry->fixup;
return;
}
/* kernel access in kernel space, load it directly */
value = *((unsigned long *)lwa_addr);
}
lwa_flag = 1;
regs->gpr[rd] = value;
}
static inline void simulate_swa(struct pt_regs *regs, unsigned long address,
unsigned int insn)
{
unsigned long __user *vaddr;
unsigned long orig_pc;
unsigned int ra, rb;
long imm;
const struct exception_table_entry *entry;
orig_pc = regs->pc;
adjust_pc(regs, address);
ra = (insn >> 16) & 0x1f;
rb = (insn >> 11) & 0x1f;
imm = (short)(((insn & 0x2200000) >> 10) | (insn & 0x7ff));
vaddr = (unsigned long __user *)(regs->gpr[ra] + imm);
if (!lwa_flag || vaddr != lwa_addr) {
regs->sr &= ~SPR_SR_F;
return;
}
if ((unsigned long)vaddr & 0x3) {
do_unaligned_access(regs, address);
return;
}
if (put_user(regs->gpr[rb], vaddr)) {
if (user_mode(regs)) {
force_sig(SIGSEGV);
return;
}
if ((entry = search_exception_tables(orig_pc))) {
regs->pc = entry->fixup;
return;
}
/* kernel access in kernel space, store it directly */
*((unsigned long *)vaddr) = regs->gpr[rb];
}
lwa_flag = 0;
regs->sr |= SPR_SR_F;
}
#define INSN_LWA 0x1b
#define INSN_SWA 0x33
asmlinkage void do_illegal_instruction(struct pt_regs *regs,
unsigned long address)
{
unsigned int op;
unsigned int insn = *((unsigned int *)address);
op = insn >> 26;
switch (op) {
case INSN_LWA:
simulate_lwa(regs, address, insn);
return;
case INSN_SWA:
simulate_swa(regs, address, insn);
return;
default:
break;
}
if (user_mode(regs)) {
/* Send a SIGILL */
force_sig_fault(SIGILL, ILL_ILLOPC, (void __user *)address);
} else { /* Kernel mode */
pr_emerg("KERNEL: Illegal instruction (SIGILL) 0x%.8lx\n",
address);
die("Die:", regs, address);
}
}
+105
View File
@@ -0,0 +1,105 @@
/*
* OpenRISC unwinder.c
*
* Reusable arch specific api for unwinding stacks.
*
* Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/sched/task_stack.h>
#include <linux/kernel.h>
#include <asm/unwinder.h>
#ifdef CONFIG_FRAME_POINTER
struct or1k_frameinfo {
unsigned long *fp;
unsigned long ra;
unsigned long top;
};
/*
* Verify a frameinfo structure. The return address should be a valid text
* address. The frame pointer may be null if its the last frame, otherwise
* the frame pointer should point to a location in the stack after the
* top of the next frame up.
*/
static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo)
{
return (frameinfo->fp == NULL ||
(!kstack_end(frameinfo->fp) &&
frameinfo->fp > &frameinfo->top)) &&
__kernel_text_address(frameinfo->ra);
}
/*
* Create a stack trace doing scanning which is frame pointer aware. We can
* get reliable stack traces by matching the previously found frame
* pointer with the top of the stack address every time we find a valid
* or1k_frameinfo.
*
* Ideally the stack parameter will be passed as FP, but it can not be
* guaranteed. Therefore we scan each address looking for the first sign
* of a return address.
*
* The OpenRISC stack frame looks something like the following. The
* location SP is held in r1 and location FP is held in r2 when frame pointers
* enabled.
*
* SP -> (top of stack)
* - (callee saved registers)
* - (local variables)
* FP-8 -> previous FP \
* FP-4 -> return address |- or1k_frameinfo
* FP -> (previous top of stack) /
*/
void unwind_stack(void *data, unsigned long *stack,
void (*trace)(void *data, unsigned long addr, int reliable))
{
unsigned long *next_fp = NULL;
struct or1k_frameinfo *frameinfo = NULL;
int reliable = 0;
while (!kstack_end(stack)) {
frameinfo = container_of(stack,
struct or1k_frameinfo,
top);
if (__kernel_text_address(frameinfo->ra)) {
if (or1k_frameinfo_valid(frameinfo) &&
(next_fp == NULL ||
next_fp == &frameinfo->top)) {
reliable = 1;
next_fp = frameinfo->fp;
} else
reliable = 0;
trace(data, frameinfo->ra, reliable);
}
stack++;
}
}
#else /* CONFIG_FRAME_POINTER */
/*
* Create a stack trace by doing a simple scan treating all text addresses
* as return addresses.
*/
void unwind_stack(void *data, unsigned long *stack,
void (*trace)(void *data, unsigned long addr, int reliable))
{
unsigned long addr;
while (!kstack_end(stack)) {
addr = *stack++;
if (__kernel_text_address(addr))
trace(data, addr, 0);
}
}
#endif /* CONFIG_FRAME_POINTER */
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __OPENRISC_VMLINUX_H_
#define __OPENRISC_VMLINUX_H_
#ifdef CONFIG_BLK_DEV_INITRD
extern char __initrd_start, __initrd_end;
#endif
#endif
@@ -0,0 +1,109 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OpenRISC vmlinux.lds.S
*
* Linux architectural port borrowing liberally from similar works of
* others. All original copyrights apply as per the original source
* declaration.
*
* Modifications for the OpenRISC architecture:
* Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
*
* ld script for OpenRISC architecture
*/
/* TODO
* - clean up __offset & stuff
* - change all 8192 alignment to PAGE !!!
* - recheck if all alignments are really needed
*/
# define LOAD_OFFSET PAGE_OFFSET
# define LOAD_BASE PAGE_OFFSET
#include <asm/page.h>
#include <asm/cache.h>
#include <asm/thread_info.h>
#include <asm-generic/vmlinux.lds.h>
#ifdef __OR1K__
#define __OUTPUT_FORMAT "elf32-or1k"
#else
#define __OUTPUT_FORMAT "elf32-or32"
#endif
OUTPUT_FORMAT(__OUTPUT_FORMAT, __OUTPUT_FORMAT, __OUTPUT_FORMAT)
jiffies = jiffies_64 + 4;
SECTIONS
{
/* Read-only sections, merged into text segment: */
. = LOAD_BASE ;
_text = .;
/* _s_kernel_ro must be page aligned */
. = ALIGN(PAGE_SIZE);
_s_kernel_ro = .;
.text : AT(ADDR(.text) - LOAD_OFFSET)
{
_stext = .;
HEAD_TEXT
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
*(.fixup)
*(.text.__*)
_etext = .;
}
/* TODO: Check if fixup and text.__* are really necessary
* fixup is definitely necessary
*/
_sdata = .;
/* Page alignment required for RO_DATA */
RO_DATA(PAGE_SIZE)
_e_kernel_ro = .;
/* Whatever comes after _e_kernel_ro had better be page-aligend, too */
/* 32 here is cacheline size... recheck this */
RW_DATA(32, PAGE_SIZE, PAGE_SIZE)
_edata = .;
EXCEPTION_TABLE(4)
/* Init code and data */
. = ALIGN(PAGE_SIZE);
__init_begin = .;
/* Page aligned */
INIT_TEXT_SECTION(PAGE_SIZE)
/* Align __setup_start on 16 byte boundary */
INIT_DATA_SECTION(16)
PERCPU_SECTION(L1_CACHE_BYTES)
__init_end = .;
BSS_SECTION(0, 0, 0x20)
_end = .;
/* Throw in the debugging sections */
STABS_DEBUG
DWARF_DEBUG
MODINFO
ELF_DETAILS
/* Sections to be discarded -- must be last */
DISCARDS
}