diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /arch/ppc/kernel | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'arch/ppc/kernel')
51 files changed, 4227 insertions, 2639 deletions
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 635dd91b5..a177a3642 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -14,8 +14,11 @@ O_TARGET := kernel.o OX_OBJS := ppc_ksyms.o setup.o -O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \ - bitops.o ptrace.o align.o ppc_htab.o +O_OBJS := entry.o traps.o irq.o idle.o time.o process.o signal.o syscalls.o \ + misc.o bitops.o ptrace.o align.o ppc_htab.o semaphore.o +ifndef CONFIG_8xx +O_OBJS += hashtable.o +endif ifdef CONFIG_PCI O_OBJS += pci.o endif @@ -27,17 +30,18 @@ O_OBJS += totalmp.o endif ifeq ($(CONFIG_MBX),y) -O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o i8259.o ppc8xx_pic.o +O_OBJS += mbx_setup.o mbx_pci.o i8259.o ppc8xx_pic.o else ifeq ($(CONFIG_APUS),y) -O_OBJS += apus_setup.o prom.o openpic.o +O_OBJS += apus_setup.o prom.o open_pic.o else ifneq ($(CONFIG_MBX),y) O_OBJS += prep_time.o pmac_time.o chrp_time.o \ pmac_setup.o pmac_support.o \ prep_pci.o pmac_pci.o chrp_pci.o \ - residual.o prom.o openpic.o feature.o \ - prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o + residual.o prom.o open_pic.o feature.o \ + prep_nvram.o i8259.o pmac_pic.o indirect_pci.o \ + gemini_pci.o gemini_prom.o gemini_setup.o OX_OBJS += chrp_setup.o prep_setup.o endif endif @@ -49,7 +53,7 @@ endif all: head.o kernel.o -head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h +head.o: head.S ppc_defs.h ppc_defs.h: mk_defs.c ppc_defs.head \ $(TOPDIR)/include/asm/mmu.h \ @@ -65,7 +69,7 @@ find_name : find_name.c $(HOSTCC) $(HOSTCFLAGS) -o find_name find_name.c checks: checks.c - $(HOSTCC) $(HOSTCFLAGS) -D__KERNEL__ -o checks checks.c + $(HOSTCC) -I$(HPATH) $(HOSTCFLAGS) -D__KERNEL__ -fno-builtin -o checks checks.c ./checks include $(TOPDIR)/Rules.make diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c index cf5fcffd3..6a20863c5 100644 --- a/arch/ppc/kernel/align.c +++ b/arch/ppc/kernel/align.c @@ -243,10 +243,10 @@ fix_alignment(struct pt_regs *regs) } break; case LD+F: - current->tss.fpr[reg] = data.d; + current->thread.fpr[reg] = data.d; break; case ST+F: - data.d = current->tss.fpr[reg]; + data.d = current->thread.fpr[reg]; break; /* these require some floating point conversions... */ /* we'd like to use the assignment, but we have to compile @@ -254,13 +254,13 @@ fix_alignment(struct pt_regs *regs) * fp regs for copying 8-byte objects. */ case LD+F+S: enable_kernel_fp(); - cvt_fd(&data.f, ¤t->tss.fpr[reg], ¤t->tss.fpscr); - /* current->tss.fpr[reg] = data.f; */ + cvt_fd(&data.f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); + /* current->thread.fpr[reg] = data.f; */ break; case ST+F+S: enable_kernel_fp(); - cvt_df(¤t->tss.fpr[reg], &data.f, ¤t->tss.fpscr); - /* data.f = current->tss.fpr[reg]; */ + cvt_df(¤t->thread.fpr[reg], &data.f, ¤t->thread.fpscr); + /* data.f = current->thread.fpr[reg]; */ break; default: printk("align: can't handle flags=%x\n", flags); diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index 2540e0911..5b9e3f137 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -103,8 +103,8 @@ static int __60nsram = 0; /*********************************************************** SETUP */ /* From arch/m68k/kernel/setup.c. */ -__initfunc(void apus_setup_arch(unsigned long * memory_start_p, - unsigned long * memory_end_p)) +void __init apus_setup_arch(unsigned long * memory_start_p, + unsigned long * memory_end_p) { extern char cmd_line[]; int i; @@ -245,7 +245,7 @@ void arch_gettod(int *year, int *mon, int *day, int *hour, /*********************************************************** FLOPPY */ #if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) -__initfunc(void floppy_setup(char *str, int *ints)) +void __init floppy_setup(char *str, int *ints) { if (mach_floppy_setup) mach_floppy_setup (str, ints); @@ -325,11 +325,11 @@ void kernel_set_cachemode( unsigned long address, unsigned long size, switch (cmode) { - case KERNELMAP_FULL_CACHING: + case IOMAP_FULL_CACHING: mask = ~(_PAGE_NO_CACHE | _PAGE_GUARDED); flags = 0; break; - case KERNELMAP_NOCACHE_SER: + case IOMAP_NOCACHE_SER: mask = ~0; flags = (_PAGE_NO_CACHE | _PAGE_GUARDED); break; @@ -345,7 +345,7 @@ void kernel_set_cachemode( unsigned long address, unsigned long size, { pte_t *pte; - pte = my_find_pte(init_task.mm, address); + pte = my_find_pte(&init_mm, address); if ( !pte ) { printk("pte NULL in kernel_set_cachemode()\n"); @@ -354,7 +354,7 @@ void kernel_set_cachemode( unsigned long address, unsigned long size, pte_val (*pte) &= mask; pte_val (*pte) |= flags; - flush_tlb_page(find_vma(init_task.mm,address),address); + flush_tlb_page(find_vma(&init_mm,address),address); address += PAGE_SIZE; } @@ -560,24 +560,24 @@ apus_ide_fix_driveid(struct hd_driveid *id) m68k_ide_fix_driveid(id); } -__initfunc(void -apus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)) +void __init +apus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { m68k_ide_init_hwif_ports(hw, data_port, ctrl_port, irq); } #endif -__initfunc(void -apus_local_init_IRQ(void)) +void __init +apus_local_init_IRQ(void) { ppc_md.mask_irq = amiga_disable_irq; ppc_md.unmask_irq = amiga_enable_irq; apus_init_IRQ(); } -__initfunc(void +void __init apus_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7)) + unsigned long r6, unsigned long r7) { /* Parse bootinfo. The bootinfo is located right after the kernel bss */ diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index c82671947..397f69c18 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -97,7 +97,7 @@ int gg2_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, #define python_config_data(bus) ((0xfef00000+0xf8010)-(bus*0x100000)) #define PYTHON_CFA(b, d, o) (0x80 | ((b<<6) << 8) | ((d) << 16) \ | (((o) & ~3) << 24)) -unsigned int python_busnr = 1; +unsigned int python_busnr = 0; int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) @@ -279,8 +279,7 @@ chrp_pcibios_fixup(void) if ( !strncmp("IBM", get_property(find_path_device("/"), "name", NULL),3) ) { - pci_scan_peer_bridge(1); - pci_scan_peer_bridge(2); + } /* PCI interrupts are controlled by the OpenPIC */ @@ -290,22 +289,22 @@ chrp_pcibios_fixup(void) dev->irq = openpic_to_irq( dev->irq ); /* adjust the io_port for the NCR cards for busses other than 0 -- Cort */ if ( (dev->bus->number > 0) && (dev->vendor == PCI_VENDOR_ID_NCR) ) - dev->base_address[0] += (dev->bus->number*0x08000000); + dev->resource[0].start += (dev->bus->number*0x08000000); /* these need to be absolute addrs for OF and Matrox FB -- Cort */ if ( dev->vendor == PCI_VENDOR_ID_MATROX ) { - if ( dev->base_address[0] < isa_mem_base ) - dev->base_address[0] += isa_mem_base; - if ( dev->base_address[1] < isa_mem_base ) - dev->base_address[1] += isa_mem_base; + if ( dev->resource[0].start < isa_mem_base ) + dev->resource[0].start += isa_mem_base; + if ( dev->resource[1].start < isa_mem_base ) + dev->resource[1].start += isa_mem_base; } /* the F50 identifies the amd as a trident */ if ( (dev->vendor == PCI_VENDOR_ID_TRIDENT) && - (dev->class == PCI_CLASS_NETWORK_ETHERNET) ) + (dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET) ) { dev->vendor = PCI_VENDOR_ID_AMD; - pcibios_write_config_word(dev->bus->number, dev->devfn, - PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + pcibios_write_config_word(dev->bus->number, + dev->devfn, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); } } } @@ -347,7 +346,7 @@ chrp_setup_pci_ptrs(void) } else if ( !strncmp("IBM,7043-260", get_property(find_path_device("/"), "name", NULL),12) ) { - pci_dram_offset = 0x80000000; + pci_dram_offset = 0x0; isa_mem_base = 0xc0000000; isa_io_base = 0xf8000000; } diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 1653ef0d7..7faa1ed4b 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -71,6 +71,8 @@ void chrp_calibrate_decr(void); void chrp_time_init(void); void chrp_setup_pci_ptrs(void); +extern void chrp_progress(char *, unsigned short); +void chrp_event_scan(void); extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); extern int pckbd_getkeycode(unsigned int scancode); @@ -189,20 +191,20 @@ chrp_get_cpuinfo(char *buffer) * for keyboard and mouse */ -__initfunc(static inline void sio_write(u8 val, u8 index)) +static inline void __init sio_write(u8 val, u8 index) { outb(index, 0x15c); outb(val, 0x15d); } -__initfunc(static inline u8 sio_read(u8 index)) +static inline u8 __init sio_read(u8 index) { outb(index, 0x15c); return inb(0x15d); } -__initfunc(static void sio_fixup_irq(const char *name, u8 device, u8 level, - u8 type)) +static void __init sio_fixup_irq(const char *name, u8 device, u8 level, + u8 type) { u8 level0, type0, active; @@ -224,7 +226,7 @@ __initfunc(static void sio_fixup_irq(const char *name, u8 device, u8 level, } -__initfunc(static void sio_init(void)) +static void __init sio_init(void) { /* logical device 0 (KBC/Keyboard) */ sio_fixup_irq("keyboard", 0, 1, 2); @@ -233,8 +235,8 @@ __initfunc(static void sio_init(void)) } -__initfunc(void - chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) +void __init + chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) { extern char cmd_line[]; struct device_node *device; @@ -313,7 +315,10 @@ void chrp_event_scan(void) { unsigned char log[1024]; - call_rtas( "event-scan", 4, 1, NULL, 0x0, 1, __pa(log), 1024 ); + unsigned long ret = 0; + /* XXX: we should loop until the hardware says no more error logs -- Cort */ + call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, + __pa(log), 1024 ); ppc_md.heartbeat_count = ppc_md.heartbeat_reset; } @@ -329,12 +334,8 @@ void chrp_power_off(void) { /* allow power on only with power button press */ -#define PWR_FIELD(x) (0x8000000000000000ULL >> ((x)-96)) printk("RTAS power-off returned %d\n", - call_rtas("power-off", 2, 1, NULL, - ((PWR_FIELD(96)|PWR_FIELD(97))>>32)&0xffffffff, - (PWR_FIELD(96)|PWR_FIELD(97))&0xffffffff)); -#undef PWR_FIELD + call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); for (;;); } @@ -432,8 +433,8 @@ out: openpic_eoi(0); } -__initfunc(void - chrp_init_IRQ(void)) +void __init + chrp_init_IRQ(void) { struct device_node *np; int i; @@ -446,12 +447,9 @@ __initfunc(void (*(unsigned long *)get_property(np, "8259-interrupt-acknowledge", NULL)); } + open_pic.irq_offset = 16; for ( i = 16 ; i < NR_IRQS ; i++ ) irq_desc[i].ctl = &open_pic; - /* openpic knows that it's at irq 16 offset - * so we don't need to set it in the pic structure - * -- Cort - */ openpic_init(1); for ( i = 0 ; i < 16 ; i++ ) irq_desc[i].ctl = &i8259_pic; @@ -466,8 +464,8 @@ __initfunc(void #endif /* __SMP__ */ } -__initfunc(void - chrp_init2(void)) +void __init + chrp_init2(void) { adb_init(); @@ -491,12 +489,9 @@ void chrp_ide_probe(void) { chrp_ide_ports_known = 1; if(pdev) { - chrp_ide_regbase[0]=pdev->base_address[0] & - PCI_BASE_ADDRESS_IO_MASK; - chrp_ide_regbase[1]=pdev->base_address[2] & - PCI_BASE_ADDRESS_IO_MASK; - chrp_idedma_regbase=pdev->base_address[4] & - PCI_BASE_ADDRESS_IO_MASK; + chrp_ide_regbase[0]=pdev->resource[0].start; + chrp_ide_regbase[1]=pdev->resource[2].start; + chrp_idedma_regbase=pdev->resource[4].start; chrp_ide_irq=pdev->irq; } } @@ -582,17 +577,17 @@ EXPORT_SYMBOL(chrp_ide_probe); #endif -__initfunc(void +void __init chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7)) + unsigned long r6, unsigned long r7) { chrp_setup_pci_ptrs(); #ifdef CONFIG_BLK_DEV_INITRD /* take care of initrd if we have one */ - if ( r3 ) + if ( r6 ) { - initrd_start = r3 + KERNELBASE; - initrd_end = r3 + r4 + KERNELBASE; + initrd_start = r6 + KERNELBASE; + initrd_end = r6 + r7 + KERNELBASE; } #endif /* CONFIG_BLK_DEV_INITRD */ @@ -658,6 +653,8 @@ __initfunc(void ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; SYSRQ_KEY = 0x54; #endif + if ( rtas_data ) + ppc_md.progress = chrp_progress; #endif #endif @@ -678,16 +675,48 @@ __initfunc(void * Print the banner, then scroll down so boot progress * can be printed. -- Cort */ - chrp_progress("Linux/PPC "UTS_RELEASE"\n"); + if ( ppc_md.progress ) ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0x0); } -void chrp_progress(char *s) +void chrp_progress(char *s, unsigned short hex) { extern unsigned int rtas_data; - + int max_width, width; + struct device_node *root; + char *os = s; + unsigned long *p; + + if ( (root = find_path_device("/rtas")) && + (p = (unsigned long *)get_property(root, + "ibm,display-line-length", + NULL)) ) + max_width = *p; + else + max_width = 0x10; + if ( (_machine != _MACH_chrp) || !rtas_data ) return; - call_rtas( "display-character", 1, 1, NULL, '\r' ); - while ( *s ) - call_rtas( "display-character", 1, 1, NULL, *s++ ); + if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) + { + /* assume no display-character RTAS method - use hex display */ + return; + } + + width = max_width; + while ( *os ) + { + if ( (*os == '\n') || (*os == '\r') ) + width = max_width; + else + width--; + call_rtas( "display-character", 1, 1, NULL, *os++ ); + /* if we overwrite the screen length */ + if ( width == 0 ) + while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) + os++; + } + + /*while ( width-- > 0 )*/ + call_rtas( "display-character", 1, 1, NULL, ' ' ); } + diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index c374c9bd1..50c7417fb 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -31,7 +31,7 @@ static int nvram_as1 = NVRAM_AS1; static int nvram_as0 = NVRAM_AS0; static int nvram_data = NVRAM_DATA; -__initfunc(void chrp_time_init(void)) +void __init chrp_time_init(void) { struct device_node *rtcs; int base; @@ -151,7 +151,7 @@ unsigned long chrp_get_rtc_time(void) } -__initfunc(void chrp_calibrate_decr(void)) +void __init chrp_calibrate_decr(void) { struct device_node *cpu; int *fp, divisor; diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S new file mode 100644 index 000000000..abff78bc3 --- /dev/null +++ b/arch/ppc/kernel/entry.S @@ -0,0 +1,434 @@ +/* + * arch/ppc/kernel/entry.S + * + * $Id: entry.S,v 1.3 1999/09/05 11:56:26 paulus Exp $ + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains the system call entry code, context switch + * code, and exception/interrupt return code for PowerPC. + * + * 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. + * + */ + +#include "ppc_asm.h" +#include <asm/processor.h> +#include <asm/page.h> +#include <linux/errno.h> +#include <linux/sys.h> +#include <linux/config.h> + +#define SHOW_SYSCALLS +#define SHOW_SYSCALLS_TASK + +#ifdef SHOW_SYSCALLS_TASK + .data +show_syscalls_task: + .long -1 +#endif + +/* + * Handle a system call. + */ + .text +_GLOBAL(DoSyscall) + stw r0,THREAD+LAST_SYSCALL(r2) + lwz r11,_CCR(r1) /* Clear SO bit in CR */ + lis r10,0x1000 + andc r11,r11,r10 + stw r11,_CCR(r1) +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + lis r31,show_syscalls_task@ha + lwz r31,show_syscalls_task@l(r31) + cmp 0,r2,r31 + bne 1f +#endif + lis r3,7f@ha + addi r3,r3,7f@l + lwz r4,GPR0(r1) + lwz r5,GPR3(r1) + lwz r6,GPR4(r1) + lwz r7,GPR5(r1) + lwz r8,GPR6(r1) + lwz r9,GPR7(r1) + bl printk + lis r3,77f@ha + addi r3,r3,77f@l + lwz r4,GPR8(r1) + lwz r5,GPR9(r1) + mr r6,r2 + bl printk + lwz r0,GPR0(r1) + lwz r3,GPR3(r1) + lwz r4,GPR4(r1) + lwz r5,GPR5(r1) + lwz r6,GPR6(r1) + lwz r7,GPR7(r1) + lwz r8,GPR8(r1) +1: +#endif /* SHOW_SYSCALLS */ + cmpi 0,r0,0x7777 /* Special case for 'sys_sigreturn' */ + beq- 10f + lwz r10,TASK_FLAGS(r2) + andi. r10,r10,PF_TRACESYS + bne- 50f + cmpli 0,r0,NR_syscalls + bge- 66f + lis r10,sys_call_table@h + ori r10,r10,sys_call_table@l + slwi r0,r0,2 + lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ + .globl ret_from_syscall_1 +ret_from_syscall_1: +20: stw r3,RESULT(r1) /* Save result */ +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + cmp 0,r2,r31 + bne 91f +#endif + mr r4,r3 + lis r3,79f@ha + addi r3,r3,79f@l + bl printk + lwz r3,RESULT(r1) +91: +#endif + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 30f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 22f + li r3,EINTR +22: lwz r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + stw r10,_CCR(r1) +30: stw r3,GPR3(r1) /* Update return value */ + b ret_from_except +66: li r3,ENOSYS + b 22b +/* sys_sigreturn */ +10: addi r3,r1,STACK_FRAME_OVERHEAD + bl sys_sigreturn + cmpi 0,r3,0 /* Check for restarted system call */ + bge ret_from_except + b 20b +/* Traced system call support */ +50: bl syscall_trace + lwz r0,GPR0(r1) /* Restore original registers */ + lwz r3,GPR3(r1) + lwz r4,GPR4(r1) + lwz r5,GPR5(r1) + lwz r6,GPR6(r1) + lwz r7,GPR7(r1) + lwz r8,GPR8(r1) + lwz r9,GPR9(r1) + cmpli 0,r0,NR_syscalls + bge- 66f + lis r10,sys_call_table@h + ori r10,r10,sys_call_table@l + slwi r0,r0,2 + lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ + .globl ret_from_syscall_2 +ret_from_syscall_2: + stw r3,RESULT(r1) /* Save result */ + stw r3,GPR0(r1) /* temporary gross hack to make strace work */ + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 60f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 52f + li r3,EINTR +52: lwz r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + stw r10,_CCR(r1) +60: stw r3,GPR3(r1) /* Update return value */ + bl syscall_trace + b ret_from_except +66: li r3,ENOSYS + b 52b +#ifdef SHOW_SYSCALLS +7: .string "syscall %d(%x, %x, %x, %x, %x, " +77: .string "%x, %x), current=%p\n" +79: .string " -> %x\n" + .align 2 +#endif + +/* + * This routine switches between two different tasks. The process + * state of one is saved on its kernel stack. Then the state + * of the other is restored from its kernel stack. The memory + * management hardware is updated to the second process's state. + * Finally, we can return to the second process, via ret_from_except. + * On entry, r3 points to the THREAD for the current task, r4 + * points to the THREAD for the new task. + * + * Note: there are two ways to get to the "going out" portion + * of this code; either by coming in via the entry (_switch) + * or via "fork" which must set up an environment equivalent + * to the "_switch" path. If you change this (or in particular, the + * SAVE_REGS macro), you'll have to change the fork code also. + * + * The code which creates the new task context is in 'copy_thread' + * in arch/ppc/kernel/process.c + */ +_GLOBAL(_switch) + stwu r1,-INT_FRAME_SIZE(r1) + stw r0,GPR0(r1) + lwz r0,0(r1) + stw r0,GPR1(r1) + /* r3-r13 are caller saved -- Cort */ + SAVE_GPR(2, r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + mflr r20 /* Return to switch caller */ + mfmsr r22 + li r0,MSR_FP /* Disable floating-point */ + andc r22,r22,r0 + stw r20,_NIP(r1) + stw r22,_MSR(r1) + stw r20,_LINK(r1) + mfcr r20 + mfctr r22 + mfspr r23,XER + stw r20,_CCR(r1) + stw r22,_CTR(r1) + stw r23,_XER(r1) + li r0,0x0ff0 + stw r0,TRAP(r1) + stw r1,KSP(r3) /* Set old stack pointer */ + sync + tophys(r0,r4) + mtspr SPRG3,r0 /* Update current THREAD phys addr */ +#ifdef CONFIG_8xx + /* XXX it would be nice to find a SPRGx for this on 6xx,7xx too */ + lwz r9,PGDIR(r4) /* cache the page table root */ + tophys(r9,r9) /* convert to phys addr */ + mtspr M_TWB,r9 /* Update MMU base address */ +#endif /* CONFIG_8xx */ + lwz r1,KSP(r4) /* Load new stack pointer */ + /* save the old current 'last' for return value */ + mr r3,r2 + addi r2,r4,-THREAD /* Update current */ + lwz r9,_MSR(r1) /* Returning to user mode? */ + andi. r9,r9,MSR_PR + beq+ 10f /* if not, don't adjust kernel stack */ +8: addi r4,r1,INT_FRAME_SIZE /* size of frame */ + stw r4,THREAD+KSP(r2) /* save kernel stack pointer */ + tophys(r9,r1) + mtspr SPRG2,r9 /* phys exception stack pointer */ +10: lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + /* r3-r13 are destroyed -- Cort */ + REST_GPR(14, r1) + REST_8GPRS(15, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* + * ret_from_int(): + * + * Return from an interrupt (external interrupt and + * decrementer). This checks the first argument so + * we know if rtl_intercept wants us to check for + * a bottom half, signals and so on (normal return) or + * we're returning from a real-time interrupt or have + * interrupts soft disabled so we cannot enter Linux. + * -- Cort + */ + .globl ret_from_int +ret_from_int: + cmpi 0,r3,0 + beq 10f + /* we're allowed to do signal/bh checks */ + b ret_from_syscall +#ifdef __SMP__ + .globl ret_from_smpfork +ret_from_smpfork: + bl schedule_tail +#endif + .globl ret_from_syscall +ret_from_syscall: + .globl ret_from_except +ret_from_except: +0: mfmsr r30 /* Disable interrupts */ + rlwinm r30,r30,0,17,15 /* clear MSR_EE */ + SYNC /* Some chip revs need this... */ + mtmsr r30 + SYNC + lwz r5,_MSR(r1) + andi. r5,r5,MSR_EE + beq 2f +3: lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) + cmpi 0,r4,0 + beq+ 1f + addi r3,r1,STACK_FRAME_OVERHEAD + bl do_IRQ + .globl lost_irq_ret +lost_irq_ret: + b 3b +1: lis r4,bh_mask@ha + lwz r4,bh_mask@l(r4) + lis r5,bh_active@ha + lwz r5,bh_active@l(r5) + and. r4,r4,r5 + beq+ 2f + bl do_bottom_half + .globl do_bottom_half_ret +do_bottom_half_ret: +2: SYNC + mtmsr r30 /* disable interrupts again */ + SYNC + lwz r3,_MSR(r1) /* Returning to user mode? */ + andi. r3,r3,MSR_PR + beq+ 10f /* if so, check need_resched and signals */ + lwz r3,NEED_RESCHED(r2) + cmpi 0,r3,0 /* check need_resched flag */ + beq+ 7f + bl schedule + b 0b +7: lwz r5,SIGPENDING(r2) /* Check for pending unblocked signals */ + cmpwi 0,r5,0 + beq+ 8f + li r3,0 + addi r4,r1,STACK_FRAME_OVERHEAD + bl do_signal + .globl do_signal_ret +do_signal_ret: + b 0b +8: addi r4,r1,INT_FRAME_SIZE /* size of frame */ + stw r4,THREAD+KSP(r2) /* save kernel stack pointer */ + tophys(r3,r1) + mtspr SPRG2,r3 /* phys exception stack pointer */ +10: lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* + * Fake an interrupt from kernel mode. + * This is used when enable_irq loses an interrupt. + * We only fill in the stack frame minimally. + */ +_GLOBAL(fake_interrupt) + mflr r0 + stw r0,4(r1) + stwu r1,-INT_FRAME_SIZE(r1) + stw r0,_NIP(r1) + stw r0,_LINK(r1) + mfmsr r3 + stw r3,_MSR(r1) + li r0,0x0fac + stw r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + li r4,1 + bl do_IRQ + addi r1,r1,INT_FRAME_SIZE + lwz r0,4(r1) + mtlr r0 + blr + +#ifndef CONFIG_8xx +/* + * PROM code for specific machines follows. Put it + * here so it's easy to add arch-specific sections later. + * -- Cort + */ + +/* + * On CHRP, the Run-Time Abstraction Services (RTAS) have to be + * called with the MMU off. + */ + .globl enter_rtas +enter_rtas: + mflr r0 + stw r0,20(r1) + lis r4,rtas_data@ha + lwz r4,rtas_data@l(r4) + addis r4,r4,-KERNELBASE@h + lis r6,1f@ha /* physical return address for rtas */ + addi r6,r6,1f@l + addis r6,r6,-KERNELBASE@h + subi r7,r1,INT_FRAME_SIZE + addis r7,r7,-KERNELBASE@h + lis r8,rtas_entry@ha + lwz r8,rtas_entry@l(r8) + addis r5,r8,-KERNELBASE@h + mfmsr r9 + stw r9,8(r1) + ori r0,r0,MSR_EE|MSR_SE|MSR_BE + andc r0,r9,r0 + andi. r9,r9,MSR_ME|MSR_RI + sync /* disable interrupts so SRR0/1 */ + mtmsr r0 /* don't get trashed */ + mtlr r6 + mtspr SPRG2,r7 + mtspr SRR0,r8 + mtspr SRR1,r9 + rfi +1: addis r9,r1,-KERNELBASE@h + lwz r8,20(r9) /* get return address */ + lwz r9,8(r9) /* original msr value */ + li r0,0 + mtspr SPRG2,r0 + mtspr SRR0,r8 + mtspr SRR1,r9 + rfi /* return to caller */ +#endif /* CONFIG_8xx */ diff --git a/arch/ppc/kernel/gemini_pci.c b/arch/ppc/kernel/gemini_pci.c new file mode 100644 index 000000000..89ed43501 --- /dev/null +++ b/arch/ppc/kernel/gemini_pci.c @@ -0,0 +1,265 @@ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/malloc.h> + +#include <asm/machdep.h> +#include <asm/gemini.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +#include "pci.h" + +#define pci_config_addr(bus,dev,offset) \ + (0x80000000 | (bus<<16) | (dev<<8) | offset) + + +int +gemini_pcibios_read_config_byte(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned char *val) +{ + unsigned long reg; + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + *val = ((reg >> ((offset & 0x3) << 3)) & 0xff); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_read_config_word(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned short *val) +{ + unsigned long reg; + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + *val = ((reg >> ((offset & 0x3) << 3)) & 0xffff); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_read_config_dword(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned int *val) +{ + *val = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_byte(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned char val) +{ + unsigned long reg; + int shifts = offset & 0x3; + + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + reg = (reg & ~(0xff << (shifts << 3))) | (val << (shifts << 3)); + grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), reg ); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_word(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned short val) +{ + unsigned long reg; + int shifts = offset & 0x3; + + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + reg = (reg & ~(0xffff << (shifts << 3))) | (val << (shifts << 3)); + grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), reg ); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_dword(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned int val) +{ + grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), val ); + return PCIBIOS_SUCCESSFUL; +} + +struct gemini_device { + unsigned short vendor, device; + unsigned char irq; + unsigned short cmd; + unsigned char cache_line, latency; + void (*init)(struct pci_dev *dev); +}; + +static struct gemini_device gemini_map[] = { + { PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C885, 11, 0x15, 32, 248, NULL }, + { PCI_VENDOR_ID_NCR, 0x701, 10, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C042, 3, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_MPIC, 0xff, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_670, 0xff, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_MPC106, 0xff, 0, 0, 0, NULL }, +}; + +static int gemini_map_count = (sizeof( gemini_map ) / + sizeof( gemini_map[0] )); + + + +/* This just sets up the known devices on the board. */ +static void __init mapin_device( struct pci_dev *dev ) +{ + struct gemini_device *p; + unsigned short cmd; + int i; + + + for( i=0; i < gemini_map_count; i++ ) { + p = &(gemini_map[i]); + + if ( p->vendor == dev->vendor && + p->device == dev->device ) { + + if (p->irq != 0xff) { + pci_write_config_byte( dev, PCI_INTERRUPT_LINE, p->irq ); + dev->irq = p->irq; + } + + if (p->cmd) { + pci_read_config_word( dev, PCI_COMMAND, &cmd ); + pci_write_config_word( dev, PCI_COMMAND, (p->cmd|cmd)); + } + + if (p->cache_line) + pci_write_config_byte( dev, PCI_CACHE_LINE_SIZE, p->cache_line ); + + if (p->latency) + pci_write_config_byte( dev, PCI_LATENCY_TIMER, p->latency ); + } + } +} + +#define KB 1024 +#define MB (KB*KB) + +#define ALIGN(val,align) (((val) + ((align) -1))&(~((align) -1))) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define FIRST_IO_ADDR 0x10000 +#define FIRST_MEM_ADDR 0x02000000 + +#define GEMINI_PCI_MEM_BASE (0xf0000000) +#define GEMINI_PCI_IO_BASE (0xfe800000) + +static unsigned long pci_mem_base = GEMINI_PCI_MEM_BASE; +static unsigned long pci_io_base = GEMINI_PCI_IO_BASE; + +static unsigned int io_base = FIRST_IO_ADDR; +static unsigned int mem_base = FIRST_MEM_ADDR; + + + +__init void layout_dev( struct pci_dev *dev ) +{ + int i; + struct pci_bus *bus; + unsigned short cmd; + unsigned int reg, base, mask, size, alignto, type; + + bus = dev->bus; + + /* make any known settings happen */ + mapin_device( dev ); + + gemini_pcibios_read_config_word( bus->number, dev->devfn, PCI_COMMAND, &cmd ); + + for( reg = PCI_BASE_ADDRESS_0, i=0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++ ) { + + /* MPIC already done */ + if (dev->vendor == PCI_VENDOR_ID_IBM && + dev->device == PCI_DEVICE_ID_IBM_MPIC) + return; + + gemini_pcibios_write_config_dword( bus->number, dev->devfn, reg, 0xffffffff ); + gemini_pcibios_read_config_dword( bus->number, dev->devfn, reg, &base ); + if (!base) { + dev->resource[i].start = 0; + continue; + } + + if (base & PCI_BASE_ADDRESS_SPACE_IO) { + cmd |= PCI_COMMAND_IO; + base &= PCI_BASE_ADDRESS_IO_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + alignto = MAX(0x400, size); + base = ALIGN(io_base, alignto); + io_base = base + size; + gemini_pcibios_write_config_dword( bus->number, dev->devfn, reg, + ((pci_io_base + base) & 0x00ffffff) | 0x1); + dev->resource[i].start = (pci_io_base + base) | 0x1; + } + + else { + cmd |= PCI_COMMAND_MEMORY; + type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + switch( type ) { + + case PCI_BASE_ADDRESS_MEM_TYPE_32: + break; + case PCI_BASE_ADDRESS_MEM_TYPE_64: + printk("Warning: Ignoring 64-bit device; slot %d, function %d.\n", + PCI_SLOT( dev->devfn ), PCI_FUNC( dev->devfn )); + reg += 4; + continue; + } + + alignto = MAX(0x1000, size); + base = ALIGN(mem_base, alignto); + mem_base = base + size; + gemini_pcibios_write_config_dword( bus->number, dev->devfn, + reg, (pci_mem_base + base)); + dev->resource[i].start = pci_mem_base + base; + } + } + + if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + cmd |= PCI_COMMAND_IO; + + gemini_pcibios_write_config_word( bus->number, dev->devfn, PCI_COMMAND, + (cmd|PCI_COMMAND_MASTER)); +} + +__init void layout_bus( struct pci_bus *bus ) +{ + struct pci_dev *dev; + + if (!bus->devices && !bus->children) + return; + + io_base = ALIGN(io_base, 4*KB); + mem_base = ALIGN(mem_base, 4*KB); + + for( dev = bus->devices; dev; dev = dev->sibling ) { + if (((dev->class >> 16) != PCI_BASE_CLASS_BRIDGE) || + ((dev->class >> 8) == PCI_CLASS_BRIDGE_OTHER)) + layout_dev( dev ); + } +} + +void __init gemini_pcibios_fixup(void) +{ + struct pci_bus *bus; + unsigned long orig_mem_base, orig_io_base; + + orig_mem_base = pci_mem_base; + orig_io_base = pci_io_base; + + pci_mem_base = orig_mem_base; + pci_io_base = orig_io_base; +} + +decl_config_access_method(gemini); + +/* The "bootloader" for Synergy boards does none of this for us, so we need to + lay it all out ourselves... --Dan */ +void __init gemini_setup_pci_ptrs(void) +{ + set_config_access_method(gemini); + ppc_md.pcibios_fixup = gemini_pcibios_fixup; +} diff --git a/arch/ppc/kernel/gemini_prom.S b/arch/ppc/kernel/gemini_prom.S new file mode 100644 index 000000000..095f50e8f --- /dev/null +++ b/arch/ppc/kernel/gemini_prom.S @@ -0,0 +1,94 @@ +/* + * arch/ppc/kernel/gemini_prom.S + * + * Not really prom support code (yet), but sort of anti-prom code. The current + * bootloader does a number of things it shouldn't and doesn't do things that it + * should. The stuff in here is mainly a hodge-podge collection of setup code + * to get the board up and running. + * ---Dan + */ + +#include "ppc_asm.tmpl" +#include "ppc_defs.h" +#include <asm/processor.h> +#include <asm/page.h> +#include <asm/gemini.h> + +#define HID0_ABE (1<<3) + +/* + * On 750's the MMU is on when Linux is booted, so we need to clear out the + * bootloader's BAT settings, make sure we're in supervisor state (gotcha!), + * and turn off the MMU. + * + */ + +_GLOBAL(gemini_prom_init) +#ifdef __SMP__ + /* Since the MMU's on, get stuff in rom space that we'll need */ + lis r4,GEMINI_CPUSTAT@h + ori r4,r4,GEMINI_CPUSTAT@l + lbz r5,0(r4) + andi. r5,r5,3 + mr r24,r5 /* cpu # used later on */ +#endif + mfmsr r4 + li r3,MSR_PR /* ensure supervisor! */ + ori r3,r3,MSR_IR|MSR_DR + andc r4,r4,r3 + mtmsr r4 +#if 0 + /* zero out the bats now that the MMU is off */ +prom_no_mmu: + li r3,0 + mtspr IBAT0U,r3 + mtspr IBAT0L,r3 + mtspr IBAT1U,r3 + mtspr IBAT1L,r3 + mtspr IBAT2U,r3 + mtspr IBAT2L,r3 + mtspr IBAT3U,r3 + mtspr IBAT3L,r3 + + mtspr DBAT0U,r3 + mtspr DBAT0L,r3 + mtspr DBAT1U,r3 + mtspr DBAT1L,r3 + mtspr DBAT2U,r3 + mtspr DBAT2L,r3 + mtspr DBAT3U,r3 + mtspr DBAT3L,r3 +#endif + + /* the bootloader (as far as I'm currently aware) doesn't mess with page + tables, but since we're already here, might as well zap these, too */ + li r4,0 + mtspr SDR1,r4 + + li r4,16 + mtctr r4 + li r3,0 + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 + bdnz 3b + +#ifdef __SMP__ + /* The 750 book (and Mot/IBM support) says that this will "assist" snooping + when in SMP. Not sure yet whether this should stay or leave... */ + mfspr r4,HID0 + ori r4,r4,HID0_ABE + mtspr HID0,r4 + sync +#endif /* __SMP__ */ + blr + +/* apparently, SMon doesn't pay attention to HID0[SRST]. Disable the MMU and + branch to 0xfff00100 */ +_GLOBAL(_gemini_reboot) + lis r5,GEMINI_BOOT_INIT@h + ori r5,r5,GEMINI_BOOT_INIT@l + li r6,MSR_IP + mtspr SRR0,r5 + mtspr SRR1,r6 + rfi diff --git a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c new file mode 100644 index 000000000..4af02958a --- /dev/null +++ b/arch/ppc/kernel/gemini_setup.c @@ -0,0 +1,527 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Synergy Microsystems board support by Dan Cox (dan@synergymicro.com) + * + */ + +#include <linux/config.h> +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/types.h> +#include <linux/major.h> +#include <linux/blk.h> +#include <linux/console.h> +#include <linux/openpic.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/m48t35.h> +#include <asm/gemini.h> + +#include "time.h" +#include "local_irq.h" +#include "open_pic.h" + +void gemini_setup_pci_ptrs(void); + +static int l2_printed = 0; +static unsigned char gemini_switch_map = 0; +static char *gemini_board_families[] = { + "VGM", "VSS", "KGM", "VGR", "KSS" +}; + +static char *gemini_memtypes[] = { + "EDO DRAM, 60nS", "SDRAM, 15nS, CL=2", "SDRAM, 15nS, CL=2 with ECC" +}; + +static unsigned int cpu_7xx[16] = { + 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static unsigned int cpu_6xx[16] = { + 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 +}; + + +static inline unsigned long _get_HID1(void) +{ + unsigned long val; + + __asm__ __volatile__("mfspr %0,1009" : "=r" (val)); + return val; +} + +int +gemini_get_cpuinfo(char *buffer) +{ + int i, len; + unsigned char reg, rev; + char *family; + unsigned int type; + + reg = readb(GEMINI_FEAT); + family = gemini_board_families[((reg>>4) & 0xf)]; + if (((reg>>4) & 0xf) > 2) + printk(KERN_ERR "cpuinfo(): unable to determine board family\n"); + + reg = readb(GEMINI_BREV); + type = (reg>>4) & 0xf; + rev = reg & 0xf; + + reg = readb(GEMINI_BECO); + + len = sprintf( buffer, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", + family, type, (rev + 'A'), (reg & 0xf)); + + len += sprintf( buffer+len, "vendor\t\t: %s\n", + (_get_PVR() & (1<<15)) ? "IBM" : "Motorola"); + + reg = readb(GEMINI_MEMCFG); + len += sprintf( buffer+len, "memory type\t: %s\n", + gemini_memtypes[(reg & 0xc0)>>6]); + len += sprintf( buffer+len, "switches on\t: "); + for( i=0; i < 8; i++ ) { + if ( gemini_switch_map & (1<<i)) + len += sprintf(buffer+len, "%d ", i); + } + len += sprintf(buffer+len, "\n"); + + return len; +} + +static u_char gemini_openpic_initsenses[] = { + 1, + 1, + 1, + 1, + 0, + 0, + 1, /* remainder are level-triggered */ +}; + +#define GEMINI_MPIC_ADDR (0xfcfc0000) +#define GEMINI_MPIC_PCI_CFG (0x80005800) + +void __init gemini_openpic_init(void) +{ + grackle_write(GEMINI_MPIC_PCI_CFG + PCI_BASE_ADDRESS_0, + GEMINI_MPIC_ADDR); + grackle_write(GEMINI_MPIC_PCI_CFG + PCI_COMMAND, PCI_COMMAND_MEMORY); + + OpenPIC = (volatile struct OpenPIC *) GEMINI_MPIC_ADDR; + OpenPIC_InitSenses = gemini_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof( gemini_openpic_initsenses ); + + ioremap( GEMINI_MPIC_ADDR, sizeof( struct OpenPIC )); +} + + +extern unsigned long loops_per_sec; +extern int root_mountflags; +extern char cmd_line[]; + + +void __init gemini_setup_arch(unsigned long *memstart, unsigned long *memend) +{ + unsigned int cpu; + extern char cmd_line[]; + + + loops_per_sec = 50000000; + +#ifdef CONFIG_BLK_DEV_INITRD + /* bootable off CDROM */ + if (initrd_start) + ROOT_DEV = MKDEV(SCSI_CDROM_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(0x0801); + + /* nothing but serial consoles... */ + sprintf(cmd_line, "%s console=ttyS0", cmd_line); + + + /* The user switches on the front panel can be used as follows: + + Switch 0 - adds "debug" to the command line for verbose boot info, + Switch 7 - boots in single-user mode + + */ + + gemini_switch_map = readb( GEMINI_USWITCH ); + + if ( gemini_switch_map & (1<<GEMINI_SWITCH_VERBOSE)) + sprintf(cmd_line, "%s debug", cmd_line); + + if ( gemini_switch_map & (1<<GEMINI_SWITCH_SINGLE_USER)) + sprintf(cmd_line, "%s single", cmd_line); + + printk("Boot arguments: %s\n", cmd_line); + + /* mutter some kind words about who made the CPU */ + cpu = _get_PVR(); + printk("CPU manufacturer: %s [rev=%04x]\n", (cpu & (1<<15)) ? "IBM" : + "Motorola", (cpu & 0xffff)); + + /* take special pains to map the MPIC, since it isn't mapped yet */ + gemini_openpic_init(); + + /* start the L2 */ + gemini_init_l2(); + +} + + +int +gemini_get_clock_speed(void) +{ + unsigned long hid1; + int clock; + unsigned char reg; + + hid1 = _get_HID1(); + if ((_get_PVR()>>16) == 8) + hid1 = cpu_7xx[hid1]; + else + hid1 = cpu_6xx[hid1]; + + reg = readb(GEMINI_BSTAT) & 0xc0; + + switch( reg >> 2 ) { + + case 0: + default: + clock = (hid1*100)/3; + break; + + case 1: + clock = (hid1*125)/3; + break; + + case 2: + clock = (hid1*50)/3; + break; + } + + return clock; +} + + +#define L2CR_PIPE_LATEWR (0x01800000) /* late-write SRAM */ +#define L2CR_L2CTL (0x00100000) /* RAM control */ +#define L2CR_INST_DISABLE (0x00400000) /* disable for insn's */ +#define L2CR_L2I (0x00200000) /* global invalidate */ +#define L2CR_L2E (0x80000000) /* enable */ +#define L2CR_L2WT (0x00080000) /* write-through */ + +void __init gemini_init_l2(void) +{ + unsigned char reg; + unsigned long cache; + int speed; + + reg = readb(GEMINI_L2CFG); + + /* 750's L2 initializes differently from a 604's. Also note that a Grackle + bug will hang a dual-604 board, so make sure that doesn't happen by not + turning on the L2 */ + if ( _get_PVR() >> 16 != 8 ) { + + /* check for dual cpus and cry sadly about the loss of an L2... */ + if ((( readb(GEMINI_CPUSTAT) & 0x0c ) >> 2) != 1) + printk("Sorry. Your dual-604 does not allow the L2 to be enabled due " + "to a Grackle bug.\n"); + else if ( reg & GEMINI_L2_SIZE_MASK ) { + printk("Enabling 604 L2 cache: %dKb\n", + (128<<((reg & GEMINI_L2_SIZE_MASK)>>6))); + writeb( 1, GEMINI_L2CFG ); + } + } + + /* do a 750 */ + else { + /* Synergy's first round of 750 boards had the L2 size stuff into the + board register above. If it's there, it's used; if not, the + standard default is 1Mb. The L2 type, I'm told, is "most likely + probably always going to be late-write". --Dan */ + + if (reg & 0xc0) { + if (!l2_printed) { + printk("Enabling 750 L2 cache: %dKb\n", + (128 << ((reg & 0xc0)>>6))); + l2_printed=1; + } + + /* take the size given */ + cache = (((reg>>6) & 0x3)<<28); + } + else + /* default of 1Mb */ + cache = 0x3<<28; + + reg &= 0x3; + + /* a cache ratio of 1:1 and CPU clock speeds in excess of 300Mhz are bad + things. If found, tune it down to 1:1.5. -- Dan */ + if (!reg) { + + speed = gemini_get_clock_speed(); + + if (speed >= 300) { + printk("Warning: L2 ratio is 1:1 on a %dMhz processor. Dropping to 1:1.5.\n", + speed ); + printk("Contact Synergy Microsystems for an ECO to fix this problem\n"); + reg = 0x1; + } + } + + /* standard stuff */ + cache |= ((1<<reg)<<25); +#ifdef __SMP__ + /* A couple errata for the 750's (both IBM and Motorola silicon) + note that you can get missed cache lines on MP implementations. + The workaround - if you call it that - is to make the L2 + write-through. This is fixed in IBM's 3.1 rev (I'm told), but + for now, always make 2.x versions use L2 write-through. --Dan */ + if (((_get_PVR()>>8) & 0xf) <= 2) + cache |= L2CR_L2WT; +#endif + cache |= L2CR_PIPE_LATEWR|L2CR_L2CTL|L2CR_INST_DISABLE; + _set_L2CR(0); + _set_L2CR(cache|L2CR_L2I|L2CR_L2E); + } +} + +void +gemini_restart(char *cmd) +{ + __cli(); + /* make a clean restart, not via the MPIC */ + _gemini_reboot(); + for(;;); +} + +void +gemini_power_off(void) +{ + for(;;); +} + +void +gemini_halt(void) +{ + gemini_restart(NULL); +} + +void __init gemini_init_IRQ(void) +{ + int i; + + /* gemini has no 8259 */ + open_pic.irq_offset = 0; + for( i=0; i < 16; i++ ) + irq_desc[i].ctl = &open_pic; + openpic_init(1); +} + +#define gemini_rtc_read(x) (readb(GEMINI_RTC+(x))) +#define gemini_rtc_write(val,x) (writeb((val),(GEMINI_RTC+(x)))) + +/* ensure that the RTC is up and running */ +void __init gemini_time_init(void) +{ + unsigned char reg; + + reg = gemini_rtc_read(M48T35_RTC_CONTROL); + + if ( reg & M48T35_RTC_STOPPED ) { + printk(KERN_INFO "M48T35 real-time-clock was stopped. Now starting...\n"); + gemini_rtc_write((reg & ~(M48T35_RTC_STOPPED)), M48T35_RTC_CONTROL); + gemini_rtc_write((reg | M48T35_RTC_SET), M48T35_RTC_CONTROL); + } +} + +#undef DEBUG_RTC + +unsigned long +gemini_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + unsigned char reg; + + reg = gemini_rtc_read(M48T35_RTC_CONTROL); + gemini_rtc_write((reg|M48T35_RTC_READ), M48T35_RTC_CONTROL); +#ifdef DEBUG_RTC + printk("get rtc: reg = %x\n", reg); +#endif + + do { + sec = gemini_rtc_read(M48T35_RTC_SECONDS); + min = gemini_rtc_read(M48T35_RTC_MINUTES); + hour = gemini_rtc_read(M48T35_RTC_HOURS); + day = gemini_rtc_read(M48T35_RTC_DOM); + mon = gemini_rtc_read(M48T35_RTC_MONTH); + year = gemini_rtc_read(M48T35_RTC_YEAR); + } while( sec != gemini_rtc_read(M48T35_RTC_SECONDS)); +#ifdef DEBUG_RTC + printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", + sec, min, hour, day, mon, year); +#endif + + gemini_rtc_write(reg, M48T35_RTC_CONTROL); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ((year += 1900) < 1970) + year += 100; +#ifdef DEBUG_RTC + printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", + sec, min, hour, day, mon, year); +#endif + + return mktime( year, mon, day, hour, min, sec ); +} + + +int +gemini_set_rtc_time( unsigned long now ) +{ + unsigned char reg; + struct rtc_time tm; + + to_tm( now, &tm ); + + reg = gemini_rtc_read(M48T35_RTC_CONTROL); +#if DEBUG_RTC + printk("set rtc: reg = %x\n", reg); +#endif + + gemini_rtc_write((reg|M48T35_RTC_SET), M48T35_RTC_CONTROL); +#if DEBUG_RTC + printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n", + tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year); +#endif + + tm.tm_year -= 1900; + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); +#ifdef DEBUG_RTC + printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n", + tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year); +#endif + + gemini_rtc_write(tm.tm_sec, M48T35_RTC_SECONDS); + gemini_rtc_write(tm.tm_min, M48T35_RTC_MINUTES); + gemini_rtc_write(tm.tm_hour, M48T35_RTC_HOURS); + gemini_rtc_write(tm.tm_mday, M48T35_RTC_DOM); + gemini_rtc_write(tm.tm_mon, M48T35_RTC_MONTH); + gemini_rtc_write(tm.tm_year, M48T35_RTC_YEAR); + + /* done writing */ + gemini_rtc_write(reg, M48T35_RTC_CONTROL); + + if ((time_state == TIME_ERROR) || (time_state == TIME_BAD)) + time_state = TIME_OK; + + return 0; +} + +/* use the RTC to determine the decrementer count */ +void __init gemini_calibrate_decr(void) +{ + int freq, divisor; + unsigned char reg; + + /* determine processor bus speed */ + reg = readb(GEMINI_BSTAT); + + switch(((reg & 0x0c)>>2)&0x3) { + case 0: + default: + freq = 66; + break; + case 1: + freq = 83; + break; + case 2: + freq = 100; + break; + } + + freq *= 1000000; + divisor = 4; + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + + +void __init gemini_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + void chrp_do_IRQ(struct pt_regs *, int, int); + void layout_bus( struct pci_bus * ); + + gemini_setup_pci_ptrs(); + + ISA_DMA_THRESHOLD = 0; + DMA_MODE_READ = 0; + DMA_MODE_WRITE = 0; + +#ifdef CONFIG_BLK_DEV_INITRD + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + ppc_md.setup_arch = gemini_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = gemini_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = gemini_init_IRQ; + ppc_md.do_IRQ = chrp_do_IRQ; + ppc_md.init = NULL; + + ppc_md.restart = gemini_restart; + ppc_md.power_off = gemini_power_off; + ppc_md.halt = gemini_halt; + + ppc_md.time_init = gemini_time_init; + ppc_md.set_rtc_time = gemini_set_rtc_time; + ppc_md.get_rtc_time = gemini_get_rtc_time; + ppc_md.calibrate_decr = gemini_calibrate_decr; + + /* no keyboard/mouse/video stuff yet.. */ + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + ppc_md.pcibios_fixup_bus = layout_bus; +} diff --git a/arch/ppc/kernel/hashtable.S b/arch/ppc/kernel/hashtable.S new file mode 100644 index 000000000..74f00ce10 --- /dev/null +++ b/arch/ppc/kernel/hashtable.S @@ -0,0 +1,478 @@ +/* + * arch/ppc/kernel/hashtable.S + * + * $Id: hashtable.S,v 1.3 1999/09/05 11:56:27 paulus Exp $ + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * + * This file contains low-level assembler routines for managing + * the PowerPC MMU hash table. (PPC 8xx processors don't use a + * hash table, so this file is not used on them.) + * + * 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. + * + */ + +#include "ppc_asm.h" +#include <asm/processor.h> +#include <asm/page.h> +#include <linux/config.h> + +/* + * Load a PTE into the hash table, if possible. + * The address is in r3, and r4 contains access flags: + * _PAGE_USER (4) if a user-mode access, ored with + * _PAGE_RW (2) if a write. r20 contains DSISR or SRR1, + * so bit 1 (0x40000000) is set if the exception was due + * to no matching PTE being found in the hash table. + * r5 contains the physical address of the current task's thread. + * + * Returns to the caller if the access is illegal or there is no + * mapping for the address. Otherwise it places an appropriate PTE + * in the hash table and returns from the exception. + * Uses r0, r2 - r6, ctr, lr. + * + * For speed, 4 of the instructions get patched once the size and + * physical address of the hash table are known. These definitions + * of Hash_base and Hash_bits below are just an example. + */ +Hash_base = 0x180000 +Hash_bits = 12 /* e.g. 256kB hash table */ +Hash_msk = (((1 << Hash_bits) - 1) * 64) + + .globl hash_page +hash_page: +#ifdef __SMP__ + eieio + lis r2,hash_table_lock@h + ori r2,r2,hash_table_lock@l + tophys(r2,r2) + lis r6,100000000@h + mtctr r6 + lwz r0,PROCESSOR-THREAD(r5) + or r0,r0,r6 +10: lwarx r6,0,r2 + cmpi 0,r6,0 + bne- 12f + stwcx. r0,0,r2 + beq+ 11f +12: cmpw r6,r0 + bdnzf 2,10b + tw 31,31,31 +11: eieio +#endif + /* Get PTE (linux-style) and check access */ + mfspr r2,SPRG3 /* current task's THREAD (phys) */ + lwz r5,PGDIR(r2) /* virt page-table root */ + tophys(r5,r5) /* convert to phys addr */ + rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ + lwz r5,0(r5) /* get pmd entry */ + rlwinm. r5,r5,0,0,19 /* extract address of pte page */ +#ifdef __SMP__ + beq- hash_page_out /* return if no mapping */ +#else + /* XXX it seems like the 601 will give a machine fault on the + rfi if its alignment is wrong (bottom 4 bits of address are + 8 or 0xc) and we have had a not-taken conditional branch + to the address following the rfi. */ + beqlr- +#endif + tophys(r2,r5) + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r6,0(r2) /* get linux-style pte */ + ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ + andc. r0,r4,r6 /* check access & ~permission */ +#ifdef __SMP__ + bne- hash_page_out /* return if access not permitted */ +#else + bnelr- +#endif + + ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ + rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ + rlwimi r5,r4,7,22,22 /* _PAGE_RW -> _PAGE_HWWRITE */ + or r6,r6,r5 + stw r6,0(r2) /* update PTE (accessed/dirty bits) */ + + /* Convert linux-style PTE to low word of PPC-style PTE */ +#ifdef CONFIG_PPC64 + /* clear the high 32 bits just in case */ + clrldi r6,r6,32 + clrldi r4,r4,32 +#endif /* CONFIG_PPC64 */ + rlwinm r4,r6,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ + rlwimi r6,r6,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ + ori r4,r4,0xe04 /* clear out reserved bits */ + andc r6,r6,r4 /* PP=2 or 0, when _PAGE_HWWRITE */ + + /* Construct the high word of the PPC-style PTE */ + mfsrin r5,r3 /* get segment reg for segment */ +#ifdef CONFIG_PPC64 + sldi r5,r5,12 +#else /* CONFIG_PPC64 */ + rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ +#endif /* CONFIG_PPC64 */ + +#ifndef __SMP__ /* do this later for SMP */ +#ifdef CONFIG_PPC64 + ori r5,r5,1 /* set V (valid) bit */ +#else /* CONFIG_PPC64 */ + oris r5,r5,0x8000 /* set V (valid) bit */ +#endif /* CONFIG_PPC64 */ +#endif + +#ifdef CONFIG_PPC64 +/* XXX: does this insert the api correctly? -- Cort */ + rlwimi r5,r3,17,21,25 /* put in API (abbrev page index) */ +#else /* CONFIG_PPC64 */ + rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ +#endif /* CONFIG_PPC64 */ + /* Get the address of the primary PTE group in the hash table */ + .globl hash_page_patch_A +hash_page_patch_A: + lis r4,Hash_base@h /* base address of hash table */ +#ifdef CONFIG_PPC64 + /* just in case */ + clrldi r4,r4,32 +#endif + rlwimi r4,r5,32-1,26-Hash_bits,25 /* (VSID & hash_mask) << 6 */ + rlwinm r0,r3,32-6,26-Hash_bits,25 /* (PI & hash_mask) << 6 */ + xor r4,r4,r0 /* make primary hash */ + + /* See whether it was a PTE not found exception or a + protection violation. */ + andis. r0,r20,0x4000 + li r2,8 /* PTEs/group */ + bne 10f /* no PTE: go look for an empty slot */ + tlbie r3 /* invalidate TLB entry */ + + /* Search the primary PTEG for a PTE whose 1st word matches r5 */ + mtctr r2 + addi r3,r4,-8 +1: lwzu r0,8(r3) /* get next PTE */ + cmp 0,r0,r5 + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ found_slot + + /* Search the secondary PTEG for a matching PTE */ + ori r5,r5,0x40 /* set H (secondary hash) bit */ + .globl hash_page_patch_B +hash_page_patch_B: + xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ + xori r3,r3,0xffc0 + addi r3,r3,-8 + mtctr r2 +2: lwzu r0,8(r3) + cmp 0,r0,r5 + bdnzf 2,2b + beq+ found_slot + xori r5,r5,0x40 /* clear H bit again */ + + /* Search the primary PTEG for an empty slot */ +10: mtctr r2 + addi r3,r4,-8 /* search primary PTEG */ +1: lwzu r0,8(r3) /* get next PTE */ + srwi. r0,r0,31 /* only want to check valid bit */ + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ found_empty + + /* Search the secondary PTEG for an empty slot */ + ori r5,r5,0x40 /* set H (secondary hash) bit */ + .globl hash_page_patch_C +hash_page_patch_C: + xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ + xori r3,r3,0xffc0 + addi r3,r3,-8 + mtctr r2 +2: lwzu r0,8(r3) + srwi. r0,r0,31 /* only want to check valid bit */ + bdnzf 2,2b + beq+ found_empty + + /* + * Choose an arbitrary slot in the primary PTEG to overwrite. + * Since both the primary and secondary PTEGs are full, and we + * have no information that the PTEs in the primary PTEG are + * more important or useful than those in the secondary PTEG, + * and we know there is a definite (although small) speed + * advantage to putting the PTE in the primary PTEG, we always + * put the PTE in the primary PTEG. + */ + xori r5,r5,0x40 /* clear H bit again */ + lwz r2,next_slot@l(0) + addi r2,r2,8 + andi. r2,r2,0x38 + stw r2,next_slot@l(0) + add r3,r4,r2 +11: + /* update counter of evicted pages */ + lis r2,htab_evicts@h + ori r2,r2,htab_evicts@l + tophys(r2,r2) + lwz r4,0(r2) + addi r4,r4,1 + stw r4,0(r2) + +#ifndef __SMP__ + /* Store PTE in PTEG */ +found_empty: + stw r5,0(r3) +found_slot: + stw r6,4(r3) + sync + +#else /* __SMP__ */ +/* + * Between the tlbie above and updating the hash table entry below, + * another CPU could read the hash table entry and put it in its TLB. + * There are 3 cases: + * 1. using an empty slot + * 2. updating an earlier entry to change permissions (i.e. enable write) + * 3. taking over the PTE for an unrelated address + * + * In each case it doesn't really matter if the other CPUs have the old + * PTE in their TLB. So we don't need to bother with another tlbie here, + * which is convenient as we've overwritten the register that had the + * address. :-) The tlbie above is mainly to make sure that this CPU comes + * and gets the new PTE from the hash table. + * + * We do however have to make sure that the PTE is never in an invalid + * state with the V bit set. + */ +found_empty: +found_slot: + stw r5,0(r3) /* clear V (valid) bit in PTE */ + sync + tlbsync + sync + stw r6,4(r3) /* put in correct RPN, WIMG, PP bits */ + sync + oris r5,r5,0x8000 + stw r5,0(r3) /* finally set V bit in PTE */ +#endif /* __SMP__ */ + +/* + * Update the hash table miss count. We only want misses here + * that _are_ valid addresses and have a pte otherwise we don't + * count it as a reload. do_page_fault() takes care of bad addrs + * and entries that need linux-style pte's created. + * + * safe to use r2 here since we're not using it as current yet + * update the htab misses count + * -- Cort + */ + lis r2,htab_reloads@h + ori r2,r2,htab_reloads@l + tophys(r2,r2) + lwz r3,0(r2) + addi r3,r3,1 + stw r3,0(r2) + +#ifdef __SMP__ + lis r2,hash_table_lock@ha + tophys(r2,r2) + li r0,0 + stw r0,hash_table_lock@l(r2) + eieio +#endif + + /* Return from the exception */ + lwz r3,_CCR(r21) + lwz r4,_LINK(r21) + lwz r5,_CTR(r21) + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + lwz r0,GPR0(r21) + lwz r1,GPR1(r21) + lwz r2,GPR2(r21) + lwz r3,GPR3(r21) + lwz r4,GPR4(r21) + lwz r5,GPR5(r21) + lwz r6,GPR6(r21) + /* we haven't used xer */ + mtspr SRR1,r23 + mtspr SRR0,r22 + lwz r20,GPR20(r21) + lwz r22,GPR22(r21) + lwz r23,GPR23(r21) + lwz r21,GPR21(r21) + rfi + +#ifdef __SMP__ +hash_page_out: + lis r2,hash_table_lock@ha + tophys(r2,r2) + li r0,0 + stw r0,hash_table_lock@l(r2) + eieio + blr + + .data + .globl hash_table_lock +hash_table_lock: + .long 0 + .text +#endif /* __SMP__ */ + +/* next_slot is assumed to be within the first 32kB of physical RAM */ +next_slot: + .long 0 + +/* + * Flush entries from the hash table with VSIDs in the range + * given. + */ +_GLOBAL(flush_hash_segments) + lis r5,Hash@ha + lwz r5,Hash@l(r5) /* base of hash table */ + cmpwi 0,r5,0 + bne+ 99f + tlbia + sync +#ifdef __SMP__ + tlbsync + sync +#endif + blr +99: +#ifdef __SMP__ + /* Note - we had better not do anything which could generate + a hash table miss while we have the hash table locked, + or we'll get a deadlock. -paulus */ + mfmsr r10 + sync + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + mtmsr r0 + SYNC + lis r9,hash_table_lock@h + ori r9,r9,hash_table_lock@l + lwz r8,PROCESSOR(r2) + oris r8,r8,8 +10: lwarx r6,0,r9 + cmpi 0,r6,0 + bne- 10b + stwcx. r8,0,r9 + bne- 10b + eieio +#endif + rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ + oris r3,r3,0x8000 /* set V bit */ + rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ + oris r4,r4,0x8000 + ori r4,r4,0x7f + lis r6,Hash_size@ha + lwz r6,Hash_size@l(r6) /* size in bytes */ + srwi r6,r6,3 /* # PTEs */ + mtctr r6 + addi r5,r5,-8 + li r0,0 +1: lwzu r6,8(r5) /* get next tag word */ + cmplw 0,r6,r3 + cmplw 1,r6,r4 + cror 0,0,5 /* set cr0.lt if out of range */ + blt 2f /* branch if out of range */ + stw r0,0(r5) /* invalidate entry */ +2: bdnz 1b /* continue with loop */ + sync + tlbia + sync +#ifdef __SMP__ + tlbsync + sync + lis r3,hash_table_lock@ha + stw r0,hash_table_lock@l(r3) + mtmsr r10 + SYNC +#endif + blr + +/* + * Flush the entry for a particular page from the hash table. + * + * flush_hash_page(unsigned context, unsigned long va) + */ +_GLOBAL(flush_hash_page) + lis r6,Hash@ha + lwz r6,Hash@l(r6) /* hash table base */ + cmpwi 0,r6,0 /* hash table in use? */ + bne+ 99f + tlbie r4 /* in hw tlb too */ + sync +#ifdef __SMP__ + tlbsync + sync +#endif + blr +99: +#ifdef __SMP__ + /* Note - we had better not do anything which could generate + a hash table miss while we have the hash table locked, + or we'll get a deadlock. -paulus */ + mfmsr r10 + sync + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + mtmsr r0 + SYNC + lis r9,hash_table_lock@h + ori r9,r9,hash_table_lock@l + lwz r8,PROCESSOR(r2) + oris r8,r8,9 +10: lwarx r7,0,r9 + cmpi 0,r7,0 + bne- 10b + stwcx. r8,0,r9 + bne- 10b + eieio +#endif + rlwinm r3,r3,11,1,20 /* put context into vsid */ + rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ + oris r3,r3,0x8000 /* set V (valid) bit */ + rlwimi r3,r4,10,26,31 /* put in API (abbrev page index) */ + rlwinm r7,r4,32-6,10,25 /* get page index << 6 */ + rlwinm r5,r3,32-1,7,25 /* vsid << 6 */ + xor r7,r7,r5 /* primary hash << 6 */ + lis r5,Hash_mask@ha + lwz r5,Hash_mask@l(r5) /* hash mask */ + slwi r5,r5,6 /* << 6 */ + and r7,r7,r5 + add r6,r6,r7 /* address of primary PTEG */ + li r8,8 + mtctr r8 + addi r7,r6,-8 +1: lwzu r0,8(r7) /* get next PTE */ + cmpw 0,r0,r3 /* see if tag matches */ + bdnzf 2,1b /* while --ctr != 0 && !cr0.eq */ + beq 3f /* if we found it */ + ori r3,r3,0x40 /* set H (alt. hash) bit */ + xor r6,r6,r5 /* address of secondary PTEG */ + mtctr r8 + addi r7,r6,-8 +2: lwzu r0,8(r7) /* get next PTE */ + cmpw 0,r0,r3 /* see if tag matches */ + bdnzf 2,2b /* while --ctr != 0 && !cr0.eq */ + bne 4f /* if we didn't find it */ +3: li r0,0 + stw r0,0(r7) /* invalidate entry */ +4: sync + tlbie r4 /* in hw tlb too */ + sync +#ifdef __SMP__ + tlbsync + sync + li r0,0 + stw r0,0(r9) /* clear hash_table_lock */ + mtmsr r10 + SYNC +#endif + blr diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index e451ac87f..9a0ee6346 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -1,12 +1,13 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.133 1999/05/20 05:13:08 cort Exp $ + * $Id: head.S,v 1.143 1999/09/05 11:56:28 paulus Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> * Adapted for Power Macintosh by Paul Mackerras. * Low-level exception handlers and MMU support * rewritten by Paul Mackerras. @@ -16,7 +17,7 @@ * * This file contains the low-level support and setup for the * PowerPC platform, including trap and interrupt dispatch. - * Also included here is low-level thread/task switch support. + * (The PPC 8xx embedded CPUs use head_8xx.S instead.) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,118 +26,42 @@ * */ -#include "ppc_asm.tmpl" -#include "ppc_defs.h" +#include "ppc_asm.h" #include <asm/processor.h> #include <asm/page.h> -#include <asm/ptrace.h> -#include <linux/sys.h> -#include <linux/errno.h> #include <linux/config.h> #include <asm/mmu.h> -#include <asm/pgtable.h> -#include <asm/cache.h> #ifdef CONFIG_APUS #include <asm/amigappc.h> #endif -/* optimization for 603 to load the tlb directly from the linux table */ -#define NO_RELOAD_HTAB 1 - -#ifndef CONFIG_8xx -CACHE_LINE_SIZE = 32 -LG_CACHE_LINE_SIZE = 5 -#else -CACHE_LINE_SIZE = 16 -LG_CACHE_LINE_SIZE = 4 -#endif - -#define TOPHYS(x) (x - KERNELBASE) - -/* - * Macros for storing registers into and loading registers from - * exception frames. - */ -#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) -#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) -#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) -#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) -#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) -#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) -#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) -#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) -#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) -#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) - -#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base) -#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) -#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) -#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) -#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) -#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) -#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base) -#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) -#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) -#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) -#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) -#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) - -#define SYNC \ - sync; \ - isync - -#ifndef CONFIG_8xx -/* This instruction is not implemented on the PPC 603 or 601 */ -#define tlbia \ - li r4,128; \ - mtctr r4; \ - lis r4,KERNELBASE@h; \ -0: tlbie r4; \ - addi r4,r4,0x1000; \ - bdnz 0b -#endif - #ifdef CONFIG_PPC64 -#define LOAD_BAT(n, offset, reg, RA, RB) \ - ld RA,offset+0(reg); \ - ld RB,offset+8(reg); \ +#define LOAD_BAT(n, reg, RA, RB) \ + ld RA,(n*32)+0(reg); \ + ld RB,(n*32)+8(reg); \ mtspr IBAT##n##U,RA; \ mtspr IBAT##n##L,RB; \ - ld RA,offset+16(reg); \ - ld RB,offset+24(reg); \ + ld RA,(n*32)+16(reg); \ + ld RB,(n*32)+24(reg); \ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ #else /* CONFIG_PPC64 */ -/* 601 only have IBAT cr0.eq is set on 601 when using this macro */ -#define LOAD_BAT(n, offset, reg, RA, RB) \ - lwz RA,offset+0(reg); \ - lwz RB,offset+4(reg); \ +/* 601 only have IBAT; cr0.eq is set on 601 when using this macro */ +#define LOAD_BAT(n, reg, RA, RB) \ + lwz RA,(n*16)+0(reg); \ + lwz RB,(n*16)+4(reg); \ mtspr IBAT##n##U,RA; \ mtspr IBAT##n##L,RB; \ beq 1f; \ - lwz RA,offset+8(reg); \ - lwz RB,offset+12(reg); \ + lwz RA,(n*16)+8(reg); \ + lwz RB,(n*16)+12(reg); \ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ 1: #endif /* CONFIG_PPC64 */ - -#ifndef CONFIG_APUS -#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h -#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h -#else -#define tophys(rd,rs,rt) \ - lis rt,CYBERBASEp@h; \ - lwz rt,0(rt); \ - add rd,rs,rt -#define tovirt(rd,rs,rt) \ - lis rt,CYBERBASEp@h; \ - lwz rt,0(rt); \ - sub rd,rs,rt -#endif .text .globl _stext @@ -149,7 +74,15 @@ _stext: .text .globl _start _start: - .long TOPHYS(__start),0,0 + /* + * These are here for legacy reasons, the kernel used to + * need to look like a coff function entry for the pmac + * but we're always started by some kind of bootloader now. + * -- Cort + */ + nop + nop + nop /* PMAC * Enter here with the kernel text, data and bss loaded starting at @@ -168,6 +101,7 @@ _start: * * APUS * r3: 'APUS' + * r4: physical address of memory base * Linux/m68k style BootInfo structure at &_end. * * PREP @@ -183,39 +117,6 @@ _start: * This just gets a minimal mmu environment setup so we can call * start_here() to do the real work. * -- Cort - * - * MPC8xx - * This port was done on an MBX board with an 860. Right now I only - * support an ELF compressed (zImage) boot from EPPC-Bug because the - * code there loads up some registers before calling us: - * r3: ptr to board info data - * r4: initrd_start or if no initrd then 0 - * r5: initrd_end - unused if r4 is 0 - * r6: Start of command line string - * r7: End of command line string - * - * I decided to use conditional compilation instead of checking PVR and - * adding more processor specific branches around code I don't need. - * Since this is an embedded processor, I also appreciate any memory - * savings I can get. - * - * The MPC8xx does not have any BATs, but it supports large page sizes. - * We first initialize the MMU to support 8M byte pages, then load one - * entry into each of the instruction and data TLBs to map the first - * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to - * the "internal" processor registers before MMU_init is called. - * - * The TLB code currently contains a major hack. Since I use the condition - * code register, I have to save and restore it. I am out of registers, so - * I just store it in memory location 0 (the TLB handlers are not reentrant). - * To avoid making any decisions, I need to use the "segment" valid bit - * in the first level table, but that would require many changes to the - * Linux page directory/table functions that I don't want to do right now. - * - * I used to use SPRG2 for a temporary register in the TLB handler, but it - * has since been put to other uses. I now use a hack to save a register - * and the CCR at memory location 0.....Someday I'll fix this..... - * -- Dan */ .globl __start @@ -241,17 +142,23 @@ __start: mr r28,r6 mr r27,r7 li r24,0 /* cpu # */ -#ifndef CONFIG_8xx bl prom_init - .globl __secondary_start -__secondary_start: + +#ifdef CONFIG_APUS +/* On APUS the __va/__pa constants need to be set to the correct + * values before continuing. + */ + mr r4,r30 + bl fix_mem_constants +#endif + /* * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. From this point on we can't safely * call OF any more. */ lis r11,KERNELBASE@h -#ifndef CONFIG_PPC64 +#ifndef CONFIG_PPC64xxx mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ cmpi 0,r9,1 @@ -267,16 +174,9 @@ __secondary_start: b 5f #endif /* CONFIG_PPC64 */ 4: -#ifdef CONFIG_APUS - ori r11,r11,BL_8M<<2|0x2 /* set up an 8MB mapping */ - ori r11,r11,0xfe /* set up an 8MB mapping */ - lis r8,CYBERBASEp@h - lwz r8,0(r8) - addis r8,r8,KERNELBASE@h - addi r8,r8,2 -#else + tophys(r8,r11) + ori r8,r8,2 /* R/W access */ ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ - li r8,2 /* R/W access */ #ifdef CONFIG_PPC64 /* clear out the high 32 bits in the BAT */ clrldi r11,r11,32 @@ -289,21 +189,29 @@ __secondary_start: * allow secondary cpus to get at all of ram in early bootup * since their init_task may be up there -- Cort */ +#if 0 oris r18,r8,0x10000000@h oris r21,r11,(KERNELBASE+0x10000000)@h +#else + lis r18,0x9000 + ori r18,r18,0x12 + lis r21,0x9000 + ori r21,r21,0x7fe +#endif mtspr DBAT1L,r18 /* N.B. 6xx (not 601) have valid */ mtspr DBAT1U,r21 /* bit in upper BAT register */ mtspr IBAT1L,r18 mtspr IBAT1U,r21 - + +#if 0 /* for now, otherwise we overflow the 0x100 bytes we have here */ oris r18,r8,0x20000000@h oris r21,r11,(KERNELBASE+0x20000000)@h mtspr DBAT2L,r18 /* N.B. 6xx (not 601) have valid */ mtspr DBAT2U,r21 /* bit in upper BAT register */ - mtspr IBAT2L,r28 + mtspr IBAT2L,r18 mtspr IBAT2U,r21 +#endif /* 0 */ #endif /* CONFIG_PPC64 */ -#endif mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr DBAT0U,r11 /* bit in upper BAT register */ mtspr IBAT0L,r8 @@ -325,7 +233,7 @@ __secondary_start: /* Copy exception code to exception vector base. */ lis r3,KERNELBASE@h - tophys(r4,r3,r5) + tophys(r4,r3) lis r3,0xfff0 /* Copy to 0xfff00000 on APUS */ li r5,0x4000 /* # bytes of memory to copy */ li r6,0 @@ -359,80 +267,6 @@ __secondary_start: * this shouldn't bother the pmac since it just gets turned on again * as we jump to our code at KERNELBASE. -- Cort */ - -#else /* CONFIG_8xx */ - tlbia /* Invalidate all TLB entries */ - li r8, 0 - mtspr MI_CTR, r8 /* Set instruction control to zero */ - lis r8, MD_RESETVAL@h - mtspr MD_CTR, r8 /* Set data TLB control */ - - /* Now map the lower 8 Meg into the TLBs. For this quick hack, - * we can load the instruction and data TLB registers with the - * same values. - */ - lis r8, KERNELBASE@h /* Create vaddr for TLB */ - ori r8, r8, MI_EVALID /* Mark it valid */ - mtspr MI_EPN, r8 - mtspr MD_EPN, r8 - li r8, MI_PS8MEG /* Set 8M byte page */ - ori r8, r8, MI_SVALID /* Make it valid */ - mtspr MI_TWC, r8 - mtspr MD_TWC, r8 - li r8, MI_BOOTINIT /* Create RPN for address 0 */ - mtspr MI_RPN, r8 /* Store TLB entry */ - mtspr MD_RPN, r8 - lis r8, MI_Kp@h /* Set the protection mode */ - mtspr MI_AP, r8 - mtspr MD_AP, r8 - -/* We will get these from a configuration file as soon as I verify - * the extraneous bits don't cause problems in the TLB. - */ -#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE) -#define BOOT_IMMR 0xfa000000 -#endif -#ifdef CONFIG_BSEIP -#define BOOT_IMMR 0xff000000 -#endif - /* Map another 8 MByte at 0xfa000000 to get the processor - * internal registers (among other things). - */ - lis r8, BOOT_IMMR@h /* Create vaddr for TLB */ - ori r8, r8, MD_EVALID /* Mark it valid */ - mtspr MD_EPN, r8 - li r8, MD_PS8MEG /* Set 8M byte page */ - ori r8, r8, MD_SVALID /* Make it valid */ - mtspr MD_TWC, r8 - lis r8, BOOT_IMMR@h /* Create paddr for TLB */ - ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ - mtspr MD_RPN, r8 - - /* Since the cache is enabled according to the information we - * just loaded into the TLB, invalidate and enable the caches here. - * We should probably check/set other modes....later. - */ - lis r8, IDC_INVALL@h - mtspr IC_CST, r8 - mtspr DC_CST, r8 - lis r8, IDC_ENABLE@h - mtspr IC_CST, r8 -#if 0 - mtspr DC_CST, r8 -#else - /* For a debug option, I left this here to easily enable - * the write through cache mode - */ - lis r8, DC_SFWT@h - mtspr DC_CST, r8 - lis r8, IDC_ENABLE@h - mtspr DC_CST, r8 -#endif - -/* We now have the lower 8 Meg mapped into TLB entries, and the caches - * ready to work. - */ -#endif /* CONFIG_8xx */ turn_on_mmu: mfmsr r0 @@ -445,14 +279,6 @@ turn_on_mmu: rfi /* enables MMU */ /* - * GCC sometimes accesses words at negative offsets from the stack - * pointer, although the SysV ABI says it shouldn't. To cope with - * this, we leave this much untouched space on the stack on exception - * entry. - */ -#define STACK_UNDERHEAD 0 - -/* * Exception entry code. This code runs with address translation * turned off, i.e. using physical addresses. * We assume sprg3 has the physical address of the current @@ -465,8 +291,8 @@ turn_on_mmu: mfspr r21,SPRG2; /* exception stack to use from */ \ cmpwi 0,r21,0; /* user mode or RTAS */ \ bne 1f; \ - tophys(r21,r1,r21); /* use tophys(kernel sp) otherwise */ \ - subi r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD; /* alloc exc. frame */\ + tophys(r21,r1); /* use tophys(kernel sp) otherwise */ \ + subi r21,r21,INT_FRAME_SIZE; /* alloc exc. frame */\ 1: stw r20,_CCR(r21); /* save registers */ \ stw r22,GPR22(r21); \ stw r23,GPR23(r21); \ @@ -486,7 +312,7 @@ turn_on_mmu: stw r1,GPR1(r21); \ stw r2,GPR2(r21); \ stw r1,0(r21); \ - tovirt(r1,r21,r1); /* set new kernel sp */ \ + tovirt(r1,r21); /* set new kernel sp */ \ SAVE_4GPRS(3, r21); /* * Note: code which follows this uses cr0.eq (set if from kernel), @@ -504,35 +330,30 @@ label: \ li r20,MSR_KERNEL; \ bl transfer_to_handler; \ .long hdlr; \ - .long int_return + .long ret_from_except /* System reset */ #ifdef CONFIG_SMP /* MVME/MTX start the secondary here */ STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) #else STD_EXCEPTION(0x100, Reset, UnknownException) -#endif +#endif /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) -/* Data access exception. - * This is "never generated" by the MPC8xx. We jump to it for other - * translation errors. - */ +/* Data access exception. */ . = 0x300 DataAccess: EXCEPTION_PROLOG mfspr r20,DSISR -#ifndef CONFIG_8xx andis. r0,r20,0xa470 /* weird error? */ bne 1f /* if not, try to put a PTE */ mfspr r3,DAR /* into the hash table */ rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ - mfspr r5,SPRG3 /* phys addr of TSS */ + mfspr r5,SPRG3 /* phys addr of THREAD */ bl hash_page -#endif 1: stw r20,_DSISR(r21) mr r5,r20 mfspr r4,DAR @@ -542,24 +363,19 @@ DataAccess: rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long do_page_fault - .long int_return + .long ret_from_except -/* Instruction access exception. - * This is "never generated" by the MPC8xx. We jump to it for other - * translation errors. - */ +/* Instruction access exception. */ . = 0x400 InstructionAccess: EXCEPTION_PROLOG -#ifndef CONFIG_8xx andis. r0,r23,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ mr r3,r22 /* into the hash table */ rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ mr r20,r23 /* SRR1 has reason bits */ - mfspr r5,SPRG3 /* phys addr of TSS */ + mfspr r5,SPRG3 /* phys addr of THREAD */ bl hash_page -#endif 1: addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r22 mr r5,r23 @@ -567,7 +383,7 @@ InstructionAccess: rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long do_page_fault - .long int_return + .long ret_from_except /* External interrupt */ . = 0x500; @@ -606,7 +422,7 @@ HardwareInterrupt: li r4,0 bl transfer_to_handler .long do_IRQ; - .long int_return + .long ret_from_except /* Alignment exception */ @@ -622,7 +438,7 @@ Alignment: rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long AlignmentException - .long int_return + .long ret_from_except /* Program check exception */ . = 0x700 @@ -633,9 +449,8 @@ ProgramCheck: rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long ProgramCheckException - .long int_return + .long ret_from_except -#ifndef CONFIG_8xx /* Floating-point unavailable */ . = 0x800 FPUnavailable: @@ -644,12 +459,7 @@ FPUnavailable: li r20,MSR_KERNEL bl transfer_to_handler /* if from kernel, take a trap */ .long KernelFP - .long int_return -#else -/* No FPU on MPC8xx. This exception is not supposed to happen. -*/ - STD_EXCEPTION(0x800, FPUnavailable, UnknownException) -#endif + .long ret_from_except STD_EXCEPTION(0x900, Decrementer, timer_interrupt) STD_EXCEPTION(0xa00, Trap_0a, UnknownException) @@ -664,7 +474,7 @@ SystemCall: rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long DoSyscall - .long int_return + .long ret_from_except /* Single step - not used on 601 */ STD_EXCEPTION(0xd00, SingleStep, SingleStepException) @@ -672,14 +482,12 @@ SystemCall: STD_EXCEPTION(0xe00, Trap_0e, UnknownException) STD_EXCEPTION(0xf00, Trap_0f, UnknownException) -#ifndef CONFIG_8xx /* * Handle TLB miss for instruction on 603/603e. * Note: we get an alternate set of r0 - r3 to use automatically. */ . = 0x1000 InstructionTLBMiss: -#ifdef NO_RELOAD_HTAB /* * r0: stored ctr * r1: linux style pte ( later becomes ppc hardware pte ) @@ -689,14 +497,14 @@ InstructionTLBMiss: mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r2,SPRG3 - lwz r2,PG_TABLES(r2) - tophys(r2,r2,r3) + lwz r2,PGDIR(r2) + tophys(r2,r2) mfspr r3,IMISS rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ lwz r2,0(r2) /* get pmd entry */ rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- InstructionAddressInvalid /* return if no mapping */ - tophys(r2,r2,r1) + tophys(r2,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r1,0(r2) /* get linux-style pte */ /* setup access flags in r3 */ @@ -719,40 +527,11 @@ InstructionTLBMiss: mfspr r3,SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 rfi -#else - mfctr r0 /* Need to save this - CTR can't be touched! */ - mfspr r2,HASH1 /* Get PTE pointer */ - mfspr r3,ICMP /* Partial item compare value */ -00: li r1,8 /* 8 items / bucket */ - mtctr r1 - subi r2,r2,8 /* Preset pointer */ -10: lwzu r1,8(r2) /* Get next PTE */ - cmp 0,r1,r3 /* Found entry yet? */ - bdnzf 2,10b /* Jump back if not, until CTR==0 */ - bne 30f /* Try secondary hash if CTR==0 */ - lwz r1,4(r2) /* Get second word of entry */ -20: mtctr r0 /* Restore CTR */ - mfspr r3,SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 - mfspr r0,IMISS /* Set to update TLB */ - mtspr RPA,r1 - tlbli r0 - rfi /* All done */ -/* Secondary hash */ -30: andi. r1,r3,0x40 /* Already doing secondary hash? */ - bne InstructionAddressInvalid /* Yes - item not in hash table */ - mfspr r2,HASH2 /* Get hash table pointer */ - ori r3,r3,0x40 /* Set secondary hash */ - b 00b /* Try lookup again */ -#endif /* NO_RELOAD_HTAB */ InstructionAddressInvalid: mfspr r3,SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ -#ifdef NO_RELOAD_HTAB + addis r1,r1,0x2000 -#else - addis r1,r1,0x4000 /* Set bit 1 -> PTE not found (in HTAB) */ -#endif /* NO_RELOAD_HTAB */ mtspr DSISR,r1 /* (shouldn't be needed) */ mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ @@ -769,20 +548,12 @@ InstructionAddressInvalid: sync /* Some chip revs have problems here... */ mtmsr r0 b InstructionAccess -#else -/* On the MPC8xx, this is a software emulation interrupt. It occurs - * for all unimplemented and illegal instructions. - */ - STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation) -#endif /* * Handle TLB miss for DATA Load operation on 603/603e */ . = 0x1100 -#ifndef CONFIG_8xx DataLoadTLBMiss: -#ifdef NO_RELOAD_HTAB /* * r0: stored ctr * r1: linux style pte ( later becomes ppc hardware pte ) @@ -792,14 +563,14 @@ DataLoadTLBMiss: mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r2,SPRG3 - lwz r2,PG_TABLES(r2) - tophys(r2,r2,r3) + lwz r2,PGDIR(r2) + tophys(r2,r2) mfspr r3,DMISS rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ lwz r2,0(r2) /* get pmd entry */ rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- DataAddressInvalid /* return if no mapping */ - tophys(r2,r2,r1) + tophys(r2,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r1,0(r2) /* get linux-style pte */ /* setup access flags in r3 */ @@ -823,40 +594,10 @@ DataLoadTLBMiss: mfspr r3,SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 rfi -#else - mfctr r0 /* Need to save this - CTR can't be touched! */ - mfspr r2,HASH1 /* Get PTE pointer */ - mfspr r3,DCMP /* Partial item compare value */ -00: li r1,8 /* 8 items / bucket */ - mtctr r1 - subi r2,r2,8 /* Preset pointer */ -10: lwzu r1,8(r2) /* Get next PTE */ - cmp 0,r1,r3 /* Found entry yet? */ - bdnzf 2,10b /* Jump back if not, until CTR==0 */ - bne 30f /* Try secondary hash if CTR==0 */ - lwz r1,4(r2) /* Get second word of entry */ -20: mtctr r0 /* Restore CTR */ - mfspr r3,SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 - mfspr r0,DMISS /* Set to update TLB */ - mtspr RPA,r1 - tlbld r0 - rfi /* All done */ -/* Secondary hash */ -30: andi. r1,r3,0x40 /* Already doing secondary hash? */ - bne DataAddressInvalid /* Yes - item not in hash table */ - mfspr r2,HASH2 /* Get hash table pointer */ - ori r3,r3,0x40 /* Set secondary hash */ - b 00b /* Try lookup again */ -#endif /* NO_RELOAD_HTAB */ DataAddressInvalid: mfspr r3,SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ -#ifdef NO_RELOAD_HTAB addis r1,r1,0x2000 -#else - addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ -#endif /* NO_RELOAD_HTAB */ mtspr DSISR,r1 mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ @@ -872,79 +613,12 @@ DataAddressInvalid: sync /* Some chip revs have problems here... */ mtmsr r0 b DataAccess -#else -/* - * For the MPC8xx, this is a software tablewalk to load the instruction - * TLB. It is modelled after the example in the Motorola manual. The task - * switch loads the M_TWB register with the pointer to the first level table. - * If we discover there is no second level table (the value is zero), the - * plan was to load that into the TLB, which causes another fault into the - * TLB Error interrupt where we can handle such problems. However, that did - * not work, so if we discover there is no second level table, we restore - * registers and branch to the error exception. We have to use the MD_xxx - * registers for the tablewalk because the equivalent MI_xxx registers - * only perform the attribute functions. - */ -InstructionTLBMiss: - mtspr M_TW, r20 /* Save a couple of working registers */ - mfcr r20 - stw r20, 0(r0) - stw r21, 4(r0) - mfspr r20, SRR0 /* Get effective address of fault */ - mtspr MD_EPN, r20 /* Have to use MD_EPN for walk, MI_EPN can't */ - mfspr r20, M_TWB /* Get level 1 table entry address */ - lwz r21, 0(r20) /* Get the level 1 entry */ - rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ - beq 2f /* If zero, don't try to find a pte */ - - /* We have a pte table, so load the MI_TWC with the attributes - * for this page, which has only bit 31 set. - */ - tophys(r21,r21,0) - ori r21,r21,1 /* Set valid bit */ - mtspr MI_TWC, r21 /* Set page attributes */ - mtspr MD_TWC, r21 /* Load pte table base address */ - mfspr r21, MD_TWC /* ....and get the pte address */ - lwz r21, 0(r21) /* Get the pte */ - - /* Set four subpage valid bits (24, 25, 26, and 27). - * Since we currently run MI_CTR.PPCS = 0, the manual says, - * "If the page size is larger than 4k byte, then all the - * 4 bits should have the same value." - * I don't really know what to do if the page size is 4k Bytes, - * but I know setting them all to 0 does not work, and setting them - * all to 1 does, so that is the way it is right now. - * BTW, these four bits map to the software only bits in the - * linux page table. I used to turn them all of, but now just - * set them all for the hardware. - li r20, 0x00f0 - andc r20, r21, r20 - ori r20, r20, 0x0080 - */ - ori r20, r21, 0x00f0 - - mtspr MI_RPN, r20 /* Update TLB entry */ - - mfspr r20, M_TW /* Restore registers */ - lwz r21, 0(r0) - mtcr r21 - lwz r21, 4(r0) - rfi - -2: mfspr r20, M_TW /* Restore registers */ - lwz r21, 0(r0) - mtcr r21 - lwz r21, 4(r0) - b InstructionAccess -#endif /* CONFIG_8xx */ /* * Handle TLB miss for DATA Store on 603/603e */ . = 0x1200 DataStoreTLBMiss: -#ifndef CONFIG_8xx -#ifdef NO_RELOAD_HTAB /* * r0: stored ctr * r1: linux style pte ( later becomes ppc hardware pte ) @@ -954,14 +628,14 @@ DataStoreTLBMiss: mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r2,SPRG3 - lwz r2,PG_TABLES(r2) - tophys(r2,r2,r3) + lwz r2,PGDIR(r2) + tophys(r2,r2) mfspr r3,DMISS rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ lwz r2,0(r2) /* get pmd entry */ rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- DataAddressInvalid /* return if no mapping */ - tophys(r2,r2,r1) + tophys(r2,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r1,0(r2) /* get linux-style pte */ /* setup access flags in r3 */ @@ -985,171 +659,12 @@ DataStoreTLBMiss: mfspr r3,SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 rfi -#else - mfctr r0 /* Need to save this - CTR can't be touched! */ - mfspr r2,HASH1 /* Get PTE pointer */ - mfspr r3,DCMP /* Partial item compare value */ -00: li r1,8 /* 8 items / bucket */ - mtctr r1 - subi r2,r2,8 /* Preset pointer */ -10: lwzu r1,8(r2) /* Get next PTE */ - cmp 0,r1,r3 /* Found entry yet? */ - bdnzf 2,10b /* Jump back if not, until CTR==0 */ - bne 30f /* Try secondary hash if CTR==0 */ - lwz r1,4(r2) /* Get second word of entry */ -20: mtctr r0 /* Restore CTR */ - mfspr r3,SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 - mfspr r0,DMISS /* Set to update TLB */ - mtspr RPA,r1 - tlbld r0 - rfi /* All done */ -/* Secondary hash */ -30: andi. r1,r3,0x40 /* Already doing secondary hash? */ - bne DataAddressInvalid /* Yes - item not in hash table */ - mfspr r2,HASH2 /* Get hash table pointer */ - ori r3,r3,0x40 /* Set secondary hash */ - b 00b /* Try lookup again */ -#endif /* NO_RELOAD_HTAB */ -#else /* CONFIG_8xx */ - mtspr M_TW, r20 /* Save a couple of working registers */ - mfcr r20 - stw r20, 0(r0) - stw r21, 4(r0) - mfspr r20, M_TWB /* Get level 1 table entry address */ - lwz r21, 0(r20) /* Get the level 1 entry */ - rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ - beq 2f /* If zero, don't try to find a pte */ - - /* We have a pte table, so load fetch the pte from the table. - */ - tophys(r21, r21, 0) - ori r21, r21, 1 /* Set valid bit in physical L2 page */ - mtspr MD_TWC, r21 /* Load pte table base address */ - mfspr r21, MD_TWC /* ....and get the pte address */ - lwz r21, 0(r21) /* Get the pte */ - - /* Set four subpage valid bits (24, 25, 26, and 27). - * Since we currently run MD_CTR.PPCS = 0, the manual says, - * "If the page size is larger than 4k byte, then all the - * 4 bits should have the same value." - * I don't really know what to do if the page size is 4k Bytes, - * but I know setting them all to 0 does not work, and setting them - * all to 1 does, so that is the way it is right now. - * BTW, these four bits map to the software only bits in the - * linux page table. I used to turn them all of, but now just - * set them all for the hardware. - li r20, 0x00f0 - andc r20, r21, r20 - ori r20, r20, 0x0080 - */ - ori r20, r21, 0x00f0 - - mtspr MD_RPN, r20 /* Update TLB entry */ - - mfspr r20, M_TW /* Restore registers */ - lwz r21, 0(r0) - mtcr r21 - lwz r21, 4(r0) - rfi - -2: mfspr r20, M_TW /* Restore registers */ - lwz r21, 0(r0) - mtcr r21 - lwz r21, 4(r0) - b DataAccess -#endif /* CONFIG_8xx */ -#ifndef CONFIG_8xx /* Instruction address breakpoint exception (on 603/604) */ STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) -#else - -/* This is an instruction TLB error on the MPC8xx. This could be due - * to many reasons, such as executing guarded memory or illegal instruction - * addresses. There is nothing to do but handle a big time error fault. - */ - . = 0x1300 -InstructionTLBError: - b InstructionAccess -#endif /* System management exception (603?) */ -#ifndef CONFIG_8xx STD_EXCEPTION(0x1400, Trap_14, UnknownException) -#else - -/* This is the data TLB error on the MPC8xx. This could be due to - * many reasons, including a dirty update to a pte. We can catch that - * one here, but anything else is an error. First, we track down the - * Linux pte. If it is valid, write access is allowed, but the - * page dirty bit is not set, we will set it and reload the TLB. For - * any other case, we bail out to a higher level function that can - * handle it. - */ - . = 0x1400 -DataTLBError: - mtspr M_TW, r20 /* Save a couple of working registers */ - mfcr r20 - stw r20, 0(r0) - stw r21, 4(r0) - - /* First, make sure this was a store operation. - */ - mfspr r20, DSISR - andis. r21, r20, 0x0200 /* If set, indicates store op */ - beq 2f - - mfspr r20, M_TWB /* Get level 1 table entry address */ - lwz r21, 0(r20) /* Get the level 1 entry */ - rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ - beq 2f /* If zero, bail */ - - /* We have a pte table, so fetch the pte from the table. - */ - tophys(r21, r21, 0) - ori r21, r21, 1 /* Set valid bit in physical L2 page */ - mtspr MD_TWC, r21 /* Load pte table base address */ - mfspr r21, MD_TWC /* ....and get the pte address */ - lwz r21, 0(r21) /* Get the pte */ - - andi. r20, r21, _PAGE_RW /* Is it writeable? */ - beq 2f /* Bail out if not */ - - ori r21, r21, _PAGE_DIRTY /* Update changed bit */ - mfspr r20, MD_TWC /* Get pte address again */ - stw r21, 0(r20) /* and update pte in table */ - - /* Set four subpage valid bits (24, 25, 26, and 27). - * Since we currently run MD_CTR.PPCS = 0, the manual says, - * "If the page size is larger than 4k byte, then all the - * 4 bits should have the same value." - * I don't really know what to do if the page size is 4k Bytes, - * but I know setting them all to 0 does not work, and setting them - * all to 1 does, so that is the way it is right now. - * BTW, these four bits map to the software only bits in the - * linux page table. I used to turn them all of, but now just - * set them all for the hardware. - li r20, 0x00f0 - andc r20, r21, r20 - ori r20, r20, 0x0080 - */ - ori r20, r21, 0x00f0 - - mtspr MD_RPN, r20 /* Update TLB entry */ - - mfspr r20, M_TW /* Restore registers */ - lwz r21, 0(r0) - mtcr r21 - lwz r21, 4(r0) - rfi -2: - mfspr r20, M_TW /* Restore registers */ - lwz r21, 0(r0) - mtcr r21 - lwz r21, 4(r0) - b DataAccess -#endif /* CONFIG_8xx */ STD_EXCEPTION(0x1500, Trap_15, UnknownException) STD_EXCEPTION(0x1600, Trap_16, UnknownException) @@ -1158,16 +673,11 @@ DataTLBError: STD_EXCEPTION(0x1900, Trap_19, UnknownException) STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) -/* On the MPC8xx, these next four traps are used for development - * support of breakpoints and such. Someday I will get around to - * using them. - */ STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) -#ifndef CONFIG_8xx /* Run mode exception */ STD_EXCEPTION(0x2000, RunMode, RunModeException) @@ -1188,9 +698,6 @@ DataTLBError: STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) . = 0x3000 -#else - . = 0x2000 -#endif /* * This code finishes saving the registers to the exception frame @@ -1208,12 +715,12 @@ transfer_to_handler: SAVE_8GPRS(12, r21) SAVE_8GPRS(24, r21) andi. r23,r23,MSR_PR - mfspr r23,SPRG3 /* if from user, fix up tss.regs */ + mfspr r23,SPRG3 /* if from user, fix up THREAD.regs */ beq 2f addi r24,r1,STACK_FRAME_OVERHEAD stw r24,PT_REGS(r23) -2: addi r2,r23,-TSS /* set r2 to current */ - tovirt(r2,r2,r23) +2: addi r2,r23,-THREAD /* set r2 to current */ + tovirt(r2,r2) mflr r23 andi. r24,r23,0x3f00 /* get vector offset */ stw r24,TRAP(r21) @@ -1252,300 +759,6 @@ stack_ovf: SYNC rfi -#ifndef CONFIG_8xx -/* - * Load a PTE into the hash table, if possible. - * The address is in r3, and r4 contains access flags: - * _PAGE_USER (4) if a user-mode access, ored with - * _PAGE_RW (2) if a write. r20 contains DSISR or SRR1, - * so bit 1 (0x40000000) is set if the exception was due - * to no matching PTE being found in the hash table. - * r5 contains the physical address of the current task's tss. - * - * Returns to the caller if the access is illegal or there is no - * mapping for the address. Otherwise it places an appropriate PTE - * in the hash table and returns from the exception. - * Uses r0, r2 - r6, ctr, lr. - * - * For speed, 4 of the instructions get patched once the size and - * physical address of the hash table are known. These definitions - * of Hash_base and Hash_bits below are just an example. - */ -Hash_base = 0x180000 -Hash_bits = 12 /* e.g. 256kB hash table */ -Hash_msk = (((1 << Hash_bits) - 1) * 64) - - .globl hash_page -hash_page: -#ifdef __SMP__ - eieio - lis r2,hash_table_lock@h - ori r2,r2,hash_table_lock@l - tophys(r2,r2,r6) - lis r6,100000000@h - mtctr r6 - lwz r0,PROCESSOR-TSS(r5) - or r0,r0,r6 -10: lwarx r6,0,r2 - cmpi 0,r6,0 - bne- 12f - stwcx. r0,0,r2 - beq+ 11f -12: cmpw r6,r0 - bdnzf 2,10b - tw 31,31,31 -11: eieio -#endif - /* Get PTE (linux-style) and check access */ - lwz r5,PG_TABLES(r5) - tophys(r5,r5,r2) /* convert to phys addr */ - rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ - lwz r5,0(r5) /* get pmd entry */ - rlwinm. r5,r5,0,0,19 /* extract address of pte page */ -#ifdef __SMP__ - beq- hash_page_out /* return if no mapping */ -#else - /* XXX it seems like the 601 will give a machine fault on the - rfi if its alignment is wrong (bottom 4 bits of address are - 8 or 0xc) and we have had a not-taken conditional branch - to the address following the rfi. */ - beqlr- -#endif - tophys(r2,r5,r2) - rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ - lwz r6,0(r2) /* get linux-style pte */ - ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ - andc. r0,r4,r6 /* check access & ~permission */ -#ifdef __SMP__ - bne- hash_page_out /* return if access not permitted */ -#else - bnelr- -#endif - - ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ - rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ - rlwimi r5,r4,7,22,22 /* _PAGE_RW -> _PAGE_HWWRITE */ - or r6,r6,r5 - stw r6,0(r2) /* update PTE (accessed/dirty bits) */ - - /* Convert linux-style PTE to low word of PPC-style PTE */ -#ifdef CONFIG_PPC64 - /* clear the high 32 bits just in case */ - clrldi r6,r6,32 - clrldi r4,r4,32 -#endif /* CONFIG_PPC64 */ - rlwinm r4,r6,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ - rlwimi r6,r6,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ - ori r4,r4,0xe04 /* clear out reserved bits */ - andc r6,r6,r4 /* PP=2 or 0, when _PAGE_HWWRITE */ - - /* Construct the high word of the PPC-style PTE */ - mfsrin r5,r3 /* get segment reg for segment */ -#ifdef CONFIG_PPC64 - sldi r5,r5,12 -#else /* CONFIG_PPC64 */ - rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ -#endif /* CONFIG_PPC64 */ - -#ifndef __SMP__ /* do this later for SMP */ -#ifdef CONFIG_PPC64 - ori r5,r5,1 /* set V (valid) bit */ -#else /* CONFIG_PPC64 */ - oris r5,r5,0x8000 /* set V (valid) bit */ -#endif /* CONFIG_PPC64 */ -#endif - -#ifdef CONFIG_PPC64 -/* XXX: does this insert the api correctly? -- Cort */ - rlwimi r5,r3,17,21,25 /* put in API (abbrev page index) */ -#else /* CONFIG_PPC64 */ - rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ -#endif /* CONFIG_PPC64 */ - /* Get the address of the primary PTE group in the hash table */ - .globl hash_page_patch_A -hash_page_patch_A: - lis r4,Hash_base@h /* base address of hash table */ -#ifdef CONFIG_PPC64 - /* just in case */ - clrldi r4,r4,32 -#endif - rlwimi r4,r5,32-1,26-Hash_bits,25 /* (VSID & hash_mask) << 6 */ - rlwinm r0,r3,32-6,26-Hash_bits,25 /* (PI & hash_mask) << 6 */ - xor r4,r4,r0 /* make primary hash */ - - /* See whether it was a PTE not found exception or a - protection violation. */ - andis. r0,r20,0x4000 - li r2,8 /* PTEs/group */ - bne 10f /* no PTE: go look for an empty slot */ - tlbie r3 /* invalidate TLB entry */ - - /* Search the primary PTEG for a PTE whose 1st word matches r5 */ - mtctr r2 - addi r3,r4,-8 -1: lwzu r0,8(r3) /* get next PTE */ - cmp 0,r0,r5 - bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ - beq+ found_slot - - /* Search the secondary PTEG for a matching PTE */ - ori r5,r5,0x40 /* set H (secondary hash) bit */ - .globl hash_page_patch_B -hash_page_patch_B: - xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ - xori r3,r3,0xffc0 - addi r3,r3,-8 - mtctr r2 -2: lwzu r0,8(r3) - cmp 0,r0,r5 - bdnzf 2,2b - beq+ found_slot - xori r5,r5,0x40 /* clear H bit again */ - - /* Search the primary PTEG for an empty slot */ -10: mtctr r2 - addi r3,r4,-8 /* search primary PTEG */ -1: lwzu r0,8(r3) /* get next PTE */ - srwi. r0,r0,31 /* only want to check valid bit */ - bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ - beq+ found_empty - - /* Search the secondary PTEG for an empty slot */ - ori r5,r5,0x40 /* set H (secondary hash) bit */ - .globl hash_page_patch_C -hash_page_patch_C: - xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ - xori r3,r3,0xffc0 - addi r3,r3,-8 - mtctr r2 -2: lwzu r0,8(r3) - srwi. r0,r0,31 /* only want to check valid bit */ - bdnzf 2,2b - beq+ found_empty - - /* - * Choose an arbitrary slot in the primary PTEG to overwrite. - * Since both the primary and secondary PTEGs are full, and we - * have no information that the PTEs in the primary PTEG are - * more important or useful than those in the secondary PTEG, - * and we know there is a definite (although small) speed - * advantage to putting the PTE in the primary PTEG, we always - * put the PTE in the primary PTEG. - */ - xori r5,r5,0x40 /* clear H bit again */ - lwz r2,next_slot@l(0) - addi r2,r2,8 - andi. r2,r2,0x38 - stw r2,next_slot@l(0) - add r3,r4,r2 -11: - /* update counter of evicted pages */ - lis r2,htab_evicts@h - ori r2,r2,htab_evicts@l - tophys(r2,r2,r4) - lwz r4,0(r2) - addi r4,r4,1 - stw r4,0(r2) - -#ifndef __SMP__ - /* Store PTE in PTEG */ -found_empty: - stw r5,0(r3) -found_slot: - stw r6,4(r3) - sync - -#else /* __SMP__ */ -/* - * Between the tlbie above and updating the hash table entry below, - * another CPU could read the hash table entry and put it in its TLB. - * There are 3 cases: - * 1. using an empty slot - * 2. updating an earlier entry to change permissions (i.e. enable write) - * 3. taking over the PTE for an unrelated address - * - * In each case it doesn't really matter if the other CPUs have the old - * PTE in their TLB. So we don't need to bother with another tlbie here, - * which is convenient as we've overwritten the register that had the - * address. :-) The tlbie above is mainly to make sure that this CPU comes - * and gets the new PTE from the hash table. - * - * We do however have to make sure that the PTE is never in an invalid - * state with the V bit set. - */ -found_empty: -found_slot: - stw r5,0(r3) /* clear V (valid) bit in PTE */ - sync - tlbsync - sync - stw r6,4(r3) /* put in correct RPN, WIMG, PP bits */ - sync - oris r5,r5,0x8000 - stw r5,0(r3) /* finally set V bit in PTE */ -#endif /* __SMP__ */ - -/* - * Update the hash table miss count. We only want misses here - * that _are_ valid addresses and have a pte otherwise we don't - * count it as a reload. do_page_fault() takes care of bad addrs - * and entries that need linux-style pte's created. - * - * safe to use r2 here since we're not using it as current yet - * update the htab misses count - * -- Cort - */ - lis r2,htab_reloads@h - ori r2,r2,htab_reloads@l - tophys(r2,r2,r3) - lwz r3,0(r2) - addi r3,r3,1 - stw r3,0(r2) - -#ifdef __SMP__ - lis r2,hash_table_lock@ha - tophys(r2,r2,r6) - li r0,0 - stw r0,hash_table_lock@l(r2) - eieio -#endif - - /* Return from the exception */ - lwz r3,_CCR(r21) - lwz r4,_LINK(r21) - lwz r5,_CTR(r21) - mtcrf 0xff,r3 - mtlr r4 - mtctr r5 - REST_GPR(0, r21) - REST_2GPRS(1, r21) - REST_4GPRS(3, r21) - /* we haven't used xer */ - mtspr SRR1,r23 - mtspr SRR0,r22 - REST_GPR(20, r21) - REST_2GPRS(22, r21) - lwz r21,GPR21(r21) - rfi - -#ifdef __SMP__ -hash_page_out: - lis r2,hash_table_lock@ha - tophys(r2,r2,r6) - li r0,0 - stw r0,hash_table_lock@l(r2) - eieio - blr - - .globl hash_table_lock -hash_table_lock: - .long 0 -#endif - -next_slot: - .long 0 - -load_up_fpu: /* * Disable FP for the task which had the FPU previously, * and save its floating-point registers in its thread_struct. @@ -1553,6 +766,7 @@ load_up_fpu: * On SMP we know the fpu is free, since we give it up every * switch. -- Cort */ +load_up_fpu: mfmsr r5 ori r5,r5,MSR_FP SYNC @@ -1564,21 +778,17 @@ load_up_fpu: * to another. Instead we call giveup_fpu in switch_to. */ #ifndef __SMP__ -#ifndef CONFIG_APUS - lis r6,-KERNELBASE@h -#else - lis r6,CYBERBASEp@h - lwz r6,0(r6) -#endif + lis r6,0 /* get __pa constant */ + tophys(r6,r6) addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) cmpi 0,r4,0 beq 1f add r4,r4,r6 - addi r4,r4,TSS /* want TSS of last_task_used_math */ + addi r4,r4,THREAD /* want THREAD of last_task_used_math */ SAVE_32FPRS(0, r4) mffs fr0 - stfd fr0,TSS_FPSCR-4(r4) + stfd fr0,THREAD_FPSCR-4(r4) lwz r5,PT_REGS(r4) add r5,r5,r6 lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) @@ -1589,12 +799,12 @@ load_up_fpu: #endif /* __SMP__ */ /* enable use of FP after return */ ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 - mfspr r5,SPRG3 /* current task's TSS (phys) */ - lfd fr0,TSS_FPSCR-4(r5) + mfspr r5,SPRG3 /* current task's THREAD (phys) */ + lfd fr0,THREAD_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) #ifndef __SMP__ - subi r4,r5,TSS + subi r4,r5,THREAD sub r4,r4,r6 stw r4,last_task_used_math@l(r3) #endif /* __SMP__ */ @@ -1627,7 +837,7 @@ KernelFP: mr r4,r2 /* current */ lwz r5,_NIP(r1) bl printk - b int_return + b ret_from_except 86: .string "floating point used in kernel (task=%p, pc=%x)\n" .align 4 @@ -1646,12 +856,12 @@ giveup_fpu: SYNC cmpi 0,r3,0 beqlr- /* if no previous owner, done */ - addi r3,r3,TSS /* want TSS of task */ + addi r3,r3,THREAD /* want THREAD of task */ lwz r5,PT_REGS(r3) cmpi 0,r5,0 SAVE_32FPRS(0, r3) mffs fr0 - stfd fr0,TSS_FPSCR-4(r3) + stfd fr0,THREAD_FPSCR-4(r3) beq 1f lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) li r3,MSR_FP|MSR_FE0|MSR_FE1 @@ -1665,12 +875,6 @@ giveup_fpu: #endif /* __SMP__ */ blr -#else /* CONFIG_8xx */ - .globl giveup_fpu -giveup_fpu: - blr -#endif /* CONFIG_8xx */ - /* * This code is jumped to from the startup code to copy * the kernel image to physical address 0. @@ -1721,20 +925,70 @@ copy_and_flush: blr #ifdef CONFIG_APUS - /* On APUS the first 0x4000 bytes of the kernel will be mapped - * at a different physical address than the rest. For this - * reason, the exception code cannot use relative branches to - * access the code below. - */ +/* + * On APUS the physical base address of the kernel is not known at compile + * time, which means the __pa/__va constants used are incorect. In the + * __init section is recorded the virtual addresses of instructions using + * these constants, so all that has to be done is fix these before + * continuing the kernel boot. + * + * r4 = The physical address of the kernel base. + */ +fix_mem_constants: + mr r10,r4 + addis r10,r10,-KERNELBASE@h /* virt_to_phys constant */ + neg r11,r10 /* phys_to_virt constant */ + + lis r12,__vtop_table_begin@h + ori r12,r12,__vtop_table_begin@l + add r12,r12,r10 /* table begin phys address */ + lis r13,__vtop_table_end@h + ori r13,r13,__vtop_table_end@l + add r13,r13,r10 /* table end phys address */ + subi r12,r12,4 + subi r13,r13,4 +1: lwzu r14,4(r12) /* virt address of instruction */ + add r14,r14,r10 /* phys address of instruction */ + lwz r15,0(r14) /* instruction, now insert top */ + rlwimi r15,r10,16,16,31 /* half of vp const in low half */ + stw r15,0(r14) /* of instruction and restore. */ + dcbst r0,r14 /* write it to memory */ + sync + icbi r0,r14 /* flush the icache line */ + cmpw r12,r13 + bne 1b + + lis r12,__ptov_table_begin@h + ori r12,r12,__ptov_table_begin@l + add r12,r12,r10 /* table begin phys address */ + lis r13,__ptov_table_end@h + ori r13,r13,__ptov_table_end@l + add r13,r13,r10 /* table end phys address */ + subi r12,r12,4 + subi r13,r13,4 +1: lwzu r14,4(r12) /* virt address of instruction */ + add r14,r14,r10 /* phys address of instruction */ + lwz r15,0(r14) /* instruction, now insert top */ + rlwimi r15,r11,16,16,31 /* half of pv const in low half*/ + stw r15,0(r14) /* of instruction and restore. */ + dcbst r0,r14 /* write it to memory */ + sync + icbi r0,r14 /* flush the icache line */ + cmpw r12,r13 + bne 1b + + isync /* No speculative loading until now */ + blr + + /* On APUS the first 0x4000 bytes of the kernel will be mapped + * at a different physical address than the rest. For this + * reason, the exception code cannot use relative branches to + * access the code below. + */ . = 0x4000 #endif #ifdef CONFIG_SMP - .globl __secondary_start_psurge -__secondary_start_psurge: - li r24,1 /* cpu # */ - b __secondary_start - .globl __secondary_hold __secondary_hold: /* tell the master we're here */ @@ -1752,20 +1006,63 @@ __secondary_hold: /* our cpu # was at addr 0 - go */ lis r5,__secondary_start@h ori r5,r5,__secondary_start@l - tophys(r5,r5,r4) + tophys(r5,r5) mtlr r5 mr r24,r3 /* cpu # */ blr + + .globl __secondary_start_psurge +__secondary_start_psurge: + li r24,1 /* cpu # */ + /* we come in here with IR=0 and DR=1, and DBAT 0 + set to map the 0xf0000000 - 0xffffffff region */ + mfmsr r0 + rlwinm r0,r0,0,28,26 /* clear DR (0x10) */ + sync + mtmsr r0 + isync + + .globl __secondary_start +__secondary_start: + bl enable_caches + + /* get current */ + lis r2,current_set@h + ori r2,r2,current_set@l + tophys(r2,r2) + slwi r24,r24,2 /* get current_set[cpu#] */ + lwzx r2,r2,r24 + + /* stack */ + addi r1,r2,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD + li r0,0 + tophys(r3,r1) + stw r0,0(r3) + + /* load up the MMU */ + bl load_up_mmu + + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* phys address of our thread_struct */ + mtspr SPRG3,r4 + li r3,0 + mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ + + /* enable MMU and jump to start_secondary */ + li r4,MSR_KERNEL + lis r3,start_secondary@h + ori r3,r3,start_secondary@l + mtspr SRR0,r3 + mtspr SRR1,r4 + rfi + #endif /* CONFIG_SMP */ - + /* - * This is where the main kernel code starts. + * Enable caches and 604-specific features if necessary. */ -start_here: -#ifndef CONFIG_8xx - /* - * Enable caches and 604-specific features if necessary. - */ +enable_caches: mfspr r9,PVR rlwinm r9,r9,16,16,31 cmpi 0,r9,1 @@ -1793,26 +1090,57 @@ start_here: bne 2,5f ori r11,r11,HID0_BTCD 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ -4: -#endif /* CONFIG_8xx */ -#ifdef __SMP__ - /* if we're the second cpu stack and r2 are different - * and we want to not clear the bss -- Cort */ - lis r5,first_cpu_booted@h - ori r5,r5,first_cpu_booted@l - lwz r5,0(r5) - cmpi 0,r5,0 - beq 99f +4: blr + +/* + * Load stuff into the MMU. Intended to be called with + * IR=0 and DR=0. + */ +load_up_mmu: + /* Load the SDR1 register (hash table base & size) */ + lis r6,_SDR1@ha + tophys(r6,r6) +#ifdef CONFIG_PPC64 + ld r6,_SDR1@l(r6) + mtspr SDR1,r6 + /* clear the v bit in the ASR so we can + * behave as if we have segment registers + * -- Cort + */ + clrldi r6,r6,63 + mtasr r6 +#else + lwz r6,_SDR1@l(r6) + mtspr SDR1,r6 +#endif /* CONFIG_PPC64 */ + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b +/* Load the BAT registers with the values set up by MMU_init. + MMU_init takes care of whether we're on a 601 or not. */ + mfpvr r3 + srwi r3,r3,16 + cmpwi r3,1 + lis r3,BATS@ha + addi r3,r3,BATS@l + tophys(r3,r3) + LOAD_BAT(0,r3,r4,r5) + LOAD_BAT(1,r3,r4,r5) + LOAD_BAT(2,r3,r4,r5) + LOAD_BAT(3,r3,r4,r5) + blr + +/* + * This is where the main kernel code starts. + */ +start_here: + bl enable_caches - /* get current */ - lis r2,current_set@h - ori r2,r2,current_set@l - slwi r24,r24,2 /* cpu # to current_set[cpu#] */ - add r2,r2,r24 - lwz r2,0(r2) - b 10f -99: -#endif /* __SMP__ */ /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l @@ -1831,9 +1159,6 @@ start_here: 3: stwu r0,4(r8) bdnz 3b 2: -#ifdef __SMP__ -10: -#endif /* __SMP__ */ /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 @@ -1848,33 +1173,15 @@ start_here: mr r7,r27 bl identify_machine bl MMU_init + /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers * and change to using our exception vectors. - * On the 8xx, all we have to do is invalidate the TLB to clear - * the old 8M byte TLB mappings and load the page table base register. */ -#ifndef CONFIG_8xx - lis r6,_SDR1@ha -#ifdef CONFIG_PPC64 - ld r6,_SDR1@l(r6) -#else - lwz r6,_SDR1@l(r6) -#endif -#else - /* The right way to do this would be to track it down through - * init's TSS like the context switch code does, but this is - * easier......until someone changes init's static structures. - */ - lis r6, swapper_pg_dir@h - tophys(r6,r6,0) - ori r6, r6, swapper_pg_dir@l - mtspr M_TWB, r6 -#endif lis r4,2f@h ori r4,r4,2f@l - tophys(r4,r4,r3) + tophys(r4,r4) li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) mtspr SRR0,r4 mtspr SRR1,r3 @@ -1888,48 +1195,12 @@ start_here: tlbsync /* ... on all CPUs */ sync #endif -#ifndef CONFIG_8xx - mtspr SDR1,r6 -#ifdef CONFIG_PPC64 - /* clear the v bit in the ASR so we can - * behave as if we have segment registers - * -- Cort - */ - clrldi r6,r6,63 - mtasr r6 -#endif /* CONFIG_PPC64 */ - li r0,16 /* load up segment register values */ - mtctr r0 /* for context 0 */ - lis r3,0x2000 /* Ku = 1, VSID = 0 */ - li r4,0 -3: mtsrin r3,r4 - addi r3,r3,1 /* increment VSID */ - addis r4,r4,0x1000 /* address of next segment */ - bdnz 3b -/* Load the BAT registers with the values set up by MMU_init. - MMU_init takes care of whether we're on a 601 or not. */ - mfpvr r3 - srwi r3,r3,16 - cmpwi r3,1 - lis r3,BATS@ha - addi r3,r3,BATS@l - tophys(r3,r3,r4) -#ifdef CONFIG_PPC64 - LOAD_BAT(0,0,r3,r4,r5) - LOAD_BAT(1,32,r3,r4,r5) - LOAD_BAT(2,64,r3,r4,r5) - LOAD_BAT(3,96,r3,r4,r5) -#else /* CONFIG_PPC64 */ - LOAD_BAT(0,0,r3,r4,r5) - LOAD_BAT(1,16,r3,r4,r5) - LOAD_BAT(2,32,r3,r4,r5) - LOAD_BAT(3,48,r3,r4,r5) -#endif /* CONFIG_PPC64 */ -#endif /* CONFIG_8xx */ + bl load_up_mmu + /* Set up for using our exception vectors */ - /* ptr to phys current tss */ - tophys(r4,r2,r4) - addi r4,r4,TSS /* init task's TSS */ + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* init task's THREAD */ mtspr SPRG3,r4 li r3,0 mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ @@ -1937,383 +1208,11 @@ start_here: li r4,MSR_KERNEL lis r3,start_kernel@h ori r3,r3,start_kernel@l -#ifdef __SMP__ - /* the second time through here we go to - * start_secondary(). -- Cort - */ - lis r5,first_cpu_booted@h - ori r5,r5,first_cpu_booted@l - tophys(r5,r5,r3) - lwz r5,0(r5) - cmpi 0,r5,0 - beq 10f - lis r3,start_secondary@h - ori r3,r3,start_secondary@l -10: -#endif /* __SMP__ */ mtspr SRR0,r3 mtspr SRR1,r4 rfi /* enable MMU and jump to start_kernel */ /* - * Handle a system call. - */ -DoSyscall: - stw r0,TSS+LAST_SYSCALL(r2) - lwz r11,_CCR(r1) /* Clear SO bit in CR */ - lis r10,0x1000 - andc r11,r11,r10 - stw r11,_CCR(r1) -#ifdef SHOW_SYSCALLS -#ifdef SHOW_SYSCALLS_TASK - lis r31,show_syscalls_task@ha - lwz r31,show_syscalls_task@l(r31) - cmp 0,r2,r31 - bne 1f -#endif - lis r3,7f@ha - addi r3,r3,7f@l - lwz r4,GPR0(r1) - lwz r5,GPR3(r1) - lwz r6,GPR4(r1) - lwz r7,GPR5(r1) - lwz r8,GPR6(r1) - mr r9,r2 - bl printk - lwz r0,GPR0(r1) - lwz r3,GPR3(r1) - lwz r4,GPR4(r1) - lwz r5,GPR5(r1) - lwz r6,GPR6(r1) - lwz r7,GPR7(r1) - lwz r8,GPR8(r1) -1: -#endif /* SHOW_SYSCALLS */ - cmpi 0,r0,0x7777 /* Special case for 'sys_sigreturn' */ - beq- 10f - lwz r10,TASK_FLAGS(r2) - andi. r10,r10,PF_TRACESYS - bne- 50f - cmpli 0,r0,NR_syscalls - bge- 66f - lis r10,sys_call_table@h - ori r10,r10,sys_call_table@l - slwi r0,r0,2 - lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ - cmpi 0,r10,0 - beq- 66f - mtlr r10 - addi r9,r1,STACK_FRAME_OVERHEAD - blrl /* Call handler */ - .globl syscall_ret_1 -syscall_ret_1: -20: stw r3,RESULT(r1) /* Save result */ -#ifdef SHOW_SYSCALLS -#ifdef SHOW_SYSCALLS_TASK - cmp 0,r2,r31 - bne 91f -#endif - mr r4,r3 - lis r3,79f@ha - addi r3,r3,79f@l - bl printk - lwz r3,RESULT(r1) -91: -#endif - li r10,-_LAST_ERRNO - cmpl 0,r3,r10 - blt 30f - neg r3,r3 - cmpi 0,r3,ERESTARTNOHAND - bne 22f - li r3,EINTR -22: lwz r10,_CCR(r1) /* Set SO bit in CR */ - oris r10,r10,0x1000 - stw r10,_CCR(r1) -30: stw r3,GPR3(r1) /* Update return value */ - b int_return -66: li r3,ENOSYS - b 22b -/* sys_sigreturn */ -10: addi r3,r1,STACK_FRAME_OVERHEAD - bl sys_sigreturn - cmpi 0,r3,0 /* Check for restarted system call */ - bge int_return - b 20b -/* Traced system call support */ -50: bl syscall_trace - lwz r0,GPR0(r1) /* Restore original registers */ - lwz r3,GPR3(r1) - lwz r4,GPR4(r1) - lwz r5,GPR5(r1) - lwz r6,GPR6(r1) - lwz r7,GPR7(r1) - lwz r8,GPR8(r1) - lwz r9,GPR9(r1) - cmpli 0,r0,NR_syscalls - bge- 66f - lis r10,sys_call_table@h - ori r10,r10,sys_call_table@l - slwi r0,r0,2 - lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ - cmpi 0,r10,0 - beq- 66f - mtlr r10 - addi r9,r1,STACK_FRAME_OVERHEAD - blrl /* Call handler */ - .globl syscall_ret_2 -syscall_ret_2: - stw r3,RESULT(r1) /* Save result */ - stw r3,GPR0(r1) /* temporary gross hack to make strace work */ - li r10,-_LAST_ERRNO - cmpl 0,r3,r10 - blt 60f - neg r3,r3 - cmpi 0,r3,ERESTARTNOHAND - bne 52f - li r3,EINTR -52: lwz r10,_CCR(r1) /* Set SO bit in CR */ - oris r10,r10,0x1000 - stw r10,_CCR(r1) -60: stw r3,GPR3(r1) /* Update return value */ - bl syscall_trace - b int_return -66: li r3,ENOSYS - b 52b -#ifdef SHOW_SYSCALLS -7: .string "syscall %d(%x, %x, %x, %x), current=%p\n" -79: .string " -> %x\n" - .align 2 -#endif - -/* - * This routine switches between two different tasks. The process - * state of one is saved on its kernel stack. Then the state - * of the other is restored from its kernel stack. The memory - * management hardware is updated to the second process's state. - * Finally, we can return to the second process, via int_return. - * On entry, r3 points to the TSS for the current task, r4 - * points to the TSS for the new task, and r5 contains the - * MMU context number for the new task. - * - * Note: there are two ways to get to the "going out" portion - * of this code; either by coming in via the entry (_switch) - * or via "fork" which must set up an environment equivalent - * to the "_switch" path. If you change this (or in particular, the - * SAVE_REGS macro), you'll have to change the fork code also. - * - * The code which creates the new task context is in 'copy_thread' - * in arch/ppc/kernel/process.c - * - * The MPC8xx has something that currently happens "automagically." - * Unshared user space address translations are subject to ASID (context) - * match. During each task switch, the ASID is incremented. We can - * guarantee (I hope :-) that no entries currently match this ASID - * because every task will cause at least a TLB entry to be loaded for - * the first instruction and data access, plus the kernel running will - * have displaced several more TLBs. The MMU contains 32 entries for - * each TLB, and there are 16 contexts, so we just need to make sure - * two pages get replaced for every context switch, which currently - * happens. There are other TLB management techniques that I will - * eventually implement, but this is the easiest for now. -- Dan - */ -_GLOBAL(_switch) - stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1) - stw r0,GPR0(r1) - lwz r0,0(r1) - stw r0,GPR1(r1) - /* r3-r13 are caller saved -- Cort */ - SAVE_GPR(2, r1) - SAVE_8GPRS(14, r1) - SAVE_10GPRS(22, r1) - mflr r20 /* Return to switch caller */ - mfmsr r22 - li r0,MSR_FP /* Disable floating-point */ - andc r22,r22,r0 - stw r20,_NIP(r1) - stw r22,_MSR(r1) - stw r20,_LINK(r1) - mfcr r20 - mfctr r22 - mfspr r23,XER - stw r20,_CCR(r1) - stw r22,_CTR(r1) - stw r23,_XER(r1) - li r0,0x0ff0 - stw r0,TRAP(r1) - stw r1,KSP(r3) /* Set old stack pointer */ - sync - tophys(r0,r4,r3) - mtspr SPRG3,r0 /* Update current TSS phys addr */ - SYNC - lwz r1,KSP(r4) /* Load new stack pointer */ - /* save the old current 'last' for return value */ - mr r3,r2 - addi r2,r4,-TSS /* Update current */ -#ifndef CONFIG_8xx - /* Set up segment registers for new task */ - rlwinm r5,r5,4,8,27 /* VSID = context << 4 */ - addis r5,r5,0x6000 /* Set Ks, Ku bits */ - li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ - mtctr r0 - li r9,0 -3: mtsrin r5,r9 - addi r5,r5,1 /* next VSID */ - addis r9,r9,0x1000 /* address of next segment */ - bdnz 3b -#else -/* On the MPC8xx, we place the physical address of the new task - * page directory loaded into the MMU base register, and set the - * ASID compare register with the new "context". - */ - lwz r9,MM-TSS(r4) /* Get virtual address of mm */ - lwz r9,PGD(r9) /* get new->mm->pgd */ - addis r9,r9,-KERNELBASE@h /* convert to phys addr */ - mtspr M_TWB, r9 /* Update MMU base address */ - mtspr M_CASID, r5 /* Update context */ - tlbia -#endif - SYNC -2: lwz r9,_MSR(r1) /* Returning to user mode? */ - andi. r9,r9,MSR_PR - beq+ 10f /* if not, don't adjust kernel stack */ -8: addi r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD /* size of frame */ - stw r4,TSS+KSP(r2) /* save kernel stack pointer */ - tophys(r9,r1,r9) - mtspr SPRG2,r9 /* phys exception stack pointer */ -10: lwz r2,_CTR(r1) - lwz r0,_LINK(r1) - mtctr r2 - mtlr r0 - lwz r2,_XER(r1) - lwz r0,_CCR(r1) - mtspr XER,r2 - mtcrf 0xFF,r0 - /* r3-r13 are destroyed -- Cort */ - REST_GPR(14, r1) - REST_8GPRS(15, r1) - REST_8GPRS(23, r1) - REST_GPR(31, r1) - lwz r2,_NIP(r1) /* Restore environment */ - lwz r0,_MSR(r1) - mtspr SRR0,r2 - mtspr SRR1,r0 - lwz r0,GPR0(r1) - lwz r2,GPR2(r1) - lwz r1,GPR1(r1) - SYNC - rfi - -/* - * Trap exit. - */ -#ifdef __SMP__ - .globl ret_from_smpfork -ret_from_smpfork: - bl schedule_tail -#endif - .globl ret_from_syscall -ret_from_syscall: - .globl int_return -int_return: -0: mfmsr r30 /* Disable interrupts */ - li r4,0 - ori r4,r4,MSR_EE - andc r30,r30,r4 - SYNC /* Some chip revs need this... */ - mtmsr r30 - SYNC - lwz r5,_MSR(r1) - and. r5,r5,r4 - beq 2f -3: lis r4,ppc_n_lost_interrupts@ha - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 - beq+ 1f - addi r3,r1,STACK_FRAME_OVERHEAD - bl do_IRQ - .globl lost_irq_ret -lost_irq_ret: - b 3b -1: lis r4,bh_mask@ha - lwz r4,bh_mask@l(r4) - lis r5,bh_active@ha - lwz r5,bh_active@l(r5) - and. r4,r4,r5 - beq+ 2f - bl do_bottom_half - .globl do_bottom_half_ret -do_bottom_half_ret: - SYNC - mtmsr r30 /* disable interrupts again */ - SYNC -2: lwz r3,_MSR(r1) /* Returning to user mode? */ - andi. r3,r3,MSR_PR - beq+ 10f /* if so, check need_resched and signals */ - lwz r3,NEED_RESCHED(r2) - cmpi 0,r3,0 /* check need_resched flag */ - beq+ 7f - bl schedule - b 0b -7: lwz r5,SIGPENDING(r2) /* Check for pending unblocked signals */ - cmpwi 0,r5,0 - beq+ 8f - li r3,0 - addi r4,r1,STACK_FRAME_OVERHEAD - bl do_signal - .globl do_signal_ret -do_signal_ret: - b 0b -8: addi r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD /* size of frame */ - stw r4,TSS+KSP(r2) /* save kernel stack pointer */ - tophys(r3,r1,r3) - mtspr SPRG2,r3 /* phys exception stack pointer */ -10: lwz r2,_CTR(r1) - lwz r0,_LINK(r1) - mtctr r2 - mtlr r0 - lwz r2,_XER(r1) - lwz r0,_CCR(r1) - mtspr XER,r2 - mtcrf 0xFF,r0 - REST_10GPRS(3, r1) - REST_10GPRS(13, r1) - REST_8GPRS(23, r1) - REST_GPR(31, r1) - lwz r2,_NIP(r1) /* Restore environment */ - lwz r0,_MSR(r1) - mtspr SRR0,r2 - mtspr SRR1,r0 - lwz r0,GPR0(r1) - lwz r2,GPR2(r1) - lwz r1,GPR1(r1) - SYNC - rfi - -/* - * Fake an interrupt from kernel mode. - * This is used when enable_irq loses an interrupt. - * We only fill in the stack frame minimally. - */ -_GLOBAL(fake_interrupt) - mflr r0 - stw r0,4(r1) - stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1) - stw r0,_NIP(r1) - stw r0,_LINK(r1) - mfmsr r3 - stw r3,_MSR(r1) - li r0,0x0fac - stw r0,TRAP(r1) - addi r3,r1,STACK_FRAME_OVERHEAD - li r4,1 - bl do_IRQ - addi r1,r1,INT_FRAME_SIZE+STACK_UNDERHEAD - lwz r0,4(r1) - mtlr r0 - blr - -/* * Set up the segment registers for a new context. */ _GLOBAL(set_context) @@ -2330,349 +1229,6 @@ _GLOBAL(set_context) blr /* - * Flush instruction cache. - * This is a no-op on the 601. - */ -_GLOBAL(flush_instruction_cache) - mfspr r3,PVR - rlwinm r3,r3,16,16,31 - cmpi 0,r3,1 - beqlr /* for 601, do nothing */ - /* 603/604 processor - use invalidate-all bit in HID0 */ - mfspr r3,HID0 - ori r3,r3,HID0_ICFI - mtspr HID0,r3 - SYNC - blr - -/* - * Write any modified data cache blocks out to memory - * and invalidate the corresponding instruction cache blocks. - * This is a no-op on the 601. - * - * flush_icache_range(unsigned long start, unsigned long stop) - */ -_GLOBAL(flush_icache_range) - mfspr r5,PVR - rlwinm r5,r5,16,16,31 - cmpi 0,r5,1 - beqlr /* for 601, do nothing */ - li r5,CACHE_LINE_SIZE-1 - andc r3,r3,r5 - subf r4,r3,r4 - add r4,r4,r5 - srwi. r4,r4,LG_CACHE_LINE_SIZE - beqlr - mtctr r4 - mr r6,r3 -1: dcbst 0,r3 - addi r3,r3,CACHE_LINE_SIZE - bdnz 1b - sync /* wait for dcbst's to get to ram */ - mtctr r4 -2: icbi 0,r6 - addi r6,r6,CACHE_LINE_SIZE - bdnz 2b - sync - isync - blr - -/* - * Like above, but only do the D-cache. This is used by the 8xx - * to push the cache so the CPM doesn't get stale data. - * - * flush_dcache_range(unsigned long start, unsigned long stop) - */ -_GLOBAL(flush_dcache_range) - li r5,CACHE_LINE_SIZE-1 - andc r3,r3,r5 - subf r4,r3,r4 - add r4,r4,r5 - srwi. r4,r4,LG_CACHE_LINE_SIZE - beqlr - mtctr r4 - -1: dcbst 0,r3 - addi r3,r3,CACHE_LINE_SIZE - bdnz 1b - sync /* wait for dcbst's to get to ram */ - blr - -/* - * Flush a particular page from the DATA cache - * Note: this is necessary because the instruction cache does *not* - * snoop from the data cache. - * This is a no-op on the 601 which has a unified cache. - * - * void flush_page_to_ram(void *page) - */ -_GLOBAL(flush_page_to_ram) - mfspr r5,PVR - rlwinm r5,r5,16,16,31 - cmpi 0,r5,1 - beqlr /* for 601, do nothing */ - li r4,0x0FFF - andc r3,r3,r4 /* Get page base address */ - li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ - mtctr r4 - mr r6,r3 -0: dcbst 0,r3 /* Write line to ram */ - addi r3,r3,CACHE_LINE_SIZE - bdnz 0b - sync - mtctr r4 -1: icbi 0,r6 - addi r6,r6,CACHE_LINE_SIZE - bdnz 1b - sync - isync - blr - -/* - * Clear a page using the dcbz instruction, which doesn't cause any - * memory traffic (except to write out any cache lines which get - * displaced). This only works on cacheable memory. - */ -_GLOBAL(clear_page) - li r0,4096/CACHE_LINE_SIZE - mtctr r0 -1: dcbz 0,r3 - addi r3,r3,CACHE_LINE_SIZE - bdnz 1b - blr - -/* - * Flush entries from the hash table with VSIDs in the range - * given. - */ -#ifndef CONFIG_8xx -_GLOBAL(flush_hash_segments) - lis r5,Hash@ha - lwz r5,Hash@l(r5) /* base of hash table */ -#ifdef NO_RELOAD_HTAB - cmpwi 0,r5,0 - bne+ 99f - tlbia - sync -#ifdef __SMP__ - tlbsync - sync -#endif - blr -99: -#endif /* NO_RELOAD_HTAB */ -#ifdef __SMP__ - /* Note - we had better not do anything which could generate - a hash table miss while we have the hash table locked, - or we'll get a deadlock. -paulus */ - mfmsr r10 - sync - rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ - mtmsr r0 - SYNC - lis r9,hash_table_lock@h - ori r9,r9,hash_table_lock@l - lwz r8,PROCESSOR(r2) - oris r8,r8,8 -10: lwarx r6,0,r9 - cmpi 0,r6,0 - bne- 10b - stwcx. r8,0,r9 - bne- 10b - eieio -#endif - rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ - oris r3,r3,0x8000 /* set V bit */ - rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ - oris r4,r4,0x8000 - ori r4,r4,0x7f - lis r6,Hash_size@ha - lwz r6,Hash_size@l(r6) /* size in bytes */ - srwi r6,r6,3 /* # PTEs */ - mtctr r6 - addi r5,r5,-8 - li r0,0 -1: lwzu r6,8(r5) /* get next tag word */ - cmplw 0,r6,r3 - cmplw 1,r6,r4 - cror 0,0,5 /* set cr0.lt if out of range */ - blt 2f /* branch if out of range */ - stw r0,0(r5) /* invalidate entry */ -2: bdnz 1b /* continue with loop */ - sync - tlbia - sync -#ifdef __SMP__ - tlbsync - sync - lis r3,hash_table_lock@ha - stw r0,hash_table_lock@l(r3) - mtmsr r10 - SYNC -#endif - blr - -/* - * Flush the entry for a particular page from the hash table. - * - * flush_hash_page(unsigned context, unsigned long va) - */ -_GLOBAL(flush_hash_page) - lis r6,Hash@ha - lwz r6,Hash@l(r6) /* hash table base */ -#ifdef NO_RELOAD_HTAB - cmpwi 0,r6,0 /* hash table in use? */ - bne+ 99f - tlbie r4 /* in hw tlb too */ - sync -#ifdef __SMP__ - tlbsync - sync -#endif - blr -99: -#endif /* NO_RELOAD_HTAB */ -#ifdef __SMP__ - /* Note - we had better not do anything which could generate - a hash table miss while we have the hash table locked, - or we'll get a deadlock. -paulus */ - mfmsr r10 - sync - rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ - mtmsr r0 - SYNC - lis r9,hash_table_lock@h - ori r9,r9,hash_table_lock@l - lwz r8,PROCESSOR(r2) - oris r8,r8,9 -10: lwarx r7,0,r9 - cmpi 0,r7,0 - bne- 10b - stwcx. r8,0,r9 - bne- 10b - eieio -#endif - rlwinm r3,r3,11,1,20 /* put context into vsid */ - rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ - oris r3,r3,0x8000 /* set V (valid) bit */ - rlwimi r3,r4,10,26,31 /* put in API (abbrev page index) */ - rlwinm r7,r4,32-6,10,25 /* get page index << 6 */ - rlwinm r5,r3,32-1,7,25 /* vsid << 6 */ - xor r7,r7,r5 /* primary hash << 6 */ - lis r5,Hash_mask@ha - lwz r5,Hash_mask@l(r5) /* hash mask */ - slwi r5,r5,6 /* << 6 */ - and r7,r7,r5 - add r6,r6,r7 /* address of primary PTEG */ - li r8,8 - mtctr r8 - addi r7,r6,-8 -1: lwzu r0,8(r7) /* get next PTE */ - cmpw 0,r0,r3 /* see if tag matches */ - bdnzf 2,1b /* while --ctr != 0 && !cr0.eq */ - beq 3f /* if we found it */ - ori r3,r3,0x40 /* set H (alt. hash) bit */ - xor r6,r6,r5 /* address of secondary PTEG */ - mtctr r8 - addi r7,r6,-8 -2: lwzu r0,8(r7) /* get next PTE */ - cmpw 0,r0,r3 /* see if tag matches */ - bdnzf 2,2b /* while --ctr != 0 && !cr0.eq */ - bne 4f /* if we didn't find it */ -3: li r0,0 - stw r0,0(r7) /* invalidate entry */ -4: sync - tlbie r4 /* in hw tlb too */ - sync -#ifdef __SMP__ - tlbsync - sync - lis r3,hash_table_lock@h - li r0,0 - stw r0,hash_table_lock@l(r3) - mtmsr r10 - SYNC -#endif - blr -#endif /* CONFIG_8xx */ - -/* - * This routine is just here to keep GCC happy - sigh... - */ -_GLOBAL(__main) - blr - -/* - * PROM code for specific machines follows. Put it - * here so it's easy to add arch-specific sections later. - * -- Cort - */ - -#ifndef CONFIG_8xx -/* - * On CHRP, the Run-Time Abstraction Services (RTAS) have to be - * called with the MMU off. - */ - .globl enter_rtas -enter_rtas: - mflr r0 - stw r0,20(r1) - lis r4,rtas_data@ha - lwz r4,rtas_data@l(r4) - addis r4,r4,-KERNELBASE@h - lis r6,1f@ha /* physical return address for rtas */ - addi r6,r6,1f@l - addis r6,r6,-KERNELBASE@h - subi r7,r1,INT_FRAME_SIZE+STACK_UNDERHEAD - addis r7,r7,-KERNELBASE@h - lis r8,rtas_entry@ha - lwz r8,rtas_entry@l(r8) - addis r5,r8,-KERNELBASE@h - mfmsr r9 - stw r9,8(r1) - ori r0,r0,MSR_EE|MSR_SE|MSR_BE - andc r0,r9,r0 - andi. r9,r9,MSR_ME|MSR_RI - sync /* disable interrupts so SRR0/1 */ - mtmsr r0 /* don't get trashed */ - mtlr r6 - mtspr SPRG2,r7 - mtspr SRR0,r8 - mtspr SRR1,r9 - rfi -1: addis r9,r1,-KERNELBASE@h - lwz r8,20(r9) /* get return address */ - lwz r9,8(r9) /* original msr value */ - li r0,0 - mtspr SPRG2,r0 - mtspr SRR0,r8 - mtspr SRR1,r9 - rfi /* return to caller */ -#endif /* CONFIG_8xx */ - -#ifdef CONFIG_8xx -/* Jump into the system reset for the rom. - * We first disable the MMU, and then jump to the ROM reset address. - * - * r3 is the board info structure, r4 is the location for starting. - * I use this for building a small kernel that can load other kernels, - * rather than trying to write or rely on a rom monitor that can tftp load. - */ - .globl m8xx_gorom -m8xx_gorom: - li r5,MSR_KERNEL & ~(MSR_IR|MSR_DR) - lis r6,2f@h - addis r6,r6,-KERNELBASE@h - ori r6,r6,2f@l - mtspr SRR0,r6 - mtspr SRR1,r5 - rfi -2: - mtlr r4 - blr -#endif /* CONFIG_8xx */ - -/* * We put a few things here that have to be page-aligned. * This stuff goes at the beginning of the data segment, * which is page-aligned. diff --git a/arch/ppc/kernel/head_8xx.S b/arch/ppc/kernel/head_8xx.S new file mode 100644 index 000000000..fcda01719 --- /dev/null +++ b/arch/ppc/kernel/head_8xx.S @@ -0,0 +1,903 @@ +/* + * arch/ppc/kernel/except_8xx.S + * + * $Id: head_8xx.S,v 1.2 1999/08/23 02:53:19 paulus Exp $ + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications by Dan Malek + * Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains low-level support and setup for PowerPC 8xx + * embedded processors, including trap and interrupt dispatch. + * + * 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. + * + */ + +#include "ppc_asm.h" +#include <asm/processor.h> +#include <asm/page.h> +#include <linux/config.h> +#include <asm/mmu.h> + + .text + .globl _stext +_stext: + +/* + * _start is defined this way because the XCOFF loader in the OpenFirmware + * on the powermac expects the entry point to be a procedure descriptor. + */ + .text + .globl _start +_start: + +/* MPC8xx + * This port was done on an MBX board with an 860. Right now I only + * support an ELF compressed (zImage) boot from EPPC-Bug because the + * code there loads up some registers before calling us: + * r3: ptr to board info data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * I decided to use conditional compilation instead of checking PVR and + * adding more processor specific branches around code I don't need. + * Since this is an embedded processor, I also appreciate any memory + * savings I can get. + * + * The MPC8xx does not have any BATs, but it supports large page sizes. + * We first initialize the MMU to support 8M byte pages, then load one + * entry into each of the instruction and data TLBs to map the first + * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to + * the "internal" processor registers before MMU_init is called. + * + * The TLB code currently contains a major hack. Since I use the condition + * code register, I have to save and restore it. I am out of registers, so + * I just store it in memory location 0 (the TLB handlers are not reentrant). + * To avoid making any decisions, I need to use the "segment" valid bit + * in the first level table, but that would require many changes to the + * Linux page directory/table functions that I don't want to do right now. + * + * I used to use SPRG2 for a temporary register in the TLB handler, but it + * has since been put to other uses. I now use a hack to save a register + * and the CCR at memory location 0.....Someday I'll fix this..... + * -- Dan + */ + + .globl __start +__start: + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + li r24,0 /* cpu # */ + + tlbia /* Invalidate all TLB entries */ + li r8, 0 + mtspr MI_CTR, r8 /* Set instruction control to zero */ + lis r8, MD_RESETVAL@h + mtspr MD_CTR, r8 /* Set data TLB control */ + + /* Now map the lower 8 Meg into the TLBs. For this quick hack, + * we can load the instruction and data TLB registers with the + * same values. + */ + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr MI_EPN, r8 + mtspr MD_EPN, r8 + li r8, MI_PS8MEG /* Set 8M byte page */ + ori r8, r8, MI_SVALID /* Make it valid */ + mtspr MI_TWC, r8 + mtspr MD_TWC, r8 + li r8, MI_BOOTINIT /* Create RPN for address 0 */ + mtspr MI_RPN, r8 /* Store TLB entry */ + mtspr MD_RPN, r8 + lis r8, MI_Kp@h /* Set the protection mode */ + mtspr MI_AP, r8 + mtspr MD_AP, r8 + +/* We will get these from a configuration file as soon as I verify + * the extraneous bits don't cause problems in the TLB. + */ +#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE) +#define BOOT_IMMR 0xfa000000 +#endif +#ifdef CONFIG_BSEIP +#define BOOT_IMMR 0xff000000 +#endif + /* Map another 8 MByte at 0xfa000000 to get the processor + * internal registers (among other things). + */ + lis r8, BOOT_IMMR@h /* Create vaddr for TLB */ + ori r8, r8, MD_EVALID /* Mark it valid */ + mtspr MD_EPN, r8 + li r8, MD_PS8MEG /* Set 8M byte page */ + ori r8, r8, MD_SVALID /* Make it valid */ + mtspr MD_TWC, r8 + lis r8, BOOT_IMMR@h /* Create paddr for TLB */ + ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ + mtspr MD_RPN, r8 + + /* Since the cache is enabled according to the information we + * just loaded into the TLB, invalidate and enable the caches here. + * We should probably check/set other modes....later. + */ + lis r8, IDC_INVALL@h + mtspr IC_CST, r8 + mtspr DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr IC_CST, r8 +#if 0 + mtspr DC_CST, r8 +#else + /* For a debug option, I left this here to easily enable + * the write through cache mode + */ + lis r8, DC_SFWT@h + mtspr DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr DC_CST, r8 +#endif + +/* We now have the lower 8 Meg mapped into TLB entries, and the caches + * ready to work. + */ + +turn_on_mmu: + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ + +/* + * Exception entry code. This code runs with address translation + * turned off, i.e. using physical addresses. + * We assume sprg3 has the physical address of the current + * task's thread_struct. + */ +#define EXCEPTION_PROLOG \ + mtspr SPRG0,r20; \ + mtspr SPRG1,r21; \ + mfcr r20; \ + mfspr r21,SPRG2; /* exception stack to use from */ \ + cmpwi 0,r21,0; /* user mode or RTAS */ \ + bne 1f; \ + tophys(r21,r1); /* use tophys(kernel sp) otherwise */ \ + subi r21,r21,INT_FRAME_SIZE; /* alloc exc. frame */\ +1: stw r20,_CCR(r21); /* save registers */ \ + stw r22,GPR22(r21); \ + stw r23,GPR23(r21); \ + mfspr r20,SPRG0; \ + stw r20,GPR20(r21); \ + mfspr r22,SPRG1; \ + stw r22,GPR21(r21); \ + mflr r20; \ + stw r20,_LINK(r21); \ + mfctr r22; \ + stw r22,_CTR(r21); \ + mfspr r20,XER; \ + stw r20,_XER(r21); \ + mfspr r22,SRR0; \ + mfspr r23,SRR1; \ + stw r0,GPR0(r21); \ + stw r1,GPR1(r21); \ + stw r2,GPR2(r21); \ + stw r1,0(r21); \ + tovirt(r1,r21); /* set new kernel sp */ \ + SAVE_4GPRS(3, r21); +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r21, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + */ +#define STD_EXCEPTION(n, label, hdlr) \ + . = n; \ +label: \ + EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r20,MSR_KERNEL; \ + bl transfer_to_handler; \ + .long hdlr; \ + .long ret_from_except + +/* System reset */ +#ifdef CONFIG_SMP /* MVME/MTX start the secondary here */ + STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) +#else + STD_EXCEPTION(0x100, Reset, UnknownException) +#endif + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ + . = 0x300 +DataAccess: + EXCEPTION_PROLOG + mfspr r20,DSISR + stw r20,_DSISR(r21) + mr r5,r20 + mfspr r4,DAR + stw r4,_DAR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long do_page_fault + .long ret_from_except + +/* Instruction access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ + . = 0x400 +InstructionAccess: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + mr r4,r22 + mr r5,r23 + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long do_page_fault + .long ret_from_except + +/* External interrupt */ + . = 0x500; +HardwareInterrupt: + EXCEPTION_PROLOG; +#ifdef CONFIG_APUS + /* This is horrible, but there's no way around it. Enable the + data cache so the IRQ hardware register can be accessed + without cache intervention. Then disable interrupts and get + the current emulated m68k IPL value. */ + + mfmsr 20 + xori r20,r20,MSR_DR + sync + mtmsr r20 + sync + + lis r3,APUS_IPL_EMU@h + + li r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT) + stb r20,APUS_IPL_EMU@l(r3) + eieio + + lbz r3,APUS_IPL_EMU@l(r3) + + mfmsr r20 + xori r20,r20,MSR_DR + sync + mtmsr r20 + sync + + stw r3,(_CCR+4)(r21); +#endif + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + li r4,0 + bl transfer_to_handler + .long do_IRQ; + .long ret_from_except + + +/* Alignment exception */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long AlignmentException + .long ret_from_except + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long ProgramCheckException + .long ret_from_except + +/* No FPU on MPC8xx. This exception is not supposed to happen. +*/ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + +/* System call */ + . = 0xc00 +SystemCall: + EXCEPTION_PROLOG + stw r3,ORIG_GPR3(r21) + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + bl transfer_to_handler + .long DoSyscall + .long ret_from_except + +/* Single step - not used on 601 */ + STD_EXCEPTION(0xd00, SingleStep, SingleStepException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + +/* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation) + + . = 0x1100 +/* + * For the MPC8xx, this is a software tablewalk to load the instruction + * TLB. It is modelled after the example in the Motorola manual. The task + * switch loads the M_TWB register with the pointer to the first level table. + * If we discover there is no second level table (the value is zero), the + * plan was to load that into the TLB, which causes another fault into the + * TLB Error interrupt where we can handle such problems. However, that did + * not work, so if we discover there is no second level table, we restore + * registers and branch to the error exception. We have to use the MD_xxx + * registers for the tablewalk because the equivalent MI_xxx registers + * only perform the attribute functions. + */ +InstructionTLBMiss: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + mfspr r20, SRR0 /* Get effective address of fault */ + mtspr MD_EPN, r20 /* Have to use MD_EPN for walk, MI_EPN can't */ + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load the MI_TWC with the attributes + * for this page, which has only bit 31 set. + */ + tophys(r21,r21) + ori r21,r21,1 /* Set valid bit */ + mtspr MI_TWC, r21 /* Set page attributes */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MI_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + + mtspr MI_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi + +2: mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b InstructionAccess + + . = 0x1200 +DataStoreTLBMiss: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load fetch the pte from the table. + */ + tophys(r21, r21) + ori r21, r21, 1 /* Set valid bit in physical L2 page */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MD_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + + mtspr MD_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi + +2: mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b DataAccess + +/* This is an instruction TLB error on the MPC8xx. This could be due + * to many reasons, such as executing guarded memory or illegal instruction + * addresses. There is nothing to do but handle a big time error fault. + */ + . = 0x1300 +InstructionTLBError: + b InstructionAccess + +/* This is the data TLB error on the MPC8xx. This could be due to + * many reasons, including a dirty update to a pte. We can catch that + * one here, but anything else is an error. First, we track down the + * Linux pte. If it is valid, write access is allowed, but the + * page dirty bit is not set, we will set it and reload the TLB. For + * any other case, we bail out to a higher level function that can + * handle it. + */ + . = 0x1400 +DataTLBError: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + + /* First, make sure this was a store operation. + */ + mfspr r20, DSISR + andis. r21, r20, 0x0200 /* If set, indicates store op */ + beq 2f + + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, bail */ + + /* We have a pte table, so fetch the pte from the table. + */ + tophys(r21, r21) + ori r21, r21, 1 /* Set valid bit in physical L2 page */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + andi. r20, r21, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail out if not */ + + ori r21, r21, _PAGE_DIRTY /* Update changed bit */ + mfspr r20, MD_TWC /* Get pte address again */ + stw r21, 0(r20) /* and update pte in table */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MD_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + + mtspr MD_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi +2: + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b DataAccess +#endif /* CONFIG_8xx */ + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, TAUException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) +/* On the MPC8xx, these next four traps are used for development + * support of breakpoints and such. Someday I will get around to + * using them. + */ + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + + . = 0x2000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception, turning + * on address translation. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 /* if from user, fix up THREAD.regs */ + beq 2f + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-THREAD /* set r2 to current */ + tovirt(r2,r2) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,RESULT + stwcx. r22,r22,r21 /* to clear the reservation */ + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmplw 0,r1,r2 + cmplw 1,r1,r24 + crand 1,1,4 + bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +/* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +stack_ovf: + addi r3,r1,STACK_FRAME_OVERHEAD + lis r1,init_task_union@ha + addi r1,r1,init_task_union@l + addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD + lis r24,StackOverflow@ha + addi r24,r24,StackOverflow@l + li r20,MSR_KERNEL + mtspr SRR0,r24 + mtspr SRR1,r20 + SYNC + rfi + + .globl giveup_fpu +giveup_fpu: + blr + +/* + * This code is jumped to from the startup code to copy + * the kernel image to physical address 0. + */ +relocate_kernel: + lis r9,0x426f /* if booted from BootX, don't */ + addi r9,r9,0x6f58 /* translate source addr */ + cmpw r31,r9 /* (we have to on chrp) */ + beq 7f + rlwinm r4,r4,0,8,31 /* translate source address */ + add r4,r4,r3 /* to region mapped with BATs */ +7: addis r9,r26,klimit@ha /* fetch klimit */ + lwz r25,klimit@l(r9) + addis r25,r25,-KERNELBASE@h + li r6,0 /* Destination offset */ + li r5,0x4000 /* # bytes of memory to copy */ + bl copy_and_flush /* copy the first 0x4000 bytes */ + addi r0,r3,4f@l /* jump to the address of 4f */ + mtctr r0 /* in copy and do the rest. */ + bctr /* jump to the copy */ +4: mr r5,r25 + bl copy_and_flush /* copy the rest */ + b turn_on_mmu + +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + */ +copy_and_flush: + addi r5,r5,-4 + addi r6,r6,-4 +4: li r0,8 + mtctr r0 +3: addi r6,r6,4 /* copy a cache line */ + lwzx r0,r6,r4 + stwx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmplw 0,r6,r5 + blt 4b + isync + addi r5,r5,4 + addi r6,r6,4 + blr + +#ifdef CONFIG_SMP + .globl __secondary_start_psurge +__secondary_start_psurge: + li r24,1 /* cpu # */ + b __secondary_start + + .globl __secondary_hold +__secondary_hold: + /* tell the master we're here */ + lis r5,0x4@h + ori r5,r5,0x4@l + stw r3,0(r5) + dcbf 0,r5 +100: + lis r5,0 + dcbi 0,r5 + lwz r4,0(r5) + /* wait until we're told to start */ + cmp 0,r4,r3 + bne 100b + /* our cpu # was at addr 0 - go */ + lis r5,__secondary_start@h + ori r5,r5,__secondary_start@l + tophys(r5,r5) + mtlr r5 + mr r24,r3 /* cpu # */ + blr +#endif /* CONFIG_SMP */ + +/* + * This is where the main kernel code starts. + */ +start_here: +#ifdef __SMP__ + /* if we're the second cpu stack and r2 are different + * and we want to not clear the bss -- Cort */ + lis r5,first_cpu_booted@h + ori r5,r5,first_cpu_booted@l + lwz r5,0(r5) + cmpi 0,r5,0 + beq 99f + + /* get current */ + lis r2,current_set@h + ori r2,r2,current_set@l + slwi r24,r24,2 /* cpu # to current_set[cpu#] */ + add r2,r2,r24 + lwz r2,0(r2) + b 10f +99: +#endif /* __SMP__ */ + /* ptr to current */ + lis r2,init_task_union@h + ori r2,r2,init_task_union@l + /* Clear out the BSS */ + lis r11,_end@ha + addi r11,r11,_end@l + lis r8,__bss_start@ha + addi r8,r8,__bss_start@l + subf r11,r8,r11 + addi r11,r11,3 + rlwinm. r11,r11,30,2,31 + beq 2f + addi r8,r8,-4 + mtctr r11 + li r0,0 +3: stwu r0,4(r8) + bdnz 3b +2: +#ifdef __SMP__ +10: +#endif /* __SMP__ */ + /* stack */ + addi r1,r2,TASK_UNION_SIZE + li r0,0 + stwu r0,-STACK_FRAME_OVERHEAD(r1) +/* + * Decide what sort of machine this is and initialize the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl identify_machine + bl MMU_init + +/* + * Go back to running unmapped so we can load up new values + * for SDR1 (hash table pointer) and the segment registers + * and change to using our exception vectors. + * On the 8xx, all we have to do is invalidate the TLB to clear + * the old 8M byte TLB mappings and load the page table base register. + */ + /* The right way to do this would be to track it down through + * init's THREAD like the context switch code does, but this is + * easier......until someone changes init's static structures. + */ + lis r6, swapper_pg_dir@h + tophys(r6,r6) + ori r6, r6, swapper_pg_dir@l + mtspr M_TWB, r6 + lis r4,2f@h + ori r4,r4,2f@l + tophys(r4,r4) + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) + mtspr SRR0,r4 + mtspr SRR1,r3 + rfi +/* Load up the kernel context */ +2: + SYNC /* Force all PTE updates to finish */ + tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ +#ifdef __SMP__ + tlbsync /* ... on all CPUs */ + sync +#endif +/* Set up for using our exception vectors */ + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* init task's THREAD */ + mtspr SPRG3,r4 + li r3,0 + mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ +/* Now turn on the MMU for real! */ + li r4,MSR_KERNEL + lis r3,start_kernel@h + ori r3,r3,start_kernel@l +#ifdef __SMP__ + /* the second time through here we go to + * start_secondary(). -- Cort + */ + lis r5,first_cpu_booted@h + ori r5,r5,first_cpu_booted@l + tophys(r5,r5) + lwz r5,0(r5) + cmpi 0,r5,0 + beq 10f + lis r3,start_secondary@h + ori r3,r3,start_secondary@l +10: +#endif /* __SMP__ */ + mtspr SRR0,r3 + mtspr SRR1,r4 + rfi /* enable MMU and jump to start_kernel */ + +/* + * Set up to use a given MMU context. + * + * The MPC8xx has something that currently happens "automagically." + * Unshared user space address translations are subject to ASID (context) + * match. During each task switch, the ASID is incremented. We can + * guarantee (I hope :-) that no entries currently match this ASID + * because every task will cause at least a TLB entry to be loaded for + * the first instruction and data access, plus the kernel running will + * have displaced several more TLBs. The MMU contains 32 entries for + * each TLB, and there are 16 contexts, so we just need to make sure + * two pages get replaced for every context switch, which currently + * happens. There are other TLB management techniques that I will + * eventually implement, but this is the easiest for now. -- Dan + * + * On the MPC8xx, we place the physical address of the new task + * page directory loaded into the MMU base register, and set the + * ASID compare register with the new "context". + */ +_GLOBAL(set_context) + mtspr M_CASID,r3 /* Update context */ + tlbia + SYNC + blr + +/* Jump into the system reset for the rom. + * We first disable the MMU, and then jump to the ROM reset address. + * + * r3 is the board info structure, r4 is the location for starting. + * I use this for building a small kernel that can load other kernels, + * rather than trying to write or rely on a rom monitor that can tftp load. + */ + .globl m8xx_gorom +m8xx_gorom: + li r5,MSR_KERNEL & ~(MSR_IR|MSR_DR) + lis r6,2f@h + addis r6,r6,-KERNELBASE@h + ori r6,r6,2f@l + mtspr SRR0,r6 + mtspr SRR1,r5 + rfi +2: + mtlr r4 + blr + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * Used to pass parameters into the kernel like root=/dev/sda1, etc. + */ + .globl cmd_line +cmd_line: + .space 512 diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 9d8d94e51..f8f59440c 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.62 1999/05/24 05:43:18 cort Exp $ + * $Id: idle.c,v 1.66 1999/09/05 11:56:30 paulus Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -45,7 +45,7 @@ unsigned long zeropage_hits; /* # zero'd pages request that we've done */ unsigned long zeropage_calls; /* # zero'd pages request that've been made */ unsigned long zerototal; /* # pages zero'd over time */ -int idled(void *unused) +int idled(void) { /* endless loop with no priority at all */ current->priority = 0; @@ -69,29 +69,15 @@ int idled(void *unused) return 0; } -#ifdef __SMP__ /* * SMP entry into the idle task - calls the same thing as the * non-smp versions. -- Cort */ -int cpu_idle(void *unused) +int cpu_idle() { - idled(unused); + idled(); return 0; } -#endif /* __SMP__ */ - -/* - * Syscall entry into the idle task. -- Cort - */ -asmlinkage int sys_idle(void) -{ - if(current->pid != 0) - return -EPERM; - - idled(NULL); - return 0; /* should never execute this but it makes gcc happy -- Cort */ -} /* * Mark 'zombie' pte's in the hash table as invalid. @@ -227,7 +213,7 @@ void zero_paged(void) /* * Make the page no cache so we don't blow our cache with 0's */ - pte = find_pte(init_task.mm, pageptr); + pte = find_pte(&init_mm, pageptr); if ( !pte ) { printk("pte NULL in zero_paged()\n"); @@ -235,7 +221,7 @@ void zero_paged(void) } pte_uncache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + flush_tlb_page(find_vma(&init_mm,pageptr),pageptr); /* * Important here to not take time away from real processes. */ @@ -260,7 +246,7 @@ void zero_paged(void) /* turn cache on for this page */ pte_cache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + flush_tlb_page(find_vma(&init_mm,pageptr),pageptr); /* atomically add this page to the list */ asm ( "101:lwarx %0,0,%1\n" /* reserve zero_cache */ " stw %0,0(%2)\n" /* update *pageptr */ diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 5d906f62a..eece8308a 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,5 +1,5 @@ /* - * $Id: irq.c,v 1.106 1999/05/25 21:16:04 cort Exp $ + * $Id: irq.c,v 1.109 1999/09/05 11:56:31 paulus Exp $ * * arch/ppc/kernel/irq.c * @@ -31,6 +31,7 @@ #include <linux/ptrace.h> #include <linux/errno.h> +#include <linux/threads.h> #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> diff --git a/arch/ppc/kernel/local_irq.h b/arch/ppc/kernel/local_irq.h index 5149c291a..adf028590 100644 --- a/arch/ppc/kernel/local_irq.h +++ b/arch/ppc/kernel/local_irq.h @@ -19,10 +19,6 @@ struct hw_interrupt_type { int irq_offset; }; -#define mask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->disable) irq_desc[irq].ctl->disable(irq);}) -#define unmask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->enable) irq_desc[irq].ctl->enable(irq);}) -#define mask_and_ack_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->mask_and_ack) irq_desc[irq].ctl->mask_and_ack(irq);}) - struct irqdesc { struct irqaction *action; struct hw_interrupt_type *ctl; diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c index 5114c3cfb..d7473e128 100644 --- a/arch/ppc/kernel/mbx_pci.c +++ b/arch/ppc/kernel/mbx_pci.c @@ -253,16 +253,14 @@ int mbx_pcibios_find_class(unsigned int class_code, unsigned short index, return PCIBIOS_DEVICE_NOT_FOUND; } -__initfunc( -void -mbx_pcibios_fixup(void)) +void __init +mbx_pcibios_fixup(void) { /* Nothing to do here? */ } -__initfunc( -void -mbx_setup_pci_ptrs(void)) +void __init +mbx_setup_pci_ptrs(void) { set_config_access_method(mbx); diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index fdb9c11e0..67cab4503 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -1,5 +1,5 @@ /* - * $Id: mbx_setup.c,v 1.10 1999/05/14 07:24:19 davem Exp $ + * $Id: mbx_setup.c,v 1.12 1999/08/31 06:53:56 davem Exp $ * * linux/arch/ppc/kernel/setup.c * @@ -75,8 +75,8 @@ void __init adbdev_init(void) { } -__initfunc(void -mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) +void __init +mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) { int cpm_page; extern char cmd_line[]; @@ -141,7 +141,7 @@ abort(void) * sixteen, or external oscillator divided by four. Currently, we only * support the MBX, which is system clock divided by sixteen. */ -__initfunc(void mbx_calibrate_decr(void)) +void __init mbx_calibrate_decr(void) { bd_t *binfo = (bd_t *)&res; int freq, fp, divisor; @@ -182,8 +182,7 @@ mbx_set_rtc_time(unsigned long time) return(0); } -initfunc(unsigned long -mbx_get_rtc_time(void) +unsigned long __init mbx_get_rtc_time(void) { /* First, unlock all of the registers we are going to modify. * To protect them from corruption during power down, registers @@ -310,8 +309,8 @@ static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs) * interrupts can be either edge or level triggered, but there is no * reason for us to change the EPPC-bug values (it would not work if we did). */ -__initfunc(void -mbx_init_IRQ(void)) +void __init +mbx_init_IRQ(void) { int i; @@ -413,9 +412,9 @@ mbx_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_p } #endif -__initfunc(void +void __init mbx_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7)) + unsigned long r6, unsigned long r7) { if ( r3 ) diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 0caf06a3b..c4bed05e9 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -17,19 +17,17 @@ #include <asm/unistd.h> #include <asm/errno.h> #include <asm/processor.h> -#include "ppc_asm.tmpl" -#include "ppc_defs.h" +#include <asm/page.h> +#include "ppc_asm.h" #ifndef CONFIG_8xx -/* This instruction is not implemented on the PPC 601 or 603 */ -#define tlbia \ - li r4,128; \ - mtspr CTR,r4; \ - li r4,0; \ -0: tlbie r4; \ - addi r4,r4,0x1000; \ - bdnz 0b -#endif +CACHE_LINE_SIZE = 32 +LG_CACHE_LINE_SIZE = 5 +#else +CACHE_LINE_SIZE = 16 +LG_CACHE_LINE_SIZE = 4 +#endif /* CONFIG_8xx */ + .text /* @@ -47,13 +45,7 @@ _GLOBAL(reloc_offset) mtlr r0 blr -/* - * Disable interrupts - * rc = _disable_interrupts() - */ -_GLOBAL(_disable_interrupts) _GLOBAL(__cli) -_GLOBAL(_hard_cli) mfmsr r0 /* Get current interrupt state */ rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ li r4,0 /* Need [unsigned] value of MSR_EE */ @@ -63,16 +55,7 @@ _GLOBAL(_hard_cli) mtmsr r0 /* Update machine state */ blr /* Done */ -/* - * Enable interrupts - * _enable_interrupts(int state) - * turns on interrupts if state = 1. - */ -_GLOBAL(_enable_interrupts) - cmpi 0,r3,0 /* turning them on? */ - beqlr /* nothing to do if state == 0 */ _GLOBAL(__sti) -_GLOBAL(_hard_sti) lis r4,ppc_n_lost_interrupts@ha lwz r4,ppc_n_lost_interrupts@l(r4) mfmsr r3 /* Get current state */ @@ -146,6 +129,117 @@ _GLOBAL(_tlbie) blr /* + * Flush instruction cache. + * This is a no-op on the 601. + */ +_GLOBAL(flush_instruction_cache) + mfspr r3,PVR + rlwinm r3,r3,16,16,31 + cmpi 0,r3,1 + beqlr /* for 601, do nothing */ + /* 603/604 processor - use invalidate-all bit in HID0 */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI + mtspr HID0,r3 + SYNC + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * This is a no-op on the 601. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_icache_range) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 2b + sync + isync + blr + +/* + * Like above, but only do the D-cache. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Flush a particular page from the DATA cache + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * This is a no-op on the 601 which has a unified cache. + * + * void flush_page_to_ram(void *page) + */ +_GLOBAL(flush_page_to_ram) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + li r4,0x0FFF + andc r3,r3,r4 /* Get page base address */ + li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ + addi r3,r3,CACHE_LINE_SIZE + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 1b + sync + isync + blr + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: dcbz 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* * Atomic [test&set] exchange * * unsigned long xchg_u32(void *ptr, unsigned long val) @@ -659,8 +753,8 @@ _GLOBAL(kernel_thread) sc cmpi 0,r3,0 /* parent or child? */ bnelr /* return if parent */ - li r0,0 /* clear out p->tss.regs */ - stw r0,TSS+PT_REGS(r2) /* since we don't have user ctx */ + li r0,0 /* clear out p->thread.regs */ + stw r0,THREAD+PT_REGS(r2) /* since we don't have user ctx */ mtlr r6 /* fn addr in lr */ mr r3,r4 /* load arg and call fn */ blrl @@ -668,6 +762,12 @@ _GLOBAL(kernel_thread) li r3,0 sc +/* + * This routine is just here to keep GCC happy - sigh... + */ +_GLOBAL(__main) + blr + #define SYSCALL(name) \ _GLOBAL(name) \ li r0,__NR_##name; \ @@ -680,7 +780,6 @@ _GLOBAL(name) \ #define __NR__exit __NR_exit -SYSCALL(idle) SYSCALL(sync) SYSCALL(setsid) SYSCALL(write) @@ -812,7 +911,7 @@ sys_call_table: .long sys_uname .long sys_iopl /* 110 */ .long sys_vhangup - .long sys_idle + .long sys_ni_syscall /* old 'idle' syscall */ .long sys_vm86 .long sys_wait4 .long sys_swapoff /* 115 */ diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index a3977193a..38f38ca92 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -35,19 +35,19 @@ main(void) DEFINE(COUNTER, offsetof(struct task_struct, counter)); DEFINE(PROCESSOR, offsetof(struct task_struct, processor)); DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); - DEFINE(TSS, offsetof(struct task_struct, tss)); + DEFINE(THREAD, offsetof(struct task_struct, thread)); DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(ACTIVE_MM, offsetof(struct task_struct, active_mm)); DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); - DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables)); - DEFINE(PGD, offsetof(struct mm_struct, pgd)); + DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); DEFINE(PF_TRACESYS, PF_TRACESYS); DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched)); - DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); - DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); + DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); /* Interrupt register frame */ DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); diff --git a/arch/ppc/kernel/open_pic.c b/arch/ppc/kernel/open_pic.c index 2ca879dd8..519407eea 100644 --- a/arch/ppc/kernel/open_pic.c +++ b/arch/ppc/kernel/open_pic.c @@ -1,11 +1,87 @@ -#include <linux/stddef.h> -#include <linux/init.h> +/* + * arch/ppc/kernel/openpic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * 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/types.h> +#include <linux/kernel.h> #include <linux/sched.h> -#include <linux/signal.h> +#include <linux/init.h> #include <linux/openpic.h> +#include <asm/ptrace.h> +#include <asm/signal.h> +#include <asm/io.h> #include <asm/irq.h> -#include "open_pic.h" -#include "i8259.h" +#include "local_irq.h" + +volatile struct OpenPIC *OpenPIC = NULL; +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; + +void chrp_mask_irq(unsigned int); +void chrp_unmask_irq(unsigned int); + +static u_int NumProcessors; +static u_int NumSources; + +struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + NULL, + openpic_enable_irq, + openpic_disable_irq, + 0, + 0 +}; + +/* + * Accesses to the current processor's registers + */ +#ifndef __powerpc__ +#define THIS_CPU Private +#define CHECK_THIS_CPU do {} while (0) +#else +#define THIS_CPU Processor[cpu] +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#endif + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("openpic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("openpic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("openpic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri); +#define check_arg_irq(irq) \ + if (irq < 0 || irq >= (NumSources+open_pic.irq_offset)) \ + printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq); +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors) \ + printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu); +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +static void no_action(int ir1, void *dev, struct pt_regs *regs) +{ +} #ifdef __SMP__ void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) @@ -14,35 +90,349 @@ void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) } #endif /* __SMP__ */ -void chrp_mask_and_ack_irq(unsigned int irq_nr) +#ifdef __i386__ +static inline u_int ld_le32(volatile u_int *addr) { - if (is_8259_irq(irq_nr)) - i8259_pic.mask_and_ack(irq_nr); + return *addr; } -static void chrp_mask_irq(unsigned int irq_nr) +static inline void out_le32(volatile u_int *addr, u_int val) { - if (is_8259_irq(irq_nr)) - i8259_pic.disable(irq_nr); - else - openpic_disable_irq(irq_to_openpic(irq_nr)); + *addr = val; +} +#endif + +u_int openpic_read(volatile u_int *addr) +{ + u_int val; + + val = ld_le32(addr); + return val; +} + +static inline void openpic_write(volatile u_int *addr, u_int val) +{ + out_le32(addr, val); +} + +static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +inline void openpic_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + +static void openpic_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + openpic_setfield(addr, OPENPIC_MASK); + /* wait until it's not in use */ + while (openpic_read(addr) & OPENPIC_ACTIVITY); + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); } -static void chrp_unmask_irq(unsigned int irq_nr) +void __init openpic_init(int main_pic) { - if (is_8259_irq(irq_nr)) - i8259_pic.enable(irq_nr); + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC) + panic("No OpenPIC found"); + + if ( ppc_md.progress ) ppc_md.progress("openpic enter",0x122); + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + + printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, + NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + printk("OpenPIC timer frequency is "); + if (timerfreq) + printk("%d Hz\n", timerfreq); else - openpic_enable_irq(irq_to_openpic(irq_nr)); + printk("not set\n"); + + if ( main_pic ) + { + /* Initialize timer interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i); + /* No processor */ + openpic_maptimer(i, 0); + } + + /* Initialize IPI interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic ipi",0x3bb); + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 0 */ + openpic_initipi(i, 0, OPENPIC_VEC_IPI+i); + } + + /* Initialize external interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic ext",0x3bc); + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, open_pic.irq_offset, 1, 1); + openpic_mapirq(0, 1<<0); + for (i = 1; i < NumSources; i++) { + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, open_pic.irq_offset+i, 0, + i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } + + /* Initialize the spurious interrupt */ + if ( ppc_md.progress ) ppc_md.progress("openpic spurious",0x3bd); + openpic_set_spurious(OPENPIC_VEC_SPURIOUS); + if ( _machine != _MACH_gemini ) + { + if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, + "82c59 cascade", NULL)) + printk("Unable to get OpenPIC IRQ 0 for cascade\n"); + } + openpic_set_priority(0, 0); + openpic_disable_8259_pass_through(); + } + if ( ppc_md.progress ) ppc_md.progress("openpic exit",0x222); } -struct hw_interrupt_type open_pic = { - " OpenPIC ", - NULL, - NULL, - NULL, - chrp_unmask_irq, - chrp_mask_irq, - chrp_mask_and_ack_irq, - 0 -}; +void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + +void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +#ifndef __i386__ +/* + * Find out the current interrupt + */ +u_int openpic_irq(u_int cpu) +{ + u_int vec; + + check_arg_cpu(cpu); + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} +#endif + +#ifndef __powerpc__ +void openpic_eoi(void) +#else +void openpic_eoi(u_int cpu) +#endif +{ + check_arg_cpu(cpu); + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); +} + + +#ifndef __powerpc__ +u_int openpic_get_priority(void) +#else +u_int openpic_get_priority(u_int cpu) +#endif +{ + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +#ifndef __powerpc__ +void openpic_set_priority(u_int pri) +#else +void openpic_set_priority(u_int cpu, u_int pri) +#endif +{ + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +void openpic_init_processor(u_int cpumask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask); +} + +/* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ +void openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_timer(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Send an IPI to one or more CPUs + */ +#ifndef __powerpc__ +void openpic_cause_IPI(u_int ipi, u_int cpumask) +#else +void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask) +#endif +{ + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask); +} + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +void openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +void openpic_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask); +} + +/* + * Enable/disable an interrupt source + */ +void openpic_enable_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); +} + +void openpic_disable_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); +} + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + check_arg_irq(irq); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_SENSE_POLARITY : 0) | + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +void openpic_mapirq(u_int irq, u_int cpumask) +{ + check_arg_irq(irq); + openpic_write(&OpenPIC->Source[irq].Destination, cpumask); +} + +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +void openpic_set_sense(u_int irq, int sense) +{ + check_arg_irq(irq); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} diff --git a/arch/ppc/kernel/open_pic.h b/arch/ppc/kernel/open_pic.h index 77b8a46d0..ace8590bb 100644 --- a/arch/ppc/kernel/open_pic.h +++ b/arch/ppc/kernel/open_pic.h @@ -1,9 +1,6 @@ - #ifndef _PPC_KERNEL_OPEN_PIC_H #define _PPC_KERNEL_OPEN_PIC_H -#include "local_irq.h" - extern struct hw_interrupt_type open_pic; void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); diff --git a/arch/ppc/kernel/openpic.c b/arch/ppc/kernel/openpic.c deleted file mode 100644 index f7893dd79..000000000 --- a/arch/ppc/kernel/openpic.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * arch/ppc/kernel/openpic.c -- OpenPIC Interrupt Handling - * - * Copyright (C) 1997 Geert Uytterhoeven - * - * 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. - */ - - -/* - * Note: Interprocessor Interrupt (IPI) and Timer support is incomplete - */ - - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/openpic.h> -#include <asm/ptrace.h> -#include <asm/signal.h> -#include <asm/io.h> -#include <asm/irq.h> - - -#define REGISTER_DEBUG -#undef REGISTER_DEBUG - - -volatile struct OpenPIC *OpenPIC = NULL; -u_int OpenPIC_NumInitSenses __initdata = 0; -u_char *OpenPIC_InitSenses __initdata = NULL; - -static u_int NumProcessors; -static u_int NumSources; - - - /* - * Accesses to the current processor's registers - */ - -#ifndef __powerpc__ -#define THIS_CPU Private -#define CHECK_THIS_CPU do {} while (0) -#else -#define THIS_CPU Processor[cpu] -#define CHECK_THIS_CPU check_arg_cpu(cpu) -#endif - - - /* - * Sanity checks - */ - -#if 1 -#define check_arg_ipi(ipi) \ - if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ - printk("openpic.c:%d: illegal ipi %d\n", __LINE__, ipi); -#define check_arg_timer(timer) \ - if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ - printk("openpic.c:%d: illegal timer %d\n", __LINE__, timer); -#define check_arg_vec(vec) \ - if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ - printk("openpic.c:%d: illegal vector %d\n", __LINE__, vec); -#define check_arg_pri(pri) \ - if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ - printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri); -#define check_arg_irq(irq) \ - if (irq < 0 || irq >= NumSources) \ - printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq); -#define check_arg_cpu(cpu) \ - if (cpu < 0 || cpu >= NumProcessors) \ - printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu); -#else -#define check_arg_ipi(ipi) do {} while (0) -#define check_arg_timer(timer) do {} while (0) -#define check_arg_vec(vec) do {} while (0) -#define check_arg_pri(pri) do {} while (0) -#define check_arg_irq(irq) do {} while (0) -#define check_arg_cpu(cpu) do {} while (0) -#endif - - - /* - * Dummy interrupt handler - */ - -static void no_action(int ir1, void *dev, struct pt_regs *regs) -{} - - - /* - * I/O functions - */ - -#ifdef __i386__ -static inline u_int ld_le32(volatile u_int *addr) -{ - return *addr; -} - -static inline void out_le32(volatile u_int *addr, u_int val) -{ - *addr = val; -} -#endif - -u_int openpic_read(volatile u_int *addr) -{ - u_int val; - - val = ld_le32(addr); -#ifdef REGISTER_DEBUG - printk("openpic_read(0x%08x) = 0x%08x\n", (u_int)addr, val); -#endif - return val; -} - -static inline void openpic_write(volatile u_int *addr, u_int val) -{ -#ifdef REGISTER_DEBUG - printk("openpic_write(0x%08x, 0x%08x)\n", (u_int)addr, val); -#endif - out_le32(addr, val); -} - - -static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) -{ - u_int val = openpic_read(addr); - return val & mask; -} - -inline void openpic_writefield(volatile u_int *addr, u_int mask, - u_int field) -{ - u_int val = openpic_read(addr); - openpic_write(addr, (val & ~mask) | (field & mask)); -} - -static inline void openpic_clearfield(volatile u_int *addr, u_int mask) -{ - openpic_writefield(addr, mask, 0); -} - -static inline void openpic_setfield(volatile u_int *addr, u_int mask) -{ - openpic_writefield(addr, mask, mask); -} - - - /* - * Update a Vector/Priority register in a safe manner. The interrupt will - * be disabled. - */ - -static void openpic_safe_writefield(volatile u_int *addr, u_int mask, - u_int field) -{ - openpic_setfield(addr, OPENPIC_MASK); - /* wait until it's not in use */ - while (openpic_read(addr) & OPENPIC_ACTIVITY); - openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); -} - - -/* -------- Global Operations ---------------------------------------------- */ - - - /* - * Initialize the OpenPIC - */ - -__initfunc(void openpic_init(int main_pic)) -{ - u_int t, i; - u_int timerfreq; - const char *version; - - if (!OpenPIC) - panic("No OpenPIC found"); - - t = openpic_read(&OpenPIC->Global.Feature_Reporting0); - switch (t & OPENPIC_FEATURE_VERSION_MASK) { - case 1: - version = "1.0"; - break; - case 2: - version = "1.2"; - break; - case 3: - version = "1.3"; - break; - default: - version = "?"; - break; - } - NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> - OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; - NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> - OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; - printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, - NumProcessors, NumSources, OpenPIC); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); - printk("OpenPIC timer frequency is "); - if (timerfreq) - printk("%d Hz\n", timerfreq); - else - printk("not set\n"); - - if ( main_pic ) - { - /* Initialize timer interrupts */ - for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { - /* Disabled, Priority 0 */ - openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i); - /* No processor */ - openpic_maptimer(i, 0); - } - - /* Initialize IPI interrupts */ - for (i = 0; i < OPENPIC_NUM_IPI; i++) { - /* Disabled, Priority 0 */ - openpic_initipi(i, 0, OPENPIC_VEC_IPI+i); - } - - /* Initialize external interrupts */ - /* SIOint (8259 cascade) is special */ - openpic_initirq(0, 8, OPENPIC_VEC_SOURCE, 1, 1); - /* Processor 0 */ - openpic_mapirq(0, 1<<0); - for (i = 1; i < NumSources; i++) { - /* Enabled, Priority 8 */ - openpic_initirq(i, 8, OPENPIC_VEC_SOURCE+i, 0, - i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); - /* Processor 0 */ - openpic_mapirq(i, 1<<0); - } - - /* Initialize the spurious interrupt */ - openpic_set_spurious(OPENPIC_VEC_SPURIOUS); - - if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, - "82c59 cascade", NULL)) - printk("Unable to get OpenPIC IRQ 0 for cascade\n"); - openpic_set_priority(0, 0); - openpic_disable_8259_pass_through(); - } -} - - - /* - * Reset the OpenPIC - */ - -void openpic_reset(void) -{ - openpic_setfield(&OpenPIC->Global.Global_Configuration0, - OPENPIC_CONFIG_RESET); -} - - - /* - * Enable/disable 8259 Pass Through Mode - */ - -void openpic_enable_8259_pass_through(void) -{ - openpic_clearfield(&OpenPIC->Global.Global_Configuration0, - OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); -} - -void openpic_disable_8259_pass_through(void) -{ - openpic_setfield(&OpenPIC->Global.Global_Configuration0, - OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); -} - - -#ifndef __i386__ - /* - * Find out the current interrupt - */ - -u_int openpic_irq(u_int cpu) -{ - u_int vec; - - check_arg_cpu(cpu); - vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, - OPENPIC_VECTOR_MASK); -#if 0 -if (vec != 22 /* SCSI */) -printk("++openpic_irq: %d\n", vec); -#endif - return vec; -} -#endif - - - /* - * Signal end of interrupt (EOI) processing - */ - -#ifndef __powerpc__ -void openpic_eoi(void) -#else -void openpic_eoi(u_int cpu) -#endif -{ - check_arg_cpu(cpu); - openpic_write(&OpenPIC->THIS_CPU.EOI, 0); -} - - - /* - * Get/set the current task priority - */ - -#ifndef __powerpc__ -u_int openpic_get_priority(void) -#else -u_int openpic_get_priority(u_int cpu) -#endif -{ - CHECK_THIS_CPU; - return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, - OPENPIC_CURRENT_TASK_PRIORITY_MASK); -} - -#ifndef __powerpc__ -void openpic_set_priority(u_int pri) -#else -void openpic_set_priority(u_int cpu, u_int pri) -#endif -{ - CHECK_THIS_CPU; - check_arg_pri(pri); - openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, - OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); -} - - /* - * Get/set the spurious vector - */ - -u_int openpic_get_spurious(void) -{ - return openpic_readfield(&OpenPIC->Global.Spurious_Vector, - OPENPIC_VECTOR_MASK); -} - -void openpic_set_spurious(u_int vec) -{ - check_arg_vec(vec); - openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, - vec); -} - - - /* - * Initialize one or more CPUs - */ - -void openpic_init_processor(u_int cpumask) -{ - openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask); -} - - -/* -------- Interprocessor Interrupts -------------------------------------- */ - - - /* - * Initialize an interprocessor interrupt (and disable it) - * - * ipi: OpenPIC interprocessor interrupt number - * pri: interrupt source priority - * vec: the vector it will produce - */ - -void openpic_initipi(u_int ipi, u_int pri, u_int vec) -{ - check_arg_timer(ipi); - check_arg_pri(pri); - check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi), - OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, - (pri << OPENPIC_PRIORITY_SHIFT) | vec); -} - - - /* - * Send an IPI to one or more CPUs - */ - -#ifndef __powerpc__ -void openpic_cause_IPI(u_int ipi, u_int cpumask) -#else -void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask) -#endif -{ - CHECK_THIS_CPU; - check_arg_ipi(ipi); - openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask); -} - - -/* -------- Timer Interrupts ----------------------------------------------- */ - - - /* - * Initialize a timer interrupt (and disable it) - * - * timer: OpenPIC timer number - * pri: interrupt source priority - * vec: the vector it will produce - */ - -void openpic_inittimer(u_int timer, u_int pri, u_int vec) -{ - check_arg_timer(timer); - check_arg_pri(pri); - check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, - OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, - (pri << OPENPIC_PRIORITY_SHIFT) | vec); -} - - - /* - * Map a timer interrupt to one or more CPUs - */ - -void openpic_maptimer(u_int timer, u_int cpumask) -{ - check_arg_timer(timer); - openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask); -} - - -/* -------- Interrupt Sources ---------------------------------------------- */ - - - /* - * Enable/disable an interrupt source - */ - -void openpic_enable_irq(u_int irq) -{ - check_arg_irq(irq); - openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); -} - -void openpic_disable_irq(u_int irq) -{ - check_arg_irq(irq); - openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); -} - - - /* - * Initialize an interrupt source (and disable it!) - * - * irq: OpenPIC interrupt number - * pri: interrupt source priority - * vec: the vector it will produce - * pol: polarity (1 for positive, 0 for negative) - * sense: 1 for level, 0 for edge - */ - -void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) -{ - check_arg_irq(irq); - check_arg_pri(pri); - check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, - OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | - OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, - (pri << OPENPIC_PRIORITY_SHIFT) | vec | - (pol ? OPENPIC_SENSE_POLARITY : 0) | - (sense ? OPENPIC_SENSE_LEVEL : 0)); -} - - - /* - * Map an interrupt source to one or more CPUs - */ - -void openpic_mapirq(u_int irq, u_int cpumask) -{ - check_arg_irq(irq); - openpic_write(&OpenPIC->Source[irq].Destination, cpumask); -} - - - /* - * Set the sense for an interrupt source (and disable it!) - * - * sense: 1 for level, 0 for edge - */ - -void openpic_set_sense(u_int irq, int sense) -{ - check_arg_irq(irq); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, - OPENPIC_SENSE_LEVEL, - (sense ? OPENPIC_SENSE_LEVEL : 0)); -} diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 985abf6e6..2ed0f4a5b 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.54 1999/03/18 04:16:04 cort Exp $ + * $Id: pci.c,v 1.60 1999/09/08 03:04:07 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ @@ -22,58 +22,94 @@ #include "pci.h" +static void __init pcibios_claim_resources(struct pci_bus *); + unsigned long isa_io_base = 0; unsigned long isa_mem_base = 0; unsigned long pci_dram_offset = 0; -int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val) +struct pci_fixup pcibios_fixups[] = { + { 0 } +}; + +int generic_pcibios_read_byte(struct pci_dev *dev, int where, u8 *val) { - return ppc_md.pcibios_read_config_byte(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_byte(dev->bus->number,dev->devfn,where,val); } -int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val) +int generic_pcibios_read_word(struct pci_dev *dev, int where, u16 *val) { - return ppc_md.pcibios_read_config_word(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_word(dev->bus->number,dev->devfn,where,val); } -int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val) +int generic_pcibios_read_dword(struct pci_dev *dev, int where, u32 *val) { - return ppc_md.pcibios_read_config_dword(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_dword(dev->bus->number,dev->devfn,where,val); } -int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val) +int generic_pcibios_write_byte(struct pci_dev *dev, int where, u8 val) { - return ppc_md.pcibios_write_config_byte(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_byte(dev->bus->number,dev->devfn,where,val); } -int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val) +int generic_pcibios_write_word(struct pci_dev *dev, int where, u16 val) { - return ppc_md.pcibios_write_config_word(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_word(dev->bus->number,dev->devfn,where,val); } -int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val) +int generic_pcibios_write_dword(struct pci_dev *dev, int where, u32 val) { - return ppc_md.pcibios_write_config_dword(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_dword(dev->bus->number,dev->devfn,where,val); } -int pcibios_present(void) +struct pci_ops generic_pci_ops = { - return 1; -} + generic_pcibios_read_byte, + generic_pcibios_read_word, + generic_pcibios_read_dword, + generic_pcibios_write_byte, + generic_pcibios_write_word, + generic_pcibios_write_dword +}; void __init pcibios_init(void) { + printk("PCI: Probing PCI hardware\n"); + ioport_resource.end = ~0L; + pci_scan_bus(0, &generic_pci_ops, NULL); + pcibios_claim_resources(pci_root); + if ( ppc_md.pcibios_fixup ) + ppc_md.pcibios_fixup(); } - -void __init pcibios_fixup(void) +static void __init pcibios_claim_resources(struct pci_bus *bus) { - ppc_md.pcibios_fixup(); + struct pci_dev *dev; + int idx; + + while (bus) + { + for (dev=bus->devices; dev; dev=dev->sibling) + { + for (idx = 0; idx < PCI_NUM_RESOURCES; idx++) + { + struct resource *r = &dev->resource[idx]; + struct resource *pr; + if (!r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) + { + printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", idx, dev->name); + /* We probably should disable the region, shouldn't we? */ + } + } + } + if (bus->children) + pcibios_claim_resources(bus->children); + bus = bus->next; + } } void __init pcibios_fixup_bus(struct pci_bus *bus) { + if ( ppc_md.pcibios_fixup_bus ) + ppc_md.pcibios_fixup_bus(bus); } char __init *pcibios_setup(char *str) @@ -105,3 +141,8 @@ void __init fix_intr(struct device_node *node, struct pci_dev *dev) } } #endif + +int pcibios_assign_resource(struct pci_dev *pdev, int resource) +{ + return 0; +} diff --git a/arch/ppc/kernel/pci.h b/arch/ppc/kernel/pci.h index 231f1d952..d79eb0f4a 100644 --- a/arch/ppc/kernel/pci.h +++ b/arch/ppc/kernel/pci.h @@ -11,6 +11,18 @@ extern unsigned char *pci_config_data; void fix_intr(struct device_node *node, struct pci_dev *dev); +#if 0 +#define decl_config_access_method(name) \ +struct pci_ops name##_pci_ops = { \ + name##_pcibios_read_config_byte, \ + name##_pcibios_read_config_word, \ + name##_pcibios_read_config_dword, \ + name##_pcibios_write_config_byte, \ + name##_pcibios_write_config_word, \ + name##_pcibios_write_config_dword \ +} +#endif + #define decl_config_access_method(name) \ extern int name##_pcibios_read_config_byte(unsigned char bus, \ unsigned char dev_fn, unsigned char offset, unsigned char *val); \ diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 089169e37..932e7dbb6 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -17,6 +17,8 @@ #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> + +#include <asm/init.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/prom.h> @@ -315,7 +317,7 @@ int grackle_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, * N.B. we can't use pcibios_*_config_* here because bridges[] * is not initialized yet. */ -__initfunc(static void init_bandit(struct bridge_data *bp)) +static void __init init_bandit(struct bridge_data *bp) { unsigned int vendev, magic; int rev; @@ -360,7 +362,7 @@ __initfunc(static void init_bandit(struct bridge_data *bp)) bp->io_base); } -__initfunc(unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end)) +unsigned long __init pmac_find_bridges(unsigned long mem_start, unsigned long mem_end) { int bus; struct bridge_data *bridge; @@ -385,7 +387,7 @@ __initfunc(unsigned long pmac_find_bridges(unsigned long mem_start, unsigned lon * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, * if we have one or more bandit or chaos bridges, we don't have a MPC106. */ -__initfunc(static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)) +static void __init add_bridges(struct device_node *dev, unsigned long *mem_ptr) { int *bus_range; int len; @@ -442,9 +444,8 @@ __initfunc(static void add_bridges(struct device_node *dev, unsigned long *mem_p } } -__initfunc( -void -pmac_pcibios_fixup(void)) +void __init +pmac_pcibios_fixup(void) { struct pci_dev *dev; @@ -472,9 +473,8 @@ pmac_pcibios_fixup(void)) } } -__initfunc( -void -pmac_setup_pci_ptrs(void)) +void __init +pmac_setup_pci_ptrs(void) { if (find_devices("pci") != 0) { /* looks like a G3 powermac */ diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c index 6b9d8ca53..f8404cc9a 100644 --- a/arch/ppc/kernel/pmac_pic.c +++ b/arch/ppc/kernel/pmac_pic.c @@ -3,6 +3,8 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/signal.h> + +#include <asm/init.h> #include <asm/io.h> #include <asm/smp.h> #include <asm/prom.h> @@ -290,8 +292,8 @@ static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_b } } -__initfunc(void -pmac_pic_init(void)) +void __init +pmac_pic_init(void) { int i; struct device_node *irqctrler; diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index ffdc316ed..ac84aa92b 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -43,6 +43,8 @@ #include <linux/console.h> #include <linux/ide.h> #include <linux/pci.h> + +#include <asm/init.h> #include <asm/prom.h> #include <asm/system.h> #include <asm/pgtable.h> @@ -58,6 +60,7 @@ #include <asm/ide.h> #include <asm/machdep.h> #include <asm/keyboard.h> +#include <asm/dma.h> #include "time.h" #include "local_irq.h" @@ -204,11 +207,12 @@ kdev_t sd_find_target(void *host, int tgt) { Scsi_Disk *dp; int i; - +#ifdef CONFIG_BLK_DEV_SD for (dp = rscsi_disks, i = 0; i < sd_template.dev_max; ++i, ++dp) if (dp->device != NULL && dp->device->host == host && dp->device->id == tgt) return MKDEV_SD(i); +#endif /* CONFIG_BLK_DEV_SD */ return 0; } #endif @@ -225,8 +229,8 @@ pmac_mksound(unsigned int hz, unsigned int ticks) static volatile u32 *sysctrl_regs; -__initfunc(void -pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)) +void __init +pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) { struct device_node *cpu; int *fp; @@ -306,7 +310,7 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)) /* * Tweak the PCI-PCI bridge chip on the blue & white G3s. */ -__initfunc(static void init_p2pbridge(void)) +static void __init init_p2pbridge(void) { struct device_node *p2pbridge; unsigned char bus, devfn; @@ -328,7 +332,7 @@ __initfunc(static void init_p2pbridge(void)) pcibios_read_config_word(bus, devfn, PCI_BRIDGE_CONTROL, &val); } -__initfunc(static void ohare_init(void)) +static void __init ohare_init(void) { /* * Turn on the L2 cache. @@ -353,8 +357,8 @@ int boot_target; int boot_part; kdev_t boot_dev; -__initfunc(void -pmac_init2(void)) +void __init +pmac_init2(void) { adb_init(); pmac_nvram_init(); @@ -362,8 +366,8 @@ pmac_init2(void)) } #ifdef CONFIG_SCSI -__initfunc(void -note_scsi_host(struct device_node *node, void *host)) +void __init +note_scsi_host(struct device_node *node, void *host) { int l; char *p; @@ -397,7 +401,7 @@ extern int pmac_ide_count; extern struct device_node *pmac_ide_node[]; static int ide_majors[] = { 3, 22, 33, 34, 56, 57, 88, 89 }; -__initfunc(kdev_t find_ide_boot(void)) +kdev_t __init find_ide_boot(void) { char *p; int i, n; @@ -424,7 +428,7 @@ __initfunc(kdev_t find_ide_boot(void)) } #endif /* CONFIG_BLK_DEV_IDE_PMAC */ -__initfunc(void find_boot_device(void)) +void __init find_boot_device(void) { #ifdef CONFIG_SCSI if (boot_host != NULL) { @@ -438,7 +442,7 @@ __initfunc(void find_boot_device(void)) #endif } -/* can't be initfunc - can be called whenever a disk is first accessed */ +/* can't be __init - can be called whenever a disk is first accessed */ __pmac void note_bootable_part(kdev_t dev, int part) { @@ -517,13 +521,13 @@ pmac_halt(void) void pmac_ide_insw(ide_ioreg_t port, void *buf, int ns) { - _insw_ns(port+_IO_BASE, buf, ns); + _insw_ns((unsigned short *)(port+_IO_BASE), buf, ns); } void pmac_ide_outsw(ide_ioreg_t port, void *buf, int ns) { - _outsw_ns(port+_IO_BASE, buf, ns); + _outsw_ns((unsigned short *)(port+_IO_BASE), buf, ns); } int @@ -587,9 +591,9 @@ void pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t #endif #endif -__initfunc(void +void __init pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7)) + unsigned long r6, unsigned long r7) { pmac_setup_pci_ptrs(); diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c index 0196c5eb6..b7cd026b6 100644 --- a/arch/ppc/kernel/pmac_support.c +++ b/arch/ppc/kernel/pmac_support.c @@ -5,6 +5,7 @@ #include <linux/stddef.h> #include <linux/reboot.h> #include <linux/nvram.h> +#include <linux/init.h> #include <asm/init.h> #include <asm/ptrace.h> #include <asm/io.h> diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c index a54767737..03795efdc 100644 --- a/arch/ppc/kernel/pmac_time.c +++ b/arch/ppc/kernel/pmac_time.c @@ -15,6 +15,8 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/init.h> + +#include <asm/init.h> #include <asm/adb.h> #include <asm/cuda.h> #include <asm/pmu.h> @@ -91,7 +93,7 @@ int pmac_set_rtc_time(unsigned long nowtime) * Calibrate the decrementer register using VIA timer 1. * This is used both on powermacs and CHRP machines. */ -__initfunc(int via_calibrate_decr(void)) +int __init via_calibrate_decr(void) { struct device_node *vias; volatile unsigned char *via; @@ -168,7 +170,7 @@ static struct notifier_block time_sleep_notifier = { * This was taken from the pmac time_init() when merging the prep/pmac * time functions. */ -__initfunc(void pmac_calibrate_decr(void)) +void __init pmac_calibrate_decr(void) { struct device_node *cpu; int freq, *fp, divisor; diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c index d7fef0869..b6397daac 100644 --- a/arch/ppc/kernel/ppc-stub.c +++ b/arch/ppc/kernel/ppc-stub.c @@ -1,4 +1,4 @@ -/* $Id: ppc-stub.c,v 1.4 1998/07/28 08:25:01 paulus Exp $ +/* $Id: ppc-stub.c,v 1.6 1999/08/12 22:18:11 cort Exp $ * ppc-stub.c: KGDB support for the Linux kernel. * * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC @@ -438,13 +438,13 @@ static struct hard_trap_info { 0x400, SIGBUS }, /* instruction bus error */ { 0x500, SIGINT }, /* interrupt */ { 0x600, SIGBUS }, /* alingment */ - { 0x700, SIGILL }, /* reserved instruction or sumpin' */ + { 0x700, SIGTRAP }, /* breakpoint trap */ { 0x800, SIGFPE }, /* fpu unavail */ { 0x900, SIGALRM }, /* decrementer */ { 0xa00, SIGILL }, /* reserved */ { 0xb00, SIGILL }, /* reserved */ { 0xc00, SIGCHLD }, /* syscall */ - { 0xd00, SIGINT }, /* watch */ + { 0xd00, SIGTRAP }, /* single-step/watch */ { 0xe00, SIGFPE }, /* fp assist */ { 0, 0} /* Must be last */ }; @@ -482,8 +482,10 @@ handle_exception (struct pt_regs *regs) } kgdb_active = 1; +#ifdef KGDB_DEBUG printk("kgdb: entering handle_exception; trap [0x%x]\n", (unsigned int)regs->trap); +#endif kgdb_interruptible(0); lock_kernel(); diff --git a/arch/ppc/kernel/ppc_asm.h b/arch/ppc/kernel/ppc_asm.h new file mode 100644 index 000000000..10be7ceab --- /dev/null +++ b/arch/ppc/kernel/ppc_asm.h @@ -0,0 +1,73 @@ +/* + * arch/ppc/kernel/ppc_asm.h + * + * Definitions used by various bits of low-level assembly code on PowerPC. + * + * Copyright (C) 1995-1999 Gary Thomas, Paul Mackerras, Cort Dougan. + * + * 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. + */ +#include "ppc_asm.tmpl" +#include "ppc_defs.h" + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +#define SYNC \ + sync; \ + isync + +/* This instruction is not implemented on the PPC 603 or 601 */ +#define tlbia \ + li r4,128; \ + mtctr r4; \ + lis r4,KERNELBASE@h; \ +0: tlbie r4; \ + addi r4,r4,0x1000; \ + bdnz 0b + +/* + * On APUS (Amiga PowerPC cpu upgrade board), we don't know the + * physical base address of RAM at compile time. + */ +#define tophys(rd,rs) \ +0: addis rd,rs,-KERNELBASE@h; \ + .section ".vtop_fixup","aw"; \ + .align 1; \ + .long 0b; \ + .previous + +#define tovirt(rd,rs) \ +0: addis rd,rs,KERNELBASE@h; \ + .section ".ptov_fixup","aw"; \ + .align 1; \ + .long 0b; \ + .previous diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index a2aab8354..9da8db6e8 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.26 1998/12/10 00:24:23 cort Exp $ + * $Id: ppc_htab.c,v 1.28 1999/06/27 10:53:32 davem Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 1d1340331..61806f1af 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -1,5 +1,6 @@ #include <linux/config.h> #include <linux/module.h> +#include <linux/threads.h> #include <linux/smp.h> #include <linux/elfcore.h> #include <linux/sched.h> @@ -7,6 +8,7 @@ #include <linux/interrupt.h> #include <linux/vt_kern.h> #include <linux/nvram.h> +#include <linux/spinlock.h> #include <asm/page.h> #include <asm/semaphore.h> @@ -27,7 +29,6 @@ #include <asm/pci-bridge.h> #include <asm/irq.h> #include <asm/feature.h> -#include <asm/spinlock.h> #include <asm/dma.h> #include <asm/machdep.h> @@ -35,7 +36,6 @@ #define EXPORT_SYMTAB_STROPS extern void transfer_to_handler(void); -extern void int_return(void); extern void syscall_trace(void); extern void do_IRQ(struct pt_regs *regs, int isfake); extern void MachineCheckException(struct pt_regs *regs); @@ -53,7 +53,6 @@ EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); EXPORT_SYMBOL(transfer_to_handler); -EXPORT_SYMBOL(int_return); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(init_task_union); EXPORT_SYMBOL(MachineCheckException); @@ -153,11 +152,9 @@ EXPORT_SYMBOL(ppc_ide_md); EXPORT_SYMBOL(start_thread); EXPORT_SYMBOL(kernel_thread); -EXPORT_SYMBOL(__cli); -EXPORT_SYMBOL(__sti); /*EXPORT_SYMBOL(__restore_flags);*/ -EXPORT_SYMBOL(_disable_interrupts); -EXPORT_SYMBOL(_enable_interrupts); +/*EXPORT_SYMBOL(_disable_interrupts); + EXPORT_SYMBOL(_enable_interrupts);*/ EXPORT_SYMBOL(flush_instruction_cache); EXPORT_SYMBOL(_get_PVR); EXPORT_SYMBOL(giveup_fpu); @@ -189,6 +186,7 @@ EXPORT_SYMBOL(pmu_request); EXPORT_SYMBOL(pmu_poll); #ifdef CONFIG_PMAC_PBOOK EXPORT_SYMBOL(sleep_notifier_list); +EXPORT_SYMBOL(pmu_enable_irled); #endif CONFIG_PMAC_PBOOK EXPORT_SYMBOL(abort); EXPORT_SYMBOL(find_devices); @@ -219,3 +217,7 @@ EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL(abs); EXPORT_SYMBOL(device_is_compatible); + +#ifdef CONFIG_VT +EXPORT_SYMBOL(screen_info); +#endif diff --git a/arch/ppc/kernel/prep_nvram.c b/arch/ppc/kernel/prep_nvram.c index e69563c8a..6a352b341 100644 --- a/arch/ppc/kernel/prep_nvram.c +++ b/arch/ppc/kernel/prep_nvram.c @@ -9,6 +9,7 @@ #include <linux/malloc.h> #include <linux/ioport.h> +#include <asm/init.h> #include <asm/segment.h> #include <asm/io.h> #include <asm/processor.h> @@ -57,7 +58,7 @@ void rs_nvram_write_val(int addr, rs_pcNvRAM[addr]=val; } -__initfunc(void init_prep_nvram(void)) +void __init init_prep_nvram(void) { unsigned char *nvp; int i; diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index 63a1e9ddf..78f207a54 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.35 1999/05/10 23:31:03 cort Exp $ + * $Id: prep_pci.c,v 1.39 1999/08/31 15:42:39 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/openpic.h> +#include <asm/init.h> #include <asm/byteorder.h> #include <asm/io.h> #include <asm/ptrace.h> @@ -685,7 +686,7 @@ static u_char mvme2600_openpic_initsenses[] __initdata = { int prep_keybd_present = 1; int MotMPIC = 0; -__initfunc(int raven_init(void)) +int __init raven_init(void) { unsigned int devid; unsigned int pci_membase; @@ -788,7 +789,7 @@ struct mot_info { {0x000, 0x00, 0x00, "", NULL, NULL} }; -__initfunc(unsigned long prep_route_pci_interrupts(void)) +unsigned long __init prep_route_pci_interrupts(void) { unsigned char *ibc_pirq = (unsigned char *)0x80800860; unsigned char *ibc_pcicon = (unsigned char *)0x80800840; @@ -976,9 +977,8 @@ __initfunc(unsigned long prep_route_pci_interrupts(void)) return 0; } -__initfunc( -void -prep_pcibios_fixup(void)) +void __init +prep_pcibios_fixup(void) { struct pci_dev *dev; extern unsigned char *Motherboard_map; @@ -1017,17 +1017,17 @@ prep_pcibios_fixup(void)) for ( i = 0 ; i <= 5 ; i++ ) { - if ( dev->base_address[i] > 0x10000000 ) + if ( dev->resource[i].start > 0x10000000 ) { printk("Relocating PCI address %lx -> %lx\n", - dev->base_address[i], - (dev->base_address[i] & 0x00FFFFFF) + dev->resource[i].start, + (dev->resource[i].start & 0x00FFFFFF) | 0x01000000); - dev->base_address[i] = - (dev->base_address[i] & 0x00FFFFFF) | 0x01000000; + dev->resource[i].start = + (dev->resource[i].start & 0x00FFFFFF) | 0x01000000; pci_write_config_dword(dev, PCI_BASE_ADDRESS_0+(i*0x4), - dev->base_address[i] ); + dev->resource[i].start ); } } #if 0 @@ -1044,9 +1044,8 @@ prep_pcibios_fixup(void)) decl_config_access_method(indirect); -__initfunc( -void -prep_setup_pci_ptrs(void)) +void __init +prep_setup_pci_ptrs(void) { PPC_DEVICE *hostbridge; @@ -1055,7 +1054,7 @@ prep_setup_pci_ptrs(void)) { pci_config_address = (unsigned *)0x80000cf8; pci_config_data = (char *)0x80000cfc; - set_config_access_method(indirect); + set_config_access_method(indirect); } else { diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index c99f6bbd7..14cce93bd 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -35,6 +35,7 @@ #include <linux/openpic.h> #include <linux/ide.h> +#include <asm/init.h> #include <asm/mmu.h> #include <asm/processor.h> #include <asm/residual.h> @@ -211,8 +212,8 @@ no_l2: return len; } -__initfunc(void -prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) +void __init +prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) { extern char cmd_line[]; unsigned char reg; @@ -365,7 +366,7 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) * This allows for a faster boot as we do not need to calibrate the * decrementer against another clock. This is important for embedded systems. */ -__initfunc(void prep_res_calibrate_decr(void)) +void __init prep_res_calibrate_decr(void) { int freq, divisor; @@ -386,10 +387,10 @@ __initfunc(void prep_res_calibrate_decr(void)) int calibrate_done = 0; volatile int *done_ptr = &calibrate_done; -__initfunc(void +void __init prep_calibrate_decr_handler(int irq, void *dev, - struct pt_regs *regs)) + struct pt_regs *regs) { unsigned long freq, divisor; static unsigned long t1 = 0, t2 = 0; @@ -412,7 +413,7 @@ prep_calibrate_decr_handler(int irq, } } -__initfunc(void prep_calibrate_decr(void)) +void __init prep_calibrate_decr(void) { unsigned long flags; @@ -437,7 +438,7 @@ __initfunc(void prep_calibrate_decr(void)) /* We use the NVRAM RTC to time a second to calibrate the decrementer. */ -__initfunc(void mk48t59_calibrate_decr(void)) +void __init mk48t59_calibrate_decr(void) { unsigned long freq, divisor; unsigned long t1, t2; @@ -491,7 +492,7 @@ prep_restart(char *cmd) unsigned long i = 10000; - _disable_interrupts(); + __cli(); /* set exception prefix high - to the prom */ _nmask_and_or_msr(0, MSR_IP); @@ -518,7 +519,7 @@ prep_direct_restart(char *cmd) * This will ALWAYS work regardless of port 92 * functionality */ - _disable_interrupts(); + __cli(); __asm__ __volatile__("\n\ mtspr 26, %1 /* SRR0 */ @@ -535,7 +536,7 @@ void prep_halt(void) { unsigned long flags; - _disable_interrupts(); + __cli(); /* set exception prefix high - to the prom */ save_flags( flags ); restore_flags( flags|MSR_IP ); @@ -603,8 +604,8 @@ prep_do_IRQ(struct pt_regs *regs, int cpu, int isfake) ppc_irq_dispatch_handler( regs, irq ); } -__initfunc(void -prep_init_IRQ(void)) +void __init +prep_init_IRQ(void) { int i; @@ -691,8 +692,8 @@ prep_ide_fix_driveid(struct hd_driveid *id) { } -__initfunc(void -prep_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)) +void __init +prep_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { ide_ioreg_t reg = data_port; int i; @@ -711,9 +712,9 @@ prep_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl } #endif -__initfunc(void +void __init prep_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7)) + unsigned long r6, unsigned long r7) { /* make a copy of residual data */ if ( r3 ) diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c index 5b8873d79..f720841be 100644 --- a/arch/ppc/kernel/prep_time.c +++ b/arch/ppc/kernel/prep_time.c @@ -19,6 +19,7 @@ #include <linux/kernel_stat.h> #include <linux/init.h> +#include <asm/init.h> #include <asm/segment.h> #include <asm/io.h> #include <asm/processor.h> diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index e39c2f7e0..b6f6415ff 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -1,5 +1,5 @@ /* - * $Id: process.c,v 1.85 1999/05/16 21:27:08 cort Exp $ + * $Id: process.c,v 1.95 1999/08/31 06:54:07 davem Exp $ * * linux/arch/ppc/kernel/process.c * @@ -47,11 +47,13 @@ extern unsigned long _get_SP(void); struct task_struct *last_task_used_math = NULL; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM(init_mm); -union task_union init_task_union = { INIT_TASK(init_task_union.task) }; +/* this is 16-byte aligned because it has a stack in it */ +union task_union __attribute((aligned(16))) init_task_union = { + INIT_TASK(init_task_union.task) +}; /* only used to get secondary processor up */ struct task_struct *current_set[NR_CPUS] = {&init_task, }; @@ -75,7 +77,7 @@ dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { if (regs->msr & MSR_FP) giveup_fpu(current); - memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); + memcpy(fpregs, ¤t->thread.fpr[0], sizeof(*fpregs)); return 1; } @@ -83,7 +85,7 @@ void enable_kernel_fp(void) { #ifdef __SMP__ - if (current->tss.regs && (current->tss.regs->msr & MSR_FP)) + if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) giveup_fpu(current); else giveup_fpu(NULL); /* just enables FP for kernel */ @@ -100,11 +102,11 @@ int check_stack(struct task_struct *tsk) int ret = 0; #if 0 - /* check tss magic */ - if ( tsk->tss.magic != TSS_MAGIC ) + /* check thread magic */ + if ( tsk->thread.magic != THREAD_MAGIC ) { ret |= 1; - printk("tss.magic bad: %08x\n", tsk->tss.magic); + printk("thread.magic bad: %08x\n", tsk->thread.magic); } #endif @@ -112,12 +114,12 @@ int check_stack(struct task_struct *tsk) printk("check_stack(): tsk bad tsk %p\n",tsk); /* check if stored ksp is bad */ - if ( (tsk->tss.ksp > stack_top) || (tsk->tss.ksp < tsk_top) ) + if ( (tsk->thread.ksp > stack_top) || (tsk->thread.ksp < tsk_top) ) { printk("stack out of bounds: %s/%d\n" " tsk_top %08lx ksp %08lx stack_top %08lx\n", tsk->comm,tsk->pid, - tsk_top, tsk->tss.ksp, stack_top); + tsk_top, tsk->thread.ksp, stack_top); ret |= 2; } @@ -159,8 +161,11 @@ void _switch_to(struct task_struct *prev, struct task_struct *new, struct task_struct **last) { - struct thread_struct *new_tss, *old_tss; - int s = _disable_interrupts(); + struct thread_struct *new_thread, *old_thread; + int s; + + __save_flags(s); + __cli(); #if CHECK_STACK check_stack(prev); check_stack(new); @@ -169,7 +174,7 @@ _switch_to(struct task_struct *prev, struct task_struct *new, #ifdef SHOW_TASK_SWITCHES printk("%s/%d -> %s/%d NIP %08lx cpu %d root %x/%x\n", prev->comm,prev->pid, - new->comm,new->pid,new->tss.regs->nip,new->processor, + new->comm,new->pid,new->thread.regs->nip,new->processor, new->fs->root,prev->fs->root); #endif #ifdef __SMP__ @@ -182,16 +187,16 @@ _switch_to(struct task_struct *prev, struct task_struct *new, * every switch, just a save. * -- Cort */ - if (prev->tss.regs && (prev->tss.regs->msr & MSR_FP)) + if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP)) giveup_fpu(prev); prev->last_processor = prev->processor; current_set[smp_processor_id()] = new; #endif /* __SMP__ */ - new_tss = &new->tss; - old_tss = ¤t->tss; - *last = _switch(old_tss, new_tss, new->mm->context); - _enable_interrupts(s); + new_thread = &new->thread; + old_thread = ¤t->thread; + *last = _switch(old_thread, new_thread); + __restore_flags(s); } void show_regs(struct pt_regs * regs) @@ -205,9 +210,9 @@ void show_regs(struct pt_regs * regs) regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, regs->msr&MSR_IR ? 1 : 0, regs->msr&MSR_DR ? 1 : 0); - printk("TASK = %p[%d] '%s' mm->pgd %p ", - current, current->pid, current->comm, current->mm->pgd); - printk("Last syscall: %ld ", current->tss.last_syscall); + printk("TASK = %p[%d] '%s' ", + current, current->pid, current->comm); + printk("Last syscall: %ld ", current->thread.last_syscall); printk("\nlast math %p", last_task_used_math); #ifdef __SMP__ @@ -272,10 +277,10 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, if ((childregs->msr & MSR_PR) == 0) childregs->gpr[2] = (unsigned long) p; /* `current' in new task */ childregs->gpr[3] = 0; /* Result from fork() */ - p->tss.regs = childregs; - p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; - p->tss.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; - kregs = (struct pt_regs *)(p->tss.ksp + STACK_FRAME_OVERHEAD); + p->thread.regs = childregs; + p->thread.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->thread.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; + kregs = (struct pt_regs *)(p->thread.ksp + STACK_FRAME_OVERHEAD); #ifdef __SMP__ kregs->nip = (unsigned long)ret_from_smpfork; #else @@ -292,7 +297,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, /* Provided stack is in user space */ childregs->gpr[1] = usp; } - p->tss.last_syscall = -1; + p->thread.last_syscall = -1; /* * copy fpu info - assume lazy fpu switch now always @@ -301,11 +306,10 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, if (regs->msr & MSR_FP) giveup_fpu(current); - memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); - p->tss.fpscr = current->tss.fpscr; + memcpy(&p->thread.fpr, ¤t->thread.fpr, sizeof(p->thread.fpr)); + p->thread.fpscr = current->thread.fpscr; childregs->msr &= ~MSR_FP; - p->processor = 0; #ifdef __SMP__ p->last_processor = NO_PROC_ID; #endif /* __SMP__ */ @@ -363,7 +367,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) shove_aux_table(sp); if (last_task_used_math == current) last_task_used_math = 0; - current->tss.fpscr = 0; + current->thread.fpscr = 0; } asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, @@ -454,7 +458,7 @@ print_backtrace(unsigned long *sp) /* * Low level print for debugging - Cort */ -__initfunc(int ll_printk(const char *fmt, ...)) +int __init ll_printk(const char *fmt, ...) { va_list args; char buf[256]; @@ -483,7 +487,7 @@ void puthex(unsigned long val) prom_print(buf); } -__initfunc(void ll_puts(const char *s)) +void __init ll_puts(const char *s) { int x,y; char *vidmem = (char *)/*(_ISA_MEM_BASE + 0xB8000) */0xD00B8000; diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index 9c0efa754..a7859ca9a 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -1,5 +1,5 @@ /* - * $Id: prom.c,v 1.60 1999/05/25 01:42:41 cort Exp $ + * $Id: prom.c,v 1.73 1999/09/05 11:56:32 paulus Exp $ * * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. @@ -16,7 +16,10 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/version.h> -#include <asm/spinlock.h> +#include <linux/threads.h> +#include <linux/spinlock.h> + +#include <asm/init.h> #include <asm/prom.h> #include <asm/page.h> #include <asm/processor.h> @@ -25,6 +28,7 @@ #include <asm/smp.h> #include <asm/bootx.h> #include <asm/system.h> +#include <asm/gemini.h> /* * Properties whose value is longer than this get excluded from our @@ -257,6 +261,9 @@ prom_print(const char *msg) } } +unsigned long smp_ibm_chrp_hack __initdata = 0; +unsigned long smp_chrp_cpu_nr __initdata = 1; + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -266,7 +273,7 @@ void prom_init(int r3, int r4, prom_entry pp) { #ifdef CONFIG_SMP - int cpu = 0, i; + int i; phandle node; char type[16], *path; #endif @@ -275,6 +282,11 @@ prom_init(int r3, int r4, prom_entry pp) unsigned long offset = reloc_offset(); int l; char *p, *d; + +#ifdef CONFIG_GEMINI + gemini_prom_init(); + return; +#endif /* CONFIG_GEMINI */ /* check if we're apus, return if we are */ if ( r3 == 0x61707573 ) @@ -502,8 +514,8 @@ prom_init(int r3, int r4, prom_entry pp) return; /* copy the holding pattern code to someplace safe (8M) */ - memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x10000 ); - for (i = 8<<20; i < ((8<<20)+0x10000); i += 32) + memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x100 ); + for (i = 8<<20; i < ((8<<20)+0x100); i += 32) { asm volatile("dcbf 0,%0" : : "r" (i) : "memory"); asm volatile("icbi 0,%0" : : "r" (i) : "memory"); @@ -524,17 +536,18 @@ prom_init(int r3, int r4, prom_entry pp) node, path, 255) < 0) continue; /* XXX: hack - don't start cpu 0, this cpu -- Cort */ - if ( cpu++ == 0 ) + if ( smp_chrp_cpu_nr++ == 0 ) continue; + RELOC(smp_ibm_chrp_hack) = 1; prom_print(RELOC("starting cpu ")); prom_print(path); *(unsigned long *)(0x4) = 0; asm volatile("dcbf 0,%0": : "r" (0x4) : "memory"); - call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, cpu-1); + call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, smp_chrp_cpu_nr-1); for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == (ulong)0); i++ ) ; - if (*(ulong *)(0x4) == (ulong)cpu-1 ) + if (*(ulong *)(0x4) == (ulong)smp_chrp_cpu_nr-1 ) prom_print(RELOC("...ok\n")); else prom_print(RELOC("...failed\n")); @@ -1296,8 +1309,6 @@ print_properties(struct device_node *np) } #endif -spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED; - /* this can be called after setup -- Cort */ __openfirmware int @@ -1328,12 +1339,12 @@ call_rtas(const char *service, int nargs, int nret, for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); + + save_flags(s); + cli(); - s = _disable_interrupts(); - spin_lock(&rtas_lock); enter_rtas((void *)__pa(&u)); - spin_unlock(&rtas_lock); - _enable_interrupts(s); + restore_flags(s); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index a9b51a78b..3a4e3b797 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -47,7 +47,7 @@ static inline long get_reg(struct task_struct *task, int regno) { if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) - return ((unsigned long *)task->tss.regs)[regno]; + return ((unsigned long *)task->thread.regs)[regno]; return (0); } @@ -60,8 +60,8 @@ static inline int put_reg(struct task_struct *task, int regno, if (regno <= PT_MQ) { if (regno == PT_MSR) data = (data & MSR_DEBUGCHANGE) - | (task->tss.regs->msr & ~MSR_DEBUGCHANGE); - ((unsigned long *)task->tss.regs)[regno] = data; + | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -1; @@ -70,17 +70,18 @@ static inline int put_reg(struct task_struct *task, int regno, static inline void set_single_step(struct task_struct *task) { - struct pt_regs *regs = task->tss.regs; + struct pt_regs *regs = task->thread.regs; regs->msr |= MSR_SE; } static inline void clear_single_step(struct task_struct *task) { - struct pt_regs *regs = task->tss.regs; + struct pt_regs *regs = task->thread.regs; regs->msr &= ~MSR_SE; } +#if 0 /* * This routine gets a long from any process space by following the page * tables. NOTE! You should check that the long isn't on a page boundary, @@ -283,11 +284,13 @@ static int write_long(struct task_struct * tsk, unsigned long addr, put_long(tsk, vma,addr,data); return 0; } +#endif asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret = -EPERM; + unsigned long flags; lock_kernel(); if (request == PTRACE_TRACEME) { @@ -302,7 +305,10 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (pid == 1) /* you may not mess with init */ goto out; ret = -ESRCH; - if (!(child = find_task_by_pid(pid))) + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!!! */ + if ( !child ) goto out; ret = -EPERM; if (request == PTRACE_ATTACH) { @@ -322,11 +328,15 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (child->flags & PF_PTRACED) goto out; child->flags |= PF_PTRACED; + + write_lock_irqsave(&tasklist_lock, flags); if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); } + write_unlock_irqrestore(&tasklist_lock, flags); + send_sig(SIGSTOP, child, 1); ret = 0; goto out; @@ -342,22 +352,19 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; switch (request) { - /* If I and D space are separate, these will need to be fixed. */ + /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; + int copied; - down(&child->mm->mmap_sem); - ret = read_long(child, addr, &tmp); - up(&child->mm->mmap_sem); - if (ret < 0) + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) goto out; - ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); - if (!ret) - put_user(tmp, (unsigned long *) data); + ret = put_user(tmp,(unsigned long *) data); goto out; } - /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; @@ -377,9 +384,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); } else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { - if (child->tss.regs->msr & MSR_FP) + if (child->thread.regs->msr & MSR_FP) giveup_fpu(child); - tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; + tmp = ((long *)child->thread.fpr)[addr - PT_FPR0]; } else ret = -EIO; @@ -391,11 +398,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) /* If I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - down(&child->mm->mmap_sem); - ret = write_long(child,addr,data); - up(&child->mm->mmap_sem); + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + goto out; + ret = -EIO; goto out; - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; if ((addr & 3) || addr < 0 || addr >= ((PT_FPR0 + 64) << 2)) @@ -412,9 +419,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; } if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { - if (child->tss.regs->msr & MSR_FP) + if (child->thread.regs->msr & MSR_FP) giveup_fpu(child); - ((long *)child->tss.fpr)[addr - PT_FPR0] = data; + ((long *)child->thread.fpr)[addr - PT_FPR0] = data; ret = 0; goto out; } @@ -459,9 +466,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; child->flags &= ~PF_TRACESYS; set_single_step(child); - wake_up_process(child); child->exit_code = data; /* give it a chance to run. */ + wake_up_process(child); ret = 0; goto out; } @@ -473,9 +480,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); /* make sure the single step bit is not set. */ clear_single_step(child); ret = 0; @@ -493,7 +502,6 @@ out: asmlinkage void syscall_trace(void) { - lock_kernel(); if ((current->flags & (PF_PTRACED|PF_TRACESYS)) != (PF_PTRACED|PF_TRACESYS)) goto out; @@ -511,5 +519,4 @@ asmlinkage void syscall_trace(void) current->exit_code = 0; } out: - unlock_kernel(); } diff --git a/arch/ppc/kernel/semaphore.c b/arch/ppc/kernel/semaphore.c new file mode 100644 index 000000000..d630c80dc --- /dev/null +++ b/arch/ppc/kernel/semaphore.c @@ -0,0 +1,139 @@ +/* + * $Id: semaphore.c,v 1.1 1999/08/31 15:11:44 cort Exp $ + * + * PowerPC-specific semaphore code. + * + * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu> + * + * 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. + */ + +#include <linux/sched.h> + +#include <asm/semaphore.h> +#include <asm/semaphore-helper.h> + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waking" variable is + * incremented when the "up()" code goes to wake up waiting + * processes. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * waking_non_zero() (from asm/semaphore.h) must execute + * atomically. + * + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. + * + * This routine adds one to the count of processes that need to + * wake up and exit. ALL waiting processes actually wake up but + * only the one that gets to the "waking" field first will gate + * through and acquire the semaphore. The others will go back + * to sleep. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in <asm/semaphore.h> + * where we want to avoid any extra jumps and calls. + */ +void __up(struct semaphore *sem) +{ + wake_one_more(sem); + wake_up(&sem->wait); +} + +/* + * Perform the "down" function. Return zero for semaphore acquired, + * return negative for signalled out of the function. + * + * If called from __down, the return is ignored and the wait loop is + * not interruptible. This means that a task waiting on a semaphore + * using "down()" cannot be killed until someone does an "up()" on + * the semaphore. + * + * If called from __down_interruptible, the return value gets checked + * upon return. If the return value is negative then the task continues + * with the negative value in the return register (it can be tested by + * the caller). + * + * Either form may be used in conjunction with "up()". + * + */ + +#define DOWN_VAR \ + struct task_struct *tsk = current; \ + wait_queue_t wait; \ + init_waitqueue_entry(&wait, tsk); + +#define DOWN_HEAD(task_state) \ + \ + \ + tsk->state = (task_state); \ + add_wait_queue(&sem->wait, &wait); \ + \ + /* \ + * Ok, we're set up. sem->count is known to be less than zero \ + * so we must wait. \ + * \ + * We can let go the lock for purposes of waiting. \ + * We re-acquire it after awaking so as to protect \ + * all semaphore operations. \ + * \ + * If "up()" is called before we call waking_non_zero() then \ + * we will catch it right away. If it is called later then \ + * we will have to go through a wakeup cycle to catch it. \ + * \ + * Multiple waiters contend for the semaphore lock to see \ + * who gets to gate through and who has to wait some more. \ + */ \ + for (;;) { + +#define DOWN_TAIL(task_state) \ + tsk->state = (task_state); \ + } \ + tsk->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __down(struct semaphore * sem) +{ + DOWN_VAR + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __down_interruptible(struct semaphore * sem) +{ + int ret = 0; + DOWN_VAR + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, tsk); + if (ret) + { + if (ret == 1) + /* ret != 0 only if we get interrupted -arca */ + ret = 0; + break; + } + schedule(); + DOWN_TAIL(TASK_INTERRUPTIBLE) + return ret; +} + +int __down_trylock(struct semaphore * sem) +{ + return waking_non_zero_trylock(sem); +} diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index f3d2b9039..8015b8d30 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.133 1999/05/14 07:24:30 davem Exp $ + * $Id: setup.c,v 1.148 1999/09/05 11:56:34 paulus Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -12,6 +12,7 @@ #include <linux/delay.h> #include <linux/blk.h> +#include <asm/init.h> #include <asm/adb.h> #include <asm/cuda.h> #include <asm/pmu.h> @@ -63,6 +64,12 @@ extern void apus_init(unsigned long r3, unsigned long r6, unsigned long r7); +extern void gemini_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + extern boot_infos_t *boot_infos; extern char cmd_line[512]; char saved_command_line[256]; @@ -121,11 +128,11 @@ struct screen_info screen_info = { /* * I really need to add multiple-console support... -- Cort */ -__initfunc(int pmac_display_supported(char *name)) +int __init pmac_display_supported(char *name) { return 0; } -__initfunc(void pmac_find_display(void)) +void __init pmac_find_display(void) { } @@ -267,6 +274,17 @@ int get_cpuinfo(char *buffer) cpu_node = find_type_devices("cpu"); if ( !cpu_node ) break; + { + int s; + for ( s = 0; (s < i) && cpu_node->next ; + s++, cpu_node = cpu_node->next ) + /* nothing */ ; +#if 0 /* SMP Pmacs don't have all cpu nodes -- Cort */ + if ( s != i ) + printk("get_cpuinfo(): ran out of " + "cpu nodes.\n"); +#endif + } fp = (int *) get_property(cpu_node, "clock-frequency", NULL); if ( !fp ) break; len += sprintf(len+buffer, "clock\t\t: %dMHz\n", @@ -327,10 +345,10 @@ unsigned long __init identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - #ifdef __SMP__ if ( first_cpu_booted ) return 0; #endif /* __SMP__ */ + if ( ppc_md.progress ) ppc_md.progress("id mach(): start", 0x100); #ifndef CONFIG_MACH_SPECIFIC /* boot loader will tell us if we're APUS */ @@ -391,6 +409,8 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, _machine = _MACH_fads; #elif defined(CONFIG_APUS) _machine = _MACH_apus; +#elif defined(CONFIG_GEMINI) + _machine = _MACH_gemini; #else #error "Machine not defined correctly" #endif /* CONFIG_APUS */ @@ -474,16 +494,19 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, mbx_init(r3, r4, r5, r6, r7); break; #endif + case _MACH_gemini: + gemini_init(r3, r4, r5, r6, r7); + break; default: printk("Unknown machine type in identify_machine!\n"); } - /* Check for nobats option (used in mapin_ram). */ if (strstr(cmd_line, "nobats")) { extern int __map_without_bats; __map_without_bats = 1; } - + + if ( ppc_md.progress ) ppc_md.progress("id mach(): done", 0x200); return 0; } @@ -499,16 +522,18 @@ void ppc_setup_l2cr(char *str, int *ints) } } -__initfunc(void - ppc_init(void)) +void __init ppc_init(void) { + /* clear the progress line */ + if ( ppc_md.progress ) ppc_md.progress(" ", 0xffff); + if (ppc_md.init != NULL) { ppc_md.init(); } } -__initfunc(void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p)) +void __init setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) { extern int panic_timeout; extern char _etext[], _edata[]; @@ -525,11 +550,11 @@ __initfunc(void setup_arch(char **cmdline_p, /* reboot on panic */ panic_timeout = 180; - - init_task.mm->start_code = PAGE_OFFSET; - init_task.mm->end_code = (unsigned long) _etext; - init_task.mm->end_data = (unsigned long) _edata; - init_task.mm->brk = (unsigned long) klimit; + + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) klimit; /* Save unparsed command line copy for /proc/cmdline */ strcpy(saved_command_line, cmd_line); @@ -539,6 +564,8 @@ __initfunc(void setup_arch(char **cmdline_p, *memory_end_p = (unsigned long) end_of_DRAM; ppc_md.setup_arch(memory_start_p, memory_end_p); + /* clear the progress line */ + if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); } void ppc_generic_ide_fix_driveid(struct hd_driveid *id) diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 67dfa437c..0d55bcefc 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -1,7 +1,7 @@ /* * linux/arch/ppc/kernel/signal.c * - * $Id: signal.c,v 1.24 1999/04/03 11:25:16 paulus Exp $ + * $Id: signal.c,v 1.27 1999/08/03 19:16:38 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -227,7 +227,7 @@ int sys_sigreturn(struct pt_regs *regs) | (saved_regs[PT_MSR] & MSR_USERCHANGE); memcpy(regs, saved_regs, GP_REGS_SIZE); - if (copy_from_user(current->tss.fpr, &sr->fp_regs, + if (copy_from_user(current->thread.fpr, &sr->fp_regs, sizeof(sr->fp_regs))) goto badframe; @@ -269,7 +269,7 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame, if (regs->msr & MSR_FP) giveup_fpu(current); if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) - || __copy_to_user(&frame->fp_regs, current->tss.fpr, + || __copy_to_user(&frame->fp_regs, current->thread.fpr, ELF_NFPREG * sizeof(double)) || __put_user(0x38007777UL, &frame->tramp[0]) /* li r0,0x7777 */ || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ @@ -444,12 +444,8 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: - lock_kernel(); - if (current->binfmt - && current->binfmt->core_dump - && current->binfmt->core_dump(signr, regs)) + if (do_coredump(signr, regs)) exit_code |= 0x80; - unlock_kernel(); /* FALLTHRU */ default: diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index a22275135..3d2fb057f 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.52 1999/05/23 22:43:51 cort Exp $ + * $Id: smp.c,v 1.62 1999/09/05 11:56:34 paulus Exp $ * * Smp support for ppc. * @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/tasks.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/interrupt.h> @@ -22,18 +21,19 @@ #include <linux/unistd.h> #include <linux/init.h> #include <linux/openpic.h> +#include <linux/spinlock.h> #include <asm/ptrace.h> #include <asm/atomic.h> #include <asm/irq.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/spinlock.h> #include <asm/hardirq.h> #include <asm/softirq.h> #include <asm/init.h> #include <asm/io.h> #include <asm/prom.h> +#include <asm/smp.h> #include "time.h" int first_cpu_booted = 0; @@ -242,6 +242,7 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) void __init smp_boot_cpus(void) { extern struct task_struct *current_set[NR_CPUS]; + extern unsigned long smp_chrp_cpu_nr; extern void __secondary_start_psurge(void); extern void __secondary_start_chrp(void); int i, cpu_nr; @@ -252,16 +253,18 @@ void __init smp_boot_cpus(void) /* let other processors know to not do certain initialization */ first_cpu_booted = 1; smp_num_cpus = 1; - + smp_store_cpu_info(0); + /* * assume for now that the first cpu booted is * cpu 0, the master -- Cort */ cpu_callin_map[0] = 1; - smp_store_cpu_info(0); active_kernel_processor = 0; current->processor = 0; + init_idle(); + for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; prof_multiplier[i] = 1; @@ -286,9 +289,13 @@ void __init smp_boot_cpus(void) cpu_nr = 2; break; case _MACH_chrp: + /* openpic doesn't report # of cpus, just # possible -- Cort */ +#if 0 cpu_nr = ((openpic_read(&OpenPIC->Global.Feature_Reporting0) & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1; +#endif + cpu_nr = smp_chrp_cpu_nr; break; } @@ -299,12 +306,21 @@ void __init smp_boot_cpus(void) for ( i = 1 ; i < cpu_nr; i++ ) { int c; + struct pt_regs regs; + struct task_struct *idle; /* create a process for the processor */ - kernel_thread(start_secondary, NULL, CLONE_PID); - p = task[i]; - if ( !p ) - panic("No idle task for secondary processor\n"); + /* we don't care about the values in regs since we'll + never reschedule the forked task. */ + if (do_fork(CLONE_VM|CLONE_PID, 0, ®s) < 0) + panic("failed fork for CPU %d", i); + p = init_task.prev_task; + if (!p) + panic("No idle task for CPU %d", i); + del_from_runqueue(p); + unhash_process(p); + init_tasks[i] = p; + p->processor = i; p->has_cpu = 1; current_set[i] = p; @@ -324,6 +340,7 @@ void __init smp_boot_cpus(void) eieio(); /* interrupt secondary to begin executing code */ out_be32(PSURGE_INTR, ~0); + udelay(1); out_be32(PSURGE_INTR, 0); break; case _MACH_chrp: @@ -380,6 +397,7 @@ void __init smp_commence(void) /* * Lets the callin's below out of their loop. */ + wmb(); smp_commenced = 1; } @@ -389,8 +407,10 @@ void __init initialize_secondary(void) } /* Activate a secondary processor. */ -asmlinkage int __init start_secondary(void *unused) +int __init start_secondary(void *unused) { + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; smp_callin(); return cpu_idle(NULL); } @@ -403,7 +423,7 @@ void __init smp_callin(void) #if 0 current->mm->mmap->vm_page_prot = PAGE_SHARED; current->mm->mmap->vm_start = PAGE_OFFSET; - current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + current->mm->mmap->vm_end = init_mm.mmap->vm_end; #endif cpu_callin_map[current->processor] = 1; while(!smp_commenced) diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c index 64f954b2e..a97c272d3 100644 --- a/arch/ppc/kernel/softemu8xx.c +++ b/arch/ppc/kernel/softemu8xx.c @@ -64,7 +64,7 @@ Soft_emulate_8xx(struct pt_regs *regs) disp = instword & 0xffff; ea = (uint *)(regs->gpr[idxreg] + disp); - ip = (uint *)¤t->tss.fpr[flreg]; + ip = (uint *)¤t->thread.fpr[flreg]; switch ( inst ) { @@ -108,7 +108,7 @@ Soft_emulate_8xx(struct pt_regs *regs) break; case FMR: /* assume this is a fp move -- Cort */ - memcpy( ip, ¤t->tss.fpr[(instword>>11)&0x1f], + memcpy( ip, ¤t->thread.fpr[(instword>>11)&0x1f], sizeof(double) ); break; default: diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index 571b36391..30bed889b 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -37,6 +37,7 @@ #include <asm/uaccess.h> #include <asm/ipc.h> +#include <asm/semaphore.h> void check_bugs(void) diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index 5990dad90..d38fa7a22 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.48 1999/05/22 19:35:57 cort Exp $ + * $Id: time.c,v 1.55 1999/08/31 06:54:09 davem Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -52,7 +52,7 @@ void smp_local_timer_interrupt(struct pt_regs *); /* keep track of when we need to update the rtc */ -unsigned long last_rtc_update = 0; +time_t last_rtc_update = 0; /* The decrementer counts down by 128 every 128ns on a 601. */ #define DECREMENTER_COUNT_601 (1000000000 / HZ) @@ -110,15 +110,15 @@ void timer_interrupt(struct pt_regs * regs) /* * update the rtc when needed */ - if ( xtime.tv_sec > last_rtc_update + 660 ) + if ( (time_status & STA_UNSYNC) && + ((xtime.tv_sec > last_rtc_update + 60) || + (xtime.tv_sec < last_rtc_update)) ) { - if (ppc_md.set_rtc_time(xtime.tv_sec) == 0) { + if (ppc_md.set_rtc_time(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; - } - else { + else /* do it again in 60 s */ - last_rtc_update = xtime.tv_sec - 60; - } + last_rtc_update = xtime.tv_sec; } } } @@ -176,7 +176,7 @@ void do_settimeofday(struct timeval *tv) } -__initfunc(void time_init(void)) +void __init time_init(void) { if (ppc_md.time_init != NULL) { @@ -196,10 +196,8 @@ __initfunc(void time_init(void)) xtime.tv_usec = 0; set_dec(decrementer_count); - /* mark the rtc/on-chip timer as in sync - * so we don't update right away - */ - last_rtc_update = xtime.tv_sec; + /* allow setting the time right away */ + last_rtc_update = 0; } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h index a1c912308..a0a5c62d2 100644 --- a/arch/ppc/kernel/time.h +++ b/arch/ppc/kernel/time.h @@ -1,5 +1,5 @@ /* - * $Id: time.h,v 1.11 1999/03/18 04:16:34 cort Exp $ + * $Id: time.h,v 1.12 1999/08/27 04:21:23 cort Exp $ * Common time prototypes and such for all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -15,7 +15,7 @@ extern unsigned count_period_den; extern unsigned long mktime(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); extern void to_tm(int tim, struct rtc_time * tm); -extern unsigned long last_rtc_update; +extern time_t last_rtc_update; int via_calibrate_decr(void); diff --git a/arch/ppc/kernel/totalmp.c b/arch/ppc/kernel/totalmp.c index 5f87755a7..ecbc04b57 100644 --- a/arch/ppc/kernel/totalmp.c +++ b/arch/ppc/kernel/totalmp.c @@ -1,5 +1,5 @@ /* - * $Id: totalmp.c,v 1.5 1998/08/26 13:58:50 cort Exp $ + * $Id: totalmp.c,v 1.6 1999/08/31 06:54:10 davem Exp $ * * Support for Total Impact's TotalMP PowerPC accelerator board. * @@ -25,7 +25,7 @@ extern void totalmp_init(void); extern inline void openpic_writefield(volatile u_int *addr, u_int mask, u_int field); -__initfunc(void totalmp_init(void)) +void __init totalmp_init(void) { struct pci_dev *dev; u32 val; diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index c33bff3e0..70eafd317 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -229,26 +229,26 @@ trace_syscall(struct pt_regs *regs) void SoftwareEmulation(struct pt_regs *regs) { - int errcode; - extern int Soft_emulate_8xx (struct pt_regs *regs); - extern void print_8xx_pte(struct mm_struct *, unsigned long); + extern int do_mathemu(struct pt_regs *); + int errcode; - if (user_mode(regs)) - { - if ((errcode = Soft_emulate_8xx(regs))) { -printk("Software Emulation %s/%d NIP: %lx *NIP: 0x%x code: %x", - current->comm,current->pid, - regs->nip, *((uint *)regs->nip), errcode); -/*print_8xx_pte(current->mm, regs->nip);*/ - if (errcode == EFAULT) - _exception(SIGBUS, regs); - else - _exception(SIGILL, regs); - } - } - else { + if (!user_mode(regs)) { + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif + print_backtrace((unsigned long *)regs->gpr[1]); panic("Kernel Mode Software FPU Emulation"); } + + if ((errcode = do_mathemu(regs))) { + if (errcode > 0) + _exception(SIGFPE, regs); + else if (errcode == -EFAULT; + _exception(SIGSEGV, regs); + else + _exception(SIGILL, regs); + } } #endif @@ -259,6 +259,6 @@ TAUException(struct pt_regs *regs) regs->nip, regs->msr, regs->trap); } -__initfunc(void trap_init(void)) +void __init trap_init(void) { } |