diff options
Diffstat (limited to 'arch/microblaze/kernel')
44 files changed, 2342 insertions, 3427 deletions
diff --git a/arch/microblaze/kernel/.gitignore b/arch/microblaze/kernel/.gitignore new file mode 100644 index 00000000000..c5f676c3c22 --- /dev/null +++ b/arch/microblaze/kernel/.gitignore @@ -0,0 +1 @@ +vmlinux.lds diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index e51bc152082..08d50cc55e7 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile @@ -7,26 +7,26 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_timer.o = -pg CFLAGS_REMOVE_intc.o = -pg CFLAGS_REMOVE_early_printk.o = -pg -CFLAGS_REMOVE_selfmod.o = -pg CFLAGS_REMOVE_heartbeat.o = -pg CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_process.o = -pg endif extra-y := head.o vmlinux.lds obj-y += dma.o exceptions.o \ - hw_exception_handler.o init_task.o intc.o irq.o of_device.o \ - of_platform.o process.o prom.o prom_parse.o ptrace.o \ - setup.o signal.o sys_microblaze.o timer.o traps.o reset.o + hw_exception_handler.o intc.o irq.o \ + platform.o process.o prom.o prom_parse.o ptrace.o \ + reset.o setup.o signal.o sys_microblaze.o timer.o traps.o unwind.o obj-y += cpu/ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_SELFMOD) += selfmod.o obj-$(CONFIG_HEART_BEAT) += heartbeat.o obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o obj-$(CONFIG_MMU) += misc.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o +obj-$(CONFIG_KGDB) += kgdb.o obj-y += entry$(MMU).o diff --git a/arch/microblaze/kernel/cpu/Makefile b/arch/microblaze/kernel/cpu/Makefile index 59cc7bceaf8..fceed4edea4 100644 --- a/arch/microblaze/kernel/cpu/Makefile +++ b/arch/microblaze/kernel/cpu/Makefile @@ -6,7 +6,7 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_cache.o = -pg endif -EXTRA_CFLAGS += -DCPU_MAJOR=$(CPU_MAJOR) -DCPU_MINOR=$(CPU_MINOR) \ +ccflags-y := -DCPU_MAJOR=$(CPU_MAJOR) -DCPU_MINOR=$(CPU_MINOR) \ -DCPU_REV=$(CPU_REV) obj-y += cache.o cpuinfo.o cpuinfo-pvr-full.o cpuinfo-static.o mb.o pvr.o diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c index 109876e8d64..a6e44410672 100644 --- a/arch/microblaze/kernel/cpu/cache.c +++ b/arch/microblaze/kernel/cpu/cache.c @@ -17,82 +17,70 @@ static inline void __enable_icache_msr(void) { - __asm__ __volatile__ (" msrset r0, %0; \ - nop; " \ + __asm__ __volatile__ (" msrset r0, %0;" \ + "nop;" \ : : "i" (MSR_ICE) : "memory"); } static inline void __disable_icache_msr(void) { - __asm__ __volatile__ (" msrclr r0, %0; \ - nop; " \ + __asm__ __volatile__ (" msrclr r0, %0;" \ + "nop;" \ : : "i" (MSR_ICE) : "memory"); } static inline void __enable_dcache_msr(void) { - __asm__ __volatile__ (" msrset r0, %0; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory"); + __asm__ __volatile__ (" msrset r0, %0;" \ + "nop;" \ + : : "i" (MSR_DCE) : "memory"); } static inline void __disable_dcache_msr(void) { - __asm__ __volatile__ (" msrclr r0, %0; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory"); + __asm__ __volatile__ (" msrclr r0, %0;" \ + "nop; " \ + : : "i" (MSR_DCE) : "memory"); } static inline void __enable_icache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - ori r12, r12, %0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_ICE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "ori r12, r12, %0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_ICE) : "memory", "r12"); } static inline void __disable_icache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - andi r12, r12, ~%0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_ICE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "andi r12, r12, ~%0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_ICE) : "memory", "r12"); } static inline void __enable_dcache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - ori r12, r12, %0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "ori r12, r12, %0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_DCE) : "memory", "r12"); } static inline void __disable_dcache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - andi r12, r12, ~%0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "andi r12, r12, ~%0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_DCE) : "memory", "r12"); } @@ -106,7 +94,7 @@ do { \ int align = ~(cache_line_length - 1); \ end = min(start + cache_size, end); \ start &= align; \ -} while (0); +} while (0) /* * Helper macro to loop over the specified cache_size/line_length and @@ -118,18 +106,18 @@ do { \ int step = -line_length; \ WARN_ON(step >= 0); \ \ - __asm__ __volatile__ (" 1: " #op " %0, r0; \ - bgtid %0, 1b; \ - addk %0, %0, %1; \ - " : : "r" (len), "r" (step) \ + __asm__ __volatile__ (" 1: " #op " %0, r0;" \ + "bgtid %0, 1b;" \ + "addk %0, %0, %1;" \ + : : "r" (len), "r" (step) \ : "memory"); \ -} while (0); +} while (0) /* Used for wdc.flush/clear which can use rB for offset which is not possible * to use for simple wdc or wic. * * start address is cache aligned - * end address is not aligned, if end is aligned then I have to substract + * end address is not aligned, if end is aligned then I have to subtract * cacheline length because I can't flush/invalidate the next cacheline. * If is not, I align it because I will flush/invalidate whole line. */ @@ -142,28 +130,28 @@ do { \ count = end - start; \ WARN_ON(count < 0); \ \ - __asm__ __volatile__ (" 1: " #op " %0, %1; \ - bgtid %1, 1b; \ - addk %1, %1, %2; \ - " : : "r" (start), "r" (count), \ + __asm__ __volatile__ (" 1: " #op " %0, %1;" \ + "bgtid %1, 1b;" \ + "addk %1, %1, %2;" \ + : : "r" (start), "r" (count), \ "r" (step) : "memory"); \ -} while (0); +} while (0) /* It is used only first parameter for OP - for wic, wdc */ #define CACHE_RANGE_LOOP_1(start, end, line_length, op) \ do { \ - int volatile temp; \ + int volatile temp = 0; \ int align = ~(line_length - 1); \ end = ((end & align) == end) ? end - line_length : end & align; \ WARN_ON(end - start < 0); \ \ - __asm__ __volatile__ (" 1: " #op " %1, r0; \ - cmpu %0, %1, %2; \ - bgtid %0, 1b; \ - addk %1, %1, %3; \ - " : : "r" (temp), "r" (start), "r" (end),\ + __asm__ __volatile__ (" 1: " #op " %1, r0;" \ + "cmpu %0, %1, %2;" \ + "bgtid %0, 1b;" \ + "addk %1, %1, %3;" \ + : : "r" (temp), "r" (start), "r" (end), \ "r" (line_length) : "memory"); \ -} while (0); +} while (0) #define ASM_LOOP @@ -352,7 +340,7 @@ static void __invalidate_dcache_all_noirq_wt(void) #endif pr_debug("%s\n", __func__); #ifdef ASM_LOOP - CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc) + CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); #else for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length) @@ -361,7 +349,8 @@ static void __invalidate_dcache_all_noirq_wt(void) #endif } -/* FIXME It is blindly invalidation as is expected +/* + * FIXME It is blindly invalidation as is expected * but can't be called on noMMU in microblaze_cache_init below * * MS: noMMU kernel won't boot if simple wdc is used @@ -375,7 +364,7 @@ static void __invalidate_dcache_all_wb(void) pr_debug("%s\n", __func__); #ifdef ASM_LOOP CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, - wdc) + wdc); #else for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length) @@ -519,7 +508,7 @@ static void __flush_dcache_range_wb(unsigned long start, unsigned long end) struct scache *mbc; /* new wb cache model */ -const struct scache wb_msr = { +static const struct scache wb_msr = { .ie = __enable_icache_msr, .id = __disable_icache_msr, .ifl = __flush_icache_all_noirq, @@ -535,7 +524,7 @@ const struct scache wb_msr = { }; /* There is only difference in ie, id, de, dd functions */ -const struct scache wb_nomsr = { +static const struct scache wb_nomsr = { .ie = __enable_icache_nomsr, .id = __disable_icache_nomsr, .ifl = __flush_icache_all_noirq, @@ -551,7 +540,7 @@ const struct scache wb_nomsr = { }; /* Old wt cache model with disabling irq and turn off cache */ -const struct scache wt_msr = { +static const struct scache wt_msr = { .ie = __enable_icache_msr, .id = __disable_icache_msr, .ifl = __flush_icache_all_msr_irq, @@ -566,7 +555,7 @@ const struct scache wt_msr = { .dinr = __invalidate_dcache_range_msr_irq_wt, }; -const struct scache wt_nomsr = { +static const struct scache wt_nomsr = { .ie = __enable_icache_nomsr, .id = __disable_icache_nomsr, .ifl = __flush_icache_all_nomsr_irq, @@ -582,7 +571,7 @@ const struct scache wt_nomsr = { }; /* New wt cache model for newer Microblaze versions */ -const struct scache wt_msr_noirq = { +static const struct scache wt_msr_noirq = { .ie = __enable_icache_msr, .id = __disable_icache_msr, .ifl = __flush_icache_all_noirq, @@ -597,7 +586,7 @@ const struct scache wt_msr_noirq = { .dinr = __invalidate_dcache_range_nomsr_wt, }; -const struct scache wt_nomsr_noirq = { +static const struct scache wt_nomsr_noirq = { .ie = __enable_icache_nomsr, .id = __disable_icache_nomsr, .ifl = __flush_icache_all_noirq, @@ -616,49 +605,48 @@ const struct scache wt_nomsr_noirq = { #define CPUVER_7_20_A 0x0c #define CPUVER_7_20_D 0x0f -#define INFO(s) printk(KERN_INFO "cache: " s "\n"); - void microblaze_cache_init(void) { if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) { if (cpuinfo.dcache_wb) { - INFO("wb_msr"); + pr_info("wb_msr\n"); mbc = (struct scache *)&wb_msr; - if (cpuinfo.ver_code < CPUVER_7_20_D) { + if (cpuinfo.ver_code <= CPUVER_7_20_D) { /* MS: problem with signal handling - hw bug */ - INFO("WB won't work properly"); + pr_info("WB won't work properly\n"); } } else { if (cpuinfo.ver_code >= CPUVER_7_20_A) { - INFO("wt_msr_noirq"); + pr_info("wt_msr_noirq\n"); mbc = (struct scache *)&wt_msr_noirq; } else { - INFO("wt_msr"); + pr_info("wt_msr\n"); mbc = (struct scache *)&wt_msr; } } } else { if (cpuinfo.dcache_wb) { - INFO("wb_nomsr"); + pr_info("wb_nomsr\n"); mbc = (struct scache *)&wb_nomsr; - if (cpuinfo.ver_code < CPUVER_7_20_D) { + if (cpuinfo.ver_code <= CPUVER_7_20_D) { /* MS: problem with signal handling - hw bug */ - INFO("WB won't work properly"); + pr_info("WB won't work properly\n"); } } else { if (cpuinfo.ver_code >= CPUVER_7_20_A) { - INFO("wt_nomsr_noirq"); + pr_info("wt_nomsr_noirq\n"); mbc = (struct scache *)&wt_nomsr_noirq; } else { - INFO("wt_nomsr"); + pr_info("wt_nomsr\n"); mbc = (struct scache *)&wt_nomsr; } } } -/* FIXME Invalidation is done in U-BOOT - * WT cache: Data is already written to main memory - * WB cache: Discard data on noMMU which caused that kernel doesn't boot - */ + /* + * FIXME Invalidation is done in U-BOOT + * WT cache: Data is already written to main memory + * WB cache: Discard data on noMMU which caused that kernel doesn't boot + */ /* invalidate_dcache(); */ enable_dcache(); diff --git a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c index f72dbd66c84..93c26cf50de 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c +++ b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c @@ -27,7 +27,7 @@ early_printk("ERROR: Microblaze " x "-different for PVR and DTS\n"); #else #define err_printk(x) \ - printk(KERN_INFO "ERROR: Microblaze " x "-different for PVR and DTS\n"); + pr_info("ERROR: Microblaze " x "-different for PVR and DTS\n"); #endif void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) @@ -38,12 +38,11 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) CI(ver_code, VERSION); if (!ci->ver_code) { - printk(KERN_ERR "ERROR: MB has broken PVR regs " - "-> use DTS setting\n"); + pr_err("ERROR: MB has broken PVR regs -> use DTS setting\n"); return; } - temp = PVR_USE_BARREL(pvr) | PVR_USE_MSR_INSTR(pvr) |\ + temp = PVR_USE_BARREL(pvr) | PVR_USE_MSR_INSTR(pvr) | PVR_USE_PCMP_INSTR(pvr) | PVR_USE_DIV(pvr); if (ci->use_instr != temp) err_printk("BARREL, MSR, PCMP or DIV"); @@ -59,19 +58,21 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) err_printk("HW_FPU"); ci->use_fpu = temp; - ci->use_exc = PVR_OPCODE_0x0_ILLEGAL(pvr) |\ - PVR_UNALIGNED_EXCEPTION(pvr) |\ - PVR_ILL_OPCODE_EXCEPTION(pvr) |\ - PVR_IOPB_BUS_EXCEPTION(pvr) |\ - PVR_DOPB_BUS_EXCEPTION(pvr) |\ - PVR_DIV_ZERO_EXCEPTION(pvr) |\ - PVR_FPU_EXCEPTION(pvr) |\ + ci->use_exc = PVR_OPCODE_0x0_ILLEGAL(pvr) | + PVR_UNALIGNED_EXCEPTION(pvr) | + PVR_ILL_OPCODE_EXCEPTION(pvr) | + PVR_IOPB_BUS_EXCEPTION(pvr) | + PVR_DOPB_BUS_EXCEPTION(pvr) | + PVR_DIV_ZERO_EXCEPTION(pvr) | + PVR_FPU_EXCEPTION(pvr) | PVR_FSL_EXCEPTION(pvr); CI(pvr_user1, USER1); CI(pvr_user2, USER2); CI(mmu, USE_MMU); + CI(mmu_privins, MMU_PRIVINS); + CI(endian, ENDIAN); CI(use_icache, USE_ICACHE); CI(icache_tagbits, ICACHE_ADDR_TAG_BITS); @@ -111,7 +112,4 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) CI(num_wr_brk, NUMBER_OF_WR_ADDR_BRK); CI(fpga_family_code, TARGET_FAMILY); - - /* take timebase-frequency from DTS */ - ci->cpu_clock_freq = fcpu(cpu, "timebase-frequency"); } diff --git a/arch/microblaze/kernel/cpu/cpuinfo-static.c b/arch/microblaze/kernel/cpu/cpuinfo-static.c index 6095aa6b5c8..4854285b26e 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo-static.c +++ b/arch/microblaze/kernel/cpu/cpuinfo-static.c @@ -113,12 +113,12 @@ void __init set_cpuinfo_static(struct cpuinfo *ci, struct device_node *cpu) ci->num_rd_brk = fcpu(cpu, "xlnx,number-of-rd-addr-brk"); ci->num_wr_brk = fcpu(cpu, "xlnx,number-of-wr-addr-brk"); - ci->cpu_clock_freq = fcpu(cpu, "timebase-frequency"); - ci->pvr_user1 = fcpu(cpu, "xlnx,pvr-user1"); ci->pvr_user2 = fcpu(cpu, "xlnx,pvr-user2"); ci->mmu = fcpu(cpu, "xlnx,use-mmu"); + ci->mmu_privins = fcpu(cpu, "xlnx,mmu-privileged-instr"); + ci->endian = fcpu(cpu, "xlnx,endianness"); ci->ver_code = 0; ci->fpga_family_code = 0; diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c index 255ef880351..234acad79b9 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo.c +++ b/arch/microblaze/kernel/cpu/cpuinfo.c @@ -8,6 +8,7 @@ * for more details. */ +#include <linux/clk.h> #include <linux/init.h> #include <asm/cpuinfo.h> #include <asm/pvr.h> @@ -30,6 +31,18 @@ const struct cpu_ver_key cpu_ver_lookup[] = { {"7.20.c", 0x0e}, {"7.20.d", 0x0f}, {"7.30.a", 0x10}, + {"7.30.b", 0x11}, + {"8.00.a", 0x12}, + {"8.00.b", 0x13}, + {"8.10.a", 0x14}, + {"8.20.a", 0x15}, + {"8.20.b", 0x16}, + {"8.30.a", 0x17}, + {"8.40.a", 0x18}, + {"8.40.b", 0x19}, + {"8.50.a", 0x1a}, + {"9.0", 0x1b}, + {"9.1", 0x1d}, {NULL, 0}, }; @@ -50,38 +63,62 @@ const struct family_string_key family_string_lookup[] = { {"virtex6", 0xe}, /* FIXME There is no key code defined for spartan2 */ {"spartan2", 0xf0}, + {"kintex7", 0x10}, + {"artix7", 0x11}, + {"zynq7000", 0x12}, {NULL, 0}, }; struct cpuinfo cpuinfo; +static struct device_node *cpu; void __init setup_cpuinfo(void) { - struct device_node *cpu = NULL; - cpu = (struct device_node *) of_find_node_by_type(NULL, "cpu"); if (!cpu) - printk(KERN_ERR "You don't have cpu!!!\n"); + pr_err("You don't have cpu!!!\n"); - printk(KERN_INFO "%s: initialising\n", __func__); + pr_info("%s: initialising\n", __func__); switch (cpu_has_pvr()) { case 0: - printk(KERN_WARNING - "%s: No PVR support. Using static CPU info from FDT\n", + pr_warn("%s: No PVR support. Using static CPU info from FDT\n", __func__); set_cpuinfo_static(&cpuinfo, cpu); break; /* FIXME I found weird behavior with MB 7.00.a/b 7.10.a * please do not use FULL PVR with MMU */ case 1: - printk(KERN_INFO "%s: Using full CPU PVR support\n", + pr_info("%s: Using full CPU PVR support\n", __func__); set_cpuinfo_static(&cpuinfo, cpu); set_cpuinfo_pvr_full(&cpuinfo, cpu); break; default: - printk(KERN_WARNING "%s: Unsupported PVR setting\n", __func__); + pr_warn("%s: Unsupported PVR setting\n", __func__); set_cpuinfo_static(&cpuinfo, cpu); } + + if (cpuinfo.mmu_privins) + pr_warn("%s: Stream instructions enabled" + " - USERSPACE CAN LOCK THIS KERNEL!\n", __func__); +} + +void __init setup_cpuinfo_clk(void) +{ + struct clk *clk; + + clk = of_clk_get(cpu, 0); + if (IS_ERR(clk)) { + pr_err("ERROR: CPU CCF input clock not found\n"); + /* take timebase-frequency from DTS */ + cpuinfo.cpu_clock_freq = fcpu(cpu, "timebase-frequency"); + } else { + cpuinfo.cpu_clock_freq = clk_get_rate(clk); + } + + if (!cpuinfo.cpu_clock_freq) { + pr_err("ERROR: CPU clock frequency not setup\n"); + BUG(); + } } diff --git a/arch/microblaze/kernel/cpu/mb.c b/arch/microblaze/kernel/cpu/mb.c index 4216eb1eaa3..7b5dca7ed39 100644 --- a/arch/microblaze/kernel/cpu/mb.c +++ b/arch/microblaze/kernel/cpu/mb.c @@ -51,11 +51,12 @@ static int show_cpuinfo(struct seq_file *m, void *v) count = seq_printf(m, "CPU-Family: MicroBlaze\n" "FPGA-Arch: %s\n" - "CPU-Ver: %s\n" + "CPU-Ver: %s, %s endian\n" "CPU-MHz: %d.%02d\n" "BogoMips: %lu.%02lu\n", fpga_family, cpu_ver, + cpuinfo.endian ? "little" : "big", cpuinfo.cpu_clock_freq / 1000000, cpuinfo.cpu_clock_freq % @@ -96,6 +97,10 @@ static int show_cpuinfo(struct seq_file *m, void *v) (cpuinfo.use_exc & PVR2_FPU_EXC_MASK) ? "fpu " : "", (cpuinfo.use_exc & PVR2_USE_FSL_EXC) ? "fsl " : ""); + count += seq_printf(m, + "Stream-insns:\t%sprivileged\n", + cpuinfo.mmu_privins ? "un" : ""); + if (cpuinfo.use_icache) count += seq_printf(m, "Icache:\t\t%ukB\tline length:\t%dB\n", @@ -109,10 +114,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) "Dcache:\t\t%ukB\tline length:\t%dB\n", cpuinfo.dcache_size >> 10, cpuinfo.dcache_line_length); + seq_printf(m, "Dcache-Policy:\t"); if (cpuinfo.dcache_wb) - count += seq_printf(m, "\t\twrite-back\n"); + count += seq_printf(m, "write-back\n"); else - count += seq_printf(m, "\t\twrite-through\n"); + count += seq_printf(m, "write-through\n"); } else count += seq_printf(m, "Dcache:\t\tno\n"); @@ -126,6 +132,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) cpuinfo.pvr_user1, cpuinfo.pvr_user2); + count += seq_printf(m, "Page size:\t%lu\n", PAGE_SIZE); return 0; } diff --git a/arch/microblaze/kernel/cpu/pvr.c b/arch/microblaze/kernel/cpu/pvr.c index 9bee9382bf7..8d0dc6db48c 100644 --- a/arch/microblaze/kernel/cpu/pvr.c +++ b/arch/microblaze/kernel/cpu/pvr.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/compiler.h> -#include <asm/system.h> #include <asm/exceptions.h> #include <asm/pvr.h> @@ -27,8 +26,8 @@ register unsigned tmp __asm__("r3"); \ tmp = 0x0; /* Prevent warning about unused */ \ __asm__ __volatile__ ( \ - ".byte 0x94,0x60,0xa0, " #pvrid "\n\t" \ - : "=r" (tmp) : : "memory"); \ + "mfs %0, rpvr" #pvrid ";" \ + : "=r" (tmp) : : "memory"); \ val = tmp; \ } @@ -54,7 +53,7 @@ int cpu_has_pvr(void) if (!(flags & PVR_MSR_BIT)) return 0; - get_single_pvr(0x00, pvr0); + get_single_pvr(0, pvr0); pr_debug("%s: pvr0 is 0x%08x\n", __func__, pvr0); if (pvr0 & PVR0_PVR_FULL_MASK) diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c index 79c74659f20..4633c36c1b3 100644 --- a/arch/microblaze/kernel/dma.c +++ b/arch/microblaze/kernel/dma.c @@ -10,44 +10,14 @@ #include <linux/dma-mapping.h> #include <linux/gfp.h> #include <linux/dma-debug.h> -#include <asm/bug.h> -#include <asm/cacheflush.h> - -/* - * Generic direct DMA implementation - * - * This implementation supports a per-device offset that can be applied if - * the address at which memory is visible to devices is not 0. Platform code - * can set archdata.dma_data to an unsigned long holding the offset. By - * default the offset is PCI_DRAM_OFFSET. - */ -static inline void __dma_sync_page(unsigned long paddr, unsigned long offset, - size_t size, enum dma_data_direction direction) -{ - switch (direction) { - case DMA_TO_DEVICE: - flush_dcache_range(paddr + offset, paddr + offset + size); - break; - case DMA_FROM_DEVICE: - invalidate_dcache_range(paddr + offset, paddr + offset + size); - break; - default: - BUG(); - } -} - -static unsigned long get_dma_direct_offset(struct device *dev) -{ - if (likely(dev)) - return (unsigned long)dev->archdata.dma_data; - - return PCI_DRAM_OFFSET; /* FIXME Not sure if is correct */ -} +#include <linux/export.h> +#include <linux/bug.h> #define NOT_COHERENT_CACHE static void *dma_direct_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { #ifdef NOT_COHERENT_CACHE return consistent_alloc(flag, size, dma_handle); @@ -64,14 +34,15 @@ static void *dma_direct_alloc_coherent(struct device *dev, size_t size, return NULL; ret = page_address(page); memset(ret, 0, size); - *dma_handle = virt_to_phys(ret) + get_dma_direct_offset(dev); + *dma_handle = virt_to_phys(ret); return ret; #endif } static void dma_direct_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { #ifdef NOT_COHERENT_CACHE consistent_free(size, vaddr); @@ -89,20 +60,14 @@ static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, /* FIXME this part of code is untested */ for_each_sg(sgl, sg, nents, i) { - sg->dma_address = sg_phys(sg) + get_dma_direct_offset(dev); - __dma_sync_page(page_to_phys(sg_page(sg)), sg->offset, + sg->dma_address = sg_phys(sg); + __dma_sync(page_to_phys(sg_page(sg)) + sg->offset, sg->length, direction); } return nents; } -static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction direction, - struct dma_attrs *attrs) -{ -} - static int dma_direct_dma_supported(struct device *dev, u64 mask) { return 1; @@ -115,8 +80,8 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev, enum dma_data_direction direction, struct dma_attrs *attrs) { - __dma_sync_page(page_to_phys(page), offset, size, direction); - return page_to_phys(page) + offset + get_dma_direct_offset(dev); + __dma_sync(page_to_phys(page) + offset, size, direction); + return page_to_phys(page) + offset; } static inline void dma_direct_unmap_page(struct device *dev, @@ -130,17 +95,76 @@ static inline void dma_direct_unmap_page(struct device *dev, * phys_to_virt is here because in __dma_sync_page is __virt_to_phys and * dma_address is physical address */ - __dma_sync_page(dma_address, 0 , size, direction); + __dma_sync(dma_address, size, direction); +} + +static inline void +dma_direct_sync_single_for_cpu(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + /* + * It's pointless to flush the cache as the memory segment + * is given to the CPU + */ + + if (direction == DMA_FROM_DEVICE) + __dma_sync(dma_handle, size, direction); +} + +static inline void +dma_direct_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + /* + * It's pointless to invalidate the cache if the device isn't + * supposed to write to the relevant region + */ + + if (direction == DMA_TO_DEVICE) + __dma_sync(dma_handle, size, direction); +} + +static inline void +dma_direct_sync_sg_for_cpu(struct device *dev, + struct scatterlist *sgl, int nents, + enum dma_data_direction direction) +{ + struct scatterlist *sg; + int i; + + /* FIXME this part of code is untested */ + if (direction == DMA_FROM_DEVICE) + for_each_sg(sgl, sg, nents, i) + __dma_sync(sg->dma_address, sg->length, direction); +} + +static inline void +dma_direct_sync_sg_for_device(struct device *dev, + struct scatterlist *sgl, int nents, + enum dma_data_direction direction) +{ + struct scatterlist *sg; + int i; + + /* FIXME this part of code is untested */ + if (direction == DMA_TO_DEVICE) + for_each_sg(sgl, sg, nents, i) + __dma_sync(sg->dma_address, sg->length, direction); } struct dma_map_ops dma_direct_ops = { - .alloc_coherent = dma_direct_alloc_coherent, - .free_coherent = dma_direct_free_coherent, + .alloc = dma_direct_alloc_coherent, + .free = dma_direct_free_coherent, .map_sg = dma_direct_map_sg, - .unmap_sg = dma_direct_unmap_sg, .dma_supported = dma_direct_dma_supported, .map_page = dma_direct_map_page, .unmap_page = dma_direct_unmap_page, + .sync_single_for_cpu = dma_direct_sync_single_for_cpu, + .sync_single_for_device = dma_direct_sync_single_for_device, + .sync_sg_for_cpu = dma_direct_sync_sg_for_cpu, + .sync_sg_for_device = dma_direct_sync_sg_for_device, }; EXPORT_SYMBOL(dma_direct_ops); @@ -149,8 +173,8 @@ EXPORT_SYMBOL(dma_direct_ops); static int __init dma_init(void) { - dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); + dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); - return 0; + return 0; } fs_initcall(dma_init); diff --git a/arch/microblaze/kernel/early_printk.c b/arch/microblaze/kernel/early_printk.c index 7de84923ba0..365f2d53f1b 100644 --- a/arch/microblaze/kernel/early_printk.c +++ b/arch/microblaze/kernel/early_printk.c @@ -21,10 +21,10 @@ #include <asm/setup.h> #include <asm/prom.h> -static u32 early_console_initialized; static u32 base_addr; -static void early_printk_putc(char c) +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE +static void early_printk_uartlite_putc(char c) { /* * Limit how many times we'll spin waiting for TX FIFO status. @@ -34,7 +34,7 @@ static void early_printk_putc(char c) * we'll never timeout on a working UART. */ - unsigned retries = 10000; + unsigned retries = 1000000; /* read status bit - 0x8 offset */ while (--retries && (in_be32(base_addr + 8) & (1 << 3))) ; @@ -45,66 +45,140 @@ static void early_printk_putc(char c) out_be32(base_addr + 4, c & 0xff); } -static void early_printk_write(struct console *unused, +static void early_printk_uartlite_write(struct console *unused, const char *s, unsigned n) { while (*s && n-- > 0) { - early_printk_putc(*s); if (*s == '\n') - early_printk_putc('\r'); + early_printk_uartlite_putc('\r'); + early_printk_uartlite_putc(*s); s++; } } -static struct console early_serial_console = { +static struct console early_serial_uartlite_console = { .name = "earlyser", - .write = early_printk_write, - .flags = CON_PRINTBUFFER, + .write = early_printk_uartlite_write, + .flags = CON_PRINTBUFFER | CON_BOOT, .index = -1, }; +#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */ + +#ifdef CONFIG_SERIAL_8250_CONSOLE +static void early_printk_uart16550_putc(char c) +{ + /* + * Limit how many times we'll spin waiting for TX FIFO status. + * This will prevent lockups if the base address is incorrectly + * set, or any other issue on the UARTLITE. + * This limit is pretty arbitrary, unless we are at about 10 baud + * we'll never timeout on a working UART. + */ -static struct console *early_console = &early_serial_console; + #define UART_LSR_TEMT 0x40 /* Transmitter empty */ + #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ + #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) -void early_printk(const char *fmt, ...) + unsigned retries = 10000; + + while (--retries && + !((in_be32(base_addr + 0x14) & BOTH_EMPTY) == BOTH_EMPTY)) + ; + + if (retries) + out_be32(base_addr, c & 0xff); +} + +static void early_printk_uart16550_write(struct console *unused, + const char *s, unsigned n) { - char buf[512]; - int n; - va_list ap; - - if (early_console_initialized) { - va_start(ap, fmt); - n = vscnprintf(buf, 512, fmt, ap); - early_console->write(early_console, buf, n); - va_end(ap); + while (*s && n-- > 0) { + if (*s == '\n') + early_printk_uart16550_putc('\r'); + early_printk_uart16550_putc(*s); + s++; } } +static struct console early_serial_uart16550_console = { + .name = "earlyser", + .write = early_printk_uart16550_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; +#endif /* CONFIG_SERIAL_8250_CONSOLE */ + int __init setup_early_printk(char *opt) { - if (early_console_initialized) + int version = 0; + + if (early_console) return 1; - base_addr = early_uartlite_console(); + base_addr = of_early_console(&version); if (base_addr) { - early_console_initialized = 1; #ifdef CONFIG_MMU early_console_reg_tlb_alloc(base_addr); #endif - early_printk("early_printk_console is enabled at 0x%08x\n", - base_addr); - - /* register_console(early_console); */ + switch (version) { +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + case UARTLITE: + pr_info("Early console on uartlite at 0x%08x\n", + base_addr); + early_console = &early_serial_uartlite_console; + break; +#endif +#ifdef CONFIG_SERIAL_8250_CONSOLE + case UART16550: + pr_info("Early console on uart16650 at 0x%08x\n", + base_addr); + early_console = &early_serial_uart16550_console; + break; +#endif + default: + pr_info("Unsupported early console %d\n", + version); + return 1; + } + register_console(early_console); return 0; - } else - return 1; + } + return 1; +} + +/* Remap early console to virtual address and do not allocate one TLB + * only for early console because of performance degression */ +void __init remap_early_printk(void) +{ + if (!early_console) + return; + pr_info("early_printk_console remapping from 0x%x to ", base_addr); + base_addr = (u32) ioremap(base_addr, PAGE_SIZE); + pr_cont("0x%x\n", base_addr); + +#ifdef CONFIG_MMU + /* + * Early console is on the top of skipped TLB entries + * decrease tlb_skip size ensure that hardcoded TLB entry will be + * used by generic algorithm + * FIXME check if early console mapping is on the top by rereading + * TLB entry and compare baseaddr + * mts rtlbx, (tlb_skip - 1) + * nop + * mfs rX, rtlblo + * nop + * cmp rX, orig_base_addr + */ + tlb_skip -= 1; +#endif } void __init disable_early_printk(void) { - if (!early_console_initialized || !early_console) + if (!early_console) return; - printk(KERN_WARNING "disabling early console\n"); + pr_warn("disabling early console\n"); unregister_console(early_console); - early_console_initialized = 0; + early_console = NULL; } diff --git a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S index 8cc18cd2cce..7e394fc2c43 100644 --- a/arch/microblaze/kernel/entry-nommu.S +++ b/arch/microblaze/kernel/entry-nommu.S @@ -115,7 +115,7 @@ ENTRY(_interrupt) /* restore r31 */ lwi r31, r0, PER_CPU(CURRENT_SAVE) /* prepare the link register, the argument and jump */ - la r15, r0, ret_from_intr - 8 + addik r15, r0, ret_from_intr - 8 addk r6, r0, r15 braid do_IRQ add r5, r0, r1 @@ -124,6 +124,7 @@ ret_from_intr: lwi r11, r1, PT_MODE bneid r11, no_intr_resched +3: lwi r6, r31, TS_THREAD_INFO /* get thread info */ lwi r19, r6, TI_FLAGS /* get flags in thread info */ /* do an extra work if any bits are set */ @@ -132,12 +133,13 @@ ret_from_intr: beqi r11, 1f bralid r15, schedule nop -1: andi r11, r19, _TIF_SIGPENDING + bri 3b +1: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME beqid r11, no_intr_resched addk r5, r1, r0 - addk r7, r0, r0 - bralid r15, do_signal + bralid r15, do_notify_resume addk r6, r0, r0 + bri 3b no_intr_resched: /* Disable interrupts, we are now committed to the state restore */ @@ -281,9 +283,10 @@ ENTRY(_user_exception) /* Figure out which function to use for this system call. */ /* Note Microblaze barrel shift is optional, so don't rely on it */ add r12, r12, r12 /* convert num -> ptr */ + addik r30, r0, 1 /* restarts allowed */ add r12, r12, r12 lwi r12, r12, sys_call_table /* Get function pointer */ - la r15, r0, ret_to_user-8 /* set return address */ + addik r15, r0, ret_to_user-8 /* set return address */ bra r12 /* Make the system call. */ bri 0 /* won't reach here */ 1: @@ -292,8 +295,8 @@ ENTRY(_user_exception) /* * Debug traps are like a system call, but entered via brki r14, 0x60 - * All we need to do is send the SIGTRAP signal to current, ptrace and do_signal - * will handle the rest + * All we need to do is send the SIGTRAP signal to current, ptrace and + * do_notify_resume will handle the rest */ ENTRY(_debug_exception) swi r1, r0, PER_CPU(ENTRY_SP) /* save the current sp */ @@ -370,6 +373,7 @@ ENTRY(_debug_exception) bralid r15, send_sig add r7, r0, r0 /* 3rd param zero */ + addik r30, r0, 1 /* restarts allowed ??? */ /* Restore r3/r4 to work around how ret_to_user works */ lwi r3, r1, PT_R3 lwi r4, r1, PT_R4 @@ -466,7 +470,6 @@ ENTRY(_switch_to) ENTRY(ret_from_fork) addk r5, r0, r3 - addk r6, r0, r1 brlid r15, schedule_tail nop swi r31, r1, PT_R31 /* save r31 in user context. */ @@ -475,20 +478,35 @@ ENTRY(ret_from_fork) brid ret_to_user nop +ENTRY(ret_from_kernel_thread) + brlid r15, schedule_tail + addk r5, r0, r3 + brald r15, r20 + addk r5, r0, r19 + brid ret_to_user + addk r3, r0, r0 + work_pending: + lwi r11, r1, PT_MODE + bneid r11, 2f +3: enable_irq - andi r11, r19, _TIF_NEED_RESCHED beqi r11, 1f bralid r15, schedule nop -1: andi r11, r19, _TIF_SIGPENDING + bri 4f +1: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME beqi r11, no_work_pending - addk r5, r1, r0 - addik r7, r0, 1 - bralid r15, do_signal - addk r6, r0, r0 - bri no_work_pending + addk r5, r30, r0 + bralid r15, do_notify_resume + addik r6, r0, 1 + addk r30, r0, r0 /* no restarts from now on */ +4: + disable_irq + lwi r6, r31, TS_THREAD_INFO /* get thread info */ + lwi r19, r6, TI_FLAGS /* get flags in thread info */ + bri 3b ENTRY(ret_to_user) disable_irq @@ -502,6 +520,7 @@ ENTRY(ret_to_user) no_work_pending: disable_irq +2: /* save r31 */ swi r31, r0, PER_CPU(CURRENT_SAVE) /* save mode indicator */ @@ -553,26 +572,11 @@ no_work_pending: rtid r14, 0 nop -sys_vfork: - brid microblaze_vfork - addk r5, r1, r0 - -sys_clone: - brid microblaze_clone - addk r7, r1, r0 - -sys_execve: - brid microblaze_execve - addk r8, r1, r0 - sys_rt_sigreturn_wrapper: + addk r30, r0, r0 /* no restarts for this one */ brid sys_rt_sigreturn addk r5, r1, r0 -sys_rt_sigsuspend_wrapper: - brid sys_rt_sigsuspend - addk r7, r1, r0 - /* Interrupt vector table */ .section .init.ivt, "ax" .org 0x0 @@ -588,3 +592,31 @@ sys_rt_sigsuspend_wrapper: #include "syscall_table.S" syscall_table_size=(.-sys_call_table) + +type_SYSCALL: + .ascii "SYSCALL\0" +type_IRQ: + .ascii "IRQ\0" +type_IRQ_PREEMPT: + .ascii "IRQ (PREEMPTED)\0" +type_SYSCALL_PREEMPT: + .ascii " SYSCALL (PREEMPTED)\0" + + /* + * Trap decoding for stack unwinder + * Tuples are (start addr, end addr, string) + * If return address lies on [start addr, end addr], + * unwinder displays 'string' + */ + + .align 4 +.global microblaze_trap_handlers +microblaze_trap_handlers: + /* Exact matches come first */ + .word ret_to_user ; .word ret_to_user ; .word type_SYSCALL + .word ret_from_intr; .word ret_from_intr ; .word type_IRQ + /* Fuzzy matches go here */ + .word ret_from_intr; .word no_intr_resched; .word type_IRQ_PREEMPT + .word work_pending ; .word no_work_pending; .word type_SYSCALL_PREEMPT + /* End of table */ + .word 0 ; .word 0 ; .word 0 diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S index c0ede25c5b9..0536bc021cc 100644 --- a/arch/microblaze/kernel/entry.S +++ b/arch/microblaze/kernel/entry.S @@ -33,11 +33,14 @@ #undef DEBUG -/* The size of a state save frame. */ -#define STATE_SAVE_SIZE (PT_SIZE + STATE_SAVE_ARG_SPACE) - -/* The offset of the struct pt_regs in a `state save frame' on the stack. */ -#define PTO STATE_SAVE_ARG_SPACE /* 24 the space for args */ +#ifdef DEBUG +/* Create space for syscalls counting. */ +.section .data +.global syscall_debug_table +.align 4 +syscall_debug_table: + .space (__NR_syscalls * 4) +#endif /* DEBUG */ #define C_ENTRY(name) .globl name; .align 4; name @@ -48,128 +51,107 @@ */ #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR .macro clear_bip - msrclr r11, MSR_BIP - nop + msrclr r0, MSR_BIP .endm .macro set_bip - msrset r11, MSR_BIP - nop + msrset r0, MSR_BIP .endm .macro clear_eip - msrclr r11, MSR_EIP - nop + msrclr r0, MSR_EIP .endm .macro set_ee - msrset r11, MSR_EE - nop + msrset r0, MSR_EE .endm .macro disable_irq - msrclr r11, MSR_IE - nop + msrclr r0, MSR_IE .endm .macro enable_irq - msrset r11, MSR_IE - nop + msrset r0, MSR_IE .endm .macro set_ums - msrset r11, MSR_UMS - nop - msrclr r11, MSR_VMS - nop + msrset r0, MSR_UMS + msrclr r0, MSR_VMS .endm .macro set_vms - msrclr r11, MSR_UMS - nop - msrset r11, MSR_VMS - nop + msrclr r0, MSR_UMS + msrset r0, MSR_VMS + .endm + + .macro clear_ums + msrclr r0, MSR_UMS .endm .macro clear_vms_ums - msrclr r11, MSR_VMS - nop - msrclr r11, MSR_UMS - nop + msrclr r0, MSR_VMS | MSR_UMS .endm #else .macro clear_bip mfs r11, rmsr - nop andi r11, r11, ~MSR_BIP mts rmsr, r11 - nop .endm .macro set_bip mfs r11, rmsr - nop ori r11, r11, MSR_BIP mts rmsr, r11 - nop .endm .macro clear_eip mfs r11, rmsr - nop andi r11, r11, ~MSR_EIP mts rmsr, r11 - nop .endm .macro set_ee mfs r11, rmsr - nop ori r11, r11, MSR_EE mts rmsr, r11 - nop .endm .macro disable_irq mfs r11, rmsr - nop andi r11, r11, ~MSR_IE mts rmsr, r11 - nop .endm .macro enable_irq mfs r11, rmsr - nop ori r11, r11, MSR_IE mts rmsr, r11 - nop .endm .macro set_ums mfs r11, rmsr - nop ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 - nop .endm .macro set_vms mfs r11, rmsr - nop ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 - nop + .endm + + .macro clear_ums + mfs r11, rmsr + andni r11, r11, MSR_UMS + mts rmsr,r11 .endm .macro clear_vms_ums mfs r11, rmsr - nop andni r11, r11, (MSR_VMS|MSR_UMS) mts rmsr,r11 - nop .endm #endif @@ -180,77 +162,118 @@ /* turn on virtual protected mode save */ #define VM_ON \ - set_ums; \ + set_ums; \ rted r0, 2f; \ -2: nop; + nop; \ +2: /* turn off virtual protected mode save and user mode save*/ #define VM_OFF \ - clear_vms_ums; \ + clear_vms_ums; \ rted r0, TOPHYS(1f); \ -1: nop; + nop; \ +1: #define SAVE_REGS \ - swi r2, r1, PTO+PT_R2; /* Save SDA */ \ - swi r5, r1, PTO+PT_R5; \ - swi r6, r1, PTO+PT_R6; \ - swi r7, r1, PTO+PT_R7; \ - swi r8, r1, PTO+PT_R8; \ - swi r9, r1, PTO+PT_R9; \ - swi r10, r1, PTO+PT_R10; \ - swi r11, r1, PTO+PT_R11; /* save clobbered regs after rval */\ - swi r12, r1, PTO+PT_R12; \ - swi r13, r1, PTO+PT_R13; /* Save SDA2 */ \ - swi r14, r1, PTO+PT_PC; /* PC, before IRQ/trap */ \ - swi r15, r1, PTO+PT_R15; /* Save LP */ \ - swi r18, r1, PTO+PT_R18; /* Save asm scratch reg */ \ - swi r19, r1, PTO+PT_R19; \ - swi r20, r1, PTO+PT_R20; \ - swi r21, r1, PTO+PT_R21; \ - swi r22, r1, PTO+PT_R22; \ - swi r23, r1, PTO+PT_R23; \ - swi r24, r1, PTO+PT_R24; \ - swi r25, r1, PTO+PT_R25; \ - swi r26, r1, PTO+PT_R26; \ - swi r27, r1, PTO+PT_R27; \ - swi r28, r1, PTO+PT_R28; \ - swi r29, r1, PTO+PT_R29; \ - swi r30, r1, PTO+PT_R30; \ - swi r31, r1, PTO+PT_R31; /* Save current task reg */ \ + swi r2, r1, PT_R2; /* Save SDA */ \ + swi r3, r1, PT_R3; \ + swi r4, r1, PT_R4; \ + swi r5, r1, PT_R5; \ + swi r6, r1, PT_R6; \ + swi r7, r1, PT_R7; \ + swi r8, r1, PT_R8; \ + swi r9, r1, PT_R9; \ + swi r10, r1, PT_R10; \ + swi r11, r1, PT_R11; /* save clobbered regs after rval */\ + swi r12, r1, PT_R12; \ + swi r13, r1, PT_R13; /* Save SDA2 */ \ + swi r14, r1, PT_PC; /* PC, before IRQ/trap */ \ + swi r15, r1, PT_R15; /* Save LP */ \ + swi r16, r1, PT_R16; \ + swi r17, r1, PT_R17; \ + swi r18, r1, PT_R18; /* Save asm scratch reg */ \ + swi r19, r1, PT_R19; \ + swi r20, r1, PT_R20; \ + swi r21, r1, PT_R21; \ + swi r22, r1, PT_R22; \ + swi r23, r1, PT_R23; \ + swi r24, r1, PT_R24; \ + swi r25, r1, PT_R25; \ + swi r26, r1, PT_R26; \ + swi r27, r1, PT_R27; \ + swi r28, r1, PT_R28; \ + swi r29, r1, PT_R29; \ + swi r30, r1, PT_R30; \ + swi r31, r1, PT_R31; /* Save current task reg */ \ mfs r11, rmsr; /* save MSR */ \ - nop; \ - swi r11, r1, PTO+PT_MSR; + swi r11, r1, PT_MSR; #define RESTORE_REGS \ - lwi r11, r1, PTO+PT_MSR; \ + lwi r11, r1, PT_MSR; \ mts rmsr , r11; \ - nop; \ - lwi r2, r1, PTO+PT_R2; /* restore SDA */ \ - lwi r5, r1, PTO+PT_R5; \ - lwi r6, r1, PTO+PT_R6; \ - lwi r7, r1, PTO+PT_R7; \ - lwi r8, r1, PTO+PT_R8; \ - lwi r9, r1, PTO+PT_R9; \ - lwi r10, r1, PTO+PT_R10; \ - lwi r11, r1, PTO+PT_R11; /* restore clobbered regs after rval */\ - lwi r12, r1, PTO+PT_R12; \ - lwi r13, r1, PTO+PT_R13; /* restore SDA2 */ \ - lwi r14, r1, PTO+PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\ - lwi r15, r1, PTO+PT_R15; /* restore LP */ \ - lwi r18, r1, PTO+PT_R18; /* restore asm scratch reg */ \ - lwi r19, r1, PTO+PT_R19; \ - lwi r20, r1, PTO+PT_R20; \ - lwi r21, r1, PTO+PT_R21; \ - lwi r22, r1, PTO+PT_R22; \ - lwi r23, r1, PTO+PT_R23; \ - lwi r24, r1, PTO+PT_R24; \ - lwi r25, r1, PTO+PT_R25; \ - lwi r26, r1, PTO+PT_R26; \ - lwi r27, r1, PTO+PT_R27; \ - lwi r28, r1, PTO+PT_R28; \ - lwi r29, r1, PTO+PT_R29; \ - lwi r30, r1, PTO+PT_R30; \ - lwi r31, r1, PTO+PT_R31; /* Restore cur task reg */ + lwi r2, r1, PT_R2; /* restore SDA */ \ + lwi r3, r1, PT_R3; \ + lwi r4, r1, PT_R4; \ + lwi r5, r1, PT_R5; \ + lwi r6, r1, PT_R6; \ + lwi r7, r1, PT_R7; \ + lwi r8, r1, PT_R8; \ + lwi r9, r1, PT_R9; \ + lwi r10, r1, PT_R10; \ + lwi r11, r1, PT_R11; /* restore clobbered regs after rval */\ + lwi r12, r1, PT_R12; \ + lwi r13, r1, PT_R13; /* restore SDA2 */ \ + lwi r14, r1, PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\ + lwi r15, r1, PT_R15; /* restore LP */ \ + lwi r16, r1, PT_R16; \ + lwi r17, r1, PT_R17; \ + lwi r18, r1, PT_R18; /* restore asm scratch reg */ \ + lwi r19, r1, PT_R19; \ + lwi r20, r1, PT_R20; \ + lwi r21, r1, PT_R21; \ + lwi r22, r1, PT_R22; \ + lwi r23, r1, PT_R23; \ + lwi r24, r1, PT_R24; \ + lwi r25, r1, PT_R25; \ + lwi r26, r1, PT_R26; \ + lwi r27, r1, PT_R27; \ + lwi r28, r1, PT_R28; \ + lwi r29, r1, PT_R29; \ + lwi r30, r1, PT_R30; \ + lwi r31, r1, PT_R31; /* Restore cur task reg */ + +#define SAVE_STATE \ + swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \ + /* See if already in kernel mode.*/ \ + mfs r1, rmsr; \ + andi r1, r1, MSR_UMS; \ + bnei r1, 1f; \ + /* Kernel-mode state save. */ \ + /* Reload kernel stack-ptr. */ \ + lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ + /* FIXME: I can add these two lines to one */ \ + /* tophys(r1,r1); */ \ + /* addik r1, r1, -PT_SIZE; */ \ + addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \ + SAVE_REGS \ + brid 2f; \ + swi r1, r1, PT_MODE; \ +1: /* User-mode state save. */ \ + lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ + tophys(r1,r1); \ + lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ + /* MS these three instructions can be added to one */ \ + /* addik r1, r1, THREAD_SIZE; */ \ + /* tophys(r1,r1); */ \ + /* addik r1, r1, -PT_SIZE; */ \ + addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \ + SAVE_REGS \ + lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ + swi r11, r1, PT_R1; /* Store user SP. */ \ + swi r0, r1, PT_MODE; /* Was in user-mode. */ \ + /* MS: I am clearing UMS even in case when I come from kernel space */ \ + clear_ums; \ +2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); .text @@ -269,27 +292,7 @@ C_ENTRY(_user_exception): swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ addi r14, r14, 4 /* return address is 4 byte after call */ - swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ - - lwi r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/ - beqi r11, 1f; /* Jump ahead if coming from user */ -/* Kernel-mode state save. */ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ - tophys(r1,r11); - swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ - - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ - SAVE_REGS - addi r11, r0, 1; /* Was in kernel-mode. */ - swi r11, r1, PTO+PT_MODE; /* pt_regs -> kernel mode */ - brid 2f; - nop; /* Fill delay slot */ - -/* User-mode state save. */ -1: - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ tophys(r1,r1); lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */ @@ -297,17 +300,18 @@ C_ENTRY(_user_exception): addik r1, r1, THREAD_SIZE; tophys(r1,r1); - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ + addik r1, r1, -PT_SIZE; /* Make room on the stack. */ SAVE_REGS + swi r0, r1, PT_R3 + swi r0, r1, PT_R4 - swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ + swi r0, r1, PT_MODE; /* Was in user-mode. */ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); - swi r11, r1, PTO+PT_R1; /* Store user SP. */ - addi r11, r0, 1; - swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode. */ + swi r11, r1, PT_R1; /* Store user SP. */ + clear_ums; 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* Save away the syscall number. */ - swi r12, r1, PTO+PT_R0; + swi r12, r1, PT_R0; tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8*/ @@ -316,10 +320,8 @@ C_ENTRY(_user_exception): * register should point to the location where * the called function should return. [note that MAKE_SYS_CALL uses label 1] */ - # Step into virtual mode. - set_vms; - addik r11, r0, 3f - rtid r11, 0 + /* Step into virtual mode */ + rtbd r0, 3f nop 3: lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */ @@ -328,18 +330,18 @@ C_ENTRY(_user_exception): beqi r11, 4f addik r3, r0, -ENOSYS - swi r3, r1, PTO + PT_R3 + swi r3, r1, PT_R3 brlid r15, do_syscall_trace_enter - addik r5, r1, PTO + PT_R0 + addik r5, r1, PT_R0 # do_syscall_trace_enter returns the new syscall nr. addk r12, r0, r3 - lwi r5, r1, PTO+PT_R5; - lwi r6, r1, PTO+PT_R6; - lwi r7, r1, PTO+PT_R7; - lwi r8, r1, PTO+PT_R8; - lwi r9, r1, PTO+PT_R9; - lwi r10, r1, PTO+PT_R10; + lwi r5, r1, PT_R5; + lwi r6, r1, PT_R6; + lwi r7, r1, PT_R7; + lwi r8, r1, PT_R8; + lwi r9, r1, PT_R9; + lwi r10, r1, PT_R10; 4: /* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. @@ -352,38 +354,39 @@ C_ENTRY(_user_exception): /* Note Microblaze barrel shift is optional, so don't rely on it */ add r12, r12, r12; /* convert num -> ptr */ add r12, r12, r12; + addi r30, r0, 1 /* restarts allowed */ #ifdef DEBUG - /* Trac syscalls and stored them to r0_ram */ - lwi r3, r12, 0x400 + r0_ram + /* Trac syscalls and stored them to syscall_debug_table */ + /* The first syscall location stores total syscall number */ + lwi r3, r0, syscall_debug_table + addi r3, r3, 1 + swi r3, r0, syscall_debug_table + lwi r3, r12, syscall_debug_table addi r3, r3, 1 - swi r3, r12, 0x400 + r0_ram + swi r3, r12, syscall_debug_table #endif # Find and jump into the syscall handler. lwi r12, r12, sys_call_table /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_trap-8 + addi r15, r0, ret_from_trap-8 bra r12 /* The syscall number is invalid, return an error. */ 5: + rtsd r15, 8; /* looks like a normal subroutine return */ addi r3, r0, -ENOSYS; - rtsd r15,8; /* looks like a normal subroutine return */ - or r0, r0, r0 - /* Entry point used to return from a syscall/trap */ /* We re-enable BIP bit before state restore */ C_ENTRY(ret_from_trap): - set_bip; /* Ints masked for state restore*/ - lwi r11, r1, PTO+PT_MODE; + swi r3, r1, PT_R3 + swi r4, r1, PT_R4 + + lwi r11, r1, PT_MODE; /* See if returning to kernel mode, if so, skip resched &c. */ bnei r11, 2f; - - swi r3, r1, PTO + PT_R3 - swi r4, r1, PTO + PT_R4 - /* We're returning to user mode, so check for various conditions that * trigger rescheduling. */ /* FIXME: Restructure all these flag checks. */ @@ -393,50 +396,47 @@ C_ENTRY(ret_from_trap): beqi r11, 1f brlid r15, do_syscall_trace_leave - addik r5, r1, PTO + PT_R0 + addik r5, r1, PT_R0 1: /* We're returning to user mode, so check for various conditions that * trigger rescheduling. */ /* get thread info from current task */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_NEED_RESCHED; + lwi r19, r11, TI_FLAGS; /* get flags in thread info */ + andi r11, r19, _TIF_NEED_RESCHED; beqi r11, 5f; bralid r15, schedule; /* Call scheduler */ nop; /* delay slot */ + bri 1b /* Maybe handle a signal */ -5: /* get thread info from current task*/ - lwi r11, CURRENT_TASK, TS_THREAD_INFO; - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_SIGPENDING; - beqi r11, 1f; /* Signals to handle, handle them */ +5: + andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME; + beqi r11, 4f; /* Signals to handle, handle them */ - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ - addi r7, r0, 1; /* Arg 3: int in_syscall */ - bralid r15, do_signal; /* Handle any signals */ - add r6, r0, r0; /* Arg 2: sigset_t *oldset */ + addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */ + bralid r15, do_notify_resume; /* Handle any signals */ + add r6, r30, r0; /* Arg 2: int in_syscall */ + add r30, r0, r0 /* no more restarts */ + bri 1b /* Finally, return to user state. */ -1: - lwi r3, r1, PTO + PT_R3; /* restore syscall result */ - lwi r4, r1, PTO + PT_R4; - - swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ +4: set_bip; /* Ints masked for state restore */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); RESTORE_REGS; - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ + addik r1, r1, PT_SIZE /* Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */ bri 6f; /* Return to kernel state. */ -2: VM_OFF; +2: set_bip; /* Ints masked for state restore */ + VM_OFF; tophys(r1,r1); RESTORE_REGS; - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ + addik r1, r1, PT_SIZE /* Clean up stack space. */ tovirt(r1,r1); 6: TRAP_return: /* Make global symbol for debugging */ @@ -444,19 +444,6 @@ TRAP_return: /* Make global symbol for debugging */ nop; -/* These syscalls need access to the struct pt_regs on the stack, so we - implement them in assembly (they're basically all wrappers anyway). */ - -C_ENTRY(sys_fork_wrapper): - addi r5, r0, SIGCHLD /* Arg 0: flags */ - lwi r6, r1, PTO+PT_R1 /* Arg 1: child SP (use parent's) */ - la r7, r1, PTO /* Arg 2: parent context */ - add r8. r0, r0 /* Arg 3: (unused) */ - add r9, r0, r0; /* Arg 4: (unused) */ - add r10, r0, r0; /* Arg 5: (unused) */ - brid do_fork /* Do real work (tail-call) */ - nop; - /* This the initial entry point for a new child thread, with an appropriate stack in place that makes it look the the child is in the middle of an syscall. This function is actually `returned to' from switch_thread @@ -464,122 +451,45 @@ C_ENTRY(sys_fork_wrapper): saved context). */ C_ENTRY(ret_from_fork): bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ - add r3, r5, r0; /* switch_thread returns the prev task */ + add r5, r3, r0; /* switch_thread returns the prev task */ /* ( in the delay slot ) */ - add r3, r0, r0; /* Child's fork call should return 0. */ brid ret_from_trap; /* Do normal trap return */ - nop; - -C_ENTRY(sys_vfork): - brid microblaze_vfork /* Do real work (tail-call) */ - la r5, r1, PTO - -C_ENTRY(sys_clone): - bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */ - lwi r6, r1, PTO+PT_R1; /* If so, use paret's stack ptr */ -1: la r7, r1, PTO; /* Arg 2: parent context */ - add r8, r0, r0; /* Arg 3: (unused) */ - add r9, r0, r0; /* Arg 4: (unused) */ - add r10, r0, r0; /* Arg 5: (unused) */ - brid do_fork /* Do real work (tail-call) */ - nop; + add r3, r0, r0; /* Child's fork call should return 0. */ -C_ENTRY(sys_execve): - la r8, r1, PTO; /* add user context as 4th arg */ - brid microblaze_execve; /* Do real work (tail-call).*/ - nop; +C_ENTRY(ret_from_kernel_thread): + bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ + add r5, r3, r0; /* switch_thread returns the prev task */ + /* ( in the delay slot ) */ + brald r15, r20 /* fn was left in r20 */ + addk r5, r0, r19 /* ... and argument - in r19 */ + brid ret_from_trap + add r3, r0, r0 C_ENTRY(sys_rt_sigreturn_wrapper): - swi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - swi r4, r1, PTO+PT_R4; - la r5, r1, PTO; /* add user context as 1st arg */ - brlid r15, sys_rt_sigreturn /* Do real work */ - nop; - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; - bri ret_from_trap /* fall through will not work here due to align */ - nop; + addik r30, r0, 0 /* no restarts */ + brid sys_rt_sigreturn /* Do real work */ + addik r5, r1, 0; /* add user context as 1st arg */ /* * HW EXCEPTION rutine start */ - -#define SAVE_STATE \ - swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ \ - set_bip; /*equalize initial state for all possible entries*/\ - clear_eip; \ - enable_irq; \ - set_ee; \ - /* See if already in kernel mode.*/ \ - lwi r11, r0, TOPHYS(PER_CPU(KM)); \ - beqi r11, 1f; /* Jump ahead if coming from user */\ - /* Kernel-mode state save. */ \ - /* Reload kernel stack-ptr. */ \ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ - tophys(r1,r11); \ - swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ \ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\ - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */\ - /* store return registers separately because \ - * this macros is use for others exceptions */ \ - swi r3, r1, PTO + PT_R3; \ - swi r4, r1, PTO + PT_R4; \ - SAVE_REGS \ - /* PC, before IRQ/trap - this is one instruction above */ \ - swi r17, r1, PTO+PT_PC; \ - \ - addi r11, r0, 1; /* Was in kernel-mode. */ \ - swi r11, r1, PTO+PT_MODE; \ - brid 2f; \ - nop; /* Fill delay slot */ \ -1: /* User-mode state save. */ \ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\ - lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ - tophys(r1,r1); \ - lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ - addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */\ - tophys(r1,r1); \ - \ - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */\ - /* store return registers separately because this macros \ - * is use for others exceptions */ \ - swi r3, r1, PTO + PT_R3; \ - swi r4, r1, PTO + PT_R4; \ - SAVE_REGS \ - /* PC, before IRQ/trap - this is one instruction above FIXME*/ \ - swi r17, r1, PTO+PT_PC; \ - \ - swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ \ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ - swi r11, r1, PTO+PT_R1; /* Store user SP. */ \ - addi r11, r0, 1; \ - swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode.*/\ -2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); \ - /* Save away the syscall number. */ \ - swi r0, r1, PTO+PT_R0; \ - tovirt(r1,r1) - C_ENTRY(full_exception_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ /* adjust exception address for privileged instruction * for finding where is it */ addik r17, r17, -4 SAVE_STATE /* Save registers */ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PT_PC; + tovirt(r1,r1) /* FIXME this can be store directly in PT_ESR reg. * I tested it but there is a fault */ /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc - 8 - la r5, r1, PTO /* parameter struct pt_regs * regs */ + addik r15, r0, ret_from_exc - 8 mfs r6, resr - nop mfs r7, rfsr; /* save FSR */ - nop mts rfsr, r0; /* Clear sticky fsr */ - nop - la r12, r0, full_exception - set_vms; - rtbd r12, 0; - nop; + rted r0, full_exception + addik r5, r1, 0 /* parameter struct pt_regs * regs */ /* * Unaligned data trap. @@ -592,19 +502,27 @@ C_ENTRY(full_exception_trap): * The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S" */ C_ENTRY(unaligned_data_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ + /* MS: I have to save r11 value and then restore it because + * set_bit, clear_eip, set_ee use r11 as temp register if MSR + * instructions are not used. We don't need to do if MSR instructions + * are used and they use r0 instead of r11. + * I am using ENTRY_SP which should be primary used only for stack + * pointer saving. */ + swi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); + set_bip; /* equalize initial state for all possible entries */ + clear_eip; + set_ee; + lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); SAVE_STATE /* Save registers.*/ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PT_PC; + tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc-8 + addik r15, r0, ret_from_exc-8 mfs r3, resr /* ESR */ - nop mfs r4, rear /* EAR */ - nop - la r7, r1, PTO /* parameter struct pt_regs * regs */ - la r12, r0, _unaligned_data_exception - set_vms; - rtbd r12, 0; /* interrupts enabled */ - nop; + rtbd r0, _unaligned_data_exception + addik r7, r1, 0 /* parameter struct pt_regs * regs */ /* * Page fault traps. @@ -625,57 +543,51 @@ C_ENTRY(unaligned_data_trap): */ /* data and intruction trap - which is choose is resolved int fault.c */ C_ENTRY(page_fault_data_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ SAVE_STATE /* Save registers.*/ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PT_PC; + tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc-8 - la r5, r1, PTO /* parameter struct pt_regs * regs */ + addik r15, r0, ret_from_exc-8 mfs r6, rear /* parameter unsigned long address */ - nop mfs r7, resr /* parameter unsigned long error_code */ - nop - la r12, r0, do_page_fault - set_vms; - rtbd r12, 0; /* interrupts enabled */ - nop; + rted r0, do_page_fault + addik r5, r1, 0 /* parameter struct pt_regs * regs */ C_ENTRY(page_fault_instr_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ SAVE_STATE /* Save registers.*/ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PT_PC; + tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc-8 - la r5, r1, PTO /* parameter struct pt_regs * regs */ + addik r15, r0, ret_from_exc-8 mfs r6, rear /* parameter unsigned long address */ - nop ori r7, r0, 0 /* parameter unsigned long error_code */ - la r12, r0, do_page_fault - set_vms; - rtbd r12, 0; /* interrupts enabled */ - nop; + rted r0, do_page_fault + addik r5, r1, 0 /* parameter struct pt_regs * regs */ /* Entry point used to return from an exception. */ C_ENTRY(ret_from_exc): - set_bip; /* Ints masked for state restore*/ - lwi r11, r1, PTO+PT_MODE; + lwi r11, r1, PT_MODE; bnei r11, 2f; /* See if returning to kernel mode, */ /* ... if so, skip resched &c. */ /* We're returning to user mode, so check for various conditions that trigger rescheduling. */ +1: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_NEED_RESCHED; + lwi r19, r11, TI_FLAGS; /* get flags in thread info */ + andi r11, r19, _TIF_NEED_RESCHED; beqi r11, 5f; /* Call the scheduler before returning from a syscall/trap. */ bralid r15, schedule; /* Call scheduler */ nop; /* delay slot */ + bri 1b /* Maybe handle a signal */ -5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_SIGPENDING; - beqi r11, 1f; /* Signals to handle, handle them */ +5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME; + beqi r11, 4f; /* Signals to handle, handle them */ /* * Handle a signal return; Pending signals should be in r18. @@ -687,34 +599,29 @@ C_ENTRY(ret_from_exc): * traps), but signal handlers may want to examine or change the * complete register state. Here we save anything not saved by * the normal entry sequence, so that it may be safely restored - * (in a possibly modified form) after do_signal returns. - * store return registers separately because this macros is use - * for others exceptions */ - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ - addi r7, r0, 0; /* Arg 3: int in_syscall */ - bralid r15, do_signal; /* Handle any signals */ - add r6, r0, r0; /* Arg 2: sigset_t *oldset */ + * (in a possibly modified form) after do_notify_resume returns. */ + addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */ + bralid r15, do_notify_resume; /* Handle any signals */ + addi r6, r0, 0; /* Arg 2: int in_syscall */ + bri 1b /* Finally, return to user state. */ -1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ +4: set_bip; /* Ints masked for state restore */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; RESTORE_REGS; - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ + addik r1, r1, PT_SIZE /* Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */ bri 6f; /* Return to kernel state. */ -2: VM_OFF; +2: set_bip; /* Ints masked for state restore */ + VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; RESTORE_REGS; - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ + addik r1, r1, PT_SIZE /* Clean up stack space. */ tovirt(r1,r1); 6: @@ -736,36 +643,23 @@ C_ENTRY(_interrupt): /* MS: we are in physical address */ /* Save registers, switch to proper stack, convert SP to virtual.*/ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) - swi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); /* MS: See if already in kernel mode. */ - lwi r11, r0, TOPHYS(PER_CPU(KM)); - beqi r11, 1f; /* MS: Jump ahead if coming from user */ + mfs r1, rmsr + nop + andi r1, r1, MSR_UMS + bnei r1, 1f /* Kernel-mode state save. */ - or r11, r1, r0 - tophys(r1,r11); /* MS: I have in r1 physical address where stack is */ -/* MS: Save original SP - position PT_R1 to next stack frame 4 *1 - 152*/ - swi r11, r1, (PT_R1 - PT_SIZE); -/* MS: restore r11 because of saving in SAVE_REGS */ - lwi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); + lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) + tophys(r1,r1); /* MS: I have in r1 physical address where stack is */ /* save registers */ /* MS: Make room on the stack -> activation record */ - addik r1, r1, -STATE_SAVE_SIZE; -/* MS: store return registers separately because - * this macros is use for others exceptions */ - swi r3, r1, PTO + PT_R3; - swi r4, r1, PTO + PT_R4; + addik r1, r1, -PT_SIZE; SAVE_REGS - /* MS: store mode */ - addi r11, r0, 1; /* MS: Was in kernel-mode. */ - swi r11, r1, PTO + PT_MODE; /* MS: and save it */ brid 2f; - nop; /* MS: Fill delay slot */ - + swi r1, r1, PT_MODE; /* 0 - user mode, 1 - kernel mode */ 1: /* User-mode state save. */ -/* MS: restore r11 -> FIXME move before SAVE_REG */ - lwi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); /* MS: get the saved current */ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); tophys(r1,r1); @@ -773,64 +667,52 @@ C_ENTRY(_interrupt): addik r1, r1, THREAD_SIZE; tophys(r1,r1); /* save registers */ - addik r1, r1, -STATE_SAVE_SIZE; - swi r3, r1, PTO+PT_R3; - swi r4, r1, PTO+PT_R4; + addik r1, r1, -PT_SIZE; SAVE_REGS /* calculate mode */ - swi r0, r1, PTO + PT_MODE; + swi r0, r1, PT_MODE; lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); - swi r11, r1, PTO+PT_R1; - /* setup kernel mode to KM */ - addi r11, r0, 1; - swi r11, r0, TOPHYS(PER_CPU(KM)); - + swi r11, r1, PT_R1; + clear_ums; 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); - swi r0, r1, PTO + PT_R0; tovirt(r1,r1) - la r5, r1, PTO; - set_vms; - la r11, r0, do_IRQ; - la r15, r0, irq_call; -irq_call:rtbd r11, 0; - nop; + addik r15, r0, irq_call; +irq_call:rtbd r0, do_IRQ; + addik r5, r1, 0; /* MS: we are in virtual mode */ ret_from_irq: - lwi r11, r1, PTO + PT_MODE; + lwi r11, r1, PT_MODE; bnei r11, 2f; +1: lwi r11, CURRENT_TASK, TS_THREAD_INFO; - lwi r11, r11, TI_FLAGS; /* MS: get flags from thread info */ - andi r11, r11, _TIF_NEED_RESCHED; + lwi r19, r11, TI_FLAGS; /* MS: get flags from thread info */ + andi r11, r19, _TIF_NEED_RESCHED; beqi r11, 5f bralid r15, schedule; nop; /* delay slot */ + bri 1b /* Maybe handle a signal */ -5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* MS: get thread info */ - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_SIGPENDING; +5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME; beqid r11, no_intr_resched /* Handle a signal return; Pending signals should be in r18. */ - addi r7, r0, 0; /* Arg 3: int in_syscall */ - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ - bralid r15, do_signal; /* Handle any signals */ - add r6, r0, r0; /* Arg 2: sigset_t *oldset */ + addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */ + bralid r15, do_notify_resume; /* Handle any signals */ + addi r6, r0, 0; /* Arg 2: int in_syscall */ + bri 1b /* Finally, return to user state. */ no_intr_resched: /* Disable interrupts, we are now committed to the state restore */ disable_irq - swi r0, r0, PER_CPU(KM); /* MS: Now officially in user state. */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ - lwi r4, r1, PTO + PT_R4; RESTORE_REGS - addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ + addik r1, r1, PT_SIZE /* MS: Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE; bri 6f; /* MS: Return to kernel state. */ @@ -857,10 +739,8 @@ restore: #endif VM_OFF /* MS: turn off MMU */ tophys(r1,r1) - lwi r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ - lwi r4, r1, PTO + PT_R4; RESTORE_REGS - addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ + addik r1, r1, PT_SIZE /* MS: Clean up stack space. */ tovirt(r1,r1); 6: IRQ_return: /* MS: Make global symbol for debugging */ @@ -868,141 +748,125 @@ IRQ_return: /* MS: Make global symbol for debugging */ nop /* - * `Debug' trap - * We enter dbtrap in "BIP" (breakpoint) mode. - * So we exit the breakpoint mode with an 'rtbd' and proceed with the - * original dbtrap. - * however, wait to save state first + * Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18 + * and call handling function with saved pt_regs */ C_ENTRY(_debug_exception): /* BIP bit is set on entry, no interrupts can occur */ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) - swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ - set_bip; /*equalize initial state for all possible entries*/ - clear_eip; - enable_irq; - lwi r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/ - beqi r11, 1f; /* Jump ahead if coming from user */ - /* Kernel-mode state save. */ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ - tophys(r1,r11); - swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ - - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ - swi r3, r1, PTO + PT_R3; - swi r4, r1, PTO + PT_R4; + mfs r1, rmsr + nop + andi r1, r1, MSR_UMS + bnei r1, 1f +/* MS: Kernel-mode state save - kgdb */ + lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ + + /* BIP bit is set on entry, no interrupts can occur */ + addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; SAVE_REGS; + /* save all regs to pt_reg structure */ + swi r0, r1, PT_R0; /* R0 must be saved too */ + swi r14, r1, PT_R14 /* rewrite saved R14 value */ + swi r16, r1, PT_PC; /* PC and r16 are the same */ + /* save special purpose registers to pt_regs */ + mfs r11, rear; + swi r11, r1, PT_EAR; + mfs r11, resr; + swi r11, r1, PT_ESR; + mfs r11, rfsr; + swi r11, r1, PT_FSR; + + /* stack pointer is in physical address at it is decrease + * by PT_SIZE but we need to get correct R1 value */ + addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + PT_SIZE; + swi r11, r1, PT_R1 + /* MS: r31 - current pointer isn't changed */ + tovirt(r1,r1) +#ifdef CONFIG_KGDB + addi r5, r1, 0 /* pass pt_reg address as the first arg */ + addik r15, r0, dbtrap_call; /* return address */ + rtbd r0, microblaze_kgdb_break + nop; +#endif + /* MS: Place handler for brki from kernel space if KGDB is OFF. + * It is very unlikely that another brki instruction is called. */ + bri 0 - addi r11, r0, 1; /* Was in kernel-mode. */ - swi r11, r1, PTO + PT_MODE; - brid 2f; - nop; /* Fill delay slot */ -1: /* User-mode state save. */ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ - lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ +/* MS: User-mode state save - gdb */ +1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ tophys(r1,r1); lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */ tophys(r1,r1); - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ - swi r3, r1, PTO + PT_R3; - swi r4, r1, PTO + PT_R4; + addik r1, r1, -PT_SIZE; /* Make room on the stack. */ SAVE_REGS; - - swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ + swi r16, r1, PT_PC; /* Save LP */ + swi r0, r1, PT_MODE; /* Was in user-mode. */ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); - swi r11, r1, PTO+PT_R1; /* Store user SP. */ - addi r11, r0, 1; - swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode. */ -2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); - /* Save away the syscall number. */ - swi r0, r1, PTO+PT_R0; + swi r11, r1, PT_R1; /* Store user SP. */ + lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); tovirt(r1,r1) - - addi r5, r0, SIGTRAP /* send the trap signal */ - add r6, r0, CURRENT_TASK; /* Get current task ptr into r11 */ - addk r7, r0, r0 /* 3rd param zero */ - set_vms; - la r11, r0, send_sig; - la r15, r0, dbtrap_call; -dbtrap_call: rtbd r11, 0; - nop; + addik r5, r1, 0; + addik r15, r0, dbtrap_call; +dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */ + rtbd r0, sw_exception + nop - set_bip; /* Ints masked for state restore*/ - lwi r11, r1, PTO+PT_MODE; + /* MS: The first instruction for the second part of the gdb/kgdb */ + set_bip; /* Ints masked for state restore */ + lwi r11, r1, PT_MODE; bnei r11, 2f; - +/* MS: Return to user space - gdb */ +1: /* Get current task ptr into r11 */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_NEED_RESCHED; + lwi r19, r11, TI_FLAGS; /* get flags in thread info */ + andi r11, r19, _TIF_NEED_RESCHED; beqi r11, 5f; -/* Call the scheduler before returning from a syscall/trap. */ - + /* Call the scheduler before returning from a syscall/trap. */ bralid r15, schedule; /* Call scheduler */ nop; /* delay slot */ - /* XXX Is PT_DTRACE handling needed here? */ - /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here. */ + bri 1b /* Maybe handle a signal */ -5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ - lwi r11, r11, TI_FLAGS; /* get flags in thread info */ - andi r11, r11, _TIF_SIGPENDING; - beqi r11, 1f; /* Signals to handle, handle them */ - -/* Handle a signal return; Pending signals should be in r18. */ - /* Not all registers are saved by the normal trap/interrupt entry - points (for instance, call-saved registers (because the normal - C-compiler calling sequence in the kernel makes sure they're - preserved), and call-clobbered registers in the case of - traps), but signal handlers may want to examine or change the - complete register state. Here we save anything not saved by - the normal entry sequence, so that it may be safely restored - (in a possibly modified form) after do_signal returns. */ - - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ - addi r7, r0, 0; /* Arg 3: int in_syscall */ - bralid r15, do_signal; /* Handle any signals */ - add r6, r0, r0; /* Arg 2: sigset_t *oldset */ +5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME; + beqi r11, 4f; /* Signals to handle, handle them */ + addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */ + bralid r15, do_notify_resume; /* Handle any signals */ + addi r6, r0, 0; /* Arg 2: int in_syscall */ + bri 1b /* Finally, return to user state. */ -1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ - swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ +4: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); - - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; + /* MS: Restore all regs */ RESTORE_REGS - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ - - - lwi r1, r1, PT_R1 - PT_SIZE; - /* Restore user stack pointer. */ - bri 6f; + addik r1, r1, PT_SIZE /* Clean up stack space */ + lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */ +DBTRAP_return_user: /* MS: Make global symbol for debugging */ + rtbd r16, 0; /* MS: Instructions to return from a debug trap */ + nop; -/* Return to kernel state. */ +/* MS: Return to kernel state - kgdb */ 2: VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; + /* MS: Restore all regs */ RESTORE_REGS - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ - + lwi r14, r1, PT_R14; + lwi r16, r1, PT_PC; + addik r1, r1, PT_SIZE; /* MS: Clean up stack space */ tovirt(r1,r1); -6: -DBTRAP_return: /* Make global symbol for debugging */ - rtbd r14, 0; /* Instructions to return from an IRQ */ +DBTRAP_return_kernel: /* MS: Make global symbol for debugging */ + rtbd r16, 0; /* MS: Instructions to return from a debug trap */ nop; - ENTRY(_switch_to) /* prepare return value */ addk r3, r0, CURRENT_TASK @@ -1037,16 +901,12 @@ ENTRY(_switch_to) swi r30, r11, CC_R30 /* special purpose registers */ mfs r12, rmsr - nop swi r12, r11, CC_MSR mfs r12, rear - nop swi r12, r11, CC_EAR mfs r12, resr - nop swi r12, r11, CC_ESR mfs r12, rfsr - nop swi r12, r11, CC_FSR /* update r31, the current-give me pointer to task which will be next */ @@ -1085,45 +945,60 @@ ENTRY(_switch_to) /* special purpose registers */ lwi r12, r11, CC_FSR mts rfsr, r12 - nop lwi r12, r11, CC_MSR mts rmsr, r12 - nop rtsd r15, 8 nop ENTRY(_reset) - brai 0x70; /* Jump back to FS-boot */ - -ENTRY(_break) - mfs r5, rmsr - nop - swi r5, r0, 0x250 + TOPHYS(r0_ram) - mfs r5, resr - nop - swi r5, r0, 0x254 + TOPHYS(r0_ram) - bri 0 + brai 0; /* Jump to reset vector */ /* These are compiled and loaded into high memory, then * copied into place in mach_early_setup */ .section .init.ivt, "ax" +#if CONFIG_MANUAL_RESET_VECTOR .org 0x0 - /* this is very important - here is the reset vector */ - /* in current MMU branch you don't care what is here - it is - * used from bootloader site - but this is correct for FS-BOOT */ - brai 0x70 - nop + brai CONFIG_MANUAL_RESET_VECTOR +#endif + .org 0x8 brai TOPHYS(_user_exception); /* syscall handler */ + .org 0x10 brai TOPHYS(_interrupt); /* Interrupt handler */ - brai TOPHYS(_break); /* nmi trap handler */ + .org 0x18 + brai TOPHYS(_debug_exception); /* debug trap handler */ + .org 0x20 brai TOPHYS(_hw_exception_handler); /* HW exception handler */ - .org 0x60 - brai TOPHYS(_debug_exception); /* debug trap handler*/ - .section .rodata,"a" #include "syscall_table.S" syscall_table_size=(.-sys_call_table) +type_SYSCALL: + .ascii "SYSCALL\0" +type_IRQ: + .ascii "IRQ\0" +type_IRQ_PREEMPT: + .ascii "IRQ (PREEMPTED)\0" +type_SYSCALL_PREEMPT: + .ascii " SYSCALL (PREEMPTED)\0" + + /* + * Trap decoding for stack unwinder + * Tuples are (start addr, end addr, string) + * If return address lies on [start addr, end addr], + * unwinder displays 'string' + */ + + .align 4 +.global microblaze_trap_handlers +microblaze_trap_handlers: + /* Exact matches come first */ + .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL + .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ + /* Fuzzy matches go here */ + .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT + .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT + /* End of table */ + .word 0 ; .word 0 ; .word 0 diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index 02cbdfe5aa8..42dd12a62ff 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c @@ -13,11 +13,11 @@ * This file handles the architecture-dependent parts of hardware exceptions */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/kallsyms.h> -#include <linux/module.h> #include <asm/exceptions.h> #include <asm/entry.h> /* For KM CPU var */ @@ -25,6 +25,7 @@ #include <linux/errno.h> #include <linux/ptrace.h> #include <asm/current.h> +#include <asm/cacheflush.h> #define MICROBLAZE_ILL_OPCODE_EXCEPTION 0x02 #define MICROBLAZE_IBUS_EXCEPTION 0x03 @@ -39,7 +40,7 @@ void die(const char *str, struct pt_regs *fp, long err) { console_verbose(); spin_lock_irq(&die_lock); - printk(KERN_WARNING "Oops: %s, sig: %ld\n", str, err); + pr_warn("Oops: %s, sig: %ld\n", str, err); show_regs(fp); spin_unlock_irq(&die_lock); /* do_exit() should take care of panic'ing from an interrupt @@ -48,14 +49,21 @@ void die(const char *str, struct pt_regs *fp, long err) do_exit(err); } +/* for user application debugging */ +asmlinkage void sw_exception(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs, TRAP_BRKPT, regs->r16); + flush_dcache_range(regs->r16, regs->r16 + 0x4); + flush_icache_range(regs->r16, regs->r16 + 0x4); +} + void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) { siginfo_t info; - if (kernel_mode(regs)) { - debugger(regs); + if (kernel_mode(regs)) die("Exception in kernel mode", regs, signr); - } + info.si_signo = signr; info.si_errno = 0; info.si_code = code; @@ -67,13 +75,11 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, int fsr, int addr) { #ifdef CONFIG_MMU - int code; addr = regs->pc; #endif #if 0 - printk(KERN_WARNING "Exception %02x in %s mode, FSR=%08x PC=%08x " \ - "ESR=%08x\n", + pr_warn("Exception %02x in %s mode, FSR=%08x PC=%08x ESR=%08x\n", type, user_mode(regs) ? "user" : "kernel", fsr, (unsigned int) regs->pc, (unsigned int) regs->esr); #endif @@ -81,50 +87,42 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, switch (type & 0x1F) { case MICROBLAZE_ILL_OPCODE_EXCEPTION: if (user_mode(regs)) { - pr_debug(KERN_WARNING "Illegal opcode exception " \ - "in user mode.\n"); + pr_debug("Illegal opcode exception in user mode\n"); _exception(SIGILL, regs, ILL_ILLOPC, addr); return; } - printk(KERN_WARNING "Illegal opcode exception " \ - "in kernel mode.\n"); + pr_warn("Illegal opcode exception in kernel mode.\n"); die("opcode exception", regs, SIGBUS); break; case MICROBLAZE_IBUS_EXCEPTION: if (user_mode(regs)) { - pr_debug(KERN_WARNING "Instruction bus error " \ - "exception in user mode.\n"); + pr_debug("Instruction bus error exception in user mode\n"); _exception(SIGBUS, regs, BUS_ADRERR, addr); return; } - printk(KERN_WARNING "Instruction bus error exception " \ - "in kernel mode.\n"); + pr_warn("Instruction bus error exception in kernel mode.\n"); die("bus exception", regs, SIGBUS); break; case MICROBLAZE_DBUS_EXCEPTION: if (user_mode(regs)) { - pr_debug(KERN_WARNING "Data bus error exception " \ - "in user mode.\n"); + pr_debug("Data bus error exception in user mode\n"); _exception(SIGBUS, regs, BUS_ADRERR, addr); return; } - printk(KERN_WARNING "Data bus error exception " \ - "in kernel mode.\n"); + pr_warn("Data bus error exception in kernel mode.\n"); die("bus exception", regs, SIGBUS); break; case MICROBLAZE_DIV_ZERO_EXCEPTION: if (user_mode(regs)) { - pr_debug(KERN_WARNING "Divide by zero exception " \ - "in user mode\n"); - _exception(SIGILL, regs, FPE_INTDIV, addr); + pr_debug("Divide by zero exception in user mode\n"); + _exception(SIGFPE, regs, FPE_INTDIV, addr); return; } - printk(KERN_WARNING "Divide by zero exception " \ - "in kernel mode.\n"); + pr_warn("Divide by zero exception in kernel mode.\n"); die("Divide by zero exception", regs, SIGBUS); break; case MICROBLAZE_FPU_EXCEPTION: - pr_debug(KERN_WARNING "FPU exception\n"); + pr_debug("FPU exception\n"); /* IEEE FP exception */ /* I removed fsr variable and use code var for storing fsr */ if (fsr & FSR_IO) @@ -142,20 +140,14 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, #ifdef CONFIG_MMU case MICROBLAZE_PRIVILEGED_EXCEPTION: - pr_debug(KERN_WARNING "Privileged exception\n"); - /* "brk r0,r0" - used as debug breakpoint */ - if (get_user(code, (unsigned long *)regs->pc) == 0 - && code == 0x980c0000) { - _exception(SIGTRAP, regs, TRAP_BRKPT, addr); - } else { - _exception(SIGILL, regs, ILL_PRVOPC, addr); - } + pr_debug("Privileged exception\n"); + _exception(SIGILL, regs, ILL_PRVOPC, addr); break; #endif default: /* FIXME what to do in unexpected exception */ - printk(KERN_WARNING "Unexpected exception %02x " - "PC=%08x in %s mode\n", type, (unsigned int) addr, + pr_warn("Unexpected exception %02x PC=%08x in %s mode\n", + type, (unsigned int) addr, kernel_mode(regs) ? "kernel" : "user"); } return; diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c index 515feb40455..bbcd2533766 100644 --- a/arch/microblaze/kernel/ftrace.c +++ b/arch/microblaze/kernel/ftrace.c @@ -35,22 +35,25 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) * happen. This tool is too much intrusive to * ignore such a protection. */ - asm volatile(" 1: lwi %0, %2, 0; \ - 2: swi %3, %2, 0; \ - addik %1, r0, 0; \ - 3: \ - .section .fixup, \"ax\"; \ - 4: brid 3b; \ - addik %1, r0, 1; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,4b; \ - .word 2b,4b; \ - .previous;" \ + asm volatile(" 1: lwi %0, %2, 0;" \ + "2: swi %3, %2, 0;" \ + " addik %1, r0, 0;" \ + "3:" \ + " .section .fixup, \"ax\";" \ + "4: brid 3b;" \ + " addik %1, r0, 1;" \ + " .previous;" \ + " .section __ex_table,\"a\";" \ + " .word 1b,4b;" \ + " .word 2b,4b;" \ + " .previous;" \ : "=&r" (old), "=r" (faulted) : "r" (parent), "r" (return_hooker) ); + flush_dcache_range((u32)parent, (u32)parent + 4); + flush_icache_range((u32)parent, (u32)parent + 4); + if (unlikely(faulted)) { ftrace_graph_stop(); WARN_ON(1); @@ -78,16 +81,16 @@ static int ftrace_modify_code(unsigned long addr, unsigned int value) { int faulted = 0; - __asm__ __volatile__(" 1: swi %2, %1, 0; \ - addik %0, r0, 0; \ - 2: \ - .section .fixup, \"ax\"; \ - 3: brid 2b; \ - addik %0, r0, 1; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,3b; \ - .previous;" \ + __asm__ __volatile__(" 1: swi %2, %1, 0;" \ + " addik %0, r0, 0;" \ + "2:" \ + " .section .fixup, \"ax\";" \ + "3: brid 2b;" \ + " addik %0, r0, 1;" \ + " .previous;" \ + " .section __ex_table,\"a\";" \ + " .word 1b,3b;" \ + " .previous;" \ : "=r" (faulted) : "r" (addr), "r" (value) ); @@ -95,6 +98,9 @@ static int ftrace_modify_code(unsigned long addr, unsigned int value) if (unlikely(faulted)) return -EFAULT; + flush_dcache_range(addr, addr + 4); + flush_icache_range(addr, addr + 4); + return 0; } @@ -165,11 +171,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return ret; } -int __init ftrace_dyn_arch_init(void *data) +int __init ftrace_dyn_arch_init(void) { - /* The return code is retured via data */ - *(unsigned long *)data = 0; - return 0; } @@ -195,8 +198,6 @@ int ftrace_update_ftrace_func(ftrace_func_t func) ret += ftrace_modify_code((unsigned long)&ftrace_caller, MICROBLAZE_NOP); - /* All changes are done - lets do caches consistent */ - flush_icache(); return ret; } @@ -210,7 +211,6 @@ int ftrace_enable_ftrace_graph_caller(void) old_jump = *(unsigned int *)ip; /* save jump over instruction */ ret = ftrace_modify_code(ip, MICROBLAZE_NOP); - flush_icache(); pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump); return ret; @@ -222,7 +222,6 @@ int ftrace_disable_ftrace_graph_caller(void) unsigned long ip = (unsigned long)(&ftrace_call_graph); ret = ftrace_modify_code(ip, old_jump); - flush_icache(); pr_debug("%s\n", __func__); return ret; diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S index 1bf73988826..4655ff342c6 100644 --- a/arch/microblaze/kernel/head.S +++ b/arch/microblaze/kernel/head.S @@ -39,17 +39,22 @@ #include <asm/mmu.h> #include <asm/processor.h> -.data +.section .data .global empty_zero_page .align 12 empty_zero_page: - .space 4096 + .space PAGE_SIZE .global swapper_pg_dir swapper_pg_dir: - .space 4096 + .space PAGE_SIZE #endif /* CONFIG_MMU */ +.section .rodata +.align 4 +endian_check: + .word 1 + __HEAD ENTRY(_start) #if CONFIG_KERNEL_BASE_ADDR == 0 @@ -58,34 +63,43 @@ ENTRY(_start) real_start: #endif - mfs r1, rmsr - andi r1, r1, ~2 - mts rmsr, r1 + mts rmsr, r0 +/* Disable stack protection from bootloader */ + mts rslr, r0 + addi r8, r0, 0xFFFFFFFF + mts rshr, r8 /* - * Here is checking mechanism which check if Microblaze has msr instructions - * We load msr and compare it with previous r1 value - if is the same, - * msr instructions works if not - cpu don't have them. + * According to Xilinx, msrclr instruction behaves like 'mfs rX,rpc' + * if the msrclr instruction is not enabled. We use this to detect + * if the opcode is available, by issuing msrclr and then testing the result. + * r8 == 0 - msr instructions are implemented + * r8 != 0 - msr instructions are not implemented */ - /* r8=0 - I have msr instr, 1 - I don't have them */ - rsubi r0, r0, 1 /* set the carry bit */ - msrclr r0, 0x4 /* try to clear it */ - /* read the carry bit, r8 will be '0' if msrclr exists */ - addik r8, r0, 0 + mfs r1, rmsr + msrclr r8, 0 /* clear nothing - just read msr for test */ + cmpu r8, r8, r1 /* r1 must contain msr reg content */ /* r7 may point to an FDT, or there may be one linked in. if it's in r7, we've got to save it away ASAP. We ensure r7 points to a valid FDT, just in case the bootloader is broken or non-existent */ beqi r7, no_fdt_arg /* NULL pointer? don't copy */ - lw r11, r0, r7 /* Does r7 point to a */ - rsubi r11, r11, OF_DT_HEADER /* valid FDT? */ +/* Does r7 point to a valid FDT? Load HEADER magic number */ + /* Run time Big/Little endian platform */ + /* Save 1 as word and load byte - 0 - BIG, 1 - LITTLE */ + lbui r11, r0, TOPHYS(endian_check) + beqid r11, big_endian /* DO NOT break delay stop dependency */ + lw r11, r0, r7 /* Big endian load in delay slot */ + lwr r11, r0, r7 /* Little endian load */ +big_endian: + rsubi r11, r11, OF_DT_HEADER /* Check FDT header */ beqi r11, _prepare_copy_fdt or r7, r0, r0 /* clear R7 when not valid DTB */ bnei r11, no_fdt_arg /* No - get out of here */ _prepare_copy_fdt: or r11, r0, r0 /* incremment */ ori r4, r0, TOPHYS(_fdt_start) - ori r3, r0, (0x4000 - 4) + ori r3, r0, (0x8000 - 4) _copy_fdt: lw r12, r7, r11 /* r12 = r7 + r11 */ sw r12, r4, r11 /* addr[r4 + r11] = r12 */ @@ -99,31 +113,35 @@ no_fdt_arg: #ifndef CONFIG_CMDLINE_BOOL /* * handling command line - * copy command line to __init_end. There is space for storing command line. + * copy command line directly to cmd_line placed in data section. */ - or r6, r0, r0 /* incremment */ - ori r4, r0, __init_end /* load address of command line */ + beqid r5, skip /* Skip if NULL pointer */ + or r11, r0, r0 /* incremment */ + ori r4, r0, cmd_line /* load address of command line */ tophys(r4,r4) /* convert to phys address */ ori r3, r0, COMMAND_LINE_SIZE - 1 /* number of loops */ _copy_command_line: - lbu r2, r5, r6 /* r2=r5+r6 - r5 contain pointer to command line */ - sb r2, r4, r6 /* addr[r4+r6]= r2*/ - addik r6, r6, 1 /* increment counting */ + /* r2=r5+r6 - r5 contain pointer to command line */ + lbu r2, r5, r11 + beqid r2, skip /* Skip if no data */ + sb r2, r4, r11 /* addr[r4+r6]= r2 */ + addik r11, r11, 1 /* increment counting */ bgtid r3, _copy_command_line /* loop for all entries */ - addik r3, r3, -1 /* descrement loop */ + addik r3, r3, -1 /* decrement loop */ addik r5, r4, 0 /* add new space for command line */ tovirt(r5,r5) +skip: #endif /* CONFIG_CMDLINE_BOOL */ #ifdef NOT_COMPILE /* save bram context */ - or r6, r0, r0 /* incremment */ + or r11, r0, r0 /* incremment */ ori r4, r0, TOPHYS(_bram_load_start) /* save bram context */ ori r3, r0, (LMB_SIZE - 4) _copy_bram: - lw r7, r0, r6 /* r7 = r0 + r6 */ - sw r7, r4, r6 /* addr[r4 + r6] = r7*/ - addik r6, r6, 4 /* increment counting */ + lw r7, r0, r11 /* r7 = r0 + r6 */ + sw r7, r4, r11 /* addr[r4 + r6] = r7 */ + addik r11, r11, 4 /* increment counting */ bgtid r3, _copy_bram /* loop for all entries */ addik r3, r3, -4 /* descrement loop */ #endif @@ -139,6 +157,7 @@ _copy_bram: _invalidate: mts rtlbx, r3 mts rtlbhi, r0 /* flush: ensure V is clear */ + mts rtlblo, r0 bgtid r3, _invalidate /* loop for all entries */ addik r3, r3, -1 /* sync */ @@ -158,6 +177,53 @@ _invalidate: addik r3,r0, CONFIG_KERNEL_START /* Load the kernel virtual address */ tophys(r4,r3) /* Load the kernel physical address */ + /* start to do TLB calculation */ + addik r12, r0, _end + rsub r12, r3, r12 + addik r12, r12, CONFIG_LOWMEM_SIZE >> PTE_SHIFT /* that's the pad */ + + or r9, r0, r0 /* TLB0 = 0 */ + or r10, r0, r0 /* TLB1 = 0 */ + + addik r11, r12, -0x1000000 + bgei r11, GT16 /* size is greater than 16MB */ + addik r11, r12, -0x0800000 + bgei r11, GT8 /* size is greater than 8MB */ + addik r11, r12, -0x0400000 + bgei r11, GT4 /* size is greater than 4MB */ + /* size is less than 4MB */ + addik r11, r12, -0x0200000 + bgei r11, GT2 /* size is greater than 2MB */ + addik r9, r0, 0x0100000 /* TLB0 must be 1MB */ + addik r11, r12, -0x0100000 + bgei r11, GT1 /* size is greater than 1MB */ + /* TLB1 is 0 which is setup above */ + bri tlb_end +GT4: /* r11 contains the rest - will be either 1 or 4 */ + ori r9, r0, 0x400000 /* TLB0 is 4MB */ + bri TLB1 +GT16: /* TLB0 is 16MB */ + addik r9, r0, 0x1000000 /* means TLB0 is 16MB */ +TLB1: + /* must be used r2 because of subtract if failed */ + addik r2, r11, -0x0400000 + bgei r2, GT20 /* size is greater than 16MB */ + /* size is >16MB and <20MB */ + addik r11, r11, -0x0100000 + bgei r11, GT17 /* size is greater than 17MB */ + /* kernel is >16MB and < 17MB */ +GT1: + addik r10, r0, 0x0100000 /* means TLB1 is 1MB */ + bri tlb_end +GT2: /* TLB0 is 0 and TLB1 will be 4MB */ +GT17: /* TLB1 is 4MB - kernel size <20MB */ + addik r10, r0, 0x0400000 /* means TLB1 is 4MB */ + bri tlb_end +GT8: /* TLB0 is still zero that's why I can use only TLB1 */ +GT20: /* TLB1 is 16MB - kernel size >20MB */ + addik r10, r0, 0x1000000 /* means TLB1 is 16MB */ +tlb_end: + /* * Configure and load two entries into TLB slots 0 and 1. * In case we are pinning TLBs, these are reserved in by the @@ -167,29 +233,82 @@ _invalidate: andi r4,r4,0xfffffc00 /* Mask off the real page number */ ori r4,r4,(TLB_WR | TLB_EX) /* Set the write and execute bits */ + /* + * TLB0 is always used - check if is not zero (r9 stores TLB0 value) + * if is use TLB1 value and clear it (r10 stores TLB1 value) + */ + bnei r9, tlb0_not_zero + add r9, r10, r0 + add r10, r0, r0 +tlb0_not_zero: + + /* look at the code below */ + ori r30, r0, 0x200 + andi r29, r9, 0x100000 + bneid r29, 1f + addik r30, r30, 0x80 + andi r29, r9, 0x400000 + bneid r29, 1f + addik r30, r30, 0x80 + andi r29, r9, 0x1000000 + bneid r29, 1f + addik r30, r30, 0x80 +1: andi r3,r3,0xfffffc00 /* Mask off the effective page number */ - ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M)) + ori r3,r3,(TLB_VALID) + or r3, r3, r30 - mts rtlbx,r0 /* TLB slow 0 */ + /* Load tlb_skip size value which is index to first unused TLB entry */ + lwi r11, r0, TOPHYS(tlb_skip) + mts rtlbx,r11 /* TLB slow 0 */ mts rtlblo,r4 /* Load the data portion of the entry */ mts rtlbhi,r3 /* Load the tag portion of the entry */ - addik r4, r4, 0x01000000 /* Map next 16 M entries */ - addik r3, r3, 0x01000000 + /* Increase tlb_skip size */ + addik r11, r11, 1 + swi r11, r0, TOPHYS(tlb_skip) + + /* TLB1 can be zeroes that's why we not setup it */ + beqi r10, jump_over2 + + /* look at the code below */ + ori r30, r0, 0x200 + andi r29, r10, 0x100000 + bneid r29, 1f + addik r30, r30, 0x80 + andi r29, r10, 0x400000 + bneid r29, 1f + addik r30, r30, 0x80 + andi r29, r10, 0x1000000 + bneid r29, 1f + addik r30, r30, 0x80 +1: + addk r4, r4, r9 /* previous addr + TLB0 size */ + addk r3, r3, r9 + + andi r3,r3,0xfffffc00 /* Mask off the effective page number */ + ori r3,r3,(TLB_VALID) + or r3, r3, r30 - ori r6,r0,1 /* TLB slot 1 */ - mts rtlbx,r6 + lwi r11, r0, TOPHYS(tlb_skip) + mts rtlbx, r11 /* r11 is used from TLB0 */ mts rtlblo,r4 /* Load the data portion of the entry */ mts rtlbhi,r3 /* Load the tag portion of the entry */ + /* Increase tlb_skip size */ + addik r11, r11, 1 + swi r11, r0, TOPHYS(tlb_skip) + +jump_over2: /* * Load a TLB entry for LMB, since we need access to * the exception vectors, using a 4k real==virtual mapping. */ - ori r6,r0,3 /* TLB slot 3 */ - mts rtlbx,r6 + /* Use temporary TLB_ID for LMB - clear this temporary mapping later */ + ori r11, r0, MICROBLAZE_LMB_TLB_ID + mts rtlbx,r11 ori r4,r0,(TLB_WR | TLB_EX) ori r3,r0,(TLB_VALID | TLB_PAGESZ(PAGESZ_4K)) @@ -213,26 +332,26 @@ start_here: #endif /* CONFIG_MMU */ /* Initialize small data anchors */ - la r13, r0, _KERNEL_SDA_BASE_ - la r2, r0, _KERNEL_SDA2_BASE_ + addik r13, r0, _KERNEL_SDA_BASE_ + addik r2, r0, _KERNEL_SDA2_BASE_ /* Initialize stack pointer */ - la r1, r0, init_thread_union + THREAD_SIZE - 4 + addik r1, r0, init_thread_union + THREAD_SIZE - 4 /* Initialize r31 with current task address */ - la r31, r0, init_task + addik r31, r0, init_task /* * Call platform dependent initialize function. * Please see $(ARCH)/mach-$(SUBARCH)/setup.c for * the function. */ - la r9, r0, machine_early_init - brald r15, r9 + addik r11, r0, machine_early_init + brald r15, r11 nop #ifndef CONFIG_MMU - la r15, r0, machine_halt + addik r15, r0, machine_halt braid start_kernel nop #else @@ -257,8 +376,7 @@ start_here: /* Load up the kernel context */ kernel_load_context: - # Keep entry 0 and 1 valid. Entry 3 mapped to LMB can go away. - ori r5,r0,3 + ori r5, r0, MICROBLAZE_LMB_TLB_ID mts rtlbx,r5 nop mts rtlbhi,r0 diff --git a/arch/microblaze/kernel/heartbeat.c b/arch/microblaze/kernel/heartbeat.c index 522751737cf..4643e3ab941 100644 --- a/arch/microblaze/kernel/heartbeat.c +++ b/arch/microblaze/kernel/heartbeat.c @@ -17,7 +17,7 @@ static unsigned int base_addr; -void heartbeat(void) +void microblaze_heartbeat(void) { static unsigned int cnt, period, dist; @@ -42,16 +42,15 @@ void heartbeat(void) } } -void setup_heartbeat(void) +void microblaze_setup_heartbeat(void) { struct device_node *gpio = NULL; int *prop; int j; - char *gpio_list[] = { - "xlnx,xps-gpio-1.00.a", - "xlnx,opb-gpio-1.00.a", - NULL - }; + const char * const gpio_list[] = { + "xlnx,xps-gpio-1.00.a", + NULL + }; for (j = 0; gpio_list[j] != NULL; j++) { gpio = of_find_compatible_node(NULL, NULL, gpio_list[j]); @@ -60,9 +59,9 @@ void setup_heartbeat(void) } if (gpio) { - base_addr = *(int *) of_get_property(gpio, "reg", NULL); + base_addr = be32_to_cpup(of_get_property(gpio, "reg", NULL)); base_addr = (unsigned long) ioremap(base_addr, PAGE_SIZE); - printk(KERN_NOTICE "Heartbeat GPIO at 0x%x\n", base_addr); + pr_notice("Heartbeat GPIO at 0x%x\n", base_addr); /* GPIO is configured as output */ prop = (int *) of_get_property(gpio, "xlnx,is-bidir", NULL); diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S index 995a2123635..0b11a4469de 100644 --- a/arch/microblaze/kernel/hw_exception_handler.S +++ b/arch/microblaze/kernel/hw_exception_handler.S @@ -75,12 +75,12 @@ #include <asm/mmu.h> #include <asm/pgtable.h> #include <asm/signal.h> +#include <asm/registers.h> #include <asm/asm-offsets.h> +#undef DEBUG + /* Helpful Macros */ -#ifndef CONFIG_MMU -#define EX_HANDLER_STACK_SIZ (4*19) -#endif #define NUM_TO_REG(num) r ## num #ifdef CONFIG_MMU @@ -94,7 +94,7 @@ lwi r6, r1, PT_R6; \ lwi r11, r1, PT_R11; \ lwi r31, r1, PT_R31; \ - lwi r1, r0, TOPHYS(r0_ram + 0); + lwi r1, r1, PT_R1; #endif /* CONFIG_MMU */ #define LWREG_NOP \ @@ -147,19 +147,14 @@ or r3, r0, NUM_TO_REG (regnum); /* Shift right instruction depending on available configuration */ - #if CONFIG_XILINX_MICROBLAZE0_USE_BARREL > 0 - #define BSRLI(rD, rA, imm) \ - bsrli rD, rA, imm - #elif CONFIG_XILINX_MICROBLAZE0_USE_DIV > 0 - #define BSRLI(rD, rA, imm) \ - ori rD, r0, (1 << imm); \ - idivu rD, rD, rA - #else - #define BSRLI(rD, rA, imm) BSRLI ## imm (rD, rA) + #if CONFIG_XILINX_MICROBLAZE0_USE_BARREL == 0 /* Only the used shift constants defined here - add more if needed */ #define BSRLI2(rD, rA) \ srl rD, rA; /* << 1 */ \ srl rD, rD; /* << 2 */ + #define BSRLI4(rD, rA) \ + BSRLI2(rD, rA); \ + BSRLI2(rD, rD) #define BSRLI10(rD, rA) \ srl rD, rA; /* << 1 */ \ srl rD, rD; /* << 2 */ \ @@ -174,7 +169,33 @@ #define BSRLI20(rD, rA) \ BSRLI10(rD, rA); \ BSRLI10(rD, rD) + + .macro bsrli, rD, rA, IMM + .if (\IMM) == 2 + BSRLI2(\rD, \rA) + .elseif (\IMM) == 10 + BSRLI10(\rD, \rA) + .elseif (\IMM) == 12 + BSRLI2(\rD, \rA) + BSRLI10(\rD, \rD) + .elseif (\IMM) == 14 + BSRLI4(\rD, \rA) + BSRLI10(\rD, \rD) + .elseif (\IMM) == 20 + BSRLI20(\rD, \rA) + .elseif (\IMM) == 24 + BSRLI4(\rD, \rA) + BSRLI20(\rD, \rD) + .elseif (\IMM) == 28 + BSRLI4(\rD, \rA) + BSRLI4(\rD, \rD) + BSRLI20(\rD, \rD) + .else + .error "BSRLI shift macros \IMM" + .endif + .endm #endif + #endif /* CONFIG_MMU */ .extern other_exception_handler /* Defined in exception.c */ @@ -197,8 +218,8 @@ * - W S REG EXC * * - * STACK FRAME STRUCTURE (for NO_MMU) - * --------------------------------- + * STACK FRAME STRUCTURE (for CONFIG_MMU=n) + * ---------------------------------------- * * +-------------+ + 0 * | MSR | @@ -213,8 +234,8 @@ * | . | * | . | * - * NO_MMU kernel use the same r0_ram pointed space - look to vmlinux.lds.S - * which is used for storing register values - old style was, that value were + * MMU kernel uses the same 'pt_pool_space' pointed space + * which is used for storing register values - noMMu style was, that values were * stored in stack but in case of failure you lost information about register. * Currently you can see register value in memory in specific place. * In compare to with previous solution the speed should be the same. @@ -233,8 +254,22 @@ */ /* wrappers to restore state before coming to entry.S */ - #ifdef CONFIG_MMU +.section .data +.align 4 +pt_pool_space: + .space PT_SIZE + +#ifdef DEBUG +/* Create space for exception counting. */ +.section .data +.global exception_debug_table +.align 4 +exception_debug_table: + /* Look at exception vector table. There is 32 exceptions * word size */ + .space (32 * 4) +#endif /* DEBUG */ + .section .rodata .align 4 _MB_HW_ExceptionVectorTable: @@ -294,10 +329,10 @@ _hw_exception_handler: #ifndef CONFIG_MMU addik r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */ #else - swi r1, r0, TOPHYS(r0_ram + 0); /* GET_SP */ + swi r1, r0, TOPHYS(pt_pool_space + PT_R1); /* GET_SP */ /* Save date to kernel memory. Here is the problem * when you came from user space */ - ori r1, r0, TOPHYS(r0_ram + 28); + ori r1, r0, TOPHYS(pt_pool_space); #endif swi r3, r1, PT_R3 swi r4, r1, PT_R4 @@ -336,12 +371,12 @@ not_in_delay_slot: #ifdef DEBUG /* counting which exception happen */ - lwi r5, r0, 0x200 + TOPHYS(r0_ram) + lwi r5, r0, TOPHYS(exception_debug_table) addi r5, r5, 1 - swi r5, r0, 0x200 + TOPHYS(r0_ram) - lwi r5, r6, 0x200 + TOPHYS(r0_ram) + swi r5, r0, TOPHYS(exception_debug_table) + lwi r5, r6, TOPHYS(exception_debug_table) addi r5, r5, 1 - swi r5, r6, 0x200 + TOPHYS(r0_ram) + swi r5, r6, TOPHYS(exception_debug_table) #endif /* end */ /* Load the HW Exception vector */ @@ -481,7 +516,7 @@ ex_lw_tail: /* Get the destination register number into r5 */ lbui r5, r0, TOPHYS(ex_reg_op); /* Form load_word jump table offset (lw_table + (8 * regnum)) */ - la r6, r0, TOPHYS(lw_table); + addik r6, r0, TOPHYS(lw_table); addk r5, r5, r5; addk r5, r5, r5; addk r5, r5, r5; @@ -492,7 +527,7 @@ ex_sw: /* Get the destination register number into r5 */ lbui r5, r0, TOPHYS(ex_reg_op); /* Form store_word jump table offset (sw_table + (8 * regnum)) */ - la r6, r0, TOPHYS(sw_table); + addik r6, r0, TOPHYS(sw_table); add r5, r5, r5; add r5, r5, r5; add r5, r5, r5; @@ -572,7 +607,7 @@ ex_handler_done: * tried to access a kernel or read-protected page - always * a SEGV). All other faults here must be stores, so no * need to check ESR_S as well. */ - andi r4, r4, 0x800 /* ESR_Z - zone protection */ + andi r4, r4, ESR_DIZ /* ESR_Z - zone protection */ bnei r4, ex2 ori r4, r0, swapper_pg_dir @@ -586,25 +621,25 @@ ex_handler_done: * tried to access a kernel or read-protected page - always * a SEGV). All other faults here must be stores, so no * need to check ESR_S as well. */ - andi r4, r4, 0x800 /* ESR_Z */ + andi r4, r4, ESR_DIZ /* ESR_Z */ bnei r4, ex2 /* get current task address */ addi r4 ,CURRENT_TASK, TOPHYS(0); lwi r4, r4, TASK_THREAD+PGDIR ex4: tophys(r4,r4) - BSRLI(r5,r3,20) /* Create L1 (pgdir/pmd) address */ - andi r5, r5, 0xffc + /* Create L1 (pgdir/pmd) address */ + bsrli r5, r3, PGDIR_SHIFT - 2 + andi r5, r5, PAGE_SIZE - 4 /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ or r4, r4, r5 lwi r4, r4, 0 /* Get L1 entry */ - andi r5, r4, 0xfffff000 /* Extract L2 (pte) base address */ + andi r5, r4, PAGE_MASK /* Extract L2 (pte) base address */ beqi r5, ex2 /* Bail if no table */ tophys(r5,r5) - BSRLI(r6,r3,10) /* Compute PTE address */ - andi r6, r6, 0xffc - andi r5, r5, 0xfffff003 + bsrli r6, r3, PTE_SHIFT /* Compute PTE address */ + andi r6, r6, PAGE_SIZE - 4 or r5, r5, r6 lwi r4, r5, 0 /* Get Linux PTE */ @@ -623,7 +658,9 @@ ex_handler_done: * Many of these bits are software only. Bits we don't set * here we (properly should) assume have the appropriate value. */ - andni r4, r4, 0x0ce2 /* Make sure 20, 21 are zero */ +/* Ignore memory coherent, just LSB on ZSEL is used + EX/WR */ + andi r4, r4, PAGE_MASK | TLB_EX | TLB_WR | \ + TLB_ZSEL(1) | TLB_ATTR_MASK ori r4, r4, _PAGE_HWEXEC /* make it executable */ /* find the TLB index that caused the fault. It has to be here*/ @@ -692,18 +729,18 @@ ex_handler_done: lwi r4, r4, TASK_THREAD+PGDIR ex6: tophys(r4,r4) - BSRLI(r5,r3,20) /* Create L1 (pgdir/pmd) address */ - andi r5, r5, 0xffc + /* Create L1 (pgdir/pmd) address */ + bsrli r5, r3, PGDIR_SHIFT - 2 + andi r5, r5, PAGE_SIZE - 4 /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ or r4, r4, r5 lwi r4, r4, 0 /* Get L1 entry */ - andi r5, r4, 0xfffff000 /* Extract L2 (pte) base address */ + andi r5, r4, PAGE_MASK /* Extract L2 (pte) base address */ beqi r5, ex7 /* Bail if no table */ tophys(r5,r5) - BSRLI(r6,r3,10) /* Compute PTE address */ - andi r6, r6, 0xffc - andi r5, r5, 0xfffff003 + bsrli r6, r3, PTE_SHIFT /* Compute PTE address */ + andi r6, r6, PAGE_SIZE - 4 or r5, r5, r6 lwi r4, r5, 0 /* Get Linux PTE */ @@ -722,7 +759,8 @@ ex_handler_done: * here we (properly should) assume have the appropriate value. */ brid finish_tlb_load - andni r4, r4, 0x0ce2 /* Make sure 20, 21 are zero */ + andi r4, r4, PAGE_MASK | TLB_EX | TLB_WR | \ + TLB_ZSEL(1) | TLB_ATTR_MASK ex7: /* The bailout. Restore registers to pre-exception conditions * and call the heavyweights to help us out. @@ -762,18 +800,18 @@ ex_handler_done: lwi r4, r4, TASK_THREAD+PGDIR ex9: tophys(r4,r4) - BSRLI(r5,r3,20) /* Create L1 (pgdir/pmd) address */ - andi r5, r5, 0xffc + /* Create L1 (pgdir/pmd) address */ + bsrli r5, r3, PGDIR_SHIFT - 2 + andi r5, r5, PAGE_SIZE - 4 /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ or r4, r4, r5 lwi r4, r4, 0 /* Get L1 entry */ - andi r5, r4, 0xfffff000 /* Extract L2 (pte) base address */ + andi r5, r4, PAGE_MASK /* Extract L2 (pte) base address */ beqi r5, ex10 /* Bail if no table */ tophys(r5,r5) - BSRLI(r6,r3,10) /* Compute PTE address */ - andi r6, r6, 0xffc - andi r5, r5, 0xfffff003 + bsrli r6, r3, PTE_SHIFT /* Compute PTE address */ + andi r6, r6, PAGE_SIZE - 4 or r5, r5, r6 lwi r4, r5, 0 /* Get Linux PTE */ @@ -792,7 +830,8 @@ ex_handler_done: * here we (properly should) assume have the appropriate value. */ brid finish_tlb_load - andni r4, r4, 0x0ce2 /* Make sure 20, 21 are zero */ + andi r4, r4, PAGE_MASK | TLB_EX | TLB_WR | \ + TLB_ZSEL(1) | TLB_ATTR_MASK ex10: /* The bailout. Restore registers to pre-exception conditions * and call the heavyweights to help us out. @@ -811,19 +850,26 @@ ex_handler_done: * Upon exit, we reload everything and RFI. * A common place to load the TLB. */ +.section .data +.align 4 +.global tlb_skip + tlb_skip: + .long MICROBLAZE_TLB_SKIP tlb_index: - .long 1 /* MS: storing last used tlb index */ + /* MS: storing last used tlb index */ + .long MICROBLAZE_TLB_SIZE/2 +.previous finish_tlb_load: /* MS: load the last used TLB index. */ lwi r5, r0, TOPHYS(tlb_index) addik r5, r5, 1 /* MS: inc tlb_index -> use next one */ /* MS: FIXME this is potential fault, because this is mask not count */ - andi r5, r5, (MICROBLAZE_TLB_SIZE-1) + andi r5, r5, MICROBLAZE_TLB_SIZE - 1 ori r6, r0, 1 cmp r31, r5, r6 blti r31, ex12 - addik r5, r6, 1 + lwi r5, r0, TOPHYS(tlb_skip) ex12: /* MS: save back current TLB index */ swi r5, r0, TOPHYS(tlb_index) @@ -838,8 +884,14 @@ ex_handler_done: * set of bits. These are size, valid, E, U0, and ensure * bits 20 and 21 are zero. */ - andi r3, r3, 0xfffff000 - ori r3, r3, 0x0c0 + andi r3, r3, PAGE_MASK +#ifdef CONFIG_MICROBLAZE_64K_PAGES + ori r3, r3, TLB_VALID | TLB_PAGESZ(PAGESZ_64K) +#elif CONFIG_MICROBLAZE_16K_PAGES + ori r3, r3, TLB_VALID | TLB_PAGESZ(PAGESZ_16K) +#else + ori r3, r3, TLB_VALID | TLB_PAGESZ(PAGESZ_4K) +#endif mts rtlbhi, r3 /* Load TLB HI */ nop @@ -895,7 +947,7 @@ ex_handler_done: .ent _unaligned_data_exception _unaligned_data_exception: andi r8, r3, 0x3E0; /* Mask and extract the register operand */ - BSRLI(r8,r8,2); /* r8 >> 2 = register operand * 8 */ + bsrli r8, r8, 2; /* r8 >> 2 = register operand * 8 */ andi r6, r3, 0x400; /* Extract ESR[S] */ bneid r6, ex_sw_vm; andi r6, r3, 0x800; /* Extract ESR[W] - delay slot */ @@ -903,7 +955,7 @@ ex_lw_vm: beqid r6, ex_lhw_vm; load1: lbui r5, r4, 0; /* Exception address in r4 - delay slot */ /* Load a word, byte-by-byte from destination address and save it in tmp space*/ - la r6, r0, ex_tmp_data_loc_0; + addik r6, r0, ex_tmp_data_loc_0; sbi r5, r6, 0; load2: lbui r5, r4, 1; sbi r5, r6, 1; @@ -917,7 +969,7 @@ load4: lbui r5, r4, 3; ex_lhw_vm: /* Load a half-word, byte-by-byte from destination address and * save it in tmp space */ - la r6, r0, ex_tmp_data_loc_0; + addik r6, r0, ex_tmp_data_loc_0; sbi r5, r6, 0; load5: lbui r5, r4, 1; sbi r5, r6, 1; @@ -933,7 +985,7 @@ ex_sw_vm: addik r5, r8, sw_table_vm; bra r5; ex_sw_tail_vm: - la r5, r0, ex_tmp_data_loc_0; + addik r5, r0, ex_tmp_data_loc_0; beqid r6, ex_shw_vm; swi r3, r5, 0; /* Get the word - delay slot */ /* Store the word, byte-by-byte into destination address */ @@ -948,11 +1000,20 @@ store3: sbi r3, r4, 2; store4: sbi r3, r4, 3; /* Delay slot */ ex_shw_vm: /* Store the lower half-word, byte-by-byte into destination address */ +#ifdef __MICROBLAZEEL__ + lbui r3, r5, 0; +store5: sbi r3, r4, 0; + lbui r3, r5, 1; + brid ret_from_exc; +store6: sbi r3, r4, 1; /* Delay slot */ +#else lbui r3, r5, 2; store5: sbi r3, r4, 0; lbui r3, r5, 3; brid ret_from_exc; store6: sbi r3, r4, 1; /* Delay slot */ +#endif + ex_sw_end_vm: /* Exception handling of store word, ends. */ /* We have to prevent cases that get/put_user macros get unaligned pointer @@ -967,7 +1028,7 @@ ex_unaligned_fixup: addik r7, r0, SIGSEGV /* call bad_page_fault for finding aligned fixup, fixup address is saved * in PT_PC which is used as return address from exception */ - la r15, r0, ret_from_exc-8 /* setup return address */ + addik r15, r0, ret_from_exc-8 /* setup return address */ brid bad_page_fault nop @@ -988,6 +1049,7 @@ ex_unaligned_fixup: .end _unaligned_data_exception #endif /* CONFIG_MMU */ +.global ex_handler_unhandled ex_handler_unhandled: /* FIXME add handle function for unhandled exception - dump register */ bri 0 @@ -1094,23 +1156,23 @@ lw_r10_vm: R3_TO_LWREG_VM_V (10); lw_r11_vm: R3_TO_LWREG_VM_V (11); lw_r12_vm: R3_TO_LWREG_VM_V (12); lw_r13_vm: R3_TO_LWREG_VM_V (13); -lw_r14_vm: R3_TO_LWREG_VM (14); +lw_r14_vm: R3_TO_LWREG_VM_V (14); lw_r15_vm: R3_TO_LWREG_VM_V (15); -lw_r16_vm: R3_TO_LWREG_VM (16); +lw_r16_vm: R3_TO_LWREG_VM_V (16); lw_r17_vm: R3_TO_LWREG_VM_V (17); lw_r18_vm: R3_TO_LWREG_VM_V (18); -lw_r19_vm: R3_TO_LWREG_VM (19); -lw_r20_vm: R3_TO_LWREG_VM (20); -lw_r21_vm: R3_TO_LWREG_VM (21); -lw_r22_vm: R3_TO_LWREG_VM (22); -lw_r23_vm: R3_TO_LWREG_VM (23); -lw_r24_vm: R3_TO_LWREG_VM (24); -lw_r25_vm: R3_TO_LWREG_VM (25); -lw_r26_vm: R3_TO_LWREG_VM (26); -lw_r27_vm: R3_TO_LWREG_VM (27); -lw_r28_vm: R3_TO_LWREG_VM (28); -lw_r29_vm: R3_TO_LWREG_VM (29); -lw_r30_vm: R3_TO_LWREG_VM (30); +lw_r19_vm: R3_TO_LWREG_VM_V (19); +lw_r20_vm: R3_TO_LWREG_VM_V (20); +lw_r21_vm: R3_TO_LWREG_VM_V (21); +lw_r22_vm: R3_TO_LWREG_VM_V (22); +lw_r23_vm: R3_TO_LWREG_VM_V (23); +lw_r24_vm: R3_TO_LWREG_VM_V (24); +lw_r25_vm: R3_TO_LWREG_VM_V (25); +lw_r26_vm: R3_TO_LWREG_VM_V (26); +lw_r27_vm: R3_TO_LWREG_VM_V (27); +lw_r28_vm: R3_TO_LWREG_VM_V (28); +lw_r29_vm: R3_TO_LWREG_VM_V (29); +lw_r30_vm: R3_TO_LWREG_VM_V (30); lw_r31_vm: R3_TO_LWREG_VM_V (31); sw_table_vm: @@ -1128,23 +1190,23 @@ sw_r10_vm: SWREG_TO_R3_VM_V (10); sw_r11_vm: SWREG_TO_R3_VM_V (11); sw_r12_vm: SWREG_TO_R3_VM_V (12); sw_r13_vm: SWREG_TO_R3_VM_V (13); -sw_r14_vm: SWREG_TO_R3_VM (14); +sw_r14_vm: SWREG_TO_R3_VM_V (14); sw_r15_vm: SWREG_TO_R3_VM_V (15); -sw_r16_vm: SWREG_TO_R3_VM (16); +sw_r16_vm: SWREG_TO_R3_VM_V (16); sw_r17_vm: SWREG_TO_R3_VM_V (17); sw_r18_vm: SWREG_TO_R3_VM_V (18); -sw_r19_vm: SWREG_TO_R3_VM (19); -sw_r20_vm: SWREG_TO_R3_VM (20); -sw_r21_vm: SWREG_TO_R3_VM (21); -sw_r22_vm: SWREG_TO_R3_VM (22); -sw_r23_vm: SWREG_TO_R3_VM (23); -sw_r24_vm: SWREG_TO_R3_VM (24); -sw_r25_vm: SWREG_TO_R3_VM (25); -sw_r26_vm: SWREG_TO_R3_VM (26); -sw_r27_vm: SWREG_TO_R3_VM (27); -sw_r28_vm: SWREG_TO_R3_VM (28); -sw_r29_vm: SWREG_TO_R3_VM (29); -sw_r30_vm: SWREG_TO_R3_VM (30); +sw_r19_vm: SWREG_TO_R3_VM_V (19); +sw_r20_vm: SWREG_TO_R3_VM_V (20); +sw_r21_vm: SWREG_TO_R3_VM_V (21); +sw_r22_vm: SWREG_TO_R3_VM_V (22); +sw_r23_vm: SWREG_TO_R3_VM_V (23); +sw_r24_vm: SWREG_TO_R3_VM_V (24); +sw_r25_vm: SWREG_TO_R3_VM_V (25); +sw_r26_vm: SWREG_TO_R3_VM_V (26); +sw_r27_vm: SWREG_TO_R3_VM_V (27); +sw_r28_vm: SWREG_TO_R3_VM_V (28); +sw_r29_vm: SWREG_TO_R3_VM_V (29); +sw_r30_vm: SWREG_TO_R3_VM_V (30); sw_r31_vm: SWREG_TO_R3_VM_V (31); #endif /* CONFIG_MMU */ diff --git a/arch/microblaze/kernel/init_task.c b/arch/microblaze/kernel/init_task.c deleted file mode 100644 index b5d711f94ff..00000000000 --- a/arch/microblaze/kernel/init_task.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2009 Michal Simek <monstr@monstr.eu> - * Copyright (C) 2009 PetaLogix - * Copyright (C) 2006 Atmark Techno, Inc. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/init_task.h> -#include <linux/fs.h> -#include <linux/mqueue.h> - -#include <asm/pgtable.h> - -static struct signal_struct init_signals = INIT_SIGNALS(init_signals); -static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); - -union thread_union init_thread_union __init_task_data = - { INIT_THREAD_INFO(init_task) }; - -struct task_struct init_task = INIT_TASK(init_task); -EXPORT_SYMBOL(init_task); diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c index 03172c1da77..15c7c12ea0e 100644 --- a/arch/microblaze/kernel/intc.c +++ b/arch/microblaze/kernel/intc.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2007-2013 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2012-2013 Xilinx, Inc. * Copyright (C) 2007-2009 PetaLogix * Copyright (C) 2006 Atmark Techno, Inc. * @@ -8,24 +9,15 @@ * for more details. */ -#include <linux/init.h> +#include <linux/irqdomain.h> #include <linux/irq.h> -#include <asm/page.h> +#include <linux/of_address.h> #include <linux/io.h> #include <linux/bug.h> -#include <asm/prom.h> -#include <asm/irq.h> +#include "../../drivers/irqchip/irqchip.h" -#ifdef CONFIG_SELFMOD_INTC -#include <asm/selfmod.h> -#define INTC_BASE BARRIER_BASE_ADDR -#else -static unsigned int intc_baseaddr; -#define INTC_BASE intc_baseaddr -#endif - -unsigned int nr_irq; +static void __iomem *intc_baseaddr; /* No one else should require these constants, so define them locally here. */ #define ISR 0x00 /* Interrupt Status Register */ @@ -40,143 +32,166 @@ unsigned int nr_irq; #define MER_ME (1<<0) #define MER_HIE (1<<1) -static void intc_enable_or_unmask(unsigned int irq) +static unsigned int (*read_fn)(void __iomem *); +static void (*write_fn)(u32, void __iomem *); + +static void intc_write32(u32 val, void __iomem *addr) +{ + iowrite32(val, addr); +} + +static unsigned int intc_read32(void __iomem *addr) { - unsigned long mask = 1 << irq; - pr_debug("enable_or_unmask: %d\n", irq); - out_be32(INTC_BASE + SIE, mask); + return ioread32(addr); +} + +static void intc_write32_be(u32 val, void __iomem *addr) +{ + iowrite32be(val, addr); +} + +static unsigned int intc_read32_be(void __iomem *addr) +{ + return ioread32be(addr); +} + +static void intc_enable_or_unmask(struct irq_data *d) +{ + unsigned long mask = 1 << d->hwirq; + + pr_debug("enable_or_unmask: %ld\n", d->hwirq); /* ack level irqs because they can't be acked during * ack function since the handle_level_irq function * acks the irq before calling the interrupt handler */ - if (irq_desc[irq].status & IRQ_LEVEL) - out_be32(INTC_BASE + IAR, mask); -} + if (irqd_is_level_type(d)) + write_fn(mask, intc_baseaddr + IAR); -static void intc_disable_or_mask(unsigned int irq) -{ - pr_debug("disable: %d\n", irq); - out_be32(INTC_BASE + CIE, 1 << irq); + write_fn(mask, intc_baseaddr + SIE); } -static void intc_ack(unsigned int irq) +static void intc_disable_or_mask(struct irq_data *d) { - pr_debug("ack: %d\n", irq); - out_be32(INTC_BASE + IAR, 1 << irq); + pr_debug("disable: %ld\n", d->hwirq); + write_fn(1 << d->hwirq, intc_baseaddr + CIE); } -static void intc_mask_ack(unsigned int irq) +static void intc_ack(struct irq_data *d) { - unsigned long mask = 1 << irq; - pr_debug("disable_and_ack: %d\n", irq); - out_be32(INTC_BASE + CIE, mask); - out_be32(INTC_BASE + IAR, mask); + pr_debug("ack: %ld\n", d->hwirq); + write_fn(1 << d->hwirq, intc_baseaddr + IAR); } -static void intc_end(unsigned int irq) +static void intc_mask_ack(struct irq_data *d) { - unsigned long mask = 1 << irq; - pr_debug("end: %d\n", irq); - if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { - out_be32(INTC_BASE + SIE, mask); - /* ack level sensitive intr */ - if (irq_desc[irq].status & IRQ_LEVEL) - out_be32(INTC_BASE + IAR, mask); - } + unsigned long mask = 1 << d->hwirq; + + pr_debug("disable_and_ack: %ld\n", d->hwirq); + write_fn(mask, intc_baseaddr + CIE); + write_fn(mask, intc_baseaddr + IAR); } static struct irq_chip intc_dev = { .name = "Xilinx INTC", - .unmask = intc_enable_or_unmask, - .mask = intc_disable_or_mask, - .ack = intc_ack, - .mask_ack = intc_mask_ack, - .end = intc_end, + .irq_unmask = intc_enable_or_unmask, + .irq_mask = intc_disable_or_mask, + .irq_ack = intc_ack, + .irq_mask_ack = intc_mask_ack, }; -unsigned int get_irq(struct pt_regs *regs) +static struct irq_domain *root_domain; + +unsigned int get_irq(void) { - int irq; + unsigned int hwirq, irq = -1; - /* - * NOTE: This function is the one that needs to be improved in - * order to handle multiple interrupt controllers. It currently - * is hardcoded to check for interrupts only on the first INTC. - */ - irq = in_be32(INTC_BASE + IVR); - pr_debug("get_irq: %d\n", irq); + hwirq = read_fn(intc_baseaddr + IVR); + if (hwirq != -1U) + irq = irq_find_mapping(root_domain, hwirq); + + pr_debug("get_irq: hwirq=%d, irq=%d\n", hwirq, irq); return irq; } -void __init init_IRQ(void) +static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + u32 intr_mask = (u32)d->host_data; + + if (intr_mask & (1 << hw)) { + irq_set_chip_and_handler_name(irq, &intc_dev, + handle_edge_irq, "edge"); + irq_clear_status_flags(irq, IRQ_LEVEL); + } else { + irq_set_chip_and_handler_name(irq, &intc_dev, + handle_level_irq, "level"); + irq_set_status_flags(irq, IRQ_LEVEL); + } + return 0; +} + +static const struct irq_domain_ops xintc_irq_domain_ops = { + .xlate = irq_domain_xlate_onetwocell, + .map = xintc_map, +}; + +static int __init xilinx_intc_of_init(struct device_node *intc, + struct device_node *parent) { - u32 i, j, intr_type; - struct device_node *intc = NULL; -#ifdef CONFIG_SELFMOD_INTC - unsigned int intc_baseaddr = 0; - static int arr_func[] = { - (int)&get_irq, - (int)&intc_enable_or_unmask, - (int)&intc_disable_or_mask, - (int)&intc_mask_ack, - (int)&intc_ack, - (int)&intc_end, - 0 - }; -#endif - static char *intc_list[] = { - "xlnx,xps-intc-1.00.a", - "xlnx,opb-intc-1.00.c", - "xlnx,opb-intc-1.00.b", - "xlnx,opb-intc-1.00.a", - NULL - }; - - for (j = 0; intc_list[j] != NULL; j++) { - intc = of_find_compatible_node(NULL, NULL, intc_list[j]); - if (intc) - break; + u32 nr_irq, intr_mask; + int ret; + + intc_baseaddr = of_iomap(intc, 0); + BUG_ON(!intc_baseaddr); + + ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq); + if (ret < 0) { + pr_err("%s: unable to read xlnx,num-intr-inputs\n", __func__); + return -EINVAL; + } + + ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &intr_mask); + if (ret < 0) { + pr_err("%s: unable to read xlnx,kind-of-intr\n", __func__); + return -EINVAL; } - BUG_ON(!intc); - intc_baseaddr = *(int *) of_get_property(intc, "reg", NULL); - intc_baseaddr = (unsigned long) ioremap(intc_baseaddr, PAGE_SIZE); - nr_irq = *(int *) of_get_property(intc, "xlnx,num-intr-inputs", NULL); + if (intr_mask > (u32)((1ULL << nr_irq) - 1)) + pr_info(" ERROR: Mismatch in kind-of-intr param\n"); - intr_type = - *(int *) of_get_property(intc, "xlnx,kind-of-intr", NULL); - if (intr_type >= (1 << (nr_irq + 1))) - printk(KERN_INFO " ERROR: Mismatch in kind-of-intr param\n"); + pr_info("%s: num_irq=%d, edge=0x%x\n", + intc->full_name, nr_irq, intr_mask); -#ifdef CONFIG_SELFMOD_INTC - selfmod_function((int *) arr_func, intc_baseaddr); -#endif - printk(KERN_INFO "%s #0 at 0x%08x, num_irq=%d, edge=0x%x\n", - intc_list[j], intc_baseaddr, nr_irq, intr_type); + write_fn = intc_write32; + read_fn = intc_read32; /* * Disable all external interrupts until they are * explicity requested. */ - out_be32(intc_baseaddr + IER, 0); + write_fn(0, intc_baseaddr + IER); /* Acknowledge any pending interrupts just in case. */ - out_be32(intc_baseaddr + IAR, 0xffffffff); + write_fn(0xffffffff, intc_baseaddr + IAR); /* Turn on the Master Enable. */ - out_be32(intc_baseaddr + MER, MER_HIE | MER_ME); - - for (i = 0; i < nr_irq; ++i) { - if (intr_type & (0x00000001 << i)) { - set_irq_chip_and_handler_name(i, &intc_dev, - handle_edge_irq, intc_dev.name); - irq_desc[i].status &= ~IRQ_LEVEL; - } else { - set_irq_chip_and_handler_name(i, &intc_dev, - handle_level_irq, intc_dev.name); - irq_desc[i].status |= IRQ_LEVEL; - } + write_fn(MER_HIE | MER_ME, intc_baseaddr + MER); + if (!(read_fn(intc_baseaddr + MER) & (MER_HIE | MER_ME))) { + write_fn = intc_write32_be; + read_fn = intc_read32_be; + write_fn(MER_HIE | MER_ME, intc_baseaddr + MER); } + + /* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm + * lazy and Michal can clean it up to something nicer when he tests + * and commits this patch. ~~gcl */ + root_domain = irq_domain_add_linear(intc, nr_irq, &xintc_irq_domain_ops, + (void *)intr_mask); + + irq_set_default_host(root_domain); + + return 0; } + +IRQCHIP_DECLARE(xilinx_intc, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c index 8f120aca123..11e24de91aa 100644 --- a/arch/microblaze/kernel/irq.c +++ b/arch/microblaze/kernel/irq.c @@ -17,19 +17,8 @@ #include <linux/seq_file.h> #include <linux/kernel_stat.h> #include <linux/irq.h> - -#include <asm/prom.h> - -unsigned int irq_of_parse_and_map(struct device_node *dev, int index) -{ - struct of_irq oirq; - - if (of_irq_map_one(dev, index, &oirq)) - return NO_IRQ; - - return oirq.specifier[0]; -} -EXPORT_SYMBOL_GPL(irq_of_parse_and_map); +#include <linux/irqchip.h> +#include <linux/of_irq.h> static u32 concurrent_irq; @@ -37,14 +26,15 @@ void __irq_entry do_IRQ(struct pt_regs *regs) { unsigned int irq; struct pt_regs *old_regs = set_irq_regs(regs); + trace_hardirqs_off(); irq_enter(); - irq = get_irq(regs); + irq = get_irq(); next_irq: - BUG_ON(irq == -1U); + BUG_ON(!irq); generic_handle_irq(irq); - irq = get_irq(regs); + irq = get_irq(); if (irq != -1U) { pr_debug("next irq: %d\n", irq); ++concurrent_irq; @@ -53,59 +43,11 @@ next_irq: irq_exit(); set_irq_regs(old_regs); + trace_hardirqs_on(); } -int show_interrupts(struct seq_file *p, void *v) -{ - int i = *(loff_t *) v, j; - struct irqaction *action; - unsigned long flags; - - if (i == 0) { - seq_printf(p, " "); - for_each_online_cpu(j) - seq_printf(p, "CPU%-8d", j); - seq_putc(p, '\n'); - } - - if (i < nr_irq) { - raw_spin_lock_irqsave(&irq_desc[i].lock, flags); - action = irq_desc[i].action; - if (!action) - goto skip; - seq_printf(p, "%3d: ", i); -#ifndef CONFIG_SMP - seq_printf(p, "%10u ", kstat_irqs(i)); -#else - for_each_online_cpu(j) - seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); -#endif - seq_printf(p, " %8s", irq_desc[i].status & - IRQ_LEVEL ? "level" : "edge"); - seq_printf(p, " %8s", irq_desc[i].chip->name); - seq_printf(p, " %s", action->name); - - for (action = action->next; action; action = action->next) - seq_printf(p, ", %s", action->name); - - seq_putc(p, '\n'); -skip: - raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); - } - return 0; -} - -/* MS: There is no any advance mapping mechanism. We are using simple 32bit - intc without any cascades or any connection that's why mapping is 1:1 */ -unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq) -{ - return hwirq; -} -EXPORT_SYMBOL_GPL(irq_create_mapping); - -unsigned int irq_create_of_mapping(struct device_node *controller, - u32 *intspec, unsigned int intsize) +void __init init_IRQ(void) { - return intspec[0]; + /* process the entire interrupt tree in one go */ + irqchip_init(); } -EXPORT_SYMBOL_GPL(irq_create_of_mapping); diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c new file mode 100644 index 00000000000..09a5e828613 --- /dev/null +++ b/arch/microblaze/kernel/kgdb.c @@ -0,0 +1,150 @@ +/* + * Microblaze KGDB support + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <asm/cacheflush.h> +#include <asm/asm-offsets.h> +#include <asm/pvr.h> + +#define GDB_REG 0 +#define GDB_PC 32 +#define GDB_MSR 33 +#define GDB_EAR 34 +#define GDB_ESR 35 +#define GDB_FSR 36 +#define GDB_BTR 37 +#define GDB_PVR 38 +#define GDB_REDR 50 +#define GDB_RPID 51 +#define GDB_RZPR 52 +#define GDB_RTLBX 53 +#define GDB_RTLBSX 54 /* mfs can't read it */ +#define GDB_RTLBLO 55 +#define GDB_RTLBHI 56 + +/* keep pvr separately because it is unchangeble */ +struct pvr_s pvr; + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int i; + unsigned long *pt_regb = (unsigned long *)regs; + int temp; + /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ + for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) + gdb_regs[i] = pt_regb[i]; + + /* Branch target register can't be changed */ + __asm__ __volatile__ ("mfs %0, rbtr;" : "=r"(temp) : ); + gdb_regs[GDB_BTR] = temp; + + /* pvr part - we have 11 pvr regs */ + for (i = 0; i < sizeof(struct pvr_s)/4; i++) + gdb_regs[GDB_PVR + i] = pvr.pvr[i]; + + /* read special registers - can't be changed */ + __asm__ __volatile__ ("mfs %0, redr;" : "=r"(temp) : ); + gdb_regs[GDB_REDR] = temp; + __asm__ __volatile__ ("mfs %0, rpid;" : "=r"(temp) : ); + gdb_regs[GDB_RPID] = temp; + __asm__ __volatile__ ("mfs %0, rzpr;" : "=r"(temp) : ); + gdb_regs[GDB_RZPR] = temp; + __asm__ __volatile__ ("mfs %0, rtlbx;" : "=r"(temp) : ); + gdb_regs[GDB_RTLBX] = temp; + __asm__ __volatile__ ("mfs %0, rtlblo;" : "=r"(temp) : ); + gdb_regs[GDB_RTLBLO] = temp; + __asm__ __volatile__ ("mfs %0, rtlbhi;" : "=r"(temp) : ); + gdb_regs[GDB_RTLBHI] = temp; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int i; + unsigned long *pt_regb = (unsigned long *)regs; + + /* pt_regs and gdb_regs have the same 37 values. + * The rest of gdb_regs are unused and can't be changed. + * r0 register value can't be changed too. */ + for (i = 1; i < (sizeof(struct pt_regs) / 4) - 1; i++) + pt_regb[i] = gdb_regs[i]; +} + +void microblaze_kgdb_break(struct pt_regs *regs) +{ + if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0) + return; + + /* Jump over the first arch_kgdb_breakpoint which is barrier to + * get kgdb work. The same solution is used for powerpc */ + if (*(u32 *) (regs->pc) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) + regs->pc += BREAK_INSTR_SIZE; +} + +/* untested */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + int i; + unsigned long *pt_regb = (unsigned long *)(p->thread.regs); + + /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ + for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) + gdb_regs[i] = pt_regb[i]; + + /* pvr part - we have 11 pvr regs */ + for (i = 0; i < sizeof(struct pvr_s)/4; i++) + gdb_regs[GDB_PVR + i] = pvr.pvr[i]; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) +{ + regs->pc = ip; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long address; + + switch (remcom_in_buffer[0]) { + case 'c': + /* handle the optional parameter */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &address)) + regs->pc = address; + + return 0; + } + return -1; /* this means that we do not want to exit from the handler */ +} + +int kgdb_arch_init(void) +{ + get_pvr(&pvr); /* Fill PVR structure */ + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +/* + * Global data + */ +struct kgdb_arch arch_kgdb_ops = { +#ifdef __MICROBLAZEEL__ + .gdb_bpt_instr = {0x18, 0x00, 0x0c, 0xba}, /* brki r16, 0x18 */ +#else + .gdb_bpt_instr = {0xba, 0x0c, 0x00, 0x18}, /* brki r16, 0x18 */ +#endif +}; diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S index e7eaa7a8cbd..fc1e1322ce4 100644 --- a/arch/microblaze/kernel/mcount.S +++ b/arch/microblaze/kernel/mcount.S @@ -138,7 +138,7 @@ NOALIGN_ENTRY(ftrace_call) #endif /* CONFIG_DYNAMIC_FTRACE */ /* static normal trace */ lwi r6, r1, 120; /* MS: load parent addr */ - addik r5, r15, 0; /* MS: load current function addr */ + addik r5, r15, -4; /* MS: load current function addr */ /* MS: here is dependency on previous code */ brald r15, r20; /* MS: jump to ftrace handler */ nop; diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c index ff85f771803..9f1d02c4c5c 100644 --- a/arch/microblaze/kernel/microblaze_ksyms.c +++ b/arch/microblaze/kernel/microblaze_ksyms.c @@ -7,7 +7,7 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/string.h> #include <linux/cryptohash.h> #include <linux/delay.h> @@ -15,39 +15,12 @@ #include <linux/syscalls.h> #include <asm/checksum.h> +#include <asm/cacheflush.h> #include <linux/io.h> #include <asm/page.h> -#include <asm/system.h> #include <linux/ftrace.h> #include <linux/uaccess.h> -/* - * libgcc functions - functions that are used internally by the - * compiler... (prototypes are not correct though, but that - * doesn't really matter since they're not versioned). - */ -extern void __ashldi3(void); -EXPORT_SYMBOL(__ashldi3); -extern void __ashrdi3(void); -EXPORT_SYMBOL(__ashrdi3); -extern void __divsi3(void); -EXPORT_SYMBOL(__divsi3); -extern void __lshrdi3(void); -EXPORT_SYMBOL(__lshrdi3); -extern void __modsi3(void); -EXPORT_SYMBOL(__modsi3); -extern void __mulsi3(void); -EXPORT_SYMBOL(__mulsi3); -extern void __muldi3(void); -EXPORT_SYMBOL(__muldi3); -extern void __ucmpdi2(void); -EXPORT_SYMBOL(__ucmpdi2); -extern void __udivsi3(void); -EXPORT_SYMBOL(__udivsi3); -extern void __umodsi3(void); -EXPORT_SYMBOL(__umodsi3); -extern char *_ebss; -EXPORT_SYMBOL_GPL(_ebss); #ifdef CONFIG_FUNCTION_TRACER extern void _mcount(void); EXPORT_SYMBOL(_mcount); @@ -63,3 +36,20 @@ EXPORT_SYMBOL(__strncpy_user); EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memmove); #endif + +#ifdef CONFIG_MMU +EXPORT_SYMBOL(empty_zero_page); +#endif + +EXPORT_SYMBOL(mbc); + +extern void __divsi3(void); +EXPORT_SYMBOL(__divsi3); +extern void __modsi3(void); +EXPORT_SYMBOL(__modsi3); +extern void __mulsi3(void); +EXPORT_SYMBOL(__mulsi3); +extern void __udivsi3(void); +EXPORT_SYMBOL(__udivsi3); +extern void __umodsi3(void); +EXPORT_SYMBOL(__umodsi3); diff --git a/arch/microblaze/kernel/misc.S b/arch/microblaze/kernel/misc.S index 0fb5fc6c1fc..1dafddeb8a0 100644 --- a/arch/microblaze/kernel/misc.S +++ b/arch/microblaze/kernel/misc.S @@ -29,16 +29,16 @@ .type _tlbia, @function .align 4; _tlbia: - addik r12, r0, MICROBLAZE_TLB_SIZE - 1 /* flush all entries (63 - 3) */ + lwi r12, r0, tlb_skip; /* isync */ _tlbia_1: mts rtlbx, r12 nop mts rtlbhi, r0 /* flush: ensure V is clear */ nop - addik r11, r12, -2 + rsubi r11, r12, MICROBLAZE_TLB_SIZE - 1 bneid r11, _tlbia_1 /* loop for all entries */ - addik r12, r12, -1 + addik r12, r12, 1 /* sync */ rtsd r15, 8 nop @@ -75,8 +75,8 @@ early_console_reg_tlb_alloc: * Load a TLB entry for the UART, so that microblaze_progress() can use * the UARTs nice and early. We use a 4k real==virtual mapping. */ - ori r4, r0, MICROBLAZE_TLB_SIZE - 1 - mts rtlbx, r4 /* TLB slot 2 */ + lwi r4, r0, tlb_skip + mts rtlbx, r4 /* TLB slot 63 */ or r4,r5,r0 andi r4,r4,0xfffff000 @@ -89,6 +89,11 @@ early_console_reg_tlb_alloc: nop mts rtlbhi,r5 /* Load the tag portion of the entry */ nop + + lwi r5, r0, tlb_skip + addik r5, r5, 1 + swi r5, r0, tlb_skip + rtsd r15, 8 nop diff --git a/arch/microblaze/kernel/module.c b/arch/microblaze/kernel/module.c index 0e73f660654..182e6be856c 100644 --- a/arch/microblaze/kernel/module.c +++ b/arch/microblaze/kernel/module.c @@ -7,7 +7,7 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/moduleloader.h> #include <linux/kernel.h> #include <linux/elf.h> @@ -18,37 +18,6 @@ #include <asm/pgtable.h> #include <asm/cacheflush.h> -void *module_alloc(unsigned long size) -{ - void *ret; - ret = (size == 0) ? NULL : vmalloc(size); - pr_debug("module_alloc (%08lx@%08lx)\n", size, (unsigned long int)ret); - return ret; -} - -void module_free(struct module *module, void *region) -{ - pr_debug("module_free(%s,%08lx)\n", module->name, - (unsigned long)region); - vfree(region); -} - -int module_frob_arch_sections(Elf_Ehdr *hdr, - Elf_Shdr *sechdrs, - char *secstrings, - struct module *mod) -{ - return 0; -} - -int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, struct module *module) -{ - printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", - module->name); - return -ENOEXEC; -} - int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *module) { @@ -131,7 +100,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, break; case R_MICROBLAZE_64_NONE: - pr_debug("R_MICROBLAZE_NONE\n"); + pr_debug("R_MICROBLAZE_64_NONE\n"); break; case R_MICROBLAZE_NONE: @@ -139,8 +108,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, break; default: - printk(KERN_ERR "module %s: " - "Unknown relocation: %u\n", + pr_err("module %s: Unknown relocation: %u\n", module->name, ELF32_R_TYPE(rela[i].r_info)); return -ENOEXEC; @@ -155,7 +123,3 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, flush_dcache(); return 0; } - -void module_arch_cleanup(struct module *mod) -{ -} diff --git a/arch/microblaze/kernel/of_device.c b/arch/microblaze/kernel/of_device.c deleted file mode 100644 index b372787886e..00000000000 --- a/arch/microblaze/kernel/of_device.c +++ /dev/null @@ -1,112 +0,0 @@ -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/of.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/slab.h> -#include <linux/of_device.h> - -#include <linux/errno.h> - -void of_device_make_bus_id(struct of_device *dev) -{ - static atomic_t bus_no_reg_magic; - struct device_node *node = dev->dev.of_node; - const u32 *reg; - u64 addr; - int magic; - - /* - * For MMIO, get the physical address - */ - reg = of_get_property(node, "reg", NULL); - if (reg) { - addr = of_translate_address(node, reg); - if (addr != OF_BAD_ADDR) { - dev_set_name(&dev->dev, "%llx.%s", - (unsigned long long)addr, node->name); - return; - } - } - - /* - * No BusID, use the node name and add a globally incremented - * counter (and pray...) - */ - magic = atomic_add_return(1, &bus_no_reg_magic); - dev_set_name(&dev->dev, "%s.%d", node->name, magic - 1); -} -EXPORT_SYMBOL(of_device_make_bus_id); - -struct of_device *of_device_alloc(struct device_node *np, - const char *bus_id, - struct device *parent) -{ - struct of_device *dev; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - dev->dev.of_node = of_node_get(np); - dev->dev.dma_mask = &dev->archdata.dma_mask; - dev->dev.parent = parent; - dev->dev.release = of_release_dev; - - if (bus_id) - dev_set_name(&dev->dev, bus_id); - else - of_device_make_bus_id(dev); - - return dev; -} -EXPORT_SYMBOL(of_device_alloc); - -int of_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct of_device *ofdev; - const char *compat; - int seen = 0, cplen, sl; - - if (!dev) - return -ENODEV; - - ofdev = to_of_device(dev); - - if (add_uevent_var(env, "OF_NAME=%s", ofdev->dev.of_node->name)) - return -ENOMEM; - - if (add_uevent_var(env, "OF_TYPE=%s", ofdev->dev.of_node->type)) - return -ENOMEM; - - /* Since the compatible field can contain pretty much anything - * it's not really legal to split it out with commas. We split it - * up using a number of environment variables instead. */ - - compat = of_get_property(ofdev->dev.of_node, "compatible", &cplen); - while (compat && *compat && cplen > 0) { - if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat)) - return -ENOMEM; - - sl = strlen(compat) + 1; - compat += sl; - cplen -= sl; - seen++; - } - - if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen)) - return -ENOMEM; - - /* modalias is trickier, we add it in 2 steps */ - if (add_uevent_var(env, "MODALIAS=")) - return -ENOMEM; - sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1], - sizeof(env->buf) - env->buflen); - if (sl >= (sizeof(env->buf) - env->buflen)) - return -ENOMEM; - env->buflen += sl; - - return 0; -} -EXPORT_SYMBOL(of_device_uevent); diff --git a/arch/microblaze/kernel/of_platform.c b/arch/microblaze/kernel/of_platform.c deleted file mode 100644 index ccf6f4257f4..00000000000 --- a/arch/microblaze/kernel/of_platform.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. - * <benh@kernel.crashing.org> - * and Arnd Bergmann, IBM Corp. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -#undef DEBUG - -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/pci.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_platform.h> - -#include <linux/errno.h> -#include <linux/topology.h> -#include <asm/atomic.h> - -struct bus_type of_platform_bus_type = { - .uevent = of_device_uevent, -}; -EXPORT_SYMBOL(of_platform_bus_type); - -static int __init of_bus_driver_init(void) -{ - return of_bus_type_init(&of_platform_bus_type, "of_platform"); -} -postcore_initcall(of_bus_driver_init); - -struct of_device *of_platform_device_create(struct device_node *np, - const char *bus_id, - struct device *parent) -{ - struct of_device *dev; - - dev = of_device_alloc(np, bus_id, parent); - if (!dev) - return NULL; - - dev->archdata.dma_mask = 0xffffffffUL; - dev->dev.bus = &of_platform_bus_type; - - /* We do not fill the DMA ops for platform devices by default. - * This is currently the responsibility of the platform code - * to do such, possibly using a device notifier - */ - - if (of_device_register(dev) != 0) { - of_device_free(dev); - return NULL; - } - - return dev; -} -EXPORT_SYMBOL(of_platform_device_create); - -/** - * of_platform_bus_create - Create an OF device for a bus node and all its - * children. Optionally recursively instanciate matching busses. - * @bus: device node of the bus to instanciate - * @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to - * disallow recursive creation of child busses - */ -static int of_platform_bus_create(const struct device_node *bus, - const struct of_device_id *matches, - struct device *parent) -{ - struct device_node *child; - struct of_device *dev; - int rc = 0; - - for_each_child_of_node(bus, child) { - pr_debug(" create child: %s\n", child->full_name); - dev = of_platform_device_create(child, NULL, parent); - if (dev == NULL) - rc = -ENOMEM; - else if (!of_match_node(matches, child)) - continue; - if (rc == 0) { - pr_debug(" and sub busses\n"); - rc = of_platform_bus_create(child, matches, &dev->dev); - } - if (rc) { - of_node_put(child); - break; - } - } - return rc; -} - - -/** - * of_platform_bus_probe - Probe the device-tree for platform busses - * @root: parent of the first level to probe or NULL for the root of the tree - * @matches: match table, NULL to use the default - * @parent: parent to hook devices from, NULL for toplevel - * - * Note that children of the provided root are not instanciated as devices - * unless the specified root itself matches the bus list and is not NULL. - */ - -int of_platform_bus_probe(struct device_node *root, - const struct of_device_id *matches, - struct device *parent) -{ - struct device_node *child; - struct of_device *dev; - int rc = 0; - - if (matches == NULL) - matches = of_default_bus_ids; - if (matches == OF_NO_DEEP_PROBE) - return -EINVAL; - if (root == NULL) - root = of_find_node_by_path("/"); - else - of_node_get(root); - - pr_debug("of_platform_bus_probe()\n"); - pr_debug(" starting at: %s\n", root->full_name); - - /* Do a self check of bus type, if there's a match, create - * children - */ - if (of_match_node(matches, root)) { - pr_debug(" root match, create all sub devices\n"); - dev = of_platform_device_create(root, NULL, parent); - if (dev == NULL) { - rc = -ENOMEM; - goto bail; - } - pr_debug(" create all sub busses\n"); - rc = of_platform_bus_create(root, matches, &dev->dev); - goto bail; - } - for_each_child_of_node(root, child) { - if (!of_match_node(matches, child)) - continue; - - pr_debug(" match: %s\n", child->full_name); - dev = of_platform_device_create(child, NULL, parent); - if (dev == NULL) - rc = -ENOMEM; - else - rc = of_platform_bus_create(child, matches, &dev->dev); - if (rc) { - of_node_put(child); - break; - } - } - bail: - of_node_put(root); - return rc; -} -EXPORT_SYMBOL(of_platform_bus_probe); - -static int of_dev_node_match(struct device *dev, void *data) -{ - return to_of_device(dev)->dev.of_node == data; -} - -struct of_device *of_find_device_by_node(struct device_node *np) -{ - struct device *dev; - - dev = bus_find_device(&of_platform_bus_type, - NULL, np, of_dev_node_match); - if (dev) - return to_of_device(dev); - return NULL; -} -EXPORT_SYMBOL(of_find_device_by_node); - -static int of_dev_phandle_match(struct device *dev, void *data) -{ - phandle *ph = data; - return to_of_device(dev)->dev.of_node->phandle == *ph; -} - -struct of_device *of_find_device_by_phandle(phandle ph) -{ - struct device *dev; - - dev = bus_find_device(&of_platform_bus_type, - NULL, &ph, of_dev_phandle_match); - if (dev) - return to_of_device(dev); - return NULL; -} -EXPORT_SYMBOL(of_find_device_by_phandle); diff --git a/arch/microblaze/kernel/platform.c b/arch/microblaze/kernel/platform.c new file mode 100644 index 00000000000..b9529caa507 --- /dev/null +++ b/arch/microblaze/kernel/platform.c @@ -0,0 +1,30 @@ +/* + * Copyright 2008 Michal Simek <monstr@monstr.eu> + * + * based on virtex.c file + * + * Copyright 2007 Secret Lab Technologies Ltd. + * + * 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/init.h> +#include <linux/of_platform.h> +#include <asm/prom.h> +#include <asm/setup.h> + +static struct of_device_id xilinx_of_bus_ids[] __initdata = { + { .compatible = "simple-bus", }, + { .compatible = "xlnx,compound", }, + {} +}; + +static int __init microblaze_device_probe(void) +{ + of_platform_bus_probe(NULL, xilinx_of_bus_ids, NULL); + of_platform_reset_gpio_probe(); + return 0; +} +device_initcall(microblaze_device_probe); diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index 09bed44dfcd..b2dd37196b3 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c @@ -8,155 +8,82 @@ * for more details. */ -#include <linux/module.h> +#include <linux/cpu.h> +#include <linux/export.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/tick.h> #include <linux/bitops.h> -#include <asm/system.h> +#include <linux/ptrace.h> #include <asm/pgalloc.h> -#include <asm/uaccess.h> /* for USER_DS macros */ +#include <linux/uaccess.h> /* for USER_DS macros */ #include <asm/cacheflush.h> void show_regs(struct pt_regs *regs) { - printk(KERN_INFO " Registers dump: mode=%X\r\n", regs->pt_mode); - printk(KERN_INFO " r1=%08lX, r2=%08lX, r3=%08lX, r4=%08lX\n", + show_regs_print_info(KERN_INFO); + + pr_info(" Registers dump: mode=%X\r\n", regs->pt_mode); + pr_info(" r1=%08lX, r2=%08lX, r3=%08lX, r4=%08lX\n", regs->r1, regs->r2, regs->r3, regs->r4); - printk(KERN_INFO " r5=%08lX, r6=%08lX, r7=%08lX, r8=%08lX\n", + pr_info(" r5=%08lX, r6=%08lX, r7=%08lX, r8=%08lX\n", regs->r5, regs->r6, regs->r7, regs->r8); - printk(KERN_INFO " r9=%08lX, r10=%08lX, r11=%08lX, r12=%08lX\n", + pr_info(" r9=%08lX, r10=%08lX, r11=%08lX, r12=%08lX\n", regs->r9, regs->r10, regs->r11, regs->r12); - printk(KERN_INFO " r13=%08lX, r14=%08lX, r15=%08lX, r16=%08lX\n", + pr_info(" r13=%08lX, r14=%08lX, r15=%08lX, r16=%08lX\n", regs->r13, regs->r14, regs->r15, regs->r16); - printk(KERN_INFO " r17=%08lX, r18=%08lX, r19=%08lX, r20=%08lX\n", + pr_info(" r17=%08lX, r18=%08lX, r19=%08lX, r20=%08lX\n", regs->r17, regs->r18, regs->r19, regs->r20); - printk(KERN_INFO " r21=%08lX, r22=%08lX, r23=%08lX, r24=%08lX\n", + pr_info(" r21=%08lX, r22=%08lX, r23=%08lX, r24=%08lX\n", regs->r21, regs->r22, regs->r23, regs->r24); - printk(KERN_INFO " r25=%08lX, r26=%08lX, r27=%08lX, r28=%08lX\n", + pr_info(" r25=%08lX, r26=%08lX, r27=%08lX, r28=%08lX\n", regs->r25, regs->r26, regs->r27, regs->r28); - printk(KERN_INFO " r29=%08lX, r30=%08lX, r31=%08lX, rPC=%08lX\n", + pr_info(" r29=%08lX, r30=%08lX, r31=%08lX, rPC=%08lX\n", regs->r29, regs->r30, regs->r31, regs->pc); - printk(KERN_INFO " msr=%08lX, ear=%08lX, esr=%08lX, fsr=%08lX\n", + pr_info(" msr=%08lX, ear=%08lX, esr=%08lX, fsr=%08lX\n", regs->msr, regs->ear, regs->esr, regs->fsr); } -void (*pm_idle)(void); void (*pm_power_off)(void) = NULL; EXPORT_SYMBOL(pm_power_off); -static int hlt_counter = 1; - -void disable_hlt(void) -{ - hlt_counter++; -} -EXPORT_SYMBOL(disable_hlt); - -void enable_hlt(void) -{ - hlt_counter--; -} -EXPORT_SYMBOL(enable_hlt); - -static int __init nohlt_setup(char *__unused) -{ - hlt_counter = 1; - return 1; -} -__setup("nohlt", nohlt_setup); - -static int __init hlt_setup(char *__unused) -{ - hlt_counter = 0; - return 1; -} -__setup("hlt", hlt_setup); - -void default_idle(void) -{ - if (likely(hlt_counter)) { - while (!need_resched()) - cpu_relax(); - } else { - clear_thread_flag(TIF_POLLING_NRFLAG); - smp_mb__after_clear_bit(); - local_irq_disable(); - while (!need_resched()) - cpu_sleep(); - local_irq_enable(); - set_thread_flag(TIF_POLLING_NRFLAG); - } -} - -void cpu_idle(void) -{ - set_thread_flag(TIF_POLLING_NRFLAG); - - /* endless idle loop with no priority at all */ - while (1) { - void (*idle)(void) = pm_idle; - - if (!idle) - idle = default_idle; - - tick_nohz_stop_sched_tick(1); - while (!need_resched()) - idle(); - tick_nohz_restart_sched_tick(); - - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - check_pgt_cache(); - } -} - void flush_thread(void) { } int copy_thread(unsigned long clone_flags, unsigned long usp, - unsigned long unused, - struct task_struct *p, struct pt_regs *regs) + unsigned long arg, struct task_struct *p) { struct pt_regs *childregs = task_pt_regs(p); struct thread_info *ti = task_thread_info(p); - *childregs = *regs; - if (user_mode(regs)) + if (unlikely(p->flags & PF_KTHREAD)) { + /* if we're creating a new kernel thread then just zeroing all + * the registers. That's OK for a brand new thread.*/ + memset(childregs, 0, sizeof(struct pt_regs)); + memset(&ti->cpu_context, 0, sizeof(struct cpu_context)); + ti->cpu_context.r1 = (unsigned long)childregs; + ti->cpu_context.r20 = (unsigned long)usp; /* fn */ + ti->cpu_context.r19 = (unsigned long)arg; + childregs->pt_mode = 1; + local_save_flags(childregs->msr); +#ifdef CONFIG_MMU + ti->cpu_context.msr = childregs->msr & ~MSR_IE; +#endif + ti->cpu_context.r15 = (unsigned long)ret_from_kernel_thread - 8; + return 0; + } + *childregs = *current_pt_regs(); + if (usp) childregs->r1 = usp; - else - childregs->r1 = ((unsigned long) ti) + THREAD_SIZE; -#ifndef CONFIG_MMU memset(&ti->cpu_context, 0, sizeof(struct cpu_context)); ti->cpu_context.r1 = (unsigned long)childregs; +#ifndef CONFIG_MMU ti->cpu_context.msr = (unsigned long)childregs->msr; #else + childregs->msr |= MSR_UMS; - /* if creating a kernel thread then update the current reg (we don't - * want to use the parent's value when restoring by POP_STATE) */ - if (kernel_mode(regs)) - /* save new current on stack to use POP_STATE */ - childregs->CURRENT_TASK = (unsigned long)p; - /* if returning to user then use the parent's value of this register */ - - /* if we're creating a new kernel thread then just zeroing all - * the registers. That's OK for a brand new thread.*/ - /* Pls. note that some of them will be restored in POP_STATE */ - if (kernel_mode(regs)) - memset(&ti->cpu_context, 0, sizeof(struct cpu_context)); - /* if this thread is created for fork/vfork/clone, then we want to - * restore all the parent's context */ - /* in addition to the registers which will be restored by POP_STATE */ - else { - ti->cpu_context = *(struct cpu_context *)regs; - childregs->msr |= MSR_UMS; - } - - /* FIXME STATE_SAVE_PT_OFFSET; */ - ti->cpu_context.r1 = (unsigned long)childregs - STATE_SAVE_ARG_SPACE; /* we should consider the fact that childregs is a copy of the parent * regs which were saved immediately after entering the kernel state * before enabling VM. This MSR will be restored in switch_to and @@ -167,7 +94,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, * excepting for VM and UMS * don't touch UMS , CARRY and cache bits * right now MSR is a copy of parent one */ - childregs->msr |= MSR_BIP; childregs->msr &= ~MSR_EIP; childregs->msr |= MSR_IE; childregs->msr &= ~MSR_VM; @@ -176,11 +102,16 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ti->cpu_context.msr = (childregs->msr|MSR_VM); ti->cpu_context.msr &= ~MSR_UMS; /* switch_to to kernel mode */ + ti->cpu_context.msr &= ~MSR_IE; #endif ti->cpu_context.r15 = (unsigned long)ret_from_fork - 8; + /* + * r21 is the thread reg, r10 is 6th arg to clone + * which contains TLS area + */ if (clone_flags & CLONE_SETTLS) - ; + childregs->r21 = childregs->r10; return 0; } @@ -202,29 +133,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk) } #endif -static void kernel_thread_helper(int (*fn)(void *), void *arg) -{ - fn(arg); - do_exit(-1); -} - -int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) -{ - struct pt_regs regs; - - memset(®s, 0, sizeof(regs)); - /* store them in non-volatile registers */ - regs.r5 = (unsigned long)fn; - regs.r6 = (unsigned long)arg; - local_save_flags(regs.msr); - regs.pc = (unsigned long)kernel_thread_helper; - regs.pt_mode = 1; - - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, - ®s, 0, NULL, NULL); -} -EXPORT_SYMBOL_GPL(kernel_thread); - unsigned long get_wchan(struct task_struct *p) { /* TBD (used by procfs) */ @@ -234,12 +142,12 @@ unsigned long get_wchan(struct task_struct *p) /* Set up a thread for executing a new program */ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp) { - set_fs(USER_DS); regs->pc = pc; regs->r1 = usp; regs->pt_mode = 0; #ifdef CONFIG_MMU regs->msr |= MSR_UMS; + regs->msr &= ~MSR_VM; #endif } @@ -253,3 +161,8 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) return 0; /* MicroBlaze has no separate FPU registers */ } #endif /* CONFIG_MMU */ + +void arch_cpu_idle(void) +{ + local_irq_enable(); +} diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index 427b13b4740..68f099960eb 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -14,6 +14,7 @@ */ #include <stdarg.h> +#include <linux/export.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/init.h> @@ -25,69 +26,75 @@ #include <linux/delay.h> #include <linux/initrd.h> #include <linux/bitops.h> -#include <linux/module.h> #include <linux/kexec.h> #include <linux/debugfs.h> #include <linux/irq.h> #include <linux/memblock.h> +#include <linux/of_fdt.h> #include <asm/prom.h> #include <asm/page.h> #include <asm/processor.h> #include <asm/irq.h> #include <linux/io.h> -#include <asm/system.h> #include <asm/mmu.h> #include <asm/pgtable.h> #include <asm/sections.h> #include <asm/pci-bridge.h> -void __init early_init_dt_scan_chosen_arch(unsigned long node) -{ - /* No Microblaze specific code here */ -} - -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ - memblock_add(base, size); -} - -u64 __init early_init_dt_alloc_memory_arch(u64 size, u64 align) -{ - return memblock_alloc(size, align); -} - #ifdef CONFIG_EARLY_PRINTK -/* MS this is Microblaze specifig function */ -static int __init early_init_dt_scan_serial(unsigned long node, +static const char *stdout; + +static int __init early_init_dt_scan_chosen_serial(unsigned long node, const char *uname, int depth, void *data) { - unsigned long l; - char *p; - int *addr; - - pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); - -/* find all serial nodes */ - if (strncmp(uname, "serial", 6) != 0) - return 0; - - early_init_dt_check_for_initrd(node); - -/* find compatible node with uartlite */ - p = of_get_flat_dt_prop(node, "compatible", &l); - if ((strncmp(p, "xlnx,xps-uartlite", 17) != 0) && - (strncmp(p, "xlnx,opb-uartlite", 17) != 0)) - return 0; - - addr = of_get_flat_dt_prop(node, "reg", &l); - return *addr; /* return address */ + int l; + const char *p; + + pr_debug("%s: depth: %d, uname: %s\n", __func__, depth, uname); + + if (depth == 1 && (strcmp(uname, "chosen") == 0 || + strcmp(uname, "chosen@0") == 0)) { + p = of_get_flat_dt_prop(node, "linux,stdout-path", &l); + if (p != NULL && l > 0) + stdout = p; /* store pointer to stdout-path */ + } + + if (stdout && strstr(stdout, uname)) { + p = of_get_flat_dt_prop(node, "compatible", &l); + pr_debug("Compatible string: %s\n", p); + + if ((strncmp(p, "xlnx,xps-uart16550", 18) == 0) || + (strncmp(p, "xlnx,axi-uart16550", 18) == 0)) { + unsigned int addr; + + *(u32 *)data = UART16550; + + addr = *(u32 *)of_get_flat_dt_prop(node, "reg", &l); + addr += *(u32 *)of_get_flat_dt_prop(node, + "reg-offset", &l); + /* clear register offset */ + return be32_to_cpu(addr) & ~3; + } + if ((strncmp(p, "xlnx,xps-uartlite", 17) == 0) || + (strncmp(p, "xlnx,opb-uartlite", 17) == 0) || + (strncmp(p, "xlnx,axi-uartlite", 17) == 0) || + (strncmp(p, "xlnx,mdm", 8) == 0)) { + const unsigned int *addrp; + + *(u32 *)data = UARTLITE; + + addrp = of_get_flat_dt_prop(node, "reg", &l); + return be32_to_cpup(addrp); /* return address */ + } + } + return 0; } -/* this function is looking for early uartlite console - Microblaze specific */ -int __init early_uartlite_console(void) +/* this function is looking for early console - Microblaze specific */ +int __init of_early_console(void *version) { - return of_scan_flat_dt(early_init_dt_scan_serial, NULL); + return of_scan_flat_dt(early_init_dt_scan_chosen_serial, version); } #endif @@ -95,68 +102,15 @@ void __init early_init_devtree(void *params) { pr_debug(" -> early_init_devtree(%p)\n", params); - /* Setup flat device-tree pointer */ - initial_boot_params = params; - - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, TCE reserve, and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, NULL); - - /* Scan memory nodes and rebuild MEMBLOCKs */ - memblock_init(); - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory, NULL); + early_init_dt_scan(params); + if (!strlen(boot_command_line)) + strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); - /* Save command line for /proc/cmdline and then parse parameters */ - strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); parse_early_param(); - memblock_analyze(); + memblock_allow_resize(); pr_debug("Phys. mem: %lx\n", (unsigned long) memblock_phys_mem_size()); pr_debug(" <- early_init_devtree()\n"); } - -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - -/******* - * - * New implementation of the OF "find" APIs, return a refcounted - * object, call of_node_put() when done. The device tree and list - * are protected by a rw_lock. - * - * Note that property management will need some locking as well, - * this isn't dealt with yet. - * - *******/ - -#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) -static struct debugfs_blob_wrapper flat_dt_blob; - -static int __init export_flat_device_tree(void) -{ - struct dentry *d; - - flat_dt_blob.data = initial_boot_params; - flat_dt_blob.size = initial_boot_params->totalsize; - - d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR, - of_debugfs_root, &flat_dt_blob); - if (!d) - return 1; - - return 0; -} -device_initcall(export_flat_device_tree); -#endif diff --git a/arch/microblaze/kernel/prom_parse.c b/arch/microblaze/kernel/prom_parse.c index bf7e6c27e31..068762f55fd 100644 --- a/arch/microblaze/kernel/prom_parse.c +++ b/arch/microblaze/kernel/prom_parse.c @@ -1,622 +1,12 @@ #undef DEBUG +#include <linux/export.h> #include <linux/kernel.h> #include <linux/string.h> -#include <linux/pci_regs.h> -#include <linux/module.h> #include <linux/ioport.h> #include <linux/etherdevice.h> +#include <linux/of_address.h> #include <asm/prom.h> -#include <asm/pci-bridge.h> - -#define PRu64 "%llx" - -/* Max address size we deal with */ -#define OF_MAX_ADDR_CELLS 4 -#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ - (ns) > 0) - -static struct of_bus *of_match_bus(struct device_node *np); -static int __of_address_to_resource(struct device_node *dev, - const u32 *addrp, u64 size, unsigned int flags, - struct resource *r); - -/* Debug utility */ -#ifdef DEBUG -static void of_dump_addr(const char *s, const u32 *addr, int na) -{ - printk(KERN_INFO "%s", s); - while (na--) - printk(KERN_INFO " %08x", *(addr++)); - printk(KERN_INFO "\n"); -} -#else -static void of_dump_addr(const char *s, const u32 *addr, int na) { } -#endif - -/* Callbacks for bus specific translators */ -struct of_bus { - const char *name; - const char *addresses; - int (*match)(struct device_node *parent); - void (*count_cells)(struct device_node *child, - int *addrc, int *sizec); - u64 (*map)(u32 *addr, const u32 *range, - int na, int ns, int pna); - int (*translate)(u32 *addr, u64 offset, int na); - unsigned int (*get_flags)(const u32 *addr); -}; - -/* - * Default translator (generic bus) - */ - -static void of_bus_default_count_cells(struct device_node *dev, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = of_n_addr_cells(dev); - if (sizec) - *sizec = of_n_size_cells(dev); -} - -static u64 of_bus_default_map(u32 *addr, const u32 *range, - int na, int ns, int pna) -{ - u64 cp, s, da; - - cp = of_read_number(range, na); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr, na); - - pr_debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", - cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_default_translate(u32 *addr, u64 offset, int na) -{ - u64 a = of_read_number(addr, na); - memset(addr, 0, na * 4); - a += offset; - if (na > 1) - addr[na - 2] = a >> 32; - addr[na - 1] = a & 0xffffffffu; - - return 0; -} - -static unsigned int of_bus_default_get_flags(const u32 *addr) -{ - return IORESOURCE_MEM; -} - -#ifdef CONFIG_PCI -/* - * PCI bus specific translator - */ - -static int of_bus_pci_match(struct device_node *np) -{ - /* "vci" is for the /chaos bridge on 1st-gen PCI powermacs */ - return !strcmp(np->type, "pci") || !strcmp(np->type, "vci"); -} - -static void of_bus_pci_count_cells(struct device_node *np, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = 3; - if (sizec) - *sizec = 2; -} - -static u64 of_bus_pci_map(u32 *addr, const u32 *range, int na, int ns, int pna) -{ - u64 cp, s, da; - - /* Check address type match */ - if ((addr[0] ^ range[0]) & 0x03000000) - return OF_BAD_ADDR; - - /* Read address values, skipping high cell */ - cp = of_read_number(range + 1, na - 1); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr + 1, na - 1); - - pr_debug("OF: PCI map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_pci_translate(u32 *addr, u64 offset, int na) -{ - return of_bus_default_translate(addr + 1, offset, na - 1); -} - -static unsigned int of_bus_pci_get_flags(const u32 *addr) -{ - unsigned int flags = 0; - u32 w = addr[0]; - - switch ((w >> 24) & 0x03) { - case 0x01: - flags |= IORESOURCE_IO; - break; - case 0x02: /* 32 bits */ - case 0x03: /* 64 bits */ - flags |= IORESOURCE_MEM; - break; - } - if (w & 0x40000000) - flags |= IORESOURCE_PREFETCH; - return flags; -} - -const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, - unsigned int *flags) -{ - const u32 *prop; - unsigned int psize; - struct device_node *parent; - struct of_bus *bus; - int onesize, i, na, ns; - - /* Get parent & match bus type */ - parent = of_get_parent(dev); - if (parent == NULL) - return NULL; - bus = of_match_bus(parent); - if (strcmp(bus->name, "pci")) { - of_node_put(parent); - return NULL; - } - bus->count_cells(dev, &na, &ns); - of_node_put(parent); - if (!OF_CHECK_COUNTS(na, ns)) - return NULL; - - /* Get "reg" or "assigned-addresses" property */ - prop = of_get_property(dev, bus->addresses, &psize); - if (prop == NULL) - return NULL; - psize /= 4; - - onesize = na + ns; - for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) - if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { - if (size) - *size = of_read_number(prop + na, ns); - if (flags) - *flags = bus->get_flags(prop); - return prop; - } - return NULL; -} -EXPORT_SYMBOL(of_get_pci_address); - -int of_pci_address_to_resource(struct device_node *dev, int bar, - struct resource *r) -{ - const u32 *addrp; - u64 size; - unsigned int flags; - - addrp = of_get_pci_address(dev, bar, &size, &flags); - if (addrp == NULL) - return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); -} -EXPORT_SYMBOL_GPL(of_pci_address_to_resource); - -static u8 of_irq_pci_swizzle(u8 slot, u8 pin) -{ - return (((pin - 1) + slot) % 4) + 1; -} - -int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) -{ - struct device_node *dn, *ppnode; - struct pci_dev *ppdev; - u32 lspec; - u32 laddr[3]; - u8 pin; - int rc; - - /* Check if we have a device node, if yes, fallback to standard OF - * parsing - */ - dn = pci_device_to_OF_node(pdev); - if (dn) - return of_irq_map_one(dn, 0, out_irq); - - /* Ok, we don't, time to have fun. Let's start by building up an - * interrupt spec. we assume #interrupt-cells is 1, which is standard - * for PCI. If you do different, then don't use that routine. - */ - rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); - if (rc != 0) - return rc; - /* No pin, exit */ - if (pin == 0) - return -ENODEV; - - /* Now we walk up the PCI tree */ - lspec = pin; - for (;;) { - /* Get the pci_dev of our parent */ - ppdev = pdev->bus->self; - - /* Ouch, it's a host bridge... */ - if (ppdev == NULL) { - struct pci_controller *host; - host = pci_bus_to_host(pdev->bus); - ppnode = host ? host->dn : NULL; - /* No node for host bridge ? give up */ - if (ppnode == NULL) - return -EINVAL; - } else - /* We found a P2P bridge, check if it has a node */ - ppnode = pci_device_to_OF_node(ppdev); - - /* Ok, we have found a parent with a device-node, hand over to - * the OF parsing code. - * We build a unit address from the linux device to be used for - * resolution. Note that we use the linux bus number which may - * not match your firmware bus numbering. - * Fortunately, in most cases, interrupt-map-mask doesn't - * include the bus number as part of the matching. - * You should still be careful about that though if you intend - * to rely on this function (you ship a firmware that doesn't - * create device nodes for all PCI devices). - */ - if (ppnode) - break; - - /* We can only get here if we hit a P2P bridge with no node, - * let's do standard swizzling and try again - */ - lspec = of_irq_pci_swizzle(PCI_SLOT(pdev->devfn), lspec); - pdev = ppdev; - } - - laddr[0] = (pdev->bus->number << 16) - | (pdev->devfn << 8); - laddr[1] = laddr[2] = 0; - return of_irq_map_raw(ppnode, &lspec, 1, laddr, out_irq); -} -EXPORT_SYMBOL_GPL(of_irq_map_pci); -#endif /* CONFIG_PCI */ - -/* - * ISA bus specific translator - */ - -static int of_bus_isa_match(struct device_node *np) -{ - return !strcmp(np->name, "isa"); -} - -static void of_bus_isa_count_cells(struct device_node *child, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = 2; - if (sizec) - *sizec = 1; -} - -static u64 of_bus_isa_map(u32 *addr, const u32 *range, int na, int ns, int pna) -{ - u64 cp, s, da; - - /* Check address type match */ - if ((addr[0] ^ range[0]) & 0x00000001) - return OF_BAD_ADDR; - - /* Read address values, skipping high cell */ - cp = of_read_number(range + 1, na - 1); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr + 1, na - 1); - - pr_debug("OF: ISA map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_isa_translate(u32 *addr, u64 offset, int na) -{ - return of_bus_default_translate(addr + 1, offset, na - 1); -} - -static unsigned int of_bus_isa_get_flags(const u32 *addr) -{ - unsigned int flags = 0; - u32 w = addr[0]; - - if (w & 1) - flags |= IORESOURCE_IO; - else - flags |= IORESOURCE_MEM; - return flags; -} - -/* - * Array of bus specific translators - */ - -static struct of_bus of_busses[] = { -#ifdef CONFIG_PCI - /* PCI */ - { - .name = "pci", - .addresses = "assigned-addresses", - .match = of_bus_pci_match, - .count_cells = of_bus_pci_count_cells, - .map = of_bus_pci_map, - .translate = of_bus_pci_translate, - .get_flags = of_bus_pci_get_flags, - }, -#endif /* CONFIG_PCI */ - /* ISA */ - { - .name = "isa", - .addresses = "reg", - .match = of_bus_isa_match, - .count_cells = of_bus_isa_count_cells, - .map = of_bus_isa_map, - .translate = of_bus_isa_translate, - .get_flags = of_bus_isa_get_flags, - }, - /* Default */ - { - .name = "default", - .addresses = "reg", - .match = NULL, - .count_cells = of_bus_default_count_cells, - .map = of_bus_default_map, - .translate = of_bus_default_translate, - .get_flags = of_bus_default_get_flags, - }, -}; - -static struct of_bus *of_match_bus(struct device_node *np) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(of_busses); i++) - if (!of_busses[i].match || of_busses[i].match(np)) - return &of_busses[i]; - BUG(); - return NULL; -} - -static int of_translate_one(struct device_node *parent, struct of_bus *bus, - struct of_bus *pbus, u32 *addr, - int na, int ns, int pna) -{ - const u32 *ranges; - unsigned int rlen; - int rone; - u64 offset = OF_BAD_ADDR; - - /* Normally, an absence of a "ranges" property means we are - * crossing a non-translatable boundary, and thus the addresses - * below the current not cannot be converted to CPU physical ones. - * Unfortunately, while this is very clear in the spec, it's not - * what Apple understood, and they do have things like /uni-n or - * /ht nodes with no "ranges" property and a lot of perfectly - * useable mapped devices below them. Thus we treat the absence of - * "ranges" as equivalent to an empty "ranges" property which means - * a 1:1 translation at that level. It's up to the caller not to try - * to translate addresses that aren't supposed to be translated in - * the first place. --BenH. - */ - ranges = of_get_property(parent, "ranges", (int *) &rlen); - if (ranges == NULL || rlen == 0) { - offset = of_read_number(addr, na); - memset(addr, 0, pna * 4); - pr_debug("OF: no ranges, 1:1 translation\n"); - goto finish; - } - - pr_debug("OF: walking ranges...\n"); - - /* Now walk through the ranges */ - rlen /= 4; - rone = na + pna + ns; - for (; rlen >= rone; rlen -= rone, ranges += rone) { - offset = bus->map(addr, ranges, na, ns, pna); - if (offset != OF_BAD_ADDR) - break; - } - if (offset == OF_BAD_ADDR) { - pr_debug("OF: not found !\n"); - return 1; - } - memcpy(addr, ranges + na, 4 * pna); - - finish: - of_dump_addr("OF: parent translation for:", addr, pna); - pr_debug("OF: with offset: "PRu64"\n", offset); - - /* Translate it into parent bus space */ - return pbus->translate(addr, offset, pna); -} - -/* - * Translate an address from the device-tree into a CPU physical address, - * this walks up the tree and applies the various bus mappings on the - * way. - * - * Note: We consider that crossing any level with #size-cells == 0 to mean - * that translation is impossible (that is we are not dealing with a value - * that can be mapped to a cpu physical address). This is not really specified - * that way, but this is traditionally the way IBM at least do things - */ -u64 of_translate_address(struct device_node *dev, const u32 *in_addr) -{ - struct device_node *parent = NULL; - struct of_bus *bus, *pbus; - u32 addr[OF_MAX_ADDR_CELLS]; - int na, ns, pna, pns; - u64 result = OF_BAD_ADDR; - - pr_debug("OF: ** translation for device %s **\n", dev->full_name); - - /* Increase refcount at current level */ - of_node_get(dev); - - /* Get parent & match bus type */ - parent = of_get_parent(dev); - if (parent == NULL) - goto bail; - bus = of_match_bus(parent); - - /* Cound address cells & copy address locally */ - bus->count_cells(dev, &na, &ns); - if (!OF_CHECK_COUNTS(na, ns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); - goto bail; - } - memcpy(addr, in_addr, na * 4); - - pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", - bus->name, na, ns, parent->full_name); - of_dump_addr("OF: translating address:", addr, na); - - /* Translate */ - for (;;) { - /* Switch to parent bus */ - of_node_put(dev); - dev = parent; - parent = of_get_parent(dev); - - /* If root, we have finished */ - if (parent == NULL) { - pr_debug("OF: reached root node\n"); - result = of_read_number(addr, na); - break; - } - - /* Get new parent bus and counts */ - pbus = of_match_bus(parent); - pbus->count_cells(dev, &pna, &pns); - if (!OF_CHECK_COUNTS(pna, pns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); - break; - } - - pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", - pbus->name, pna, pns, parent->full_name); - - /* Apply bus translation */ - if (of_translate_one(dev, bus, pbus, addr, na, ns, pna)) - break; - - /* Complete the move up one level */ - na = pna; - ns = pns; - bus = pbus; - - of_dump_addr("OF: one level translation:", addr, na); - } - bail: - of_node_put(parent); - of_node_put(dev); - - return result; -} -EXPORT_SYMBOL(of_translate_address); - -const u32 *of_get_address(struct device_node *dev, int index, u64 *size, - unsigned int *flags) -{ - const u32 *prop; - unsigned int psize; - struct device_node *parent; - struct of_bus *bus; - int onesize, i, na, ns; - - /* Get parent & match bus type */ - parent = of_get_parent(dev); - if (parent == NULL) - return NULL; - bus = of_match_bus(parent); - bus->count_cells(dev, &na, &ns); - of_node_put(parent); - if (!OF_CHECK_COUNTS(na, ns)) - return NULL; - - /* Get "reg" or "assigned-addresses" property */ - prop = of_get_property(dev, bus->addresses, (int *) &psize); - if (prop == NULL) - return NULL; - psize /= 4; - - onesize = na + ns; - for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) - if (i == index) { - if (size) - *size = of_read_number(prop + na, ns); - if (flags) - *flags = bus->get_flags(prop); - return prop; - } - return NULL; -} -EXPORT_SYMBOL(of_get_address); - -static int __of_address_to_resource(struct device_node *dev, const u32 *addrp, - u64 size, unsigned int flags, - struct resource *r) -{ - u64 taddr; - - if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) - return -EINVAL; - taddr = of_translate_address(dev, addrp); - if (taddr == OF_BAD_ADDR) - return -EINVAL; - memset(r, 0, sizeof(struct resource)); - if (flags & IORESOURCE_IO) { - unsigned long port; - port = -1; /* pci_address_to_pio(taddr); */ - if (port == (unsigned long)-1) - return -EINVAL; - r->start = port; - r->end = port + size - 1; - } else { - r->start = taddr; - r->end = taddr + size - 1; - } - r->flags = flags; - r->name = dev->name; - return 0; -} - -int of_address_to_resource(struct device_node *dev, int index, - struct resource *r) -{ - const u32 *addrp; - u64 size; - unsigned int flags; - - addrp = of_get_address(dev, index, &size, &flags); - if (addrp == NULL) - return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); -} -EXPORT_SYMBOL_GPL(of_address_to_resource); void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, unsigned long *busno, unsigned long *phys, unsigned long *size) @@ -643,383 +33,3 @@ void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, cells = prop ? *(u32 *)prop : of_n_size_cells(dn); *size = of_read_number(dma_window, cells); } - -/* - * Interrupt remapper - */ - -static unsigned int of_irq_workarounds; -static struct device_node *of_irq_dflt_pic; - -static struct device_node *of_irq_find_parent(struct device_node *child) -{ - struct device_node *p; - const phandle *parp; - - if (!of_node_get(child)) - return NULL; - - do { - parp = of_get_property(child, "interrupt-parent", NULL); - if (parp == NULL) - p = of_get_parent(child); - else { - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - p = of_node_get(of_irq_dflt_pic); - else - p = of_find_node_by_phandle(*parp); - } - of_node_put(child); - child = p; - } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL); - - return p; -} - -/* This doesn't need to be called if you don't have any special workaround - * flags to pass - */ -void of_irq_map_init(unsigned int flags) -{ - of_irq_workarounds = flags; - - /* OldWorld, don't bother looking at other things */ - if (flags & OF_IMAP_OLDWORLD_MAC) - return; - - /* If we don't have phandles, let's try to locate a default interrupt - * controller (happens when booting with BootX). We do a first match - * here, hopefully, that only ever happens on machines with one - * controller. - */ - if (flags & OF_IMAP_NO_PHANDLE) { - struct device_node *np; - - for (np = NULL; (np = of_find_all_nodes(np)) != NULL;) { - if (of_get_property(np, "interrupt-controller", NULL) - == NULL) - continue; - /* Skip /chosen/interrupt-controller */ - if (strcmp(np->name, "chosen") == 0) - continue; - /* It seems like at least one person on this planet - * wants to use BootX on a machine with an AppleKiwi - * controller which happens to pretend to be an - * interrupt controller too. - */ - if (strcmp(np->name, "AppleKiwi") == 0) - continue; - /* I think we found one ! */ - of_irq_dflt_pic = np; - break; - } - } - -} - -int of_irq_map_raw(struct device_node *parent, const u32 *intspec, u32 ointsize, - const u32 *addr, struct of_irq *out_irq) -{ - struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; - const u32 *tmp, *imap, *imask; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - int imaplen, match, i; - - pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...]," - "ointsize=%d\n", - parent->full_name, intspec[0], intspec[1], ointsize); - - ipar = of_node_get(parent); - - /* First get the #interrupt-cells property of the current cursor - * that tells us how to interpret the passed-in intspec. If there - * is none, we are nice and just walk up the tree - */ - do { - tmp = of_get_property(ipar, "#interrupt-cells", NULL); - if (tmp != NULL) { - intsize = *tmp; - break; - } - tnode = ipar; - ipar = of_irq_find_parent(ipar); - of_node_put(tnode); - } while (ipar); - if (ipar == NULL) { - pr_debug(" -> no parent found !\n"); - goto fail; - } - - pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", - ipar->full_name, intsize); - - if (ointsize != intsize) - return -EINVAL; - - /* Look for this #address-cells. We have to implement the old linux - * trick of looking for the parent here as some device-trees rely on it - */ - old = of_node_get(ipar); - do { - tmp = of_get_property(old, "#address-cells", NULL); - tnode = of_get_parent(old); - of_node_put(old); - old = tnode; - } while (old && tmp == NULL); - of_node_put(old); - old = NULL; - addrsize = (tmp == NULL) ? 2 : *tmp; - - pr_debug(" -> addrsize=%d\n", addrsize); - - /* Now start the actual "proper" walk of the interrupt tree */ - while (ipar != NULL) { - /* Now check if cursor is an interrupt-controller and if it is - * then we are done - */ - if (of_get_property(ipar, "interrupt-controller", NULL) != - NULL) { - pr_debug(" -> got it !\n"); - memcpy(out_irq->specifier, intspec, - intsize * sizeof(u32)); - out_irq->size = intsize; - out_irq->controller = ipar; - of_node_put(old); - return 0; - } - - /* Now look for an interrupt-map */ - imap = of_get_property(ipar, "interrupt-map", &imaplen); - /* No interrupt map, check for an interrupt parent */ - if (imap == NULL) { - pr_debug(" -> no map, getting parent\n"); - newpar = of_irq_find_parent(ipar); - goto skiplevel; - } - imaplen /= sizeof(u32); - - /* Look for a mask */ - imask = of_get_property(ipar, "interrupt-map-mask", NULL); - - /* If we were passed no "reg" property and we attempt to parse - * an interrupt-map, then #address-cells must be 0. - * Fail if it's not. - */ - if (addr == NULL && addrsize != 0) { - pr_debug(" -> no reg passed in when needed !\n"); - goto fail; - } - - /* Parse interrupt-map */ - match = 0; - while (imaplen > (addrsize + intsize + 1) && !match) { - /* Compare specifiers */ - match = 1; - for (i = 0; i < addrsize && match; ++i) { - u32 mask = imask ? imask[i] : 0xffffffffu; - match = ((addr[i] ^ imap[i]) & mask) == 0; - } - for (; i < (addrsize + intsize) && match; ++i) { - u32 mask = imask ? imask[i] : 0xffffffffu; - match = - ((intspec[i-addrsize] ^ imap[i]) - & mask) == 0; - } - imap += addrsize + intsize; - imaplen -= addrsize + intsize; - - pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); - - /* Get the interrupt parent */ - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - newpar = of_node_get(of_irq_dflt_pic); - else - newpar = - of_find_node_by_phandle((phandle)*imap); - imap++; - --imaplen; - - /* Check if not found */ - if (newpar == NULL) { - pr_debug(" -> imap parent not found !\n"); - goto fail; - } - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - tmp = of_get_property(newpar, "#interrupt-cells", NULL); - if (tmp == NULL) { - pr_debug(" -> parent lacks " - "#interrupt-cells!\n"); - goto fail; - } - newintsize = *tmp; - tmp = of_get_property(newpar, "#address-cells", NULL); - newaddrsize = (tmp == NULL) ? 0 : *tmp; - - pr_debug(" -> newintsize=%d, newaddrsize=%d\n", - newintsize, newaddrsize); - - /* Check for malformed properties */ - if (imaplen < (newaddrsize + newintsize)) - goto fail; - - imap += newaddrsize + newintsize; - imaplen -= newaddrsize + newintsize; - - pr_debug(" -> imaplen=%d\n", imaplen); - } - if (!match) - goto fail; - - of_node_put(old); - old = of_node_get(newpar); - addrsize = newaddrsize; - intsize = newintsize; - intspec = imap - intsize; - addr = intspec - addrsize; - -skiplevel: - /* Iterate again with new parent */ - pr_debug(" -> new parent: %s\n", - newpar ? newpar->full_name : "<>"); - of_node_put(ipar); - ipar = newpar; - newpar = NULL; - } -fail: - of_node_put(ipar); - of_node_put(old); - of_node_put(newpar); - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(of_irq_map_raw); - -int of_irq_map_one(struct device_node *device, - int index, struct of_irq *out_irq) -{ - struct device_node *p; - const u32 *intspec, *tmp, *addr; - u32 intsize, intlen; - int res; - - pr_debug("of_irq_map_one: dev=%s, index=%d\n", - device->full_name, index); - - /* Get the interrupts property */ - intspec = of_get_property(device, "interrupts", (int *) &intlen); - if (intspec == NULL) - return -EINVAL; - intlen /= sizeof(u32); - - pr_debug(" intspec=%d intlen=%d\n", *intspec, intlen); - - /* Get the reg property (if any) */ - addr = of_get_property(device, "reg", NULL); - - /* Look for the interrupt parent. */ - p = of_irq_find_parent(device); - if (p == NULL) - return -EINVAL; - - /* Get size of interrupt specifier */ - tmp = of_get_property(p, "#interrupt-cells", NULL); - if (tmp == NULL) { - of_node_put(p); - return -EINVAL; - } - intsize = *tmp; - - pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); - - /* Check index */ - if ((index + 1) * intsize > intlen) - return -EINVAL; - - /* Get new specifier and map it */ - res = of_irq_map_raw(p, intspec + index * intsize, intsize, - addr, out_irq); - of_node_put(p); - return res; -} -EXPORT_SYMBOL_GPL(of_irq_map_one); - -/** - * Search the device tree for the best MAC address to use. 'mac-address' is - * checked first, because that is supposed to contain to "most recent" MAC - * address. If that isn't set, then 'local-mac-address' is checked next, - * because that is the default address. If that isn't set, then the obsolete - * 'address' is checked, just in case we're using an old device tree. - * - * Note that the 'address' property is supposed to contain a virtual address of - * the register set, but some DTS files have redefined that property to be the - * MAC address. - * - * All-zero MAC addresses are rejected, because those could be properties that - * exist in the device tree, but were not set by U-Boot. For example, the - * DTS could define 'mac-address' and 'local-mac-address', with zero MAC - * addresses. Some older U-Boots only initialized 'local-mac-address'. In - * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists - * but is all zeros. -*/ -const void *of_get_mac_address(struct device_node *np) -{ - struct property *pp; - - pp = of_find_property(np, "mac-address", NULL); - if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value)) - return pp->value; - - pp = of_find_property(np, "local-mac-address", NULL); - if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value)) - return pp->value; - - pp = of_find_property(np, "address", NULL); - if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value)) - return pp->value; - - return NULL; -} -EXPORT_SYMBOL(of_get_mac_address); - -int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) -{ - struct of_irq out_irq; - int irq; - int res; - - res = of_irq_map_one(dev, index, &out_irq); - - /* Get irq for the device */ - if (res) { - pr_debug("IRQ not found... code = %d", res); - return NO_IRQ; - } - /* Assuming single interrupt controller... */ - irq = out_irq.specifier[0]; - - pr_debug("IRQ found = %d", irq); - - /* Only dereference the resource if both the - * resource and the irq are valid. */ - if (r && irq != NO_IRQ) { - r->start = r->end = irq; - r->flags = IORESOURCE_IRQ; - } - - return irq; -} -EXPORT_SYMBOL_GPL(of_irq_to_resource); - -void __iomem *of_iomap(struct device_node *np, int index) -{ - struct resource res; - - if (of_address_to_resource(np, index, &res)) - return NULL; - - return ioremap(res.start, 1 + res.end - res.start); -} -EXPORT_SYMBOL(of_iomap); diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index a4a7770c614..39cf50841f6 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -38,6 +38,9 @@ #include <asm/processor.h> #include <linux/uaccess.h> #include <asm/asm-offsets.h> +#include <asm/cacheflush.h> +#include <asm/syscall.h> +#include <linux/io.h> /* Returns the address where the register at REG_OFFS in P is stashed away. */ static microblaze_reg_t *reg_save_addr(unsigned reg_offs, @@ -71,7 +74,8 @@ static microblaze_reg_t *reg_save_addr(unsigned reg_offs, return (microblaze_reg_t *)((char *)regs + reg_offs); } -long arch_ptrace(struct task_struct *child, long request, long addr, long data) +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) { int rval; unsigned long val = 0; @@ -97,17 +101,30 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) } else { rval = -EIO; } - } else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) { + } else if (addr < PT_SIZE && (addr & 0x3) == 0) { microblaze_reg_t *reg_addr = reg_save_addr(addr, child); if (request == PTRACE_PEEKUSR) val = *reg_addr; - else + else { +#if 1 *reg_addr = data; +#else + /* MS potential problem on WB system + * Be aware that reg_addr is virtual address + * virt_to_phys conversion is necessary. + * This could be sensible solution. + */ + u32 paddr = virt_to_phys((u32)reg_addr); + invalidate_icache_range(paddr, paddr + 4); + *reg_addr = data; + flush_dcache_range(paddr, paddr + 4); +#endif + } } else rval = -EIO; if (rval == 0 && request == PTRACE_PEEKUSR) - rval = put_user(val, (unsigned long *)data); + rval = put_user(val, (unsigned long __user *)data); break; default: rval = ptrace_request(child, request, addr, data); @@ -119,7 +136,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) { long ret = 0; - secure_computing(regs->r12); + secure_computing_strict(regs->r12); if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) @@ -130,10 +147,8 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) */ ret = -1L; - if (unlikely(current->audit_context)) - audit_syscall_entry(EM_XILINX_MICROBLAZE, regs->r12, - regs->r5, regs->r6, - regs->r7, regs->r8); + audit_syscall_entry(EM_MICROBLAZE, regs->r12, regs->r5, regs->r6, + regs->r7, regs->r8); return ret ?: regs->r12; } @@ -142,37 +157,13 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) { int step; - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->r3), regs->r3); + audit_syscall_exit(regs); step = test_thread_flag(TIF_SINGLESTEP); if (step || test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall_exit(regs, step); } -#if 0 -static asmlinkage void syscall_trace(void) -{ - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; - /* The 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } -} -#endif - void ptrace_disable(struct task_struct *child) { /* nothing to do */ diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c index a1721a33042..fbe58c6554a 100644 --- a/arch/microblaze/kernel/reset.c +++ b/arch/microblaze/kernel/reset.c @@ -19,59 +19,21 @@ static int handle; /* reset pin handle */ static unsigned int reset_val; -static int of_reset_gpio_handle(void) -{ - int ret; /* variable which stored handle reset gpio pin */ - struct device_node *root; /* root node */ - struct device_node *gpio; /* gpio node */ - struct of_gpio_chip *of_gc = NULL; - enum of_gpio_flags flags ; - const void *gpio_spec; - - /* find out root node */ - root = of_find_node_by_path("/"); - - /* give me handle for gpio node to be possible allocate pin */ - ret = of_parse_phandles_with_args(root, "hard-reset-gpios", - "#gpio-cells", 0, &gpio, &gpio_spec); - if (ret) { - pr_debug("%s: can't parse gpios property\n", __func__); - goto err0; - } - - of_gc = gpio->data; - if (!of_gc) { - pr_debug("%s: gpio controller %s isn't registered\n", - root->full_name, gpio->full_name); - ret = -ENODEV; - goto err1; - } - - ret = of_gc->xlate(of_gc, root, gpio_spec, &flags); - if (ret < 0) - goto err1; - - ret += of_gc->gc.base; -err1: - of_node_put(gpio); -err0: - pr_debug("%s exited with status %d\n", __func__, ret); - return ret; -} - void of_platform_reset_gpio_probe(void) { int ret; - handle = of_reset_gpio_handle(); + handle = of_get_named_gpio(of_find_node_by_path("/"), + "hard-reset-gpios", 0); if (!gpio_is_valid(handle)) { - printk(KERN_INFO "Skipping unavailable RESET gpio %d (%s)\n", + pr_info("Skipping unavailable RESET gpio %d (%s)\n", handle, "reset"); + return; } ret = gpio_request(handle, "reset"); if (ret < 0) { - printk(KERN_INFO "GPIO pin is already allocated\n"); + pr_info("GPIO pin is already allocated\n"); return; } @@ -88,7 +50,7 @@ void of_platform_reset_gpio_probe(void) /* Setup output direction */ gpio_set_value(handle, 0); - printk(KERN_INFO "RESET: Registered gpio device: %d, current val: %d\n", + pr_info("RESET: Registered gpio device: %d, current val: %d\n", handle, reset_val); return; err: @@ -99,10 +61,17 @@ err: static void gpio_system_reset(void) { - gpio_set_value(handle, 1 - reset_val); + if (gpio_is_valid(handle)) + gpio_set_value(handle, 1 - reset_val); + else + pr_notice("Reset GPIO unavailable - halting!\n"); } #else -#define gpio_system_reset() do {} while (0) +static void gpio_system_reset(void) +{ + pr_notice("No reset GPIO present - halting!\n"); +} + void of_platform_reset_gpio_probe(void) { return; @@ -111,30 +80,29 @@ void of_platform_reset_gpio_probe(void) void machine_restart(char *cmd) { - printk(KERN_NOTICE "Machine restart...\n"); + pr_notice("Machine restart...\n"); gpio_system_reset(); - dump_stack(); while (1) ; } void machine_shutdown(void) { - printk(KERN_NOTICE "Machine shutdown...\n"); + pr_notice("Machine shutdown...\n"); while (1) ; } void machine_halt(void) { - printk(KERN_NOTICE "Machine halt...\n"); + pr_notice("Machine halt...\n"); while (1) ; } void machine_power_off(void) { - printk(KERN_NOTICE "Machine power off...\n"); + pr_notice("Machine power off...\n"); while (1) ; } diff --git a/arch/microblaze/kernel/selfmod.c b/arch/microblaze/kernel/selfmod.c deleted file mode 100644 index 89508bdc9f3..00000000000 --- a/arch/microblaze/kernel/selfmod.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu> - * Copyright (C) 2009 PetaLogix - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include <linux/interrupt.h> -#include <asm/selfmod.h> - -#undef DEBUG - -#if __GNUC__ > 3 -#error GCC 4 unsupported SELFMOD. Please disable SELFMOD from menuconfig. -#endif - -#define OPCODE_IMM 0xB0000000 -#define OPCODE_LWI 0xE8000000 -#define OPCODE_LWI_MASK 0xEC000000 -#define OPCODE_RTSD 0xB60F0008 /* return from func: rtsd r15, 8 */ -#define OPCODE_ADDIK 0x30000000 -#define OPCODE_ADDIK_MASK 0xFC000000 - -#define IMM_BASE (OPCODE_IMM | (BARRIER_BASE_ADDR >> 16)) -#define LWI_BASE (OPCODE_LWI | (BARRIER_BASE_ADDR & 0x0000ff00)) -#define LWI_BASE_MASK (OPCODE_LWI_MASK | (BARRIER_BASE_ADDR & 0x0000ff00)) -#define ADDIK_BASE (OPCODE_ADDIK | (BARRIER_BASE_ADDR & 0x0000ff00)) -#define ADDIK_BASE_MASK (OPCODE_ADDIK_MASK | (BARRIER_BASE_ADDR & 0x0000ff00)) - -#define MODIFY_INSTR { \ - pr_debug("%s: curr instr, (%d):0x%x, next(%d):0x%x\n", \ - __func__, i, addr[i], i + 1, addr[i + 1]); \ - addr[i] = OPCODE_IMM + (base >> 16); \ - /* keep instruction opcode and add only last 16bits */ \ - addr[i + 1] = (addr[i + 1] & 0xffff00ff) + (base & 0xffff); \ - __invalidate_icache(addr[i]); \ - __invalidate_icache(addr[i + 1]); \ - pr_debug("%s: hack instr, (%d):0x%x, next(%d):0x%x\n", \ - __func__, i, addr[i], i + 1, addr[i + 1]); } - -/* NOTE - * self-modified part of code for improvement of interrupt controller - * save instruction in interrupt rutine - */ -void selfmod_function(const int *arr_fce, const unsigned int base) -{ - unsigned int flags, i, j, *addr = NULL; - - local_irq_save(flags); - __disable_icache(); - - /* zero terminated array */ - for (j = 0; arr_fce[j] != 0; j++) { - /* get start address of function */ - addr = (unsigned int *) arr_fce[j]; - pr_debug("%s: func(%d) at 0x%x\n", - __func__, j, (unsigned int) addr); - for (i = 0; ; i++) { - pr_debug("%s: instruction code at %d: 0x%x\n", - __func__, i, addr[i]); - if (addr[i] == IMM_BASE) { - /* detecting of lwi (0xE8) or swi (0xF8) instr - * I can detect both opcode with one mask */ - if ((addr[i + 1] & LWI_BASE_MASK) == LWI_BASE) { - MODIFY_INSTR; - } else /* detection addik for ack */ - if ((addr[i + 1] & ADDIK_BASE_MASK) == - ADDIK_BASE) { - MODIFY_INSTR; - } - } else if (addr[i] == OPCODE_RTSD) { - /* return from function means end of function */ - pr_debug("%s: end of array %d\n", __func__, i); - break; - } - } - } - local_irq_restore(flags); -} /* end of self-modified code */ diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 17c98dbcec8..ab5b488e1fd 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -9,12 +9,15 @@ */ #include <linux/init.h> +#include <linux/clk-provider.h> +#include <linux/clocksource.h> #include <linux/string.h> #include <linux/seq_file.h> #include <linux/cpu.h> #include <linux/initrd.h> #include <linux/console.h> #include <linux/debugfs.h> +#include <linux/of_fdt.h> #include <asm/setup.h> #include <asm/sections.h> @@ -30,7 +33,6 @@ #include <asm/entry.h> #include <asm/cpuinfo.h> -#include <asm/system.h> #include <asm/prom.h> #include <asm/pgtable.h> @@ -41,37 +43,37 @@ DEFINE_PER_CPU(unsigned int, R11_SAVE); /* Temp variable for entry */ DEFINE_PER_CPU(unsigned int, CURRENT_SAVE); /* Saved current pointer */ unsigned int boot_cpuid; -char cmd_line[COMMAND_LINE_SIZE]; +/* + * Placed cmd_line to .data section because can be initialized from + * ASM code. Default position is BSS section which is cleared + * in machine_early_init(). + */ +char cmd_line[COMMAND_LINE_SIZE] __attribute__ ((section(".data"))); void __init setup_arch(char **cmdline_p) { - *cmdline_p = cmd_line; + *cmdline_p = boot_command_line; console_verbose(); unflatten_device_tree(); - /* NOTE I think that this function is not necessary to call */ - /* irq_early_init(); */ setup_cpuinfo(); microblaze_cache_init(); setup_memory(); - xilinx_pci_init(); - -#if defined(CONFIG_SELFMOD_INTC) || defined(CONFIG_SELFMOD_TIMER) - printk(KERN_NOTICE "Self modified code enable\n"); +#ifdef CONFIG_EARLY_PRINTK + /* remap early console to virtual address */ + remap_early_printk(); #endif -#ifdef CONFIG_VT -#if defined(CONFIG_XILINX_CONSOLE) - conswitchp = &xil_con; -#elif defined(CONFIG_DUMMY_CONSOLE) + xilinx_pci_init(); + +#if defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; #endif -#endif } #ifdef CONFIG_MTD_UCLINUX @@ -92,16 +94,14 @@ inline unsigned get_romfs_len(unsigned *addr) } #endif /* CONFIG_MTD_UCLINUX_EBSS */ -#if defined(CONFIG_EARLY_PRINTK) && defined(CONFIG_SERIAL_UARTLITE_CONSOLE) -#define eprintk early_printk -#else -#define eprintk printk -#endif +unsigned long kernel_tlb; void __init machine_early_init(const char *cmdline, unsigned int ram, - unsigned int fdt, unsigned int msr) + unsigned int fdt, unsigned int msr, unsigned int tlb0, + unsigned int tlb1) { - unsigned long *src, *dst = (unsigned long *)0x0; + unsigned long *src, *dst; + unsigned int offset = 0; /* If CONFIG_MTD_UCLINUX is defined, assume ROMFS is at the * end of kernel. There are two position which we want to check. @@ -121,7 +121,7 @@ void __init machine_early_init(const char *cmdline, unsigned int ram, /* Move ROMFS out of BSS before clearing it */ if (romfs_size > 0) { - memmove(&_ebss, (int *)romfs_base, romfs_size); + memmove(&__bss_stop, (int *)romfs_base, romfs_size); klimit += romfs_size; } #endif @@ -130,51 +130,59 @@ void __init machine_early_init(const char *cmdline, unsigned int ram, memset(__bss_start, 0, __bss_stop-__bss_start); memset(_ssbss, 0, _esbss-_ssbss); - /* Copy command line passed from bootloader */ -#ifndef CONFIG_CMDLINE_BOOL - if (cmdline && cmdline[0] != '\0') - strlcpy(cmd_line, cmdline, COMMAND_LINE_SIZE); -#endif - lockdep_init(); /* initialize device tree for usage in early_printk */ - early_init_devtree((void *)_fdt_start); + early_init_devtree(_fdt_start); #ifdef CONFIG_EARLY_PRINTK setup_early_printk(NULL); #endif - eprintk("Ramdisk addr 0x%08x, ", ram); + /* setup kernel_tlb after BSS cleaning + * Maybe worth to move to asm code */ + kernel_tlb = tlb0 + tlb1; + /* printk("TLB1 0x%08x, TLB0 0x%08x, tlb 0x%x\n", tlb0, + tlb1, kernel_tlb); */ + + pr_info("Ramdisk addr 0x%08x, ", ram); if (fdt) - eprintk("FDT at 0x%08x\n", fdt); + pr_info("FDT at 0x%08x\n", fdt); else - eprintk("Compiled-in FDT at 0x%08x\n", - (unsigned int)_fdt_start); + pr_info("Compiled-in FDT at %p\n", _fdt_start); #ifdef CONFIG_MTD_UCLINUX - eprintk("Found romfs @ 0x%08x (0x%08x)\n", + pr_info("Found romfs @ 0x%08x (0x%08x)\n", romfs_base, romfs_size); - eprintk("#### klimit %p ####\n", old_klimit); + pr_info("#### klimit %p ####\n", old_klimit); BUG_ON(romfs_size < 0); /* What else can we do? */ - eprintk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n", - romfs_size, romfs_base, (unsigned)&_ebss); + pr_info("Moved 0x%08x bytes from 0x%08x to 0x%08x\n", + romfs_size, romfs_base, (unsigned)&__bss_stop); - eprintk("New klimit: 0x%08x\n", (unsigned)klimit); + pr_info("New klimit: 0x%08x\n", (unsigned)klimit); #endif #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR - if (msr) - eprintk("!!!Your kernel has setup MSR instruction but " - "CPU don't have it %d\n", msr); + if (msr) { + pr_info("!!!Your kernel has setup MSR instruction but "); + pr_cont("CPU don't have it %x\n", msr); + } #else - if (!msr) - eprintk("!!!Your kernel not setup MSR instruction but " - "CPU have it %d\n", msr); + if (!msr) { + pr_info("!!!Your kernel not setup MSR instruction but "); + pr_cont("CPU have it %x\n", msr); + } #endif - for (src = __ivt_start; src < __ivt_end; src++, dst++) + /* Do not copy reset vectors. offset = 0x2 means skip the first + * two instructions. dst is pointer to MB vectors which are placed + * in block ram. If you want to copy reset vector setup offset to 0x0 */ +#if !CONFIG_MANUAL_RESET_VECTOR + offset = 0x2; +#endif + dst = (unsigned long *) (offset * sizeof(u32)); + for (src = __ivt_start + offset; src < __ivt_end; src++, dst++) *dst = *src; /* Initialize global data */ @@ -182,6 +190,13 @@ void __init machine_early_init(const char *cmdline, unsigned int ram, per_cpu(CURRENT_SAVE, 0) = (unsigned long)current; } +void __init time_init(void) +{ + of_clk_init(NULL); + setup_cpuinfo_clk(); + clocksource_of_init(); +} + #ifdef CONFIG_DEBUG_FS struct dentry *of_debugfs_root; @@ -192,38 +207,21 @@ static int microblaze_debugfs_init(void) return of_debugfs_root == NULL; } arch_initcall(microblaze_debugfs_init); -#endif -static int dflt_bus_notify(struct notifier_block *nb, - unsigned long action, void *data) +# ifdef CONFIG_MMU +static int __init debugfs_tlb(void) { - struct device *dev = data; - - /* We are only intereted in device addition */ - if (action != BUS_NOTIFY_ADD_DEVICE) - return 0; + struct dentry *d; - set_dma_ops(dev, &dma_direct_ops); + if (!of_debugfs_root) + return -ENODEV; - return NOTIFY_DONE; -} - -static struct notifier_block dflt_plat_bus_notifier = { - .notifier_call = dflt_bus_notify, - .priority = INT_MAX, -}; - -static struct notifier_block dflt_of_bus_notifier = { - .notifier_call = dflt_bus_notify, - .priority = INT_MAX, -}; - -static int __init setup_bus_notifier(void) -{ - bus_register_notifier(&platform_bus_type, &dflt_plat_bus_notifier); - bus_register_notifier(&of_platform_bus_type, &dflt_of_bus_notifier); + d = debugfs_create_u32("tlb_skip", S_IRUGO, of_debugfs_root, &tlb_skip); + if (!d) + return -ENOMEM; return 0; } - -arch_initcall(setup_bus_notifier); +device_initcall(debugfs_tlb); +# endif +#endif diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c index d8d3bb396cd..49a07a4d76d 100644 --- a/arch/microblaze/kernel/signal.c +++ b/arch/microblaze/kernel/signal.c @@ -31,6 +31,7 @@ #include <linux/personality.h> #include <linux/percpu.h> #include <linux/linkage.h> +#include <linux/tracehook.h> #include <asm/entry.h> #include <asm/ucontext.h> #include <linux/uaccess.h> @@ -40,17 +41,6 @@ #include <asm/cacheflush.h> #include <asm/syscalls.h> -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_sycall); - -asmlinkage long -sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, - struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->r1); -} - /* * Do a signal return; undo the signal stack. */ @@ -93,29 +83,26 @@ static int restore_sigcontext(struct pt_regs *regs, asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) { struct rt_sigframe __user *frame = - (struct rt_sigframe __user *)(regs->r1 + STATE_SAVE_ARG_SPACE); + (struct rt_sigframe __user *)(regs->r1); sigset_t set; int rval; + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) goto badframe; if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval)) goto badframe; - /* It is more difficult to avoid calling this function than to - call it and ignore errors. */ - if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->r1)) + if (restore_altstack(&frame->uc.uc_stack)) goto badframe; return rval; @@ -169,7 +156,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) return (void __user *)((sp - frame_size) & -8UL); } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; @@ -197,12 +184,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user((void *)current->sas_ss_sp, - &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(regs->r1), - &frame->uc.uc_stack.ss_flags); - err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= __put_user(NULL, &frame->uc.uc_link); + err |= __save_altstack(&frame->uc.uc_stack, regs->r1); err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); @@ -233,7 +216,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* MS: I need add offset in page */ address += ((unsigned long)frame->tramp) & ~PAGE_MASK; /* MS address is virtual */ - address = virt_to_phys(address); + address = __virt_to_phys(address); invalidate_icache_range(address, address + 8); flush_dcache_range(address, address + 8); } @@ -247,7 +230,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up registers for signal handler */ - regs->r1 = (unsigned long) frame - STATE_SAVE_ARG_SPACE; + regs->r1 = (unsigned long) frame; /* Signal handler args: */ regs->r5 = signal; /* arg 0: signum */ @@ -258,21 +241,16 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, set_fs(USER_DS); - /* the tracer may want to single-step inside the handler */ - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); - #ifdef DEBUG_SIG - printk(KERN_INFO "SIG deliver (%s:%d): sp=%p pc=%08lx\n", + pr_info("SIG deliver (%s:%d): sp=%p pc=%08lx\n", current->comm, current->pid, frame, regs->pc); #endif - return; + return 0; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); + return -EFAULT; } /* Handle restarting system calls */ @@ -295,15 +273,7 @@ handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) case -ERESTARTNOINTR: do_restart: /* offset of 4 bytes to re-execute trap (brki) instruction */ -#ifndef CONFIG_MMU regs->pc -= 4; -#else - /* offset of 8 bytes required = 4 for rtbd - offset, plus 4 for size of - "brki r14,8" - instruction. */ - regs->pc -= 8; -#endif break; } } @@ -312,28 +282,24 @@ do_restart: * OK, we're invoking a handler */ -static int +static void handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) + siginfo_t *info, struct pt_regs *regs) { + sigset_t *oldset = sigmask_to_save(); + int ret; + /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_rt_frame(sig, ka, NULL, oldset, regs); - - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - - if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, - ¤t->blocked, &ka->sa.sa_mask); - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - return 1; + ret = setup_rt_frame(sig, ka, NULL, oldset, regs); + + if (ret) + return; + + signal_delivered(sig, info, ka, regs, + test_thread_flag(TIF_SINGLESTEP)); } /* @@ -345,46 +311,24 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) +static void do_signal(struct pt_regs *regs, int in_syscall) { siginfo_t info; int signr; struct k_sigaction ka; #ifdef DEBUG_SIG - printk(KERN_INFO "do signal: %p %p %d\n", regs, oldset, in_syscall); - printk(KERN_INFO "do signal2: %lx %lx %ld [%lx]\n", regs->pc, regs->r1, + pr_info("do signal: %p %d\n", regs, in_syscall); + pr_info("do signal2: %lx %lx %ld [%lx]\n", regs->pc, regs->r1, regs->r12, current_thread_info()->flags); #endif - /* - * We want the common case to go fast, which - * is why we may in certain cases get here from - * kernel mode. Just return without doing anything - * if so. - */ - if (kernel_mode(regs)) - return 1; - - if (current_thread_info()->status & TS_RESTORE_SIGMASK) - oldset = ¤t->saved_sigmask; - else - oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { /* Whee! Actually deliver the signal. */ if (in_syscall) handle_restart(regs, &ka, 1); - if (handle_signal(signr, &ka, &info, oldset, regs)) { - /* - * A signal was successfully delivered; the saved - * sigmask will have been stored in the signal frame, - * and will be restored by sigreturn, so we can simply - * clear the TS_RESTORE_SIGMASK flag. - */ - current_thread_info()->status &= - ~TS_RESTORE_SIGMASK; - } - return 1; + handle_signal(signr, &ka, &info, regs); + return; } if (in_syscall) @@ -394,11 +338,14 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) * If there's no signal to deliver, we just put the saved sigmask * back. */ - if (current_thread_info()->status & TS_RESTORE_SIGMASK) { - current_thread_info()->status &= ~TS_RESTORE_SIGMASK; - sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); - } + restore_saved_sigmask(); +} - /* Did we come from a system call? */ - return 0; +asmlinkage void do_notify_resume(struct pt_regs *regs, int in_syscall) +{ + if (test_thread_flag(TIF_SIGPENDING)) + do_signal(regs, in_syscall); + + if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) + tracehook_notify_resume(regs); } diff --git a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c index 123692f2264..b4debe283a7 100644 --- a/arch/microblaze/kernel/stacktrace.c +++ b/arch/microblaze/kernel/stacktrace.c @@ -9,57 +9,23 @@ * for more details. */ +#include <linux/export.h> #include <linux/sched.h> #include <linux/stacktrace.h> #include <linux/thread_info.h> #include <linux/ptrace.h> -#include <linux/module.h> +#include <asm/unwind.h> -/* FIXME initial support */ void save_stack_trace(struct stack_trace *trace) { - unsigned long *sp; - unsigned long addr; - asm("addik %0, r1, 0" : "=r" (sp)); - - while (!kstack_end(sp)) { - addr = *sp++; - if (__kernel_text_address(addr)) { - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = addr; - - if (trace->nr_entries >= trace->max_entries) - break; - } - } + /* Exclude our helper functions from the trace*/ + trace->skip += 2; + microblaze_unwind(NULL, trace); } EXPORT_SYMBOL_GPL(save_stack_trace); void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { - unsigned int *sp; - unsigned long addr; - - struct thread_info *ti = task_thread_info(tsk); - - if (tsk == current) - asm("addik %0, r1, 0" : "=r" (sp)); - else - sp = (unsigned int *)ti->cpu_context.r1; - - while (!kstack_end(sp)) { - addr = *sp++; - if (__kernel_text_address(addr)) { - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = addr; - - if (trace->nr_entries >= trace->max_entries) - break; - } - } + microblaze_unwind(tsk, trace); } EXPORT_SYMBOL_GPL(save_stack_trace_tsk); diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index f4e00b7f125..f1e1f666ddd 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -13,6 +13,7 @@ */ #include <linux/errno.h> +#include <linux/export.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/syscalls.h> @@ -24,48 +25,17 @@ #include <linux/sys.h> #include <linux/ipc.h> #include <linux/file.h> -#include <linux/module.h> #include <linux/err.h> #include <linux/fs.h> #include <linux/semaphore.h> #include <linux/uaccess.h> #include <linux/unistd.h> #include <linux/slab.h> - #include <asm/syscalls.h> -asmlinkage long microblaze_vfork(struct pt_regs *regs) -{ - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->r1, - regs, 0, NULL, NULL); -} - -asmlinkage long microblaze_clone(int flags, unsigned long stack, struct pt_regs *regs) -{ - if (!stack) - stack = regs->r1; - return do_fork(flags, stack, regs, 0, NULL, NULL); -} - -asmlinkage long microblaze_execve(char __user *filenamei, char __user *__user *argv, - char __user *__user *envp, struct pt_regs *regs) -{ - int error; - char *filename; - - filename = getname(filenamei); - error = PTR_ERR(filename); - if (IS_ERR(filename)) - goto out; - error = do_execve(filename, argv, envp, regs); - putname(filename); -out: - return error; -} - -asmlinkage long sys_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, off_t pgoff) +SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, unsigned long, fd, + off_t, pgoff) { if (pgoff & ~PAGE_MASK) return -EINVAL; @@ -73,21 +43,13 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT); } -/* - * Do a system call from kernel instead of calling sys_execve so we - * end up with proper pt_regs. - */ -int kernel_execve(const char *filename, char *const argv[], char *const envp[]) +SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, unsigned long, fd, + unsigned long, pgoff) { - register const char *__a __asm__("r5") = filename; - register const void *__b __asm__("r6") = argv; - register const void *__c __asm__("r7") = envp; - register unsigned long __syscall __asm__("r12") = __NR_execve; - register unsigned long __ret __asm__("r3"); - __asm__ __volatile__ ("brki r14, 0x8" - : "=r" (__ret), "=r" (__syscall) - : "1" (__syscall), "r" (__a), "r" (__b), "r" (__c) - : "r4", "r8", "r9", - "r10", "r11", "r14", "cc", "memory"); - return __ret; + if (pgoff & (~PAGE_MASK >> 12)) + return -EINVAL; + + return sys_mmap_pgoff(addr, len, prot, flags, fd, + pgoff >> (PAGE_SHIFT - 12)); } diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S index 03376dc814c..329dfbad810 100644 --- a/arch/microblaze/kernel/syscall_table.S +++ b/arch/microblaze/kernel/syscall_table.S @@ -2,11 +2,7 @@ ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - old "setup()" system call, * used for restarting */ .long sys_exit -#ifdef CONFIG_MMU - .long sys_fork_wrapper -#else - .long sys_ni_syscall -#endif + .long sys_fork .long sys_read .long sys_write .long sys_open /* 5 */ @@ -173,7 +169,7 @@ ENTRY(sys_call_table) .long sys_ni_syscall /* sys_vm86 */ .long sys_ni_syscall /* Old sys_query_module */ .long sys_poll - .long sys_nfsservctl + .long sys_ni_syscall /* old nfsservctl */ .long sys_setresgid /* 170 */ .long sys_getresgid .long sys_prctl @@ -196,7 +192,7 @@ ENTRY(sys_call_table) .long sys_ni_syscall /* reserved for streams2 */ .long sys_vfork /* 190 */ .long sys_getrlimit - .long sys_mmap_pgoff /* mmap2 */ + .long sys_mmap2 .long sys_truncate64 .long sys_ftruncate64 .long sys_stat64 /* 195 */ @@ -312,7 +308,7 @@ ENTRY(sys_call_table) .long sys_readlinkat /* 305 */ .long sys_fchmodat .long sys_faccessat - .long sys_ni_syscall /* pselect6 */ + .long sys_pselect6 .long sys_ppoll .long sys_unshare /* 310 */ .long sys_set_robust_list @@ -367,8 +363,23 @@ ENTRY(sys_call_table) .long sys_sendmsg /* 360 */ .long sys_recvmsg .long sys_accept4 - .long sys_ni_syscall - .long sys_ni_syscall + .long sys_preadv + .long sys_pwritev .long sys_rt_tgsigqueueinfo /* 365 */ .long sys_perf_event_open .long sys_recvmmsg + .long sys_fanotify_init + .long sys_fanotify_mark + .long sys_prlimit64 /* 370 */ + .long sys_name_to_handle_at + .long sys_open_by_handle_at + .long sys_clock_adjtime + .long sys_syncfs + .long sys_setns /* 375 */ + .long sys_sendmmsg + .long sys_process_vm_readv + .long sys_process_vm_writev + .long sys_kcmp + .long sys_finit_module + .long sys_sched_setattr + .long sys_sched_getattr diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index ed61b2f1771..dd96f0e4bfa 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2007-2013 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2012-2013 Xilinx, Inc. * Copyright (C) 2007-2009 PetaLogix * Copyright (C) 2006 Atmark Techno, Inc. * @@ -8,34 +9,20 @@ * for more details. */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/param.h> #include <linux/interrupt.h> -#include <linux/profile.h> -#include <linux/irq.h> #include <linux/delay.h> #include <linux/sched.h> -#include <linux/spinlock.h> -#include <linux/err.h> +#include <linux/sched_clock.h> #include <linux/clk.h> -#include <linux/clocksource.h> #include <linux/clockchips.h> -#include <linux/io.h> -#include <linux/bug.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/cpuinfo.h> -#include <asm/setup.h> -#include <asm/prom.h> -#include <asm/irq.h> -#include <asm/system.h> - -#ifdef CONFIG_SELFMOD_TIMER -#include <asm/selfmod.h> -#define TIMER_BASE BARRIER_BASE_ADDR -#else -static unsigned int timer_baseaddr; -#define TIMER_BASE timer_baseaddr -#endif + +static void __iomem *timer_baseaddr; + +static unsigned int freq_div_hz; +static unsigned int timer_clock_freq; #define TCSR0 (0x00) #define TLR0 (0x04) @@ -56,26 +43,51 @@ static unsigned int timer_baseaddr; #define TCSR_PWMA (1<<9) #define TCSR_ENALL (1<<10) -static inline void microblaze_timer0_stop(void) +static unsigned int (*read_fn)(void __iomem *); +static void (*write_fn)(u32, void __iomem *); + +static void timer_write32(u32 val, void __iomem *addr) +{ + iowrite32(val, addr); +} + +static unsigned int timer_read32(void __iomem *addr) +{ + return ioread32(addr); +} + +static void timer_write32_be(u32 val, void __iomem *addr) { - out_be32(TIMER_BASE + TCSR0, in_be32(TIMER_BASE + TCSR0) & ~TCSR_ENT); + iowrite32be(val, addr); } -static inline void microblaze_timer0_start_periodic(unsigned long load_val) +static unsigned int timer_read32_be(void __iomem *addr) +{ + return ioread32be(addr); +} + +static inline void xilinx_timer0_stop(void) +{ + write_fn(read_fn(timer_baseaddr + TCSR0) & ~TCSR_ENT, + timer_baseaddr + TCSR0); +} + +static inline void xilinx_timer0_start_periodic(unsigned long load_val) { if (!load_val) load_val = 1; - out_be32(TIMER_BASE + TLR0, load_val); /* loading value to timer reg */ + /* loading value to timer reg */ + write_fn(load_val, timer_baseaddr + TLR0); /* load the initial value */ - out_be32(TIMER_BASE + TCSR0, TCSR_LOAD); + write_fn(TCSR_LOAD, timer_baseaddr + TCSR0); /* see timer data sheet for detail * !ENALL - don't enable 'em all * !PWMA - disable pwm * TINT - clear interrupt status * ENT- enable timer itself - * EINT - enable interrupt + * ENIT - enable interrupt * !LOAD - clear the bit to let go * ARHT - auto reload * !CAPT - no external trigger @@ -83,74 +95,75 @@ static inline void microblaze_timer0_start_periodic(unsigned long load_val) * UDT - set the timer as down counter * !MDT0 - generate mode */ - out_be32(TIMER_BASE + TCSR0, - TCSR_TINT|TCSR_ENIT|TCSR_ENT|TCSR_ARHT|TCSR_UDT); + write_fn(TCSR_TINT|TCSR_ENIT|TCSR_ENT|TCSR_ARHT|TCSR_UDT, + timer_baseaddr + TCSR0); } -static inline void microblaze_timer0_start_oneshot(unsigned long load_val) +static inline void xilinx_timer0_start_oneshot(unsigned long load_val) { if (!load_val) load_val = 1; - out_be32(TIMER_BASE + TLR0, load_val); /* loading value to timer reg */ + /* loading value to timer reg */ + write_fn(load_val, timer_baseaddr + TLR0); /* load the initial value */ - out_be32(TIMER_BASE + TCSR0, TCSR_LOAD); + write_fn(TCSR_LOAD, timer_baseaddr + TCSR0); - out_be32(TIMER_BASE + TCSR0, - TCSR_TINT|TCSR_ENIT|TCSR_ENT|TCSR_ARHT|TCSR_UDT); + write_fn(TCSR_TINT|TCSR_ENIT|TCSR_ENT|TCSR_ARHT|TCSR_UDT, + timer_baseaddr + TCSR0); } -static int microblaze_timer_set_next_event(unsigned long delta, +static int xilinx_timer_set_next_event(unsigned long delta, struct clock_event_device *dev) { pr_debug("%s: next event, delta %x\n", __func__, (u32)delta); - microblaze_timer0_start_oneshot(delta); + xilinx_timer0_start_oneshot(delta); return 0; } -static void microblaze_timer_set_mode(enum clock_event_mode mode, +static void xilinx_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - printk(KERN_INFO "%s: periodic\n", __func__); - microblaze_timer0_start_periodic(cpuinfo.freq_div_hz); + pr_info("%s: periodic\n", __func__); + xilinx_timer0_start_periodic(freq_div_hz); break; case CLOCK_EVT_MODE_ONESHOT: - printk(KERN_INFO "%s: oneshot\n", __func__); + pr_info("%s: oneshot\n", __func__); break; case CLOCK_EVT_MODE_UNUSED: - printk(KERN_INFO "%s: unused\n", __func__); + pr_info("%s: unused\n", __func__); break; case CLOCK_EVT_MODE_SHUTDOWN: - printk(KERN_INFO "%s: shutdown\n", __func__); - microblaze_timer0_stop(); + pr_info("%s: shutdown\n", __func__); + xilinx_timer0_stop(); break; case CLOCK_EVT_MODE_RESUME: - printk(KERN_INFO "%s: resume\n", __func__); + pr_info("%s: resume\n", __func__); break; } } -static struct clock_event_device clockevent_microblaze_timer = { - .name = "microblaze_clockevent", +static struct clock_event_device clockevent_xilinx_timer = { + .name = "xilinx_clockevent", .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, - .shift = 24, + .shift = 8, .rating = 300, - .set_next_event = microblaze_timer_set_next_event, - .set_mode = microblaze_timer_set_mode, + .set_next_event = xilinx_timer_set_next_event, + .set_mode = xilinx_timer_set_mode, }; static inline void timer_ack(void) { - out_be32(TIMER_BASE + TCSR0, in_be32(TIMER_BASE + TCSR0)); + write_fn(read_fn(timer_baseaddr + TCSR0), timer_baseaddr + TCSR0); } static irqreturn_t timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = &clockevent_microblaze_timer; + struct clock_event_device *evt = &clockevent_xilinx_timer; #ifdef CONFIG_HEART_BEAT - heartbeat(); + microblaze_heartbeat(); #endif timer_ack(); evt->event_handler(evt); @@ -159,134 +172,147 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) static struct irqaction timer_irqaction = { .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER, + .flags = IRQF_TIMER, .name = "timer", - .dev_id = &clockevent_microblaze_timer, + .dev_id = &clockevent_xilinx_timer, }; -static __init void microblaze_clockevent_init(void) +static __init void xilinx_clockevent_init(void) { - clockevent_microblaze_timer.mult = - div_sc(cpuinfo.cpu_clock_freq, NSEC_PER_SEC, - clockevent_microblaze_timer.shift); - clockevent_microblaze_timer.max_delta_ns = - clockevent_delta2ns((u32)~0, &clockevent_microblaze_timer); - clockevent_microblaze_timer.min_delta_ns = - clockevent_delta2ns(1, &clockevent_microblaze_timer); - clockevent_microblaze_timer.cpumask = cpumask_of(0); - clockevents_register_device(&clockevent_microblaze_timer); + clockevent_xilinx_timer.mult = + div_sc(timer_clock_freq, NSEC_PER_SEC, + clockevent_xilinx_timer.shift); + clockevent_xilinx_timer.max_delta_ns = + clockevent_delta2ns((u32)~0, &clockevent_xilinx_timer); + clockevent_xilinx_timer.min_delta_ns = + clockevent_delta2ns(1, &clockevent_xilinx_timer); + clockevent_xilinx_timer.cpumask = cpumask_of(0); + clockevents_register_device(&clockevent_xilinx_timer); } -static cycle_t microblaze_read(struct clocksource *cs) +static u64 xilinx_clock_read(void) +{ + return read_fn(timer_baseaddr + TCR1); +} + +static cycle_t xilinx_read(struct clocksource *cs) { /* reading actual value of timer 1 */ - return (cycle_t) (in_be32(TIMER_BASE + TCR1)); + return (cycle_t)xilinx_clock_read(); } -static struct timecounter microblaze_tc = { +static struct timecounter xilinx_tc = { .cc = NULL, }; -static cycle_t microblaze_cc_read(const struct cyclecounter *cc) +static cycle_t xilinx_cc_read(const struct cyclecounter *cc) { - return microblaze_read(NULL); + return xilinx_read(NULL); } -static struct cyclecounter microblaze_cc = { - .read = microblaze_cc_read, +static struct cyclecounter xilinx_cc = { + .read = xilinx_cc_read, .mask = CLOCKSOURCE_MASK(32), - .shift = 24, + .shift = 8, }; -int __init init_microblaze_timecounter(void) +static int __init init_xilinx_timecounter(void) { - microblaze_cc.mult = div_sc(cpuinfo.cpu_clock_freq, NSEC_PER_SEC, - microblaze_cc.shift); + xilinx_cc.mult = div_sc(timer_clock_freq, NSEC_PER_SEC, + xilinx_cc.shift); - timecounter_init(µblaze_tc, µblaze_cc, sched_clock()); + timecounter_init(&xilinx_tc, &xilinx_cc, sched_clock()); return 0; } static struct clocksource clocksource_microblaze = { - .name = "microblaze_clocksource", + .name = "xilinx_clocksource", .rating = 300, - .read = microblaze_read, + .read = xilinx_read, .mask = CLOCKSOURCE_MASK(32), - .shift = 24, /* I can shift it */ .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -static int __init microblaze_clocksource_init(void) +static int __init xilinx_clocksource_init(void) { - clocksource_microblaze.mult = - clocksource_hz2mult(cpuinfo.cpu_clock_freq, - clocksource_microblaze.shift); - if (clocksource_register(&clocksource_microblaze)) + if (clocksource_register_hz(&clocksource_microblaze, timer_clock_freq)) panic("failed to register clocksource"); /* stop timer1 */ - out_be32(TIMER_BASE + TCSR1, in_be32(TIMER_BASE + TCSR1) & ~TCSR_ENT); + write_fn(read_fn(timer_baseaddr + TCSR1) & ~TCSR_ENT, + timer_baseaddr + TCSR1); /* start timer1 - up counting without interrupt */ - out_be32(TIMER_BASE + TCSR1, TCSR_TINT|TCSR_ENT|TCSR_ARHT); + write_fn(TCSR_TINT|TCSR_ENT|TCSR_ARHT, timer_baseaddr + TCSR1); /* register timecounter - for ftrace support */ - init_microblaze_timecounter(); + init_xilinx_timecounter(); return 0; } -void __init time_init(void) +static void __init xilinx_timer_init(struct device_node *timer) { - u32 irq, i = 0; + struct clk *clk; + static int initialized; + u32 irq; u32 timer_num = 1; - struct device_node *timer = NULL; -#ifdef CONFIG_SELFMOD_TIMER - unsigned int timer_baseaddr = 0; - int arr_func[] = { - (int)µblaze_read, - (int)&timer_interrupt, - (int)µblaze_clocksource_init, - (int)µblaze_timer_set_mode, - (int)µblaze_timer_set_next_event, - 0 - }; -#endif - char *timer_list[] = { - "xlnx,xps-timer-1.00.a", - "xlnx,opb-timer-1.00.b", - "xlnx,opb-timer-1.00.a", - NULL - }; - - for (i = 0; timer_list[i] != NULL; i++) { - timer = of_find_compatible_node(NULL, NULL, timer_list[i]); - if (timer) - break; + + if (initialized) + return; + + initialized = 1; + + timer_baseaddr = of_iomap(timer, 0); + if (!timer_baseaddr) { + pr_err("ERROR: invalid timer base address\n"); + BUG(); + } + + write_fn = timer_write32; + read_fn = timer_read32; + + write_fn(TCSR_MDT, timer_baseaddr + TCSR0); + if (!(read_fn(timer_baseaddr + TCSR0) & TCSR_MDT)) { + write_fn = timer_write32_be; + read_fn = timer_read32_be; } - BUG_ON(!timer); - timer_baseaddr = *(int *) of_get_property(timer, "reg", NULL); - timer_baseaddr = (unsigned long) ioremap(timer_baseaddr, PAGE_SIZE); - irq = *(int *) of_get_property(timer, "interrupts", NULL); - timer_num = - *(int *) of_get_property(timer, "xlnx,one-timer-only", NULL); + irq = irq_of_parse_and_map(timer, 0); + + of_property_read_u32(timer, "xlnx,one-timer-only", &timer_num); if (timer_num) { - printk(KERN_EMERG "Please enable two timers in HW\n"); + pr_emerg("Please enable two timers in HW\n"); BUG(); } -#ifdef CONFIG_SELFMOD_TIMER - selfmod_function((int *) arr_func, timer_baseaddr); -#endif - printk(KERN_INFO "%s #0 at 0x%08x, irq=%d\n", - timer_list[i], timer_baseaddr, irq); + pr_info("%s: irq=%d\n", timer->full_name, irq); + + clk = of_clk_get(timer, 0); + if (IS_ERR(clk)) { + pr_err("ERROR: timer CCF input clock not found\n"); + /* If there is clock-frequency property than use it */ + of_property_read_u32(timer, "clock-frequency", + &timer_clock_freq); + } else { + timer_clock_freq = clk_get_rate(clk); + } - cpuinfo.freq_div_hz = cpuinfo.cpu_clock_freq / HZ; + if (!timer_clock_freq) { + pr_err("ERROR: Using CPU clock frequency\n"); + timer_clock_freq = cpuinfo.cpu_clock_freq; + } + + freq_div_hz = timer_clock_freq / HZ; setup_irq(irq, &timer_irqaction); #ifdef CONFIG_HEART_BEAT - setup_heartbeat(); + microblaze_setup_heartbeat(); #endif - microblaze_clocksource_init(); - microblaze_clockevent_init(); + xilinx_clocksource_init(); + xilinx_clockevent_init(); + + sched_clock_register(xilinx_clock_read, 32, timer_clock_freq); } + +CLOCKSOURCE_OF_DECLARE(xilinx_timer, "xlnx,xps-timer-1.00.a", + xilinx_timer_init); diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c index 75e49202a5e..cb619533a19 100644 --- a/arch/microblaze/kernel/traps.c +++ b/arch/microblaze/kernel/traps.c @@ -8,90 +8,70 @@ * for more details. */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/kallsyms.h> -#include <linux/module.h> #include <linux/sched.h> #include <linux/debug_locks.h> #include <asm/exceptions.h> -#include <asm/system.h> +#include <asm/unwind.h> void trap_init(void) { __enable_hw_exceptions(); } -static unsigned long kstack_depth_to_print = 24; +static unsigned long kstack_depth_to_print; /* 0 == entire stack */ static int __init kstack_setup(char *s) { - return !strict_strtoul(s, 0, &kstack_depth_to_print); + return !kstrtoul(s, 0, &kstack_depth_to_print); } __setup("kstack=", kstack_setup); -void show_trace(struct task_struct *task, unsigned long *stack) +void show_stack(struct task_struct *task, unsigned long *sp) { - unsigned long addr; - - if (!stack) - stack = (unsigned long *)&stack; + unsigned long words_to_show; + u32 fp = (u32) sp; + + if (fp == 0) { + if (task) { + fp = ((struct thread_info *) + (task->stack))->cpu_context.r1; + } else { + /* Pick up caller of dump_stack() */ + fp = (u32)&sp - 8; + } + } - printk(KERN_NOTICE "Call Trace: "); -#ifdef CONFIG_KALLSYMS - printk(KERN_NOTICE "\n"); -#endif - while (!kstack_end(stack)) { - addr = *stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (kernel_text_address(addr)) - print_ip_sym(addr); + words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE - 1))) >> 2; + if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print)) + words_to_show = kstack_depth_to_print; + + pr_info("Kernel Stack:\n"); + + /* + * Make the first line an 'odd' size if necessary to get + * remaining lines to start at an address multiple of 0x10 + */ + if (fp & 0xF) { + unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2; + if (line1_words < words_to_show) { + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, + 4, (void *)fp, line1_words << 2, 0); + fp += line1_words << 2; + words_to_show -= line1_words; + } } - printk(KERN_NOTICE "\n"); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp, + words_to_show << 2, 0); + pr_info("\n\nCall Trace:\n"); + microblaze_unwind(task, NULL); + pr_info("\n"); if (!task) task = current; debug_show_held_locks(task); } - -void show_stack(struct task_struct *task, unsigned long *sp) -{ - unsigned long *stack; - int i; - - if (sp == NULL) { - if (task) - sp = (unsigned long *) ((struct thread_info *) - (task->stack))->cpu_context.r1; - else - sp = (unsigned long *)&sp; - } - - stack = sp; - - printk(KERN_INFO "\nStack:\n "); - - for (i = 0; i < kstack_depth_to_print; i++) { - if (kstack_end(sp)) - break; - if (i && ((i % 8) == 0)) - printk("\n "); - printk("%08lx ", *sp++); - } - printk("\n"); - show_trace(task, stack); -} - -void dump_stack(void) -{ - show_stack(NULL, NULL); -} -EXPORT_SYMBOL(dump_stack); diff --git a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c new file mode 100644 index 00000000000..1f7b8d44966 --- /dev/null +++ b/arch/microblaze/kernel/unwind.c @@ -0,0 +1,319 @@ +/* + * Backtrace support for Microblaze + * + * Copyright (C) 2010 Digital Design Corporation + * + * Based on arch/sh/kernel/cpu/sh5/unwind.c code which is: + * Copyright (C) 2004 Paul Mundt + * Copyright (C) 2004 Richard Curnow + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* #define DEBUG 1 */ +#include <linux/export.h> +#include <linux/kallsyms.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/stacktrace.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <asm/sections.h> +#include <asm/exceptions.h> +#include <asm/unwind.h> +#include <asm/switch_to.h> + +struct stack_trace; + +/* + * On Microblaze, finding the previous stack frame is a little tricky. + * At this writing (3/2010), Microblaze does not support CONFIG_FRAME_POINTERS, + * and even if it did, gcc (4.1.2) does not store the frame pointer at + * a consistent offset within each frame. To determine frame size, it is + * necessary to search for the assembly instruction that creates or reclaims + * the frame and extract the size from it. + * + * Microblaze stores the stack pointer in r1, and creates a frame via + * + * addik r1, r1, -FRAME_SIZE + * + * The frame is reclaimed via + * + * addik r1, r1, FRAME_SIZE + * + * Frame creation occurs at or near the top of a function. + * Depending on the compiler, reclaim may occur at the end, or before + * a mid-function return. + * + * A stack frame is usually not created in a leaf function. + * + */ + +/** + * get_frame_size - Extract the stack adjustment from an + * "addik r1, r1, adjust" instruction + * @instr : Microblaze instruction + * + * Return - Number of stack bytes the instruction reserves or reclaims + */ +inline long get_frame_size(unsigned long instr) +{ + return abs((s16)(instr & 0xFFFF)); +} + +/** + * find_frame_creation - Search backward to find the instruction that creates + * the stack frame (hopefully, for the same function the + * initial PC is in). + * @pc : Program counter at which to begin the search + * + * Return - PC at which stack frame creation occurs + * NULL if this cannot be found, i.e. a leaf function + */ +static unsigned long *find_frame_creation(unsigned long *pc) +{ + int i; + + /* NOTE: Distance to search is arbitrary + * 250 works well for most things, + * 750 picks up things like tcp_recvmsg(), + * 1000 needed for fat_fill_super() + */ + for (i = 0; i < 1000; i++, pc--) { + unsigned long instr; + s16 frame_size; + + if (!kernel_text_address((unsigned long) pc)) + return NULL; + + instr = *pc; + + /* addik r1, r1, foo ? */ + if ((instr & 0xFFFF0000) != 0x30210000) + continue; /* No */ + + frame_size = get_frame_size(instr); + if ((frame_size < 8) || (frame_size & 3)) { + pr_debug(" Invalid frame size %d at 0x%p\n", + frame_size, pc); + return NULL; + } + + pr_debug(" Found frame creation at 0x%p, size %d\n", pc, + frame_size); + return pc; + } + + return NULL; +} + +/** + * lookup_prev_stack_frame - Find the stack frame of the previous function. + * @fp : Frame (stack) pointer for current function + * @pc : Program counter within current function + * @leaf_return : r15 value within current function. If the current function + * is a leaf, this is the caller's return address. + * @pprev_fp : On exit, set to frame (stack) pointer for previous function + * @pprev_pc : On exit, set to current function caller's return address + * + * Return - 0 on success, -EINVAL if the previous frame cannot be found + */ +static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, + unsigned long leaf_return, + unsigned long *pprev_fp, + unsigned long *pprev_pc) +{ + unsigned long *prologue = NULL; + + /* _switch_to is a special leaf function */ + if (pc != (unsigned long) &_switch_to) + prologue = find_frame_creation((unsigned long *)pc); + + if (prologue) { + long frame_size = get_frame_size(*prologue); + + *pprev_fp = fp + frame_size; + *pprev_pc = *(unsigned long *)fp; + } else { + if (!leaf_return) + return -EINVAL; + *pprev_pc = leaf_return; + *pprev_fp = fp; + } + + /* NOTE: don't check kernel_text_address here, to allow display + * of userland return address + */ + return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0; +} + +static void microblaze_unwind_inner(struct task_struct *task, + unsigned long pc, unsigned long fp, + unsigned long leaf_return, + struct stack_trace *trace); + +/** + * unwind_trap - Unwind through a system trap, that stored previous state + * on the stack. + */ +#ifdef CONFIG_MMU +static inline void unwind_trap(struct task_struct *task, unsigned long pc, + unsigned long fp, struct stack_trace *trace) +{ + /* To be implemented */ +} +#else +static inline void unwind_trap(struct task_struct *task, unsigned long pc, + unsigned long fp, struct stack_trace *trace) +{ + const struct pt_regs *regs = (const struct pt_regs *) fp; + microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace); +} +#endif + +/** + * microblaze_unwind_inner - Unwind the stack from the specified point + * @task : Task whose stack we are to unwind (may be NULL) + * @pc : Program counter from which we start unwinding + * @fp : Frame (stack) pointer from which we start unwinding + * @leaf_return : Value of r15 at pc. If the function is a leaf, this is + * the caller's return address. + * @trace : Where to store stack backtrace (PC values). + * NULL == print backtrace to kernel log + */ +static void microblaze_unwind_inner(struct task_struct *task, + unsigned long pc, unsigned long fp, + unsigned long leaf_return, + struct stack_trace *trace) +{ + int ofs = 0; + + pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp); + if (!pc || !fp || (pc & 3) || (fp & 3)) { + pr_debug(" Invalid state for unwind, aborting\n"); + return; + } + for (; pc != 0;) { + unsigned long next_fp, next_pc = 0; + unsigned long return_to = pc + 2 * sizeof(unsigned long); + const struct trap_handler_info *handler = + µblaze_trap_handlers; + + /* Is previous function the HW exception handler? */ + if ((return_to >= (unsigned long)&_hw_exception_handler) + &&(return_to < (unsigned long)&ex_handler_unhandled)) { + /* + * HW exception handler doesn't save all registers, + * so we open-code a special case of unwind_trap() + */ +#ifndef CONFIG_MMU + const struct pt_regs *regs = + (const struct pt_regs *) fp; +#endif + pr_info("HW EXCEPTION\n"); +#ifndef CONFIG_MMU + microblaze_unwind_inner(task, regs->r17 - 4, + fp + EX_HANDLER_STACK_SIZ, + regs->r15, trace); +#endif + return; + } + + /* Is previous function a trap handler? */ + for (; handler->start_addr; ++handler) { + if ((return_to >= handler->start_addr) + && (return_to <= handler->end_addr)) { + if (!trace) + pr_info("%s\n", handler->trap_name); + unwind_trap(task, pc, fp, trace); + return; + } + } + pc -= ofs; + + if (trace) { +#ifdef CONFIG_STACKTRACE + if (trace->skip > 0) + trace->skip--; + else + trace->entries[trace->nr_entries++] = pc; + + if (trace->nr_entries >= trace->max_entries) + break; +#endif + } else { + /* Have we reached userland? */ + if (unlikely(pc == task_pt_regs(task)->pc)) { + pr_info("[<%p>] PID %lu [%s]\n", + (void *) pc, + (unsigned long) task->pid, + task->comm); + break; + } else + print_ip_sym(pc); + } + + /* Stop when we reach anything not part of the kernel */ + if (!kernel_text_address(pc)) + break; + + if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp, + &next_pc) == 0) { + ofs = sizeof(unsigned long); + pc = next_pc & ~3; + fp = next_fp; + leaf_return = 0; + } else { + pr_debug(" Failed to find previous stack frame\n"); + break; + } + + pr_debug(" Next PC=%p, next FP=%p\n", + (void *)next_pc, (void *)next_fp); + } +} + +/** + * microblaze_unwind - Stack unwinder for Microblaze (external entry point) + * @task : Task whose stack we are to unwind (NULL == current) + * @trace : Where to store stack backtrace (PC values). + * NULL == print backtrace to kernel log + */ +void microblaze_unwind(struct task_struct *task, struct stack_trace *trace) +{ + if (task) { + if (task == current) { + const struct pt_regs *regs = task_pt_regs(task); + microblaze_unwind_inner(task, regs->pc, regs->r1, + regs->r15, trace); + } else { + struct thread_info *thread_info = + (struct thread_info *)(task->stack); + const struct cpu_context *cpu_context = + &thread_info->cpu_context; + + microblaze_unwind_inner(task, + (unsigned long) &_switch_to, + cpu_context->r1, + cpu_context->r15, trace); + } + } else { + unsigned long pc, fp; + + __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp)); + + __asm__ __volatile__ ( + "brlid %0, 0f;" + "nop;" + "0:" + : "=r" (pc) + ); + + /* Since we are not a leaf function, use leaf_return = 0 */ + microblaze_unwind_inner(current, pc, fp, 0, trace); + } +} + diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S index db72d712460..be9488d6973 100644 --- a/arch/microblaze/kernel/vmlinux.lds.S +++ b/arch/microblaze/kernel/vmlinux.lds.S @@ -8,19 +8,22 @@ * for more details. */ -OUTPUT_FORMAT("elf32-microblaze", "elf32-microblaze", "elf32-microblaze") OUTPUT_ARCH(microblaze) -ENTRY(_start) +ENTRY(microblaze_start) #include <asm/page.h> #include <asm-generic/vmlinux.lds.h> #include <asm/thread_info.h> +#ifdef __MICROBLAZEEL__ +jiffies = jiffies_64; +#else jiffies = jiffies_64 + 4; +#endif SECTIONS { . = CONFIG_KERNEL_START; - _start = CONFIG_KERNEL_BASE_ADDR; + microblaze_start = CONFIG_KERNEL_BASE_ADDR; .text : AT(ADDR(.text) - LOAD_OFFSET) { _text = . ; _stext = . ; @@ -41,13 +44,14 @@ SECTIONS { __fdt_blob : AT(ADDR(__fdt_blob) - LOAD_OFFSET) { _fdt_start = . ; /* place for fdt blob */ *(__fdt_blob) ; /* Any link-placed DTB */ - . = _fdt_start + 0x4000; /* Pad up to 16kbyte */ + . = _fdt_start + 0x8000; /* Pad up to 32kbyte */ _fdt_end = . ; } . = ALIGN(16); RODATA EXCEPTION_TABLE(16) + NOTES /* * sdata2 section can go anywhere, but must be word aligned @@ -55,7 +59,7 @@ SECTIONS { */ .sdata2 : AT(ADDR(.sdata2) - LOAD_OFFSET) { _ssrw = .; - . = ALIGN(4096); /* page aligned when MMU used - origin 0x8 */ + . = ALIGN(PAGE_SIZE); /* page aligned when MMU used */ *(.sdata2) . = ALIGN(8); _essrw = .; @@ -67,11 +71,6 @@ SECTIONS { RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE) _edata = . ; - /* Reserve some low RAM for r0 based memory references */ - . = ALIGN(0x4) ; - r0_ram = . ; - . = . + 4096; /* a page should be enough */ - /* Under the microblaze ABI, .sdata and .sbss must be contiguous */ . = ALIGN(8); .sdata : AT(ADDR(.sdata) - LOAD_OFFSET) { @@ -120,32 +119,21 @@ SECTIONS { __init_end_before_initramfs = .; - .init.ramfs ALIGN(4096) : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { - __initramfs_start = .; - *(.init.ramfs) - __initramfs_end = .; - . = ALIGN(4); - LONG(0); -/* - * FIXME this can break initramfs for MMU. - * Pad init.ramfs up to page boundary, - * so that __init_end == __bss_start. This will make image.elf - * consistent with the image.bin - */ - /* . = ALIGN(4096); */ + .init.ramfs : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { + INIT_RAM_FS } + __init_end = .; - .bss ALIGN (4096) : AT(ADDR(.bss) - LOAD_OFFSET) { + .bss ALIGN (PAGE_SIZE) : AT(ADDR(.bss) - LOAD_OFFSET) { /* page aligned when MMU used */ __bss_start = . ; *(.bss*) *(COMMON) . = ALIGN (4) ; __bss_stop = . ; - _ebss = . ; } - . = ALIGN(4096); + . = ALIGN(PAGE_SIZE); _end = .; DISCARDS |
