diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /arch/sparc64/kernel | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff) |
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash.
o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'arch/sparc64/kernel')
29 files changed, 2080 insertions, 931 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 7889c84a4..1c3f459bc 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.35 1997/09/20 21:48:58 davem Exp $ +# $Id: Makefile,v 1.36 1998/02/01 11:15:55 ecd Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -61,13 +61,24 @@ head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \ binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c check_asm: dummy + @echo "/* Automatically generated. Do not edit. */" > asm_offsets.h + @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h + @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#ifndef __SMP__" >> asm_offsets.h + @echo "" >> asm_offsets.h @echo "#include <linux/sched.h>" > tmp.c $(CC) -E tmp.c -o tmp.i - @echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c + @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/sched.h>" >> check_asm.c + @echo 'struct task_struct _task;' >> check_asm.c + @echo 'struct mm_struct _mm;' >> check_asm.c + @echo 'struct thread_struct _thread;' >> check_asm.c + @echo 'int main(void) {' >> check_asm.c $(SH) ./check_asm.sh task tmp.i check_asm.c $(SH) ./check_asm.sh mm tmp.i check_asm.c $(SH) ./check_asm.sh thread tmp.i check_asm.c - @echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c + @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] #$(CC) -o check_asm check_asm.c # <hack> Until we can do this natively, a hack has to take place @@ -75,9 +86,46 @@ check_asm: dummy $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @rm -f check_asm.s # </hack> - ./check_asm > asm_offsets.h - @if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi + ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c + @echo "" >> asm_offsets.h + @echo "#else /* __SMP__ */" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#include <linux/sched.h>" > tmp.c + $(CC) -D__SMP__ -E tmp.c -o tmp.i + @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/sched.h>" >> check_asm.c + @echo 'struct task_struct _task;' >> check_asm.c + @echo 'struct mm_struct _mm;' >> check_asm.c + @echo 'struct thread_struct _thread;' >> check_asm.c + @echo 'int main(void) {' >> check_asm.c + $(SH) ./check_asm.sh task tmp.i check_asm.c + $(SH) ./check_asm.sh mm tmp.i check_asm.c + $(SH) ./check_asm.sh thread tmp.i check_asm.c + @echo 'return 0; }' >> check_asm.c + @rm -f tmp.[ci] + #$(CC) -D__SMP__ -o check_asm check_asm.c + # <hack> Until we can do this natively, a hack has to take place + $(CC) -D__SMP__ -mmedlow -ffixed-g4 -S -o check_asm.s check_asm.c + $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s + @rm -f check_asm.s + # </hack> + ./check_asm >> asm_offsets.h + @rm -f check_asm check_asm.c + @echo "" >> asm_offsets.h + @echo "#endif /* __SMP__ */" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h + @if test -r $(HPATH)/asm/asm_offsets.h; then \ + if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \ + echo $(HPATH)/asm/asm_offsets.h is unchanged; \ + rm -f asm_offsets.h; \ + else \ + mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \ + fi; \ + else \ + mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \ + fi include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index 55ccbc203..b0dd675b0 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/stat.h> #include <linux/fcntl.h> +#include <linux/file.h> #include <linux/ptrace.h> #include <linux/user.h> #include <linux/malloc.h> @@ -257,7 +258,7 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm, unsigned long p = bprm->p; unsigned long fd_offset; unsigned long rlim; -int retval; + int retval; ex = *((struct exec *) bprm->buf); /* exec-header */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && @@ -326,10 +327,10 @@ int retval; printk(KERN_NOTICE "executable not page aligned\n"); fd = open_dentry(bprm->dentry, O_RDONLY); - if (fd < 0) return fd; - file = current->files->fd[fd]; + file = fcheck(fd); + if (!file->f_op || !file->f_op->mmap) { sys_close(fd); do_mmap(NULL, 0, ex.a_text+ex.a_data, @@ -397,6 +398,7 @@ load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) return retval; } +/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */ static inline int do_load_aout32_library(int fd) { @@ -409,7 +411,7 @@ do_load_aout32_library(int fd) unsigned int start_addr; unsigned long error; - file = current->files->fd[fd]; + file = fcheck(fd); if (!file || !file->f_op) return -EACCES; diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index 817a8ecd3..a54e89f2d 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -1,4 +1,4 @@ -/* $Id: central.c,v 1.4 1997/08/19 14:17:49 jj Exp $ +/* $Id: central.c,v 1.5 1998/02/12 15:57:59 jj Exp $ * central.c: Central FHC driver for Sunfire/Starfire/Wildfire. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -35,22 +35,18 @@ unsigned long central_probe(unsigned long memory_start) printk("no central found.\n"); return memory_start; } - prom_printf("CENTRAL: found central PROM node.\n"); printk("found central PROM node.\n"); /* Ok we got one, grab some memory for software state. */ memory_start = long_align(memory_start); central_bus = (struct linux_central *) (memory_start); - prom_printf("CENTRAL: central_bus[%p] ", central_bus); memory_start += sizeof(struct linux_central); memory_start = long_align(memory_start); fhc = (struct linux_fhc *)(memory_start); memory_start += sizeof(struct linux_fhc); memory_start = long_align(memory_start); - prom_printf("fhc[%p] ", fhc); - /* First init central. */ central_bus->child = fhc; central_bus->prom_node = cnode; @@ -58,7 +54,6 @@ unsigned long central_probe(unsigned long memory_start) prom_getstring(cnode, "name", namebuf, sizeof(namebuf)); strcpy(central_bus->prom_name, namebuf); - prom_printf("init_central_ranges "); prom_central_ranges_init(cnode, central_bus); /* And then central's FHC. */ @@ -73,27 +68,15 @@ unsigned long central_probe(unsigned long memory_start) prom_getstring(fnode, "name", namebuf, sizeof(namebuf)); strcpy(fhc->prom_name, namebuf); - prom_printf("cnode[%x] fnode[%x] init_fhc_ranges\n", cnode, fnode); prom_fhc_ranges_init(fnode, fhc); - /* Finally, map in FHC register set. (From the prtconf dumps - * I have seen on Ex000 boxes only the central ranges need to - * be applied to the fhc internal register set) -DaveM - */ - err = prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)); - if(err == -1) { + /* Finally, map in FHC register set. */ + if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) { prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n"); prom_halt(); } prom_apply_central_ranges(central_bus, &fpregs[0], 6); - prom_printf("CENTRAL: FHC_REGS[(%08x,%08x) (%08x,%08x) " - "(%08x,%08x) (%08x,%08x) (%08x,%08x) (%08x,%08x)]\n", - fpregs[0].which_io, fpregs[0].phys_addr, - fpregs[1].which_io, fpregs[1].phys_addr, - fpregs[2].which_io, fpregs[2].phys_addr, - fpregs[3].which_io, fpregs[3].phys_addr, - fpregs[4].which_io, fpregs[4].phys_addr, - fpregs[5].which_io, fpregs[5].phys_addr); + fhc->fhc_regs.pregs = (struct fhc_internal_regs *) __va((((unsigned long)fpregs[0].which_io)<<32) | (((unsigned long)fpregs[0].phys_addr))); @@ -112,14 +95,8 @@ unsigned long central_probe(unsigned long memory_start) fhc->fhc_regs.tregs = (struct fhc_tod_regs *) __va((((unsigned long)fpregs[5].which_io)<<32) | (((unsigned long)fpregs[5].phys_addr))); - prom_printf("CENTRAL: FHC_REGS[%p %p %p %p %p %p]\n", - fhc->fhc_regs.pregs, fhc->fhc_regs.ireg, - fhc->fhc_regs.ffregs, fhc->fhc_regs.sregs, - fhc->fhc_regs.uregs, fhc->fhc_regs.tregs); - prom_printf("CENTRAL: reading FHC_ID register... "); err = fhc->fhc_regs.pregs->fhc_id; - prom_printf("VALUE[%x]\n", err); printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n", ((err & FHC_ID_VERS) >> 28), ((err & FHC_ID_PARTID) >> 12), diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index d009f39d8..86efc4bb7 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -32,7 +32,8 @@ struct cpu_fp_info linux_sparc_fpu[] = { { 0x17, 0x10, 0, "UltraSparc I integrated FPU"}, { 0x22, 0x10, 0, "UltraSparc II integrated FPU"}, { 0x17, 0x11, 0, "UltraSparc II integrated FPU"}, - { 0x17, 0x12, 0, "UltraSparc III integrated FPU"}, + { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"}, + { 0x17, 0x13, 0, "UltraSparc III integrated FPU"}, }; #define NSPARCFPU (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info)) @@ -41,7 +42,8 @@ struct cpu_iu_info linux_sparc_chips[] = { { 0x17, 0x10, "TI UltraSparc I (SpitFire)"}, { 0x22, 0x10, "TI UltraSparc II (BlackBird)"}, { 0x17, 0x11, "TI UltraSparc II (BlackBird)"}, - { 0x17, 0x12, "TI UltraSparc III (Cheetah)"}, /* A guess... */ + { 0x17, 0x12, "TI UltraSparc IIi"}, + { 0x17, 0x13, "TI UltraSparc III (Cheetah)"}, /* A guess... */ }; #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 24ca3ff10..8d3aca325 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -13,7 +13,8 @@ #include <asm/system.h> #include <asm/smp.h> -struct prom_cpuinfo linux_cpus[NR_CPUS]; +struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } }; +unsigned prom_cpu_nodes[NR_CPUS]; int linux_num_cpus = 0; extern void cpu_probe(void); @@ -64,6 +65,8 @@ device_scan(unsigned long mem_start)) prom_node_cpu = cpu_nds[0]; linux_num_cpus = cpu_ctr; + + prom_cpu_nodes[0] = prom_node_cpu; cpu_probe(); return central_probe(mem_start); diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc64/kernel/dtlb_miss.S index 4d71d967c..e5606cf33 100644 --- a/arch/sparc64/kernel/dtlb_miss.S +++ b/arch/sparc64/kernel/dtlb_miss.S @@ -1,9 +1,9 @@ -/* $Id: dtlb_miss.S,v 1.14 1997/10/14 01:48:28 davem Exp $ +/* $Id: dtlb_miss.S,v 1.15 1998/01/14 17:14:44 jj Exp $ * dtlb_miss.S: Data TLB miss code, this is included directly * into the trap table. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* The basic algorithm is: @@ -36,22 +36,22 @@ #define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#define KERN_LOWBITS_IO ((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS) +#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W) /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET - /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset - /*0x08*/ srlx %g1, 48, %g5 ! Shift down CONTEXT bits - /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ sllx %g1, 2, %g4 ! Position PMD offset - /*0x14*/ brz,pn %g5, 3f ! Context 0 == kernel - /*0x18*/ and %g4, %g2, %g4 ! Mask PMD offset + /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset + /*0x08*/ andcc %g1, %g2, %g0 ! Test CONTEXT bits + /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset + /*0x18*/ and %g1, 0xffe, %g4 ! Mask PMD offset + /*0x14*/ be,pn %xcc, 3f ! Context 0 == kernel + /*0x10*/ add %g4, %g4, %g4 ! Position PMD offset /*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset /* ICACHE line 2 */ - /*0x20*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD + /*0x20*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD /*0x24*/ srlx %g1, 1, %g1 ! PTE offset -2:/*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD +2:/*0x28*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD /*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE /*0x30*/ brgez,pn %g5, sparc64_dtlb_refbit_catch ! Valid set? /*0x34*/ nop ! delay @@ -61,22 +61,22 @@ 3: /* ICACHE line 3 */ /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET /*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables - /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET - /*0x4c*/ andcc %g3, 0x400, %g0 ! Slick trick... - /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up - /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO - /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page? - /*0x5c*/ xor %g1, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO... + /*0x48*/ or %g2, (KERN_LOWBITS), %g1 ! Construct PTE ^ PAGE_OFFSET + /*0x4c*/ andcc %g3, 0x100, %g0 ! Slick trick... + /*0x50*/ bne,a,pn %icc, 5f ! Is it an IO page? + /*0x54*/ or %g2, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO... +5:/*0x58*/ xor %g1, %g5, %g1 ! Slick trick II... + /*0x5c*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load /* ICACHE line 4 */ -5:/*0x60*/ xor %g1, %g5, %g1 ! Slick trick II... - /*0x64*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load - /*0x68*/ retry ! Trap return -4:/*0x6c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset - /*0x70*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD - /*0x74*/ ba,pt %xcc, 2b ! Go back up top - /*0x78*/ srlx %g1, 1, %g1 - /*0x7c*/ nop + /*0x60*/ retry ! Trap return + /*0x64*/ nop + /*0x68*/ nop + /*0x6c*/ nop +4:/*0x70*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset + /*0x74*/ lduwa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD + /*0x78*/ ba,pt %xcc, 2b ! Go back up top + /*0x7c*/ srlx %g1, 1, %g1 #undef KERN_HIGHBITS #undef KERN_LOWBITS diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S index 55e86c887..86cbfdc52 100644 --- a/arch/sparc64/kernel/dtlb_prot.S +++ b/arch/sparc64/kernel/dtlb_prot.S @@ -1,9 +1,9 @@ -/* $Id: dtlb_prot.S,v 1.14 1997/08/03 09:07:00 davem Exp $ +/* $Id: dtlb_prot.S,v 1.15 1998/01/14 17:14:46 jj Exp $ * dtlb_prot.S: Data TLB protection code, this is included directly * into the trap table. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* We know kernel never takes protection trap, @@ -15,12 +15,12 @@ /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET - /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset - /*0x08*/ sllx %g1, 2, %g4 ! Position PMD offset - /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ and %g4, %g2, %g4 ! Mask PMD offset - /*0x14*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD - /*0x18*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD + /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset + /*0x08*/ and %g1, 0xffe, %g4 ! Mask PMD offset + /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset + /*0x10*/ add %g4, %g4, %g4 ! Position PMD offset + /*0x14*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD + /*0x18*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD /*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset /* ICACHE line 2 */ @@ -34,10 +34,10 @@ /*0x3c*/ ldxa [%g5] ASI_DMMU, %g4 ! From MMU /* ICACHE line 3 */ - /*0x40*/ add %g2, 7, %g5 ! Compute mask - /*0x44*/ andn %g4, %g5, %g4 ! Mask page - /*0x48*/ mov TLB_SFSR, %g5 ! read SFSR - /*0x4c*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for + /*0x40*/ mov TLB_SFSR, %g5 ! read SFSR + /*0x44*/ srlx %g4, 13, %g4 ! Prepare... + /*0x48*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for + /*0x4c*/ sllx %g4, 13, %g4 ! ...and mask page /*0x50*/ and %g5, 0x10, %g5 ! context bit /*0x54*/ or %g4, %g5, %g4 ! for prot trap 1:/*0x58*/ stxa %g0, [%g4] ASI_DMMU_DEMAP ! TLB flush page diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 02faf4e3c..954cfd4bc 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.17 1998/01/10 18:26:13 ecd Exp $ +/* $Id: ebus.c,v 1.23 1998/03/29 16:27:24 ecd Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -29,14 +29,12 @@ struct linux_ebus *ebus_chain = 0; extern void prom_ebus_ranges_init(struct linux_ebus *); +extern void prom_ebus_intmap_init(struct linux_ebus *); extern unsigned long pci_console_init(unsigned long memory_start); #ifdef CONFIG_SUN_OPENPROMIO extern int openprom_init(void); #endif -#ifdef CONFIG_SUN_MOSTEK_RTC -extern int rtc_init(void); -#endif #ifdef CONFIG_SPARCAUDIO extern int sparcaudio_init(void); #endif @@ -46,6 +44,9 @@ extern void auxio_probe(void); #ifdef CONFIG_OBP_FLASH extern int flash_init(void); #endif +#ifdef CONFIG_ENVCTRL +extern int envctrl_init(void); +#endif extern unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino); @@ -62,7 +63,35 @@ ebus_alloc(unsigned long *memory_start, size_t size) return mem; } -__initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) +__initfunc(void ebus_intmap_match(struct linux_ebus *ebus, + struct linux_prom_registers *reg, + int *interrupt)) +{ + unsigned int hi, lo, irq; + int i; + + if (!ebus->num_ebus_intmap) + return; + + hi = reg->which_io & ebus->ebus_intmask.phys_hi; + lo = reg->phys_addr & ebus->ebus_intmask.phys_lo; + irq = *interrupt & ebus->ebus_intmask.interrupt; + for (i = 0; i < ebus->num_ebus_intmap; i++) { + if ((ebus->ebus_intmap[i].phys_hi == hi) && + (ebus->ebus_intmap[i].phys_lo == lo) && + (ebus->ebus_intmap[i].interrupt == irq)) { + *interrupt = ebus->ebus_intmap[i].cinterrupt; + return; + } + } + + prom_printf("ebus: IRQ [%08x.%08x.%08x] not found in interrupt-map\n", + reg->which_io, reg->phys_addr, *interrupt); + prom_halt(); +} + +__initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg, + struct linux_ebus_child *dev)) { int regs[PROMREG_MAX]; int irqs[PROMREG_MAX]; @@ -90,8 +119,10 @@ __initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) dev->num_irqs = 0; } else { dev->num_irqs = len / sizeof(irqs[0]); - for (i = 0; i < dev->num_irqs; i++) + for (i = 0; i < dev->num_irqs; i++) { + ebus_intmap_match(dev->bus, preg, &irqs[i]); dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]); + } } #ifdef DEBUG_FILL_EBUS_DEV @@ -108,7 +139,8 @@ __initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) #endif } -__initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *dev, +__initfunc(unsigned long fill_ebus_device(int node, + struct linux_ebus_device *dev, unsigned long memory_start)) { struct linux_prom_registers regs[PROMREG_MAX]; @@ -142,8 +174,10 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de dev->num_irqs = 0; } else { dev->num_irqs = len / sizeof(irqs[0]); - for (i = 0; i < dev->num_irqs; i++) + for (i = 0; i < dev->num_irqs; i++) { + ebus_intmap_match(dev->bus, ®s[0], &irqs[i]); dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]); + } } #ifdef DEBUG_FILL_EBUS_DEV @@ -166,7 +200,7 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de child->next = 0; child->parent = dev; child->bus = dev->bus; - fill_ebus_child(node, child); + fill_ebus_child(node, ®s[0], child); while ((node = prom_getsibling(node))) { child->next = (struct linux_ebus_child *) @@ -176,13 +210,16 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de child->next = 0; child->parent = dev; child->bus = dev->bus; - fill_ebus_child(node, child); + fill_ebus_child(node, ®s[0], child); } } return memory_start; } +extern void sun4u_start_timers(void); +extern void clock_probe(void); + __initfunc(unsigned long ebus_init(unsigned long memory_start, unsigned long memory_end)) { @@ -199,14 +236,10 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, int reg, rng, nreg; int num_ebus = 0; - if (!pcibios_present()) + if (!pci_present()) return memory_start; - for (pdev = pci_devices; pdev; pdev = pdev->next) { - if ((pdev->vendor == PCI_VENDOR_ID_SUN) && - (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) - break; - } + pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, 0); if (!pdev) { printk("ebus: No EBus's found.\n"); #ifdef PROM_DEBUG @@ -236,11 +269,9 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, ebus->parent = pbm = cookie->pbm; /* Enable BUS Master. */ - pcibios_read_config_word(pdev->bus->number, pdev->devfn, - PCI_COMMAND, &pci_command); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pdev->bus->number, pdev->devfn, - PCI_COMMAND, pci_command); + pci_write_config_word(pdev, PCI_COMMAND, pci_command); len = prom_getproperty(ebusnd, "reg", (void *)regs, sizeof(regs)); @@ -285,6 +316,7 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, #endif prom_ebus_ranges_init(ebus); + prom_ebus_intmap_init(ebus); nd = prom_getchild(ebusnd); if (!nd) @@ -312,11 +344,8 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, } next_ebus: - for (pdev = pdev->next; pdev; pdev = pdev->next) { - if ((pdev->vendor == PCI_VENDOR_ID_SUN) && - (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) - break; - } + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_EBUS, pdev); if (!pdev) break; @@ -335,9 +364,6 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, #ifdef CONFIG_SUN_OPENPROMIO openprom_init(); #endif -#ifdef CONFIG_SUN_MOSTEK_RTC - rtc_init(); -#endif #ifdef CONFIG_SPARCAUDIO sparcaudio_init(); #endif @@ -345,20 +371,15 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start, bpp_init(); #endif #ifdef CONFIG_SUN_AUXIO - if (sparc_cpu_model == sun4u) - auxio_probe(); + auxio_probe(); +#endif +#ifdef CONFIG_ENVCTRL + envctrl_init(); #endif #ifdef CONFIG_OBP_FLASH flash_init(); #endif -#ifdef __sparc_v9__ - if (sparc_cpu_model == sun4u) { - extern void sun4u_start_timers(void); - extern void clock_probe(void); - - sun4u_start_timers(); - clock_probe(); - } -#endif + sun4u_start_timers(); + clock_probe(); return memory_start; } diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 43f950b25..c0531f30a 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,9 +1,9 @@ -/* $Id: head.S,v 1.46 1997/08/08 08:33:30 jj Exp $ +/* $Id: head.S,v 1.49 1998/03/03 12:31:17 jj Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) */ @@ -26,7 +26,7 @@ /* This section from from _start to sparc64_boot_end should fit into * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to - * 0x0000.0000.0040.6000 and bootup_kernel_stack, which is from + * 0x0000.0000.0040.6000 and empty_bad_page, which is from * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000. */ @@ -326,6 +326,8 @@ sun4u_init: nop /* Not reached... */ +/* IMPORTANT NOTE: Whenever making changes here, check + * trampoline.S as well. -jj */ .globl setup_tba setup_tba: save %sp, -160, %sp @@ -346,9 +348,11 @@ setup_tba: /* Set up MMU globals */ wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate - /* PGD/PMD offset mask, used by TLB miss handlers. */ - sethi %hi(0x1ff8), %g2 - or %g2, %lo(0x1ff8), %g2 + /* Set KERN_HIGHBITS used by dTLB miss handler. */ +#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) + sethi %uhi(KERN_HIGHBITS), %g2 + sllx %g2, 32, %g2 +#undef KERN_HIGHBITS /* Kernel PGDIR used by TLB miss handlers. */ mov %i0, %g6 @@ -391,7 +395,8 @@ sparc64_boot_end: .skip 0x2000 + _start - sparc64_boot_end bootup_user_stack_end: -bootup_kernel_stack: + .globl empty_bad_page +empty_bad_page: .skip 0x2000 ! 0x0000000000408000 diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c index 1829daeea..86b6c3dd6 100644 --- a/arch/sparc64/kernel/init_task.c +++ b/arch/sparc64/kernel/init_task.c @@ -6,7 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct files * init_fd_array[NR_OPEN] = { NULL, }; +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; diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 64465663d..17e904f25 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.26 1997/12/15 15:11:02 jj Exp $ +/* $Id: ioctl32.c,v 1.35 1998/04/10 02:01:46 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -23,9 +23,11 @@ #include <linux/netlink.h> #include <linux/vt.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/fd.h> #include <linux/if_ppp.h> #include <linux/mtio.h> +#include <linux/cdrom.h> #include <scsi/scsi.h> /* Ugly hack. */ @@ -64,6 +66,30 @@ static int w_long(unsigned int fd, unsigned int cmd, u32 arg) return err; } +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct timeval32 *up = (struct timeval32 *)A(arg); + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if(!err) { + if(!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __put_user(ktv.tv_sec, &up->tv_sec) || + __put_user(ktv.tv_usec, &up->tv_usec)) + err = -EFAULT; + } + return err; +} + struct ifmap32 { u32 mem_start; u32 mem_end; @@ -948,6 +974,90 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) return 0; } +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + if (__get_user(cdread.cdread_lba, &((struct cdrom_read32 *)A(arg))->cdread_lba) || + __get_user(addr, &((struct cdrom_read32 *)A(arg))->cdread_bufaddr) || + __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)A(arg))->cdread_buflen)) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + if (copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)A(arg))->addr, sizeof(cdreadaudio.addr)) || + __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)A(arg))->addr_format) || + __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)A(arg))->nframes) || + __get_user(addr, &((struct cdrom_read_audio32 *)A(arg))->buf)) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + default: + printk("cdrom_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) { + if (data) kfree(data); + return err; + } + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + if (copy_to_user((char *)A(addr), data, cdread.cdread_buflen)) { + kfree(data); + return -EFAULT; + } + break; + case CDROMREADAUDIO: + if (copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352)) { + kfree(data); + return -EFAULT; + } + break; + default: + break; + } + if (data) kfree(data); + return 0; +} asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { @@ -955,10 +1065,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) int error = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - filp = current->files->fd[fd]; + filp = fcheck(fd); if(!filp) goto out; @@ -966,7 +1073,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; } - error = -EFAULT; switch (cmd) { case SIOCGIFCONF: error = dev_ifconf(fd, arg); @@ -1014,6 +1120,11 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = -EINVAL; goto out; + case SIOCGSTAMP: + /* Sorry, timeval in the kernel is different now. */ + error = do_siocgstamp(fd, cmd, arg); + goto out; + case HDIO_GETGEO: error = hdio_getgeo(fd, arg); goto out; @@ -1066,6 +1177,15 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = mt_ioctl_trans(fd, cmd, arg); goto out; + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + case CDROMREADAUDIO: + case CDROMREADALL: + error = cdrom_ioctl_trans(fd, cmd, arg); + goto out; + /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... */ @@ -1170,6 +1290,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) /* 0x09 */ case REGISTER_DEV: + case REGISTER_DEV_NEW: case START_MD: case STOP_MD: @@ -1219,6 +1340,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case SCSI_IOCTL_TAGGED_ENABLE: case SCSI_IOCTL_TAGGED_DISABLE: case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_SEND_COMMAND: /* Big V */ case VT_SETMODE: @@ -1267,7 +1389,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case FIOGETOWN: case SIOCGPGRP: case SIOCATMARK: - case SIOCGSTAMP: case SIOCSIFLINK: case SIOCSIFENCAP: case SIOCGIFENCAP: @@ -1305,6 +1426,36 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case PPPIOCSNPMODE: case PPPIOCGDEBUG: case PPPIOCSDEBUG: + + /* CDROM stuff */ + case CDROMPAUSE: + case CDROMRESUME: + case CDROMPLAYMSF: + case CDROMPLAYTRKIND: + case CDROMREADTOCHDR: + case CDROMREADTOCENTRY: + case CDROMSTOP: + case CDROMSTART: + case CDROMEJECT: + case CDROMVOLCTRL: + case CDROMSUBCHNL: + case CDROMEJECT_SW: + case CDROMMULTISESSION: + case CDROM_GET_MCN: + case CDROMRESET: + case CDROMVOLREAD: + case CDROMSEEK: + case CDROMPLAYBLK: + case CDROMCLOSETRAY: + case CDROM_SET_OPTIONS: + case CDROM_CLEAR_OPTIONS: + case CDROM_SELECT_SPEED: + case CDROM_SELECT_DISC: + case CDROM_MEDIA_CHANGED: + case CDROM_DRIVE_STATUS: + case CDROM_DISC_STATUS: + case CDROM_CHANGER_NSLOTS: + error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; @@ -1312,7 +1463,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", (int)fd, (unsigned int)cmd, (unsigned int)arg); error = -EINVAL; - goto out; break; } out: diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index a84fe8eaa..176079643 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,7 +1,8 @@ -/* $Id: irq.c,v 1.47 1998/01/10 18:26:17 ecd Exp $ +/* $Id: irq.c,v 1.52 1998/03/19 00:22:54 ecd Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * - * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ #include <linux/config.h> @@ -98,12 +99,22 @@ int get_irq_list(char *buf) { int i, len = 0; struct irqaction *action; +#ifdef __SMP__ + int j; +#endif for(i = 0; i < (NR_IRQS + 1); i++) { if(!(action = *(i + irq_action))) continue; - len += sprintf(buf + len, "%2d: %8d %c %s", - i, kstat.interrupts[i], + len += sprintf(buf + len, "%3d: ", i); +#ifndef __SMP__ + len += sprintf(buf + len, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf + len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#endif + len += sprintf(buf + len, "%c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for(action = action->next; action; action = action->next) { @@ -113,19 +124,6 @@ int get_irq_list(char *buf) } len += sprintf(buf + len, "\n"); } -#if 0 -#ifdef CONFIG_PCI - { - struct linux_psycho *p; - for (p = psycho_root; p; p = p->next) - len += sprintf(buf + len, - "ISTAT[%d]: PCI[%016lx] OBIO[%016lx]\n", - p->index, - p->psycho_regs->pci_istate, - p->psycho_regs->obio_istate); - } -#endif -#endif return len; } @@ -197,8 +195,7 @@ static unsigned int *sysio_irq_to_imap(unsigned int irq) unsigned long offset; struct sysio_regs *sregs; - if((irq == 14) || - (irq >= NUM_SYSIO_OFFSETS) || + if((irq >= NUM_SYSIO_OFFSETS) || ((offset = sysio_irq_offsets[irq]) == ((unsigned long)-1))) return NULL; sregs = SBus_chain->iommu->sysio_regs; @@ -224,8 +221,8 @@ static unsigned int *sysio_imap_to_iclr(unsigned int *imap) unsigned char psycho_ino_to_pil[] = { 7, 5, 5, 2, /* PCI A slot 0 Int A, B, C, D */ 7, 5, 5, 2, /* PCI A slot 1 Int A, B, C, D */ - 0, 0, 0, 0, - 0, 0, 0, 0, + 7, 5, 5, 2, /* PCI A slot 2 Int A, B, C, D */ + 7, 5, 5, 2, /* PCI A slot 3 Int A, B, C, D */ 6, 4, 3, 1, /* PCI B slot 0 Int A, B, C, D */ 6, 4, 3, 1, /* PCI B slot 1 Int A, B, C, D */ 6, 4, 3, 1, /* PCI B slot 2 Int A, B, C, D */ @@ -255,13 +252,13 @@ unsigned char psycho_ino_to_pil[] = { */ #define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x))) -#define psycho_imap_offset(ino) \ - ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \ +#define psycho_imap_offset(ino) \ + ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \ (psycho_offset(imap_a_slot0) + (((ino) & 0x3c) << 1))) -#define psycho_iclr_offset(ino) \ - ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \ - (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f) << 3))) +#define psycho_iclr_offset(ino) \ + ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \ + (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f)<<3))) #endif @@ -529,7 +526,7 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) unsigned long flags; unsigned int *imap, *iclr; void *bus_id = NULL; - int ivindex, ivindex_fixup, cpu_irq = -1, pending; + int ivindex = -1, ivindex_fixup, cpu_irq = -1, pending = 0; if(!handler) return -EINVAL; @@ -537,43 +534,47 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) imap = iclr = NULL; ivindex_fixup = 0; + + if (irq == 0) { + cpu_irq = irq; + irqflags &= ~(SA_IMAP_MASKED); + } else { + irqflags |= SA_IMAP_MASKED; #ifdef CONFIG_PCI - if(PCI_IRQ_P(irq)) { - pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); - } else + if(PCI_IRQ_P(irq)) { + pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); + } else #endif - if(irqflags & SA_DCOOKIE) { - if(!dev_id) { - printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n"); - panic("Bogus irq registry."); - } - dcookie = dev_id; - dev_id = dcookie->real_dev_id; - cpu_irq = dcookie->pil; - imap = dcookie->imap; - iclr = dcookie->iclr; - bus_id = dcookie->bus_cookie; - get_irq_translations(&cpu_irq, &ivindex_fixup, &imap, - &iclr, bus_id, irqflags, irq); - } else { - /* XXX NOTE: This code is maintained for compatability until I can - * XXX verify that all drivers sparc64 will use are updated - * XXX to use the new IRQ registry dcookie interface. -DaveM - */ - if(irq == 14) - cpu_irq = irq; - else + if(irqflags & SA_DCOOKIE) { + if(!dev_id) { + printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n"); + panic("Bogus irq registry."); + } + dcookie = dev_id; + dev_id = dcookie->real_dev_id; + cpu_irq = dcookie->pil; + imap = dcookie->imap; + iclr = dcookie->iclr; + bus_id = dcookie->bus_cookie; + get_irq_translations(&cpu_irq, &ivindex_fixup, &imap, + &iclr, bus_id, irqflags, irq); + } else { + /* XXX NOTE: This code is maintained for compatability until I can + * XXX verify that all drivers sparc64 will use are updated + * XXX to use the new IRQ registry dcookie interface. -DaveM + */ cpu_irq = sysio_ino_to_pil[irq]; - imap = sysio_irq_to_imap(irq); - if(!imap) { - printk("request_irq: BAD, null imap for old style " - "irq registry IRQ[%x].\n", irq); - panic("Bad IRQ registery..."); + imap = sysio_irq_to_imap(irq); + if(!imap) { + printk("request_irq: BAD, null imap for old style " + "irq registry IRQ[%x].\n", irq); + panic("Bad IRQ registery..."); + } + iclr = sysio_imap_to_iclr(imap); } - iclr = sysio_imap_to_iclr(imap); + ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); + ivindex += ivindex_fixup; } - ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); - ivindex += ivindex_fixup; action = *(cpu_irq + irq_action); if(action) { @@ -612,26 +613,28 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) return -ENOMEM; } - bucket = add_ino_hash(ivindex, imap, iclr, irqflags); - if(!bucket) { - kfree(action); - restore_flags(flags); - return -ENOMEM; - } + if (irqflags & SA_IMAP_MASKED) { + bucket = add_ino_hash(ivindex, imap, iclr, irqflags); + if(!bucket) { + kfree(action); + restore_flags(flags); + return -ENOMEM; + } - pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0); - ivector_to_mask[ivindex] = (1 << cpu_irq); - if(pending) - ivector_to_mask[ivindex] |= 0x80000000; + pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0); + ivector_to_mask[ivindex] = (1 << cpu_irq); + if(pending) + ivector_to_mask[ivindex] |= 0x80000000; - if(dcookie) { - dcookie->ret_ino = ivindex; - dcookie->ret_pil = cpu_irq; + if(dcookie) { + dcookie->ret_ino = ivindex; + dcookie->ret_pil = cpu_irq; + } } action->mask = (unsigned long) bucket; action->handler = handler; - action->flags = irqflags | SA_IMAP_MASKED; + action->flags = irqflags; action->name = name; action->next = NULL; action->dev_id = dev_id; @@ -664,7 +667,7 @@ void free_irq(unsigned int irq, void *dev_id) unsigned int cpu_irq; int ivindex = -1; - if(irq == 14) { + if(irq == 0) { cpu_irq = irq; } else { #ifdef CONFIG_PCI @@ -951,34 +954,43 @@ void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs) void handler_irq(int irq, struct pt_regs *regs) { struct ino_bucket *bucket = NULL; - struct irqaction *action; + struct irqaction *action, *act; int cpu = smp_processor_id(); +#ifndef __SMP__ + /* + * Check for TICK_INT on level 14 softint. + */ + if ((irq == 14) && get_softint() & (1UL << 0)) + irq = 0; +#endif clear_softint(1 << irq); irq_enter(cpu, irq); action = *(irq + irq_action); - kstat.interrupts[irq]++; + kstat.irqs[cpu][irq]++; if(!action) { unexpected_irq(irq, 0, regs); } else { + act = action; do { - unsigned long *swmask = NULL; - - if(action->flags & SA_IMAP_MASKED) { - bucket = (struct ino_bucket *)action->mask; - - swmask = &ivector_to_mask[bucket->ino]; - if(!(*swmask & 0x80000000)) + if(act->flags & SA_IMAP_MASKED) { + bucket = (struct ino_bucket *)act->mask; + if(!(ivector_to_mask[bucket->ino] & 0x80000000)) continue; } - - action->handler(irq, action->dev_id, regs); - if(swmask) { - *swmask &= ~(0x80000000); + act->handler(irq, act->dev_id, regs); + } while((act = act->next) != NULL); + act = action; + do { + if(act->flags & SA_IMAP_MASKED) { + bucket = (struct ino_bucket *)act->mask; + if(!(ivector_to_mask[bucket->ino] & 0x80000000)) + continue; + ivector_to_mask[bucket->ino] &= ~(0x80000000); *(bucket->iclr) = SYSIO_ICLR_IDLE; } - } while((action = action->next) != NULL); + } while((act = act->next) != NULL); } irq_exit(cpu, irq); } @@ -993,6 +1005,7 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) int cpu = smp_processor_id(); irq_enter(cpu, irq); + kstat.irqs[cpu][irq]++; bucket = (struct ino_bucket *)action->mask; floppy_interrupt(irq, dev_cookie, regs); ivector_to_mask[bucket->ino] &= ~(0x80000000); @@ -1036,13 +1049,19 @@ int request_fast_irq(unsigned int irq, unsigned long flags; unsigned int *imap, *iclr; void *bus_id = NULL; - int ivindex, ivindex_fixup, cpu_irq = -1; + int ivindex = -1, ivindex_fixup, cpu_irq = -1; if(!handler) return -EINVAL; imap = iclr = NULL; ivindex_fixup = 0; + + if ((irq == 0) || (irq == 14)) { + printk("request_fast_irq: Trying to register shared IRQ 0 or 14.\n"); + return -EBUSY; + } + #ifdef CONFIG_PCI if(PCI_IRQ_P(irq)) { pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); @@ -1066,10 +1085,7 @@ int request_fast_irq(unsigned int irq, * XXX verify that all drivers sparc64 will use are updated * XXX to use the new IRQ registry dcookie interface. -DaveM */ - if(irq == 14) - cpu_irq = irq; - else - cpu_irq = sysio_ino_to_pil[irq]; + cpu_irq = sysio_ino_to_pil[irq]; imap = sysio_irq_to_imap(irq); if(!imap) { printk("request_irq: BAD, null imap for old style " @@ -1153,85 +1169,100 @@ int probe_irq_off(unsigned long mask) return 0; } -struct sun5_timer *linux_timers = NULL; - -/* This is gets the master level10 timer going. */ -void init_timers(void (*cfunc)(int, void *, struct pt_regs *)) +/* This is gets the master TICK_INT timer going. */ +void init_timers(void (*cfunc)(int, void *, struct pt_regs *), + unsigned long *clock) { - struct linux_prom64_registers pregs[3]; - struct devid_cookie dcookie; - unsigned int *imap, *iclr; - u32 pirqs[2]; + unsigned long flags; + unsigned long timer_tick_offset; int node, err; - node = prom_finddevice("/counter-timer"); - if(node == 0 || node == -1) { - prom_printf("init_timers: Cannot find counter-timer PROM node.\n"); - prom_halt(); - } - err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs)); - if(err == -1) { - prom_printf("init_timers: Cannot obtain 'reg' for counter-timer.\n"); - prom_halt(); - } - err = prom_getproperty(node, "interrupts", (char *)&pirqs[0], sizeof(pirqs)); - if(err == -1) { - prom_printf("init_timers: Cannot obtain 'interrupts' " - "for counter-timer.\n"); - prom_halt(); - } - linux_timers = (struct sun5_timer *) __va(pregs[0].phys_addr); - iclr = (((unsigned int *)__va(pregs[1].phys_addr))+1); - imap = (((unsigned int *)__va(pregs[2].phys_addr))+1); - - /* Shut it up first. */ - linux_timers->limit0 = 0; + node = linux_cpus[0].prom_node; + *clock = prom_getint(node, "clock-frequency"); + timer_tick_offset = *clock / HZ; /* Register IRQ handler. */ - dcookie.real_dev_id = NULL; - dcookie.imap = imap; - dcookie.iclr = iclr; - dcookie.pil = 10; - dcookie.bus_cookie = NULL; - - err = request_irq(pirqs[0], cfunc, - (SA_DCOOKIE | SA_INTERRUPT | SA_STATIC_ALLOC), - "timer", &dcookie); + err = request_irq(0, cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC), + "timer", NULL); if(err) { - prom_printf("Serious problem, cannot register timer interrupt\n"); + prom_printf("Serious problem, cannot register TICK_INT\n"); prom_halt(); - } else { - unsigned long flags; + } - save_and_cli(flags); + save_and_cli(flags); - /* Set things up so user can access tick register for profiling - * purposes. - */ - __asm__ __volatile__(" - sethi %%hi(0x80000000), %%g1 - sllx %%g1, 32, %%g1 - rd %%tick, %%g2 - add %%g2, 6, %%g2 - andn %%g2, %%g1, %%g2 - wrpr %%g2, 0, %%tick -" : /* no outputs */ - : /* no inputs */ - : "g1", "g2"); - - linux_timers->limit0 = - (SUN5_LIMIT_ENABLE | SUN5_LIMIT_ZRESTART | SUN5_LIMIT_TOZERO | - (SUN5_HZ_TO_LIMIT(HZ) & SUN5_LIMIT_CMASK)); + /* Set things up so user can access tick register for profiling + * purposes. + */ + __asm__ __volatile__(" + sethi %%hi(0x80000000), %%g1 + sllx %%g1, 32, %%g1 + rd %%tick, %%g2 + add %%g2, 6, %%g2 + andn %%g2, %%g1, %%g2 + wrpr %%g2, 0, %%tick +" : /* no outputs */ + : /* no inputs */ + : "g1", "g2"); - restore_flags(flags); - } + __asm__ __volatile__(" + rd %%tick, %%g1 + add %%g1, %0, %%g1 + wr %%g1, 0x0, %%tick_cmpr" + : /* no outputs */ + : "r" (timer_tick_offset) + : "g1"); + restore_flags(flags); sti(); } -struct sun5_timer *prom_timers; +#ifdef __SMP__ +/* Called from smp_commence, when we know how many cpus are in the system + * and can have device IRQ's directed at them. + */ +void distribute_irqs(void) +{ + unsigned long flags; + int cpu, level; + + printk("SMP: redistributing interrupts...\n"); + save_and_cli(flags); + cpu = 0; + for(level = 0; level < NR_IRQS; level++) { + struct irqaction *p = irq_action[level]; + + while(p) { + if(p->flags & SA_IMAP_MASKED) { + struct ino_bucket *bucket = (struct ino_bucket *)p->mask; + unsigned int *imap = bucket->imap; + unsigned int val; + unsigned long tid = __cpu_logical_map[cpu] << 9; + + val = *imap; + *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); + printk("SMP: Redirecting IGN[%x] INO[%x] " + "to cpu %d [%s]\n", + (val & SYSIO_IMAP_IGN) >> 6, + (val & SYSIO_IMAP_INO), cpu, + p->name); + + cpu++; + if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1) + cpu = 0; + } + p = p->next; + } + } + restore_flags(flags); + irqs_have_been_distributed = 1; +} +#endif + + +struct sun5_timer *prom_timers; static u64 prom_limit0, prom_limit1; static void map_prom_timers(void) @@ -1245,9 +1276,8 @@ static void map_prom_timers(void) /* Assume if node is not present, PROM uses different tick mechanism * which we should not care about. */ - if(tnode == 0) { + if(tnode == 0 || tnode == -1) { prom_timers = (struct sun5_timer *) 0; - prom_printf("AIEEE, no timers\n"); return; } @@ -1300,52 +1330,6 @@ void enable_prom_timer(void) prom_timers->count0 = 0; } -#ifdef __SMP__ -/* Called from smp_commence, when we know how many cpus are in the system - * and can have device IRQ's directed at them. - */ -void distribute_irqs(void) -{ - unsigned long flags; - int cpu, level; - - printk("SMP: redistributing interrupts...\n"); - save_and_cli(flags); - cpu = 0; - for(level = 0; level < NR_IRQS; level++) { - struct irqaction *p = irq_action[level]; - - while(p) { - if(p->flags & SA_IMAP_MASKED) { - struct ino_bucket *bucket = (struct ino_bucket *)p->mask; - unsigned int *imap = bucket->imap; - unsigned int val; - unsigned long tid = linux_cpus[cpu].mid << 9; - - val = *imap; - *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); - - printk("SMP: Redirecting IGN[%x] INO[%x] " - "to cpu %d [%s]\n", - (val & SYSIO_IMAP_IGN) >> 6, - (val & SYSIO_IMAP_INO), cpu, - p->name); - - cpu += 1; - while(!(cpu_present_map & (1UL << cpu))) { - cpu += 1; - if(cpu >= smp_num_cpus) - cpu = 0; - } - } - p = p->next; - } - } - restore_flags(flags); - irqs_have_been_distributed = 1; -} -#endif - __initfunc(void init_IRQ(void)) { int i; diff --git a/arch/sparc64/kernel/itlb_miss.S b/arch/sparc64/kernel/itlb_miss.S index 9317587a7..94e3f44f6 100644 --- a/arch/sparc64/kernel/itlb_miss.S +++ b/arch/sparc64/kernel/itlb_miss.S @@ -1,42 +1,42 @@ -/* $Id: itlb_miss.S,v 1.11 1997/10/14 01:48:25 davem Exp $ +/* $Id: itlb_miss.S,v 1.12 1998/01/14 17:14:47 jj Exp $ * itlb_miss.S: Instruction TLB miss code, this is included directly * into the trap table. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* Gratuitous comment. */ /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_IMMU, %g1 ! Get TAG_TARGET - /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset - /*0x08*/ srlx %g1, 48, %g5 ! Shift down CONTEXT bits - /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ sllx %g1, 2, %g4 ! Position PMD offset + /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset + /*0x08*/ andcc %g1, %g2, %g0 ! Test CONTEXT bits + /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset + /*0x10*/ and %g1, 0xffe, %g4 ! Mask PMD offset /*0x14*/ ldxa [%g0] ASI_IMMU_TSB_8KB_PTR, %g1 ! For PTE offset - /*0x18*/ brz,pn %g5, 3f ! Context 0 == kernel - /*0x1c*/ and %g4, %g2, %g4 ! Mask PMD offset + /*0x18*/ be,pn %xcc, 3f ! Context 0 == kernel + /*0x1c*/ add %g4, %g4, %g4 ! Position PMD offset /* ICACHE line 2 */ - /*0x20*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load user PGD + /*0x20*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load user PGD /*0x24*/ srlx %g1, 1, %g1 ! PTE offset - /*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD -2:/*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE + /*0x28*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD + /*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE /*0x30*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set? /*0x34*/ nop ! delay /*0x38*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load /*0x3c*/ retry ! Trap return 3: /* ICACHE line 3 */ - /*0x40*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD + /*0x40*/ lduwa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD /*0x44*/ srlx %g1, 1, %g1 ! PTE offset - /*0x48*/ ba,pt %xcc, 2b ! Continue above - /*0x4c*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD - /*0x50*/ nop - /*0x54*/ nop - /*0x58*/ nop - /*0x5c*/ nop + /*0x48*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD + /*0x4c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE + /*0x50*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set? + /*0x54*/ nop ! delay + /*0x58*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load + /*0x5c*/ retry ! Trap return /* ICACHE line 4 */ /*0x60*/ nop diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 4e5ead982..c0058afd9 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.50 1998/01/09 16:39:33 jj Exp $ +/* $Id: process.c,v 1.52 1998/03/29 12:57:53 ecd Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -41,9 +41,6 @@ /* #define VERBOSE_SHOWREGS */ -#define PGTCACHE_HIGH_WATER 50 -#define PGTCACHE_LOW_WATER 25 - #ifndef __SMP__ /* @@ -58,16 +55,7 @@ asmlinkage int sys_idle(void) current->priority = -100; current->counter = -100; for (;;) { - if(pgtable_cache_size > PGTCACHE_LOW_WATER) { - do { - if(pgd_quicklist) - free_page((unsigned long) get_pgd_fast()); - if(pmd_quicklist) - free_page((unsigned long) get_pmd_fast()); - if(pte_quicklist) - free_page((unsigned long) get_pte_fast()); - } while(pgtable_cache_size > PGTCACHE_HIGH_WATER); - } + check_pgt_cache(); run_task_queue(&tq_scheduler); schedule(); } @@ -83,16 +71,7 @@ asmlinkage int cpu_idle(void) { current->priority = -100; while(1) { - if(pgtable_cache_size > PGTCACHE_LOW_WATER) { - do { - if(pgd_quicklist) - free_page((unsigned long) get_pgd_fast()); - if(pmd_quicklist) - free_page((unsigned long) get_pmd_fast()); - if(pte_quicklist) - free_page((unsigned long) get_pte_fast()); - } while(pgtable_cache_size > PGTCACHE_HIGH_WATER); - } + check_pgt_cache(); if(tq_scheduler) { lock_kernel(); run_task_queue(&tq_scheduler); @@ -592,6 +571,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, p->tss.flags |= SPARC_FLAG_KTHREAD; p->tss.current_ds = KERNEL_DS; p->tss.ctx = 0; + __asm__ __volatile__("flushw"); + memcpy((void *)(p->tss.ksp + STACK_BIAS), + (void *)(regs->u_regs[UREG_FP] + STACK_BIAS), + sizeof(struct reg_window)); p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p; } else { if(current->tss.flags & SPARC_FLAG_32BIT) { diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c index b3b403e33..78a69e8df 100644 --- a/arch/sparc64/kernel/psycho.c +++ b/arch/sparc64/kernel/psycho.c @@ -1,4 +1,4 @@ -/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $ +/* $Id: psycho.c,v 1.50 1998/04/10 12:29:47 ecd Exp $ * psycho.c: Ultra/AX U2P PCI controller support. * * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) @@ -8,6 +8,7 @@ #include <linux/config.h> #include <linux/kernel.h> #include <linux/types.h> +#include <linux/init.h> #include <asm/ebus.h> #include <asm/sbus.h> /* for sanity check... */ @@ -15,6 +16,7 @@ #undef PROM_DEBUG #undef FIXUP_REGS_DEBUG #undef FIXUP_IRQ_DEBUG +#undef FIXUP_VMA_DEBUG #ifdef PROM_DEBUG #define dprintf prom_printf @@ -22,6 +24,9 @@ #define dprintf printk #endif +unsigned long pci_dvma_offset = 0x00000000UL; +unsigned long pci_dvma_mask = 0xffffffffUL; + #ifndef CONFIG_PCI int pcibios_present(void) @@ -51,12 +56,12 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, #include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <asm/io.h> #include <asm/oplib.h> #include <asm/pbm.h> +#include <asm/apb.h> #include <asm/uaccess.h> struct linux_psycho *psycho_root = NULL; @@ -95,8 +100,9 @@ static inline unsigned long long_align(unsigned long addr) ~(sizeof(unsigned long) - 1)); } -static unsigned long psycho_iommu_init(struct linux_psycho *psycho, - unsigned long memory_start) +__initfunc(static unsigned long psycho_iommu_init(struct linux_psycho *psycho, + int tsbsize, + unsigned long memory_start)) { unsigned long tsbbase = PAGE_ALIGN(memory_start); unsigned long control, i; @@ -114,10 +120,10 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho, control &= ~(IOMMU_CTRL_DENAB); psycho->psycho_regs->iommu_control = control; - memory_start = (tsbbase + ((32 * 1024) * 8)); + memory_start = (tsbbase + ((tsbsize * 1024) * 8)); iopte = (unsigned long *)tsbbase; - for(i = 0; i < (32 * 1024); i++) { + for(i = 0; i < (tsbsize * 1024); i++) { *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); *iopte |= (i << 16); @@ -128,15 +134,215 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho, control = psycho->psycho_regs->iommu_control; control &= ~(IOMMU_CTRL_TSBSZ); - control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); + control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); + switch(tsbsize) { + case 8: + pci_dvma_mask = 0x1fffffffUL; + control |= IOMMU_TSBSZ_8K; + break; + case 16: + pci_dvma_mask = 0x3fffffffUL; + control |= IOMMU_TSBSZ_16K; + break; + case 32: + pci_dvma_mask = 0x7fffffffUL; + control |= IOMMU_TSBSZ_32K; + break; + default: + prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize); + prom_halt(); + break; + } psycho->psycho_regs->iommu_control = control; return memory_start; } extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm); +extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm); + +/* + * Poor man's PCI... + */ +__initfunc(unsigned long sabre_init(int pnode, unsigned long memory_start)) +{ + struct linux_prom64_registers pr_regs[2]; + struct linux_psycho *sabre; + unsigned long ctrl; + int tsbsize, node, err; + u32 busrange[2]; + u32 vdma[2]; + u32 portid; + int bus; + + sabre = (struct linux_psycho *)memory_start; + memory_start = long_align(memory_start + sizeof(struct linux_psycho)); + + portid = prom_getintdefault(pnode, "upa-portid", 0xff); + + memset(sabre, 0, sizeof(*sabre)); + + sabre->next = psycho_root; + psycho_root = sabre; + + sabre->upa_portid = portid; + sabre->index = linux_num_psycho++; + + /* + * Map in SABRE register set and report the presence of this SABRE. + */ + err = prom_getproperty(pnode, "reg", + (char *)&pr_regs[0], sizeof(pr_regs)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get U2P registers " + "from PROM.\n"); + prom_halt(); + } + + /* + * First REG in property is base of entire SABRE register space. + */ + sabre->psycho_regs = + sparc_alloc_io((pr_regs[0].phys_addr & 0xffffffff), + NULL, sizeof(struct psycho_regs), + "SABRE Registers", + (pr_regs[0].phys_addr >> 32), 0); + if(sabre->psycho_regs == NULL) { + prom_printf("SABRE: Error, cannot map SABRE main registers.\n"); + prom_halt(); + } + + printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); +#ifdef PROM_DEBUG + dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); +#endif + + ctrl = sabre->psycho_regs->pci_a_control; + ctrl = (1UL << 36) | (1UL << 34) | (1UL << 21) | (1UL << 8) | 0x0fUL; + sabre->psycho_regs->pci_a_control = ctrl; + + /* Now map in PCI config space for entire SABRE. */ + sabre->pci_config_space = + sparc_alloc_io(((pr_regs[0].phys_addr & 0xffffffff) + + 0x01000000), + NULL, 0x01000000, + "PCI Config Space", + (pr_regs[0].phys_addr >> 32), 0); + if(sabre->pci_config_space == NULL) { + prom_printf("SABRE: Error, cannot map PCI config space.\n"); + prom_halt(); + } + + /* Report some more info. */ + printk("SABRE: PCI config space at %p\n", sabre->pci_config_space); +#ifdef PROM_DEBUG + dprintf("SABRE: PCI config space at %p\n", sabre->pci_config_space); +#endif + + err = prom_getproperty(pnode, "virtual-dma", + (char *)&vdma[0], sizeof(vdma)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get virtual-dma property " + "from PROM.\n"); + prom_halt(); + } + + switch(vdma[1]) { + case 0x20000000: + tsbsize = 8; + break; + case 0x40000000: + tsbsize = 16; + break; + case 0x80000000: + tsbsize = 32; + break; + default: + prom_printf("SABRE: strange virtual-dma size.\n"); + prom_halt(); + } + + memory_start = psycho_iommu_init(sabre, tsbsize, memory_start); + pci_dvma_offset = vdma[0]; + + printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]); +#ifdef PROM_DEBUG + dprintf("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]); +#endif + + err = prom_getproperty(pnode, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if(err == 0 || err == -1) { + prom_printf("SIMBA: Error, cannot get PCI bus-range " + " from PROM.\n"); + prom_halt(); + } + + sabre->pci_first_busno = busrange[0]; + sabre->pci_last_busno = busrange[1]; + sabre->pci_bus = &pci_root; + + /* + * Handle config space reads through any Simba on APB. + */ + for (bus = sabre->pci_first_busno; bus <= sabre->pci_last_busno; bus++) + bus2pbm[bus] = &sabre->pbm_A; + + /* + * Look for APB underneath. + */ + node = prom_getchild(pnode); + while ((node = prom_searchsiblings(node, "pci"))) { + struct linux_pbm_info *pbm; + char namebuf[128]; + + err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); + if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err)) + goto next_pci; + + err = prom_getproperty(node, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if(err == 0 || err == -1) { + prom_printf("SIMBA: Error, cannot get PCI bus-range " + " from PROM.\n"); + prom_halt(); + } + + if (busrange[0] == 1) + pbm = &sabre->pbm_B; + else + pbm = &sabre->pbm_A; + + pbm->parent = sabre; + pbm->IO_assignments = NULL; + pbm->MEM_assignments = NULL; + pbm->prom_node = node; + + prom_getstring(node, "name", namebuf, sizeof(namebuf)); + strcpy(pbm->prom_name, namebuf); + + /* Now the ranges. */ + prom_pbm_ranges_init(pnode, pbm); + prom_pbm_intmap_init(node, pbm); + + pbm->pci_first_busno = busrange[0]; + pbm->pci_last_busno = busrange[1]; + memset(&pbm->pci_bus, 0, sizeof(struct pci_bus)); + + for (bus = pbm->pci_first_busno; + bus <= pbm->pci_last_busno; bus++) + bus2pbm[bus] = pbm; -unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) + next_pci: + node = prom_getsibling(node); + if (!node) + break; + } + + return memory_start; +} + +__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)) { struct linux_prom64_registers pr_regs[3]; struct linux_psycho *psycho; @@ -144,9 +350,9 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) u32 portid; int node; - printk("PSYCHO: Probing for controllers.\n"); + printk("PCI: Probing for controllers.\n"); #ifdef PROM_DEBUG - dprintf("PSYCHO: Probing for controllers.\n"); + dprintf("PCI: Probing for controllers.\n"); #endif memory_start = long_align(memory_start); @@ -157,6 +363,12 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) u32 busrange[2]; int err, is_pbm_a; + err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); + if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) { + memory_start = sabre_init(node, memory_start); + goto next_pci; + } + psycho = (struct linux_psycho *)memory_start; portid = prom_getintdefault(node, "upa-portid", 0xff); @@ -200,34 +412,30 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) * Third REG in property is base of entire PSYCHO * register space. */ - psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), - NULL, sizeof(struct psycho_regs), - "PSYCHO Registers", - (pr_regs[2].phys_addr >> 32), 0); + psycho->psycho_regs = + sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), + NULL, sizeof(struct psycho_regs), + "PSYCHO Registers", + (pr_regs[2].phys_addr >> 32), 0); if(psycho->psycho_regs == NULL) { prom_printf("PSYCHO: Error, cannot map PSYCHO " "main registers.\n"); prom_halt(); } - printk("PSYCHO: Found controller, main regs at %p\n", + printk("PCI: Found PSYCHO, main regs at %p\n", psycho->psycho_regs); #ifdef PROM_DEBUG - dprintf("PSYCHO: Found controller, main regs at %p\n", + dprintf("PCI: Found PSYCHO, main regs at %p\n", psycho->psycho_regs); #endif psycho->psycho_regs->irq_retry = 0xff; -#if 0 - psycho->psycho_regs->ecc_control |= 1; - psycho->psycho_regs->sbuf_a_control = 0; - psycho->psycho_regs->sbuf_b_control = 0; -#endif - /* Now map in PCI config space for entire PSYCHO. */ psycho->pci_config_space = - sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000), + sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff) + + 0x01000000), NULL, 0x01000000, "PCI Config Space", (pr_regs[2].phys_addr >> 32), 0); @@ -244,7 +452,8 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) psycho->pci_config_space); #endif - memory_start = psycho_iommu_init(psycho, memory_start); + memory_start = psycho_iommu_init(psycho, 32, memory_start); + pci_dvma_offset = 0x80000000UL; is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); @@ -268,6 +477,7 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) /* Now the ranges. */ prom_pbm_ranges_init(node, pbm); + prom_pbm_intmap_init(node, pbm); /* Finally grab the pci bus root array for this pbm after * having found the bus range existing under it. @@ -282,6 +492,7 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) pbm->pci_last_busno = busrange[1]; memset(&pbm->pci_bus, 0, sizeof(struct pci_bus)); + next_pci: node = prom_getsibling(node); if(!node) break; @@ -308,54 +519,18 @@ int pcibios_present(void) return psycho_root != NULL; } -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm, unsigned long start, - int io) + unsigned int offset, int io) { struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments); - while(vp) { - if(vp->end > start) + while (vp) { + if (offset && (vp->offset != offset)) + goto next; + if (vp->end >= start) break; + next: vp = vp->next; } return vp; @@ -391,7 +566,7 @@ static inline void pci_add_vma(struct linux_pbm_info *pbm, struct pci_vma *new, /* Check for programming errors. */ if(vp && ((vp->start >= new->start && vp->start < new->end) || - ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) { + (vp->end >= new->start && vp->end < new->end))) { prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n", io ? "IO" : "MEM"); prom_printf("pci_add_vma: vp[%016lx:%016lx] " @@ -414,7 +589,7 @@ static inline void pci_init_alloc_fini(void) pci_alloc_arena = NULL; } -static void *pci_init_alloc(int size) +__initfunc(static void *pci_init_alloc(int size)) { unsigned long start = long_align(*pci_alloc_arena); void *mp = (void *)start; @@ -439,8 +614,8 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void) } -static void -pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) +__initfunc(static void +pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)) { unsigned int devfn, l, class; unsigned char hdr_type = 0; @@ -487,7 +662,7 @@ pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) } } -static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) +__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)) { unsigned int nbus; @@ -513,7 +688,79 @@ static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) } -static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) +__initfunc(static void apb_init(struct linux_psycho *sabre)) +{ + struct pci_dev *pdev; + unsigned short stmp; + unsigned int itmp; + + for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) { + if (pdev->vendor == PCI_VENDOR_ID_SUN && + pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { + + pci_read_config_word(pdev, PCI_COMMAND, &stmp); + stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | + PCI_COMMAND_IO; + pci_write_config_word(pdev, PCI_COMMAND, stmp); + + pci_write_config_word(pdev, PCI_STATUS, 0xffff); + pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff); + + pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp); + stmp = PCI_BRIDGE_CTL_MASTER_ABORT | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_PARITY; + pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp); + + pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp); + itmp = APB_PCI_CTL_HIGH_SERR | + APB_PCI_CTL_HIGH_ARBITER_EN; + pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp); + + pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp); + itmp = APB_PCI_CTL_LOW_ARB_PARK | + APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; + pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp); + + /* + * Setup Registers for Guaranteed Completion. + */ + pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0); + pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0); + pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80); + pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0); + pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80); + pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0); + } + } +} + +__initfunc(static void sabre_probe(struct linux_psycho *sabre, + unsigned long *mstart)) +{ + struct pci_bus *pbus = sabre->pci_bus; + static unsigned char busno = 0; + + pbus->number = pbus->secondary = busno; + pbus->sysdata = sabre; + + pbus->subordinate = pci_scan_bus(pbus, mstart); + busno = pbus->subordinate + 1; + + for(pbus = pbus->children; pbus; pbus = pbus->next) { + if (pbus->number == sabre->pbm_A.pci_first_busno) + memcpy(&sabre->pbm_A.pci_bus, pbus, sizeof(*pbus)); + if (pbus->number == sabre->pbm_B.pci_first_busno) + memcpy(&sabre->pbm_B.pci_bus, pbus, sizeof(*pbus)); + } + + apb_init(sabre); +} + + +__initfunc(static void pbm_probe(struct linux_pbm_info *pbm, + unsigned long *mstart)) { static struct pci_bus *pchain = NULL; struct pci_bus *pbus = &pbm->pci_bus; @@ -552,9 +799,9 @@ static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) } } -static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - int node) +__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + int node)) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; int err; @@ -583,7 +830,7 @@ static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, return 0; } -static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev) +__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)) { struct pcidev_cookie *pcp; int node = prom_getchild(pbm->prom_node); @@ -597,24 +844,37 @@ static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev) pdev->sysdata = pcp; } -static void fill_in_pbm_cookies(struct linux_pbm_info *pbm) +__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus, + struct linux_pbm_info *pbm)) { - struct pci_bus *pbtmp, *pbus = &pbm->pci_bus; struct pci_dev *pdev; - for(pbtmp = pbus->children; pbtmp; pbtmp = pbtmp->children) - pbtmp->sysdata = pbm; + pbus->sysdata = pbm; + + for(pdev = pbus->devices; pdev; pdev = pdev->sibling) + pdev_cookie_fillin(pbm, pdev); + + for(pbus = pbus->children; pbus; pbus = pbus->next) + fill_in_pbm_cookies(pbus, pbm); +} + +__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre)) +{ + struct pci_bus *pbus = sabre->pci_bus; - for( ; pbus; pbus = pbus->children) - for(pdev = pbus->devices; pdev; pdev = pdev->sibling) - pdev_cookie_fillin(pbm, pdev); + for(pbus = pbus->children; pbus; pbus = pbus->next) { + if (pbus->number == sabre->pbm_A.pci_first_busno) + pdev_cookie_fillin(&sabre->pbm_A, pbus->self); + else if (pbus->number == sabre->pbm_B.pci_first_busno) + pdev_cookie_fillin(&sabre->pbm_B, pbus->self); + } } /* Walk PROM device tree under PBM, looking for 'assigned-address' * properties, and recording them in pci_vma's linked in via * PBM->assignments. */ -static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs) +__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)) { struct linux_prom_ebus_ranges erng[PROMREG_MAX]; int err, iter; @@ -632,18 +892,26 @@ static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *are ap->phys_hi = ep->parent_phys_hi; ap->phys_mid = ep->parent_phys_mid; ap->phys_lo = ep->parent_phys_lo; + + ap->size_hi = 0; + ap->size_lo = ep->size; } return err; } -static void assignment_process(struct linux_pbm_info *pbm, int node) +__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node)) { struct linux_prom_pci_registers aregs[PROMREG_MAX]; char pname[256]; int err, iter, numa; err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname)); - if(strncmp(pname, "ebus", 4) == 0) { + if (err > 0) + pname[err] = 0; +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: %s\n", __FUNCTION__, err > 0 ? pname : "???"); +#endif + if(strcmp(pname, "ebus") == 0) { numa = gimme_ebus_assignments(node, &aregs[0]); } else { err = prom_getproperty(node, "assigned-addresses", @@ -653,7 +921,7 @@ static void assignment_process(struct linux_pbm_info *pbm, int node) if(err == 0 || err == -1) return; - numa = (err / sizeof(struct linux_prom_pci_ranges)); + numa = (err / sizeof(struct linux_prom_pci_registers)); } for(iter = 0; iter < numa; iter++) { @@ -667,8 +935,6 @@ static void assignment_process(struct linux_pbm_info *pbm, int node) io = (space == 1); breg = (ap->phys_hi & 0xff); - if(breg == PCI_ROM_ADDRESS) - continue; vp = pci_vma_alloc(); @@ -677,21 +943,20 @@ static void assignment_process(struct linux_pbm_info *pbm, int node) * XXX either due to it's layout so... */ vp->start = ap->phys_lo; - vp->end = vp->start + ap->size_lo; - vp->base_reg = breg; - - /* Sanity */ - if(io && (vp->end & ~(0xffff))) { - prom_printf("assignment_process: Out of range PCI I/O " - "[%08lx:%08lx]\n", vp->start, vp->end); - prom_halt(); - } + vp->end = vp->start + ap->size_lo - 1; + vp->offset = (ap->phys_hi & 0xffffff); pci_add_vma(pbm, vp, io); + +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: BaseReg %02x", pname, breg); + dprintf(" %s vma [%08x,%08x]\n", + io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end); +#endif } } -static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node) +__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)) { while(node) { int child = prom_getchild(node); @@ -704,17 +969,112 @@ static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node) } } -static void record_assignments(struct linux_pbm_info *pbm) +static inline void record_assignments(struct linux_pbm_info *pbm) { + struct pci_vma *vp; + + if (pbm->parent->pci_bus) { + /* + * Disallow anything that is not in our IO/MEM map on SIMBA. + */ + struct pci_bus *pbus = pbm->parent->pci_bus; + struct pci_dev *pdev; + unsigned char map; + int bit; + + for (pdev = pbus->devices; pdev; pdev = pdev->sibling) { + struct pcidev_cookie *pcp = pdev->sysdata; + if (!pcp) { + prom_printf("record_assignments: " + "no pcidev_cookie for pdev %02x\n", + pdev->devfn); + prom_halt(); + } + if (pcp->pbm == pbm) + break; + } + + if (!pdev) { + prom_printf("record_assignments: no pdev for PBM\n"); + prom_halt(); + } + + pci_read_config_byte(pdev, APB_IO_ADDRESS_MAP, &map); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: IO %02x\n", __FUNCTION__, map); +#endif + for (bit = 0; bit < 8; bit++) { + if (!(map & (1 << bit))) { + vp = pci_vma_alloc(); + vp->start = (bit << 21); + vp->end = vp->start + (1 << 21) - 1; + vp->offset = 0; + pci_add_vma(pbm, vp, 1); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: IO prealloc vma [%08x,%08x]\n", + __FUNCTION__, vp->start, vp->end); +#endif + } + } + pci_read_config_byte(pdev, APB_MEM_ADDRESS_MAP, &map); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: MEM %02x\n", __FUNCTION__, map); +#endif + for (bit = 0; bit < 8; bit++) { + if (!(map & (1 << bit))) { + vp = pci_vma_alloc(); + vp->start = (bit << 29); + vp->end = vp->start + (1 << 29) - 1; + vp->offset = 0; + pci_add_vma(pbm, vp, 0); +#ifdef FIXUP_VMA_DEBUG + dprintf("%s: MEM prealloc vma [%08x,%08x]\n", + __FUNCTION__, vp->start, vp->end); +#endif + } + } + } + assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node)); + + /* + * Protect ISA IO space from being used. + */ + vp = pci_find_vma(pbm, 0, 0, 1); + if (!vp || 0x400 <= vp->start) { + vp = pci_vma_alloc(); + vp->start = 0; + vp->end = vp->start + 0x400 - 1; + vp->offset = 0; + pci_add_vma(pbm, vp, 1); + } + +#ifdef FIXUP_VMA_DEBUG + dprintf("PROM IO assignments for PBM %s:\n", + pbm == &pbm->parent->pbm_A ? "A" : "B"); + vp = pbm->IO_assignments; + while (vp) { + dprintf(" [%08x,%08x] (%s)\n", vp->start, vp->end, + vp->offset ? "Register" : "Unmapped"); + vp = vp->next; + } + dprintf("PROM MEM assignments for PBM %s:\n", + pbm == &pbm->parent->pbm_A ? "A" : "B"); + vp = pbm->MEM_assignments; + while (vp) { + dprintf(" [%08x,%08x] (%s)\n", vp->start, vp->end, + vp->offset ? "Register" : "Unmapped"); + vp = vp->next; + } +#endif } -static void fixup_regs(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - struct linux_prom_pci_registers *assigned, - int numaa) +__initfunc(static void fixup_regs(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + struct linux_prom_pci_registers *assigned, + int numaa)) { int preg, rng; int IO_seen = 0; @@ -724,34 +1084,39 @@ static void fixup_regs(struct pci_dev *pdev, struct linux_prom_pci_registers *ap = NULL; int bustype = (pregs[preg].phys_hi >> 24) & 0x3; int bsreg, brindex; + unsigned int rtmp; u64 pci_addr; if(bustype == 0) { /* Config space cookie, nothing to do. */ if(preg != 0) - printk("%s: strange, config space not 0\n", - __FUNCTION__); + printk("%s %02x.%02x [%04x,%04x]: " + "strange, config space not 0\n", + __FUNCTION__, + pdev->bus->number, pdev->devfn, + pdev->vendor, pdev->device); continue; } else if(bustype == 3) { /* XXX add support for this... */ - printk("%s: Warning, ignoring 64-bit PCI memory space, " + printk("%s %02x.%02x [%04x,%04x]: " + "Warning, ignoring 64-bit PCI memory space, " "tell Eddie C. Dost (ecd@skynet.be).\n", - __FUNCTION__); + __FUNCTION__, + pdev->bus->number, pdev->devfn, + pdev->vendor, pdev->device); continue; } - bsreg = (pregs[preg].phys_hi & 0xff); - /* We can safely ignore these. */ - if(bsreg == PCI_ROM_ADDRESS) - continue; + bsreg = (pregs[preg].phys_hi & 0xff); /* Sanity */ if((bsreg < PCI_BASE_ADDRESS_0) || - (bsreg > (PCI_BASE_ADDRESS_5 + 4)) || + ((bsreg > (PCI_BASE_ADDRESS_5 + 4)) && (bsreg != PCI_ROM_ADDRESS)) || (bsreg & 3)) { - printk("%s: [%04x:%04x]: " + printk("%s %02x.%02x [%04x:%04x]: " "Warning, ignoring bogus basereg [%x]\n", - __FUNCTION__, pdev->vendor, pdev->device, bsreg); + __FUNCTION__, pdev->bus->number, pdev->devfn, + pdev->vendor, pdev->device, bsreg); printk(" PROM reg: %08x.%08x.%08x %08x.%08x\n", pregs[preg].phys_hi, pregs[preg].phys_mid, pregs[preg].phys_lo, pregs[preg].size_hi, @@ -798,7 +1163,16 @@ static void fixup_regs(struct pci_dev *pdev, /* AIEEE */ prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n"); } - pdev->base_address[brindex] = (unsigned long)__va(pci_addr); + if (bsreg == PCI_ROM_ADDRESS) { + pdev->rom_address = (unsigned long)__va(pci_addr); + pdev->rom_address |= 1; + /* + * Enable access to the ROM. + */ + pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1); + } else + pdev->base_address[brindex] = (unsigned long)__va(pci_addr); /* Preserve I/O space bit. */ if(bustype == 0x1) { @@ -811,15 +1185,18 @@ static void fixup_regs(struct pci_dev *pdev, /* Now handle assignments PROM did not take care of. */ if(nregs) { + unsigned int rtmp, ridx; + unsigned int offset, base; + struct pci_vma *vp; + u64 pci_addr; int breg; for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) { - unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2); - unsigned int base = (unsigned int)pdev->base_address[ridx]; - struct pci_vma *vp; - u64 pci_addr; int io; + ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2); + base = (unsigned int)pdev->base_address[ridx]; + if(pdev->base_address[ridx] > PAGE_OFFSET) continue; @@ -827,19 +1204,14 @@ static void fixup_regs(struct pci_dev *pdev, base &= ~((io ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)); - vp = pci_find_vma(pbm, base, io); + offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg; + vp = pci_find_vma(pbm, base, offset, io); if(!vp || vp->start > base) { unsigned int size, new_base; - pcibios_read_config_dword(pdev->bus->number, - pdev->devfn, - breg, &rtmp); - pcibios_write_config_dword(pdev->bus->number, - pdev->devfn, - breg, 0xffffffff); - pcibios_read_config_dword(pdev->bus->number, - pdev->devfn, - breg, &size); + pci_read_config_dword(pdev, breg, &rtmp); + pci_write_config_dword(pdev, breg, 0xffffffff); + pci_read_config_dword(pdev, breg, &size); if(io) size &= ~1; size = (~(size) + 1); @@ -847,7 +1219,8 @@ static void fixup_regs(struct pci_dev *pdev, continue; new_base = 0; - for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) { + for(vp = pci_find_vma(pbm, new_base, 0, io); ; + vp = vp->next) { if(!vp || new_base + size <= vp->start) break; new_base = (vp->end + (size - 1)) & ~(size-1); @@ -859,26 +1232,27 @@ static void fixup_regs(struct pci_dev *pdev, } vp = pci_vma_alloc(); vp->start = new_base; - vp->end = vp->start + size; - vp->base_reg = breg; - - /* Sanity */ - if(io && vp->end & ~(0xffff)) { - prom_printf("PCI: Out of range PCI I/O " - "[%08lx:%08lx] during fixup\n", - vp->start, vp->end); - prom_halt(); - } + vp->end = vp->start + size - 1; + vp->offset = offset; + pci_add_vma(pbm, vp, io); +#ifdef FIXUP_VMA_DEBUG + dprintf("%02x.%02x.%x: BaseReg %02x", + pdev->bus->number, + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), + breg); + dprintf(" %s vma [%08x,%08x]\n", + io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end); +#endif rtmp = new_base; + pci_read_config_dword(pdev, breg, &base); if(io) - rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK); + rtmp |= (base & ~PCI_BASE_ADDRESS_IO_MASK); else - rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK); - pcibios_write_config_dword(pdev->bus->number, - pdev->devfn, - breg, rtmp); + rtmp |= (base & ~PCI_BASE_ADDRESS_MEM_MASK); + pci_write_config_dword(pdev, breg, rtmp); /* Apply PBM ranges and update pci_dev. */ pci_addr = new_base; @@ -912,13 +1286,93 @@ static void fixup_regs(struct pci_dev *pdev, } } } + + /* + * Handle PCI_ROM_ADDRESS. + */ + breg = PCI_ROM_ADDRESS; + base = (unsigned int)pdev->rom_address; + + if(pdev->rom_address > PAGE_OFFSET) + goto rom_address_done; + + base &= PCI_ROM_ADDRESS_MASK; + offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg; + vp = pci_find_vma(pbm, base, offset, 0); + if(!vp || vp->start > base) { + unsigned int size, new_base; + + pci_read_config_dword(pdev, breg, &rtmp); + pci_write_config_dword(pdev, breg, 0xffffffff); + pci_read_config_dword(pdev, breg, &size); + size &= ~1; + size = (~(size) + 1); + if(!size) + goto rom_address_done; + + new_base = 0; + for(vp = pci_find_vma(pbm, new_base, 0, 0); ; vp = vp->next) { + if(!vp || new_base + size <= vp->start) + break; + new_base = (vp->end + (size - 1)) & ~(size-1); + } + if(vp && (new_base + size > vp->start)) { + prom_printf("PCI: Impossible full MEM space.\n"); + prom_halt(); + } + vp = pci_vma_alloc(); + vp->start = new_base; + vp->end = vp->start + size - 1; + vp->offset = offset; + + pci_add_vma(pbm, vp, 0); + +#ifdef FIXUP_VMA_DEBUG + dprintf("%02x.%02x.%x: BaseReg %02x", + pdev->bus->number, + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), + breg); + dprintf(" %s vma [%08x,%08x]\n", + "ROM", vp->start, vp->end); +#endif + + rtmp = new_base; + pci_read_config_dword(pdev, breg, &base); + rtmp |= (base & ~PCI_ROM_ADDRESS_MASK); + pci_write_config_dword(pdev, breg, rtmp); + + /* Apply PBM ranges and update pci_dev. */ + pci_addr = new_base; + for(rng = 0; rng < pbm->num_pbm_ranges; rng++) { + struct linux_prom_pci_ranges *rp; + int rspace; + + rp = &pbm->pbm_ranges[rng]; + rspace = (rp->child_phys_hi >> 24) & 3; + if(rspace != 2) + continue; + pci_addr += ((u64)rp->parent_phys_lo); + pci_addr += (((u64)rp->parent_phys_hi)<<32UL); + break; + } + if(rng == pbm->num_pbm_ranges) { + /* AIEEE */ + prom_printf("fixup_doit: YIEEE, cannot find " + "PBM ranges\n"); + } + pdev->rom_address = (unsigned long)__va(pci_addr); + + pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK); + MEM_seen = 1; + } + rom_address_done: + } if(IO_seen || MEM_seen) { unsigned int l; - pcibios_read_config_dword(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, &l); + pci_read_config_dword(pdev, PCI_COMMAND, &l); #ifdef FIXUP_REGS_DEBUG dprintf("["); #endif @@ -937,9 +1391,7 @@ static void fixup_regs(struct pci_dev *pdev, #ifdef FIXUP_REGS_DEBUG dprintf("]"); #endif - pcibios_write_config_dword(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, l); + pci_write_config_dword(pdev, PCI_COMMAND, l); } #ifdef FIXUP_REGS_DEBUG @@ -955,7 +1407,7 @@ static void fixup_regs(struct pci_dev *pdev, #define imap_offset(__member) \ ((unsigned long)(&(((struct psycho_regs *)0)->__member))) -static unsigned long psycho_pcislot_imap_offset(unsigned long ino) +__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino)) { unsigned int bus, slot; @@ -963,17 +1415,20 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino) slot = (ino & 0x0c) >> 2; if(bus == 0) { - /* Perform a sanity check, we might as well. - * PBM A only has 2 PCI slots. - */ - if(slot > 1) { - prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot); - prom_halt(); - } - if(slot == 0) + switch(slot) { + case 0: return imap_offset(imap_a_slot0); - else + case 1: return imap_offset(imap_a_slot1); + case 2: + return imap_offset(imap_a_slot2); + case 3: + return imap_offset(imap_a_slot3); + default: + prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", + bus, slot); + prom_halt(); + } } else { switch(slot) { case 0: @@ -988,13 +1443,12 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino) prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", bus, slot); prom_halt(); - return 0; /* Make gcc happy */ - }; + } } } /* Exported for EBUS probing layer. */ -unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino) +__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)) { unsigned long imap_off, ign, ino; @@ -1089,9 +1543,62 @@ unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino) return pci_irq_encode(imap_off, pbm->parent->index, ign, ino); } -static void fixup_irq(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - int node) +__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + struct linux_prom_pci_registers *preg, + unsigned int *interrupt)) +{ + struct linux_prom_pci_registers ppreg; + unsigned int hi, mid, lo, irq; + int i; + + if (!pbm->num_pbm_intmap) + return 0; + + /* + * Underneath a bridge, use register of parent bridge. + */ + if (pdev->bus->number != pbm->pci_first_busno) { + struct pcidev_cookie *pcp = pdev->bus->self->sysdata; + int node; + + if (!pcp) + goto out; + + node = pcp->prom_node; + + i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg)); + if(i == 0 || i == -1) + goto out; + + preg = &ppreg; + } + + hi = preg->phys_hi & pbm->pbm_intmask.phys_hi; + mid = preg->phys_mid & pbm->pbm_intmask.phys_mid; + lo = preg->phys_lo & pbm->pbm_intmask.phys_lo; + irq = *interrupt & pbm->pbm_intmask.interrupt; + for (i = 0; i < pbm->num_pbm_intmap; i++) { + if ((pbm->pbm_intmap[i].phys_hi == hi) && + (pbm->pbm_intmap[i].phys_mid == mid) && + (pbm->pbm_intmap[i].phys_lo == lo) && + (pbm->pbm_intmap[i].interrupt == irq)) { + *interrupt = pbm->pbm_intmap[i].cinterrupt; + return *interrupt; + } + } + +out: + prom_printf("pbm_intmap_match: IRQ [%08x.%08x.%08x.%08x] " + "not found in interrupt-map\n", preg->phys_hi, + preg->phys_mid, preg->phys_lo, *interrupt); + prom_halt(); +} + +__initfunc(static void fixup_irq(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *preg, + int node)) { unsigned int prom_irq, portid = pbm->parent->upa_portid; unsigned char pci_irq_line = pdev->irq; @@ -1102,13 +1609,24 @@ static void fixup_irq(struct pci_dev *pdev, #endif err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq)); if(err == 0 || err == -1) { - prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n", - pdev->vendor, pdev->device); - prom_halt(); +#ifdef FIXUP_IRQ_DEBUG + dprintf("No interrupts property.\n"); +#endif + pdev->irq = 0; + return; } + /* See if we find a matching interrupt-map entry. */ + if (pbm_intmap_match(pbm, pdev, preg, &prom_irq)) { + pdev->irq = psycho_irq_build(pbm, + (pbm->parent->upa_portid << 6) + | prom_irq); +#ifdef FIXUP_IRQ_DEBUG + dprintf("interrupt-map specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); +#endif /* See if fully specified already (ie. for onboard devices like hme) */ - if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) { + } else if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) { pdev->irq = psycho_irq_build(pbm, prom_irq); #ifdef FIXUP_IRQ_DEBUG dprintf("fully specified prom_irq[%x] pdev->irq[%x]", @@ -1147,7 +1665,7 @@ static void fixup_irq(struct pci_dev *pdev, slot = (pdev->bus->self->devfn >> 3) - 2; /* Use low slot number bits of child as IRQ line. */ - line = (line + ((pdev->devfn >> 3) - 4)) % 4; + line = (pdev->devfn >> 3) & 0x03; } slot = (slot << 2); @@ -1159,16 +1677,10 @@ static void fixup_irq(struct pci_dev *pdev, do { unsigned char iline, ipin; - (void)pcibios_read_config_byte(pdev->bus->number, - pdev->devfn, - PCI_INTERRUPT_PIN, - &ipin); - (void)pcibios_read_config_byte(pdev->bus->number, - pdev->devfn, - PCI_INTERRUPT_LINE, - &iline); - dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] " - "iline[%x] ipin[%x] prom_irq[%x]", + pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &ipin); + pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &iline); + dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] " + "irq[%x] iline[%x] ipin[%x] prom_irq[%x]", portid, bus>>4, slot>>2, line, pdev->irq, iline, ipin, prom_irq); } while(0); @@ -1178,21 +1690,19 @@ static void fixup_irq(struct pci_dev *pdev, /* * Write the INO to config space PCI_INTERRUPT_LINE. */ - (void)pcibios_write_config_byte(pdev->bus->number, - pdev->devfn, - PCI_INTERRUPT_LINE, - pdev->irq & PCI_IRQ_INO); + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, + pdev->irq & PCI_IRQ_INO); #ifdef FIXUP_IRQ_DEBUG dprintf("\n"); #endif } -static void fixup_doit(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - int node) +__initfunc(static void fixup_doit(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + int node)) { struct linux_prom_pci_registers assigned[PROMREG_MAX]; int numaa, err; @@ -1209,12 +1719,12 @@ static void fixup_doit(struct pci_dev *pdev, fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa); /* Next, fixup interrupt numbers. */ - fixup_irq(pdev, pbm, node); + fixup_irq(pdev, pbm, &pregs[0], node); } -static void fixup_pci_dev(struct pci_dev *pdev, - struct pci_bus *pbus, - struct linux_pbm_info *pbm) +__initfunc(static void fixup_pci_dev(struct pci_dev *pdev, + struct pci_bus *pbus, + struct linux_pbm_info *pbm)) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; struct pcidev_cookie *pcp = pdev->sysdata; @@ -1225,18 +1735,12 @@ static void fixup_pci_dev(struct pci_dev *pdev, unsigned short cmd; /* First, enable bus mastering. */ - pcibios_read_config_word(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, &cmd); + pci_read_config_word(pdev, PCI_COMMAND, &cmd); cmd |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, cmd); + pci_write_config_word(pdev, PCI_COMMAND, cmd); /* Now, set cache line size to 64-bytes. */ - pcibios_write_config_byte(pdev->bus->number, - pdev->devfn, - PCI_CACHE_LINE_SIZE, 64); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64); } /* Ignore if this is one of the PBM's, EBUS, or a @@ -1246,8 +1750,17 @@ static void fixup_pci_dev(struct pci_dev *pdev, if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) || (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) || (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) || - (pcp == NULL)) + (pcp == NULL)) { + /* + * Prevent access to PCI_ROM_ADDRESS, in case present + * as we don't fixup the address. + */ + if (pdev->rom_address) { + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, 0); + pdev->rom_address = 0; + } return; + } node = pcp->prom_node; @@ -1260,20 +1773,30 @@ static void fixup_pci_dev(struct pci_dev *pdev, nregs = (err / sizeof(pregs[0])); fixup_doit(pdev, pbm, &pregs[0], nregs, node); + + /* Enable bus mastering on IDE interfaces. */ + if ((pdev->class >> 8 == PCI_CLASS_STORAGE_IDE) + && (pdev->class & 0x80)) { + unsigned short cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } } -static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm) +__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)) { struct pci_dev *pdev; for(pdev = pbus->devices; pdev; pdev = pdev->sibling) fixup_pci_dev(pdev, pbus, pbm); - for(pbus = pbus->children; pbus; pbus = pbus->children) + for(pbus = pbus->children; pbus; pbus = pbus->next) fixup_pci_bus(pbus, pbm); } -static void fixup_addr_irq(struct linux_pbm_info *pbm) +__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm)) { struct pci_bus *pbus = &pbm->pci_bus; @@ -1286,14 +1809,16 @@ static void fixup_addr_irq(struct linux_pbm_info *pbm) /* Walk all PCI devices probes, fixing up base registers and IRQ registers. * We use OBP for most of this work. */ -static void psycho_final_fixup(struct linux_psycho *psycho) +__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho)) { /* Second, fixup base address registers and IRQ lines... */ - fixup_addr_irq(&psycho->pbm_A); - fixup_addr_irq(&psycho->pbm_B); + if (psycho->pbm_A.parent) + fixup_addr_irq(&psycho->pbm_A); + if (psycho->pbm_B.parent) + fixup_addr_irq(&psycho->pbm_B); } -unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end) +__initfunc(unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)) { struct linux_psycho *psycho; @@ -1312,11 +1837,16 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end */ for (psycho = psycho_root; psycho; psycho = psycho->next) { - /* Probe busses under PBM B. */ - pbm_probe(&psycho->pbm_B, &memory_start); - - /* Probe busses under PBM A. */ - pbm_probe(&psycho->pbm_A, &memory_start); + /* Probe bus on builtin PCI. */ + if (psycho->pci_bus) + sabre_probe(psycho, &memory_start); + else { + /* Probe busses under PBM B. */ + pbm_probe(&psycho->pbm_B, &memory_start); + + /* Probe busses under PBM A. */ + pbm_probe(&psycho->pbm_A, &memory_start); + } } pci_init_alloc_init(&memory_start); @@ -1327,8 +1857,13 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's). */ for (psycho = psycho_root; psycho; psycho = psycho->next) { - fill_in_pbm_cookies(&psycho->pbm_A); - fill_in_pbm_cookies(&psycho->pbm_B); + if (psycho->pci_bus) + sabre_cookie_fillin(psycho); + + fill_in_pbm_cookies(&psycho->pbm_A.pci_bus, + &psycho->pbm_A); + fill_in_pbm_cookies(&psycho->pbm_B.pci_bus, + &psycho->pbm_B); /* See what OBP has taken care of already. */ record_assignments(&psycho->pbm_A); @@ -1373,11 +1908,157 @@ pci_mkaddr(struct linux_pbm_info *pbm, unsigned char bus, static inline int out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn) { - return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) || - ((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) || + return ((pbm->parent == 0) || + ((pbm == &pbm->parent->pbm_B) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 4) || + ((pbm == &pbm->parent->pbm_A) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 6) || (pci_probe_enable == 0)); } +static inline int +sabre_out_of_range(unsigned char devfn) +{ + return ((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) || + ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) || + (PCI_SLOT(devfn) > 1); +} + +static int +sabre_read_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) +{ + if (bus) + return pbm_read_config_byte(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) { + *value = 0xff; + return PCIBIOS_SUCCESSFUL; + } + + if (where < 8) { + unsigned short tmp; + + pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp); + if (where & 1) + *value = tmp >> 8; + else + *value = tmp & 0xff; + return PCIBIOS_SUCCESSFUL; + } else + return pbm_read_config_byte(pbm, bus, devfn, where, value); +} + +static int +sabre_read_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) +{ + if (bus) + return pbm_read_config_word(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) { + *value = 0xffff; + return PCIBIOS_SUCCESSFUL; + } + + if (where < 8) + return pbm_read_config_word(pbm, bus, devfn, where, value); + else { + unsigned char tmp; + + pbm_read_config_byte(pbm, bus, devfn, where, &tmp); + *value = tmp; + pbm_read_config_byte(pbm, bus, devfn, where + 1, &tmp); + *value |= tmp << 8; + return PCIBIOS_SUCCESSFUL; + } +} + +static int +sabre_read_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) +{ + unsigned short tmp; + + if (bus) + return pbm_read_config_dword(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) { + *value = 0xffffffff; + return PCIBIOS_SUCCESSFUL; + } + + sabre_read_config_word(pbm, bus, devfn, where, &tmp); + *value = tmp; + sabre_read_config_word(pbm, bus, devfn, where + 2, &tmp); + *value |= tmp << 16; + return PCIBIOS_SUCCESSFUL; +} + +static int +sabre_write_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) +{ + if (bus) + return pbm_write_config_byte(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where < 8) { + unsigned short tmp; + + pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp); + if (where & 1) { + value &= 0x00ff; + value |= tmp << 8; + } else { + value &= 0xff00; + value |= tmp; + } + return pbm_write_config_word(pbm, bus, devfn, where & ~1, tmp); + } else + return pbm_write_config_byte(pbm, bus, devfn, where, value); +} + +static int +sabre_write_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) +{ + if (bus) + return pbm_write_config_word(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where < 8) + return pbm_write_config_word(pbm, bus, devfn, where, value); + else { + pbm_write_config_byte(pbm, bus, devfn, where, value & 0xff); + pbm_write_config_byte(pbm, bus, devfn, where + 1, value >> 8); + return PCIBIOS_SUCCESSFUL; + } +} + +static int +sabre_write_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) +{ + if (bus) + return pbm_write_config_dword(pbm, bus, devfn, where, value); + + if (sabre_out_of_range(devfn)) + return PCIBIOS_SUCCESSFUL; + + sabre_write_config_word(pbm, bus, devfn, where, value & 0xffff); + sabre_write_config_word(pbm, bus, devfn, where + 2, value >> 16); + return PCIBIOS_SUCCESSFUL; +} + static int pbm_read_config_byte(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn, @@ -1574,36 +2255,60 @@ pbm_write_config_dword(struct linux_pbm_info *pbm, int pcibios_read_config_byte (unsigned char bus, unsigned char devfn, unsigned char where, unsigned char *value) { - return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_read_config_byte(pbm, bus, devfn, where, value); + return pbm_read_config_byte(pbm, bus, devfn, where, value); } int pcibios_read_config_word (unsigned char bus, unsigned char devfn, unsigned char where, unsigned short *value) { - return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_read_config_word(pbm, bus, devfn, where, value); + return pbm_read_config_word(pbm, bus, devfn, where, value); } int pcibios_read_config_dword (unsigned char bus, unsigned char devfn, unsigned char where, unsigned int *value) { - return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_read_config_dword(pbm, bus, devfn, where, value); + return pbm_read_config_dword(pbm, bus, devfn, where, value); } int pcibios_write_config_byte (unsigned char bus, unsigned char devfn, unsigned char where, unsigned char value) { - return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value); + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_write_config_byte(pbm, bus, devfn, where, value); + return pbm_write_config_byte(pbm, bus, devfn, where, value); } int pcibios_write_config_word (unsigned char bus, unsigned char devfn, unsigned char where, unsigned short value) { + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_write_config_word(pbm, bus, devfn, where, value); return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value); } int pcibios_write_config_dword (unsigned char bus, unsigned char devfn, unsigned char where, unsigned int value) { + struct linux_pbm_info *pbm = bus2pbm[bus]; + + if (pbm && pbm->parent && pbm->parent->pci_bus) + return sabre_write_config_dword(pbm, bus, devfn, where, value); return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value); } @@ -1692,4 +2397,9 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, return err; } +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} + #endif diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 932addbb4..cdc8f47de 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.18 1997/12/18 02:43:00 ecd Exp $ +/* $Id: setup.c,v 1.20 1998/02/24 17:02:39 jj Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -55,11 +55,6 @@ struct screen_info screen_info = { unsigned int phys_bytes_of_ram, end_of_phys_memory; -unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) -{ - return memory_start; -} - /* Typing sync at the prom prompt calls the function pointed to by * the sync callback which I set to the following function. * This should sync all filesystems and return, for now it just @@ -413,14 +408,17 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) extern char *sparc_cpu_type[]; extern char *sparc_fpu_type[]; -extern char *smp_info(void); -extern char *mmu_info(void); +extern int smp_info(char *); +extern int smp_bogo(char *); +extern int mmu_info(char *); int get_cpuinfo(char *buffer) { int cpuid=smp_processor_id(); + int len; - return sprintf(buffer, "cpu\t\t: %s\n" + len = sprintf(buffer, + "cpu\t\t: %s\n" "fpu\t\t: %s\n" "promlib\t\t: Version 3 Revision %d\n" "prom\t\t: %d.%d.%d\n" @@ -429,33 +427,22 @@ int get_cpuinfo(char *buffer) "ncpus active\t: %d\n" #ifndef __SMP__ "BogoMips\t: %lu.%02lu\n" -#else - "Cpu0Bogo\t: %lu.%02lu\n" - "Cpu1Bogo\t: %lu.%02lu\n" - "Cpu2Bogo\t: %lu.%02lu\n" - "Cpu3Bogo\t: %lu.%02lu\n" -#endif - "%s" -#ifdef __SMP__ - "%s" #endif , sparc_cpu_type[cpuid], sparc_fpu_type[cpuid], prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff, - linux_num_cpus, smp_num_cpus, + linux_num_cpus, smp_num_cpus #ifndef __SMP__ - loops_per_sec/500000, (loops_per_sec/5000) % 100, -#else - cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100, - cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100, - cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100, - cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100, + , loops_per_sec/500000, (loops_per_sec/5000) % 100 #endif - mmu_info() + ); #ifdef __SMP__ - , smp_info() + len += smp_bogo(buffer + len); #endif - ); - + len += mmu_info(buffer + len); +#ifdef __SMP__ + len += smp_info(buffer + len); +#endif + return len; } diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 8ca15c80b..269ff413d 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.34 1997/12/15 15:04:49 jj Exp $ +/* $Id: signal32.c,v 1.35 1998/04/01 07:00:43 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -1023,6 +1023,7 @@ static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,signr); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } } diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 932534c05..ca2aa4360 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/kernel_stat.h> #include <linux/delay.h> +#include <linux/init.h> #include <asm/head.h> #include <asm/ptrace.h> @@ -25,12 +26,14 @@ #include <asm/hardirq.h> #include <asm/softirq.h> #include <asm/uaccess.h> +#include <asm/timer.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> extern int linux_num_cpus; extern void calibrate_delay(void); +extern unsigned prom_cpu_nodes[]; volatile int smp_processors_ready = 0; unsigned long cpu_present_map = 0; @@ -39,35 +42,46 @@ int smp_threads_ready = 0; struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); -static unsigned char boot_cpu_id = 0; +static unsigned char boot_cpu_id __initdata = 0; static int smp_activated = 0; volatile int cpu_number_map[NR_CPUS]; -volatile int cpu_logical_map[NR_CPUS]; +volatile int __cpu_logical_map[NR_CPUS]; struct klock_info klock_info = { KLOCK_CLEAR, 0 }; -void smp_setup(char *str, int *ints) +__initfunc(void smp_setup(char *str, int *ints)) { /* XXX implement me XXX */ } -static char smp_buf[512]; +int smp_info(char *buf) +{ + int len = 7, i; + + strcpy(buf, "State:\n"); + for (i = 0; i < NR_CPUS; i++) + if(cpu_present_map & (1UL << i)) + len += sprintf(buf + len, + "CPU%d:\t\t%s\n", + i, klock_info.akp == i ? "akp" : "online"); + return len; +} -char *smp_info(void) +int smp_bogo(char *buf) { - /* XXX not SMP safe and need to support up to 64 penguins */ - sprintf(smp_buf, -" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n" -"State: %s\t\t%s\t\t%s\t\t%s\n", -(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline", -(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline", -(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline", -(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline"); - return smp_buf; + int len = 0, i; + + for (i = 0; i < NR_CPUS; i++) + if(cpu_present_map & (1UL << i)) + len += sprintf(buf + len, + "Cpu%dBogo\t: %lu.%02lu\n", + i, cpu_data[i].udelay_val / 500000, + (cpu_data[i].udelay_val / 5000) % 100); + return len; } -void smp_store_cpu_info(int id) +__initfunc(void smp_store_cpu_info(int id)) { cpu_data[id].udelay_val = loops_per_sec; cpu_data[id].irq_count = 0; @@ -80,7 +94,7 @@ void smp_store_cpu_info(int id) extern void distribute_irqs(void); -void smp_commence(void) +__initfunc(void smp_commence(void)) { distribute_irqs(); } @@ -92,7 +106,7 @@ static volatile unsigned long callin_flag = 0; extern void inherit_locked_prom_mappings(int save_p); extern void cpu_probe(void); -void smp_callin(void) +__initfunc(void smp_callin(void)) { int cpuid = hard_smp_processor_id(); @@ -156,22 +170,24 @@ extern struct prom_cpuinfo linux_cpus[NR_CPUS]; extern unsigned long smp_trampoline; -void smp_boot_cpus(void) +__initfunc(void smp_boot_cpus(void)) { int cpucount = 0, i; printk("Entering UltraSMPenguin Mode...\n"); + boot_cpu_id = hard_smp_processor_id(); smp_tickoffset_init(); __sti(); cpu_present_map = 0; for(i = 0; i < linux_num_cpus; i++) - cpu_present_map |= (1UL << i); + cpu_present_map |= (1UL << linux_cpus[i].mid); for(i = 0; i < NR_CPUS; i++) { cpu_number_map[i] = -1; - cpu_logical_map[i] = -1; + __cpu_logical_map[i] = -1; } cpu_number_map[boot_cpu_id] = 0; - cpu_logical_map[0] = boot_cpu_id; + prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node; + __cpu_logical_map[0] = boot_cpu_id; klock_info.akp = boot_cpu_id; current->processor = boot_cpu_id; smp_store_cpu_info(boot_cpu_id); @@ -188,13 +204,18 @@ void smp_boot_cpus(void) unsigned long entry = (unsigned long)(&smp_trampoline); struct task_struct *p; int timeout; + int no; + extern unsigned long phys_base; - entry -= KERNBASE; + entry += phys_base - KERNBASE; kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; callin_flag = 0; - prom_startcpu(linux_cpus[i].prom_node, + for (no = 0; no < linux_num_cpus; no++) + if (linux_cpus[no].mid == i) + break; + prom_startcpu(linux_cpus[no].prom_node, entry, ((unsigned long)p)); for(timeout = 0; timeout < 5000000; timeout++) { if(callin_flag) @@ -202,8 +223,9 @@ void smp_boot_cpus(void) udelay(100); } if(callin_flag) { - cpu_number_map[i] = i; - cpu_logical_map[i] = i; + cpu_number_map[i] = cpucount; + prom_cpu_nodes[i] = linux_cpus[no].prom_node; + __cpu_logical_map[cpucount] = i; } else { cpucount--; printk("Processor %d is stuck.\n", i); @@ -248,9 +270,9 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) /* #define XCALL_DEBUG */ -static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, int cpu) +static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu) { - u64 result, target = (((unsigned long)linux_cpus[cpu].mid) << 14) | 0x70; + u64 result, target = (cpu << 14) | 0x70; int stuck; #ifdef XCALL_DEBUG @@ -307,12 +329,15 @@ void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) if(smp_processors_ready) { unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id())); u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); - int i, ncpus = smp_num_cpus; + int i, ncpus = smp_num_cpus - 1; __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - for(i = 0; i < ncpus; i++) { - if(mask & (1UL << i)) + for(i = 0; i < NR_CPUS; i++) { + if(mask & (1UL << i)) { xcall_deliver(data0, data1, data2, pstate, i); + ncpus--; + } + if (!ncpus) break; } /* NOTE: Caller runs local copy on master. */ } @@ -489,13 +514,14 @@ static inline void sparc64_do_profile(unsigned long pc) #endif } -static unsigned long real_tick_offset, current_tick_offset; +static unsigned long current_tick_offset; #define prof_multiplier(__cpu) cpu_data[(__cpu)].multiplier #define prof_counter(__cpu) cpu_data[(__cpu)].counter extern void update_one_process(struct task_struct *p, unsigned long ticks, - unsigned long user, unsigned long system); + unsigned long user, unsigned long system, + int cpu); void smp_percpu_timer_interrupt(struct pt_regs *regs) { @@ -503,32 +529,62 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) int cpu = smp_processor_id(); int user = user_mode(regs); + /* + * Check for level 14 softint. + */ + if (!(get_softint() & (1UL << 0))) { + extern void handler_irq(int, struct pt_regs *); + + handler_irq(14, regs); + return; + } + clear_softint((1UL << 0)); do { if(!user) sparc64_do_profile(regs->tpc); if(!--prof_counter(cpu)) { + + if (cpu == boot_cpu_id) { + extern void irq_enter(int, int); + extern void irq_exit(int, int); + + irq_enter(cpu, 0); + kstat.irqs[cpu][0]++; + + timer_tick_interrupt(regs); + + irq_exit(cpu, 0); + } + if(current->pid) { - unsigned int *inc_me; + unsigned int *inc, *inc2; - update_one_process(current, 1, user, !user); + update_one_process(current, 1, user, !user, cpu); if(--current->counter < 0) { current->counter = 0; need_resched = 1; } if(user) { - if(current->priority < DEF_PRIORITY) - inc_me = &kstat.cpu_nice; - else - inc_me = &kstat.cpu_user; + if(current->priority < DEF_PRIORITY) { + inc = &kstat.cpu_nice; + inc2 = &kstat.per_cpu_nice[cpu]; + } else { + inc = &kstat.cpu_user; + inc2 = &kstat.per_cpu_user[cpu]; + } } else { - inc_me = &kstat.cpu_system; + inc = &kstat.cpu_system; + inc2 = &kstat.per_cpu_system[cpu]; } - atomic_inc((atomic_t *)inc_me); + atomic_inc((atomic_t *)inc); + atomic_inc((atomic_t *)inc2); } + prof_counter(cpu) = prof_multiplier(cpu); } + __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" "add %0, %2, %0\n\t" "wr %0, 0x0, %%tick_cmpr\n\t" @@ -538,12 +594,55 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) } while (tick >= compare); } -static void smp_setup_percpu_timer(void) +__initfunc(static void smp_setup_percpu_timer(void)) { int cpu = smp_processor_id(); prof_counter(cpu) = prof_multiplier(cpu) = 1; + if (cpu == boot_cpu_id) { + extern unsigned long tl0_itick; + extern unsigned long tl0_smp_itick; + unsigned long flags; + + save_flags(flags); cli(); + + /* + * Steal TICK_INT interrupts from timer_interrupt(). + */ + __asm__ __volatile__(" + .globl tl0_smp_itick + b,pt %%xcc, 1f + nop + + tl0_smp_itick: + rdpr %%pil, %%g2 + wrpr %%g0, 15, %%pil + b,pt %%xcc, etrap_irq + rd %%pc, %%g7 + call smp_percpu_timer_interrupt + add %%sp, %0, %%o0 + b,pt %%xcc, rtrap + clr %%l6 + + 1:" + : /* no outputs */ + : "i" (STACK_BIAS + REGWIN_SZ)); + + memcpy(&tl0_itick, &tl0_smp_itick, 8 * 4); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + 0x00 + flush %0 + 0x08 + flush %0 + 0x10 + flush %0 + 0x18" + : /* no outputs */ + : "r" (&tl0_itick)); + + restore_flags(flags); + } + __asm__ __volatile__("rd %%tick, %%g1\n\t" "add %%g1, %0, %%g1\n\t" "wr %%g1, 0x0, %%tick_cmpr" @@ -552,22 +651,17 @@ static void smp_setup_percpu_timer(void) : "g1"); } -static void smp_tickoffset_init(void) +__initfunc(static void smp_tickoffset_init(void)) { - int node; - - node = linux_cpus[0].prom_node; - real_tick_offset = prom_getint(node, "clock-frequency"); - real_tick_offset = real_tick_offset / HZ; - current_tick_offset = real_tick_offset; + current_tick_offset = timer_tick_offset; } -int setup_profiling_timer(unsigned int multiplier) +__initfunc(int setup_profiling_timer(unsigned int multiplier)) { unsigned long flags; int i; - if((!multiplier) || (real_tick_offset / multiplier) < 1000) + if((!multiplier) || (timer_tick_offset / multiplier) < 1000) return -EINVAL; save_and_cli(flags); @@ -575,7 +669,7 @@ int setup_profiling_timer(unsigned int multiplier) if(cpu_present_map & (1UL << i)) prof_multiplier(i) = multiplier; } - current_tick_offset = (real_tick_offset / multiplier); + current_tick_offset = (timer_tick_offset / multiplier); restore_flags(flags); return 0; diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index b776ea06e..0eb16d7bb 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.27 1997/11/19 07:57:46 jj Exp $ +/* $Id: sparc64_ksyms.c,v 1.33 1998/04/06 16:09:40 jj Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -50,6 +50,7 @@ struct poll { short revents; }; +extern unsigned prom_cpu_nodes[NR_CPUS]; extern void die_if_kernel(char *str, struct pt_regs *regs); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); @@ -112,6 +113,8 @@ EXPORT_SYMBOL_PRIVATE(global_restore_flags); #else EXPORT_SYMBOL(local_irq_count); #endif +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL_PRIVATE(_lock_kernel); EXPORT_SYMBOL_PRIVATE(_unlock_kernel); @@ -134,7 +137,9 @@ EXPORT_SYMBOL(dma_chain); #endif #if CONFIG_PCI EXPORT_SYMBOL(ebus_chain); -EXPORT_SYMBOL(pci_devices); +EXPORT_SYMBOL(pci_dvma_offset); +EXPORT_SYMBOL(pci_dvma_mask); +EXPORT_SYMBOL(empty_zero_page); #endif /* Solaris/SunOS binary compatibility */ @@ -171,7 +176,6 @@ EXPORT_SYMBOL(__prom_getsibling); /* sparc library symbols */ EXPORT_SYMBOL(bcopy); -EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strcpy); @@ -179,7 +183,6 @@ EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strcat); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strncmp); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strpbrk); @@ -201,15 +204,11 @@ EXPORT_SYMBOL(sys_sigsuspend); EXPORT_SYMBOL(sys_getppid); EXPORT_SYMBOL(svr4_getcontext); EXPORT_SYMBOL(svr4_setcontext); -EXPORT_SYMBOL(linux_cpus); +EXPORT_SYMBOL(prom_cpu_nodes); EXPORT_SYMBOL(sys_ioctl); EXPORT_SYMBOL(sys32_ioctl); #endif -#ifdef CONFIG_MATHEMU_MODULE -EXPORT_SYMBOL(handle_mathemu); -#endif - /* Special internal versions of library functions. */ EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memset); diff --git a/arch/sparc64/kernel/sunos_ioctl32.c b/arch/sparc64/kernel/sunos_ioctl32.c index d94a7c6d6..a0bfb47ef 100644 --- a/arch/sparc64/kernel/sunos_ioctl32.c +++ b/arch/sparc64/kernel/sunos_ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: sunos_ioctl32.c,v 1.5 1997/09/18 10:37:57 rth Exp $ +/* $Id: sunos_ioctl32.c,v 1.9 1998/03/29 10:10:53 davem Exp $ * sunos_ioctl32.c: SunOS ioctl compatability on sparc64. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -18,6 +18,7 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -88,15 +89,12 @@ extern asmlinkage int sys_setsid(void); asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg) { - struct file *filp; int ret = -EBADF; lock_kernel(); if(fd >= SUNOS_NR_OPEN) goto out; - - filp = current->files->fd[fd]; - if(!filp) + if(!fcheck(fd)) goto out; if(cmd == TIOCSETD) { @@ -168,7 +166,7 @@ asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg) ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); goto out; case _IOW('i', 24, struct ifreq32): - ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + ret = sys32_ioctl(fd, SIOCSIFBRDADDR, arg); goto out; case _IOWR('i', 25, struct ifreq32): ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg); diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S index 6fb6f739b..44f17ca01 100644 --- a/arch/sparc64/kernel/sys32.S +++ b/arch/sparc64/kernel/sys32.S @@ -1,4 +1,4 @@ -/* $Id: sys32.S,v 1.4 1997/09/09 17:13:29 jj Exp $ +/* $Id: sys32.S,v 1.5 1998/03/24 05:57:56 ecd Exp $ * sys32.S: I-cache tricks for 32-bit compatability layer simple * conversions. * @@ -24,7 +24,7 @@ sys32_mmap: .align 32 .globl sys32_lseek - .globl sys32_chmod, sys32_chown, sys32_mknod + .globl sys32_chmod, sys32_chown, sys32_lchown, sys32_mknod sys32_lseek: sra %o1, 0, %o1 mov %o7, %g1 @@ -46,6 +46,15 @@ sys32_chown: srl %o2, 16, %o2 call sys_chown mov %g1, %o7 +sys32_lchown: + sll %o1, 16, %o1 + mov %o7, %g1 + sll %o2, 16, %o2 + srl %o0, 0, %o0 + srl %o1, 16, %o1 + srl %o2, 16, %o2 + call sys_lchown + mov %g1, %o7 sys32_mknod: sll %o2, 16, %o2 mov %o7, %g1 diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index e0e69abd9..b5198074d 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.9 1997/12/11 15:15:44 jj Exp $ +/* $Id: sys_sparc.c,v 1.13 1998/03/29 10:10:52 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -10,6 +10,7 @@ #include <linux/types.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/mm.h> #include <linux/sem.h> #include <linux/msg.h> @@ -40,7 +41,7 @@ asmlinkage unsigned long sparc_brk(unsigned long brk) unsigned long ret; lock_kernel(); - if(brk >= 0x80000000000ULL) { /* VM hole */ + if(brk >= 0x80000000000UL) { /* VM hole */ ret = current->mm->brk; goto out; } @@ -128,6 +129,16 @@ asmlinkage int sys_ipc (unsigned call, int first, int second, unsigned long thir if (call <= SHMCTL) switch (call) { case SHMAT: + if (first >= 0) { + extern struct shmid_ds *shm_segs[]; + struct shmid_ds *shp = shm_segs[(unsigned int) first % SHMMNI]; + if (shp == IPC_UNUSED || shp == IPC_NOID) { + err = -ENOMEM; + if ((unsigned long)ptr >= 0x80000000000UL - shp->shm_segsz && + (unsigned long)ptr < 0xfffff80000000000UL) + goto out; /* Somebody is trying to fool us */ + } + } err = sys_shmat (first, (char *) ptr, second, (ulong *) third); goto out; case SHMDT: @@ -162,29 +173,39 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])){ + file = fget(fd); + if (!file) goto out; - } } retval = -ENOMEM; + len = PAGE_ALIGN(len); if(!(flags & MAP_FIXED) && !addr) { addr = get_unmapped_area(addr, len); - if(!addr){ - goto out; - } + if(!addr) + goto out_putf; } - /* See asm-sparc64/uaccess.h */ retval = -EINVAL; - if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE))) - goto out; - - if(addr >= 0x80000000000ULL) { - retval = current->mm->brk; - goto out; + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (len > 0xf0000000UL || addr > 0xf0000000UL - len) + goto out_putf; + } else { + if (len >= 0x80000000000UL || + (addr < 0x80000000000UL && + addr > 0x80000000000UL-len)) + goto out_putf; + if (addr >= 0x80000000000ULL && addr < 0xfffff80000000000UL) { + /* VM hole */ + retval = current->mm->brk; + goto out_putf; + } } retval = do_mmap(file, addr, len, prot, flags, off); + +out_putf: + if (file) + fput(file); out: unlock_kernel(); return retval; diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 66993ebcb..2844a4bf2 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,7 +1,7 @@ -/* $Id: sys_sparc32.c,v 1.71 1997/12/11 15:15:11 jj Exp $ +/* $Id: sys_sparc32.c,v 1.77 1998/03/29 10:10:50 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * * These routines maintain argument size conversion between 32bit and 64bit @@ -10,11 +10,12 @@ #include <linux/config.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/signal.h> #include <linux/utime.h> #include <linux/resource.h> -#include <linux/sched.h> #include <linux/times.h> #include <linux/utime.h> #include <linux/utsname.h> @@ -41,6 +42,7 @@ #include <linux/module.h> #include <linux/poll.h> #include <linux/personality.h> +#include <linux/stat.h> #include <asm/types.h> #include <asm/ipc.h> @@ -59,7 +61,6 @@ extern char * getname_quicklist; extern int getname_quickcount; extern struct semaphore getname_quicklock; -extern int kerneld_msqid; /* Tuning: increase locality by reusing same pages again... * if getname_quicklist becomes too long on low memory machines, either a limit @@ -324,19 +325,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u if (!p) err = -ENOMEM; else { err = 0; - if (first == kerneld_msqid) { - *(int *)p->mtext = 0; - if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) || - __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4)) - err = -EFAULT; - else - second += 4; - } else { - if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second)) - err = -EFAULT; - } + if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second)) + err = -EFAULT; if (!err) { mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); @@ -379,18 +370,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u if (err < 0) goto out; - if (first == kerneld_msqid) { - if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8)) - err = -EFAULT; - else - err -= 4; - } else { - if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err)) - err = -EFAULT; - } + if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err)) + err = -EFAULT; kfree (p); goto out; } @@ -939,14 +921,12 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count) { int error = -EBADF; struct file * file; + struct inode * inode; struct readdir_callback32 buf; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - file = current->files->fd[fd]; - if(!file) + file = fget(fd); + if (!file) goto out; buf.count = 0; @@ -954,12 +934,18 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count) error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; - + goto out_putf; + + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, fillonedir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; error = buf.count; + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -1006,16 +992,14 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count) { struct file * file; + struct inode * inode; struct linux_dirent32 * lastdirent; struct getdents_callback32 buf; int error = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - file = current->files->fd[fd]; - if(!file) + file = fget(fd); + if (!file) goto out; buf.current_dir = (struct linux_dirent32 *) A(dirent); @@ -1025,17 +1009,22 @@ asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count) error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, filldir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; lastdirent = buf.previous; error = buf.error; if(lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = count - buf.count; } +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -1258,6 +1247,27 @@ asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf) return ret; } +extern asmlinkage int sys_xstat(int ver, char *filename, struct stat64 * statbuf); + +asmlinkage int sys32_xstat(int ver, u32 file, u32 statbuf) +{ + switch (ver & __XSTAT_VER_MASK) { + case __XSTAT_VER_1: + switch (ver & __XSTAT_VER_TYPEMASK) { + case __XSTAT_VER_XSTAT: + return sys32_newstat(file, statbuf); + case __XSTAT_VER_LXSTAT: + return sys32_newlstat(file, statbuf); + case __XSTAT_VER_FXSTAT: + return sys32_newfstat(file, statbuf); + } + return -EINVAL; + case __XSTAT_VER_2: + return sys_xstat(ver, (char *)A(file), (struct stat64 *)A(statbuf)); + } + return -EINVAL; +} + extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2) @@ -2222,18 +2232,19 @@ asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags) char address[MAX_SOCK_ADDR]; struct iovec iov[UIO_FASTIOV]; unsigned char ctl[sizeof(struct cmsghdr) + 20]; - struct msghdr kern_msg; - int err; - int total_len; unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; if(msghdr_from_user32_to_kern(&kern_msg, (struct msghdr32 *)A(user_msg))) return -EFAULT; if(kern_msg.msg_iovlen > UIO_MAXIOV) return -EINVAL; - total_len = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); - if(total_len < 0) - return total_len; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + if(kern_msg.msg_controllen) { struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control; unsigned long *kcmsg; @@ -2241,41 +2252,40 @@ asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags) if(kern_msg.msg_controllen > sizeof(ctl) && kern_msg.msg_controllen <= 256) { + err = -ENOBUFS; ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); - if(!ctl_buf) { - if(kern_msg.msg_iov != iov) - kfree(kern_msg.msg_iov); - return -ENOBUFS; - } + if(!ctl_buf) + goto out_freeiov; } __get_user(cmlen, &ucmsg->cmsg_len); kcmsg = (unsigned long *) ctl_buf; *kcmsg++ = (unsigned long)cmlen; + err = -EFAULT; if(copy_from_user(kcmsg, &ucmsg->cmsg_level, - kern_msg.msg_controllen - sizeof(__kernel_size_t32))) { - if(ctl_buf != ctl) - kfree_s(ctl_buf, kern_msg.msg_controllen); - if(kern_msg.msg_iov != iov) - kfree(kern_msg.msg_iov); - return -EFAULT; - } + kern_msg.msg_controllen - sizeof(__kernel_size_t32))) + goto out_freectl; kern_msg.msg_control = ctl_buf; } kern_msg.msg_flags = user_flags; lock_kernel(); - if(current->files->fd[fd]->f_flags & O_NONBLOCK) - kern_msg.msg_flags |= MSG_DONTWAIT; - if((sock = sockfd_lookup(fd, &err)) != NULL) { + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; err = sock_sendmsg(sock, &kern_msg, total_len); sockfd_put(sock); } unlock_kernel(); +out_freectl: + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ if(ctl_buf != ctl) - kfree_s(ctl_buf, kern_msg.msg_controllen); + kfree(ctl_buf); +out_freeiov: if(kern_msg.msg_iov != iov) kfree(kern_msg.msg_iov); +out: return err; } @@ -2299,17 +2309,18 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) uaddr = kern_msg.msg_name; uaddr_len = &((struct msghdr32 *)A(user_msg))->msg_namelen; err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); - if(err < 0) - return err; + if (err < 0) + goto out; total_len = err; cmsg_ptr = (unsigned long) kern_msg.msg_control; kern_msg.msg_flags = 0; lock_kernel(); - if(current->files->fd[fd]->f_flags & O_NONBLOCK) - user_flags |= MSG_DONTWAIT; - if((sock = sockfd_lookup(fd, &err)) != NULL) { + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); if(err >= 0) len = err; @@ -2317,8 +2328,6 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) } unlock_kernel(); - if(kern_msg.msg_iov != iov) - kfree(kern_msg.msg_iov); if(uaddr != NULL && err >= 0) err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); if(err >= 0) { @@ -2330,6 +2339,10 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) &((struct msghdr32 *)A(user_msg))->msg_controllen); } } + + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: if(err < 0) return err; return len; @@ -2838,25 +2851,25 @@ asmlinkage int sys32_get_kernel_syms(u32 table) #else /* CONFIG_MODULES */ asmlinkage unsigned long -sys_create_module(const char *name_user, size_t size) +sys32_create_module(const char *name_user, size_t size) { return -ENOSYS; } asmlinkage int -sys_init_module(const char *name_user, struct module *mod_user) +sys32_init_module(const char *name_user, struct module *mod_user) { return -ENOSYS; } asmlinkage int -sys_delete_module(const char *name_user) +sys32_delete_module(const char *name_user) { return -ENOSYS; } asmlinkage int -sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, +sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t *ret) { /* Let the program know about the new interface. Not that @@ -2868,7 +2881,7 @@ sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, } asmlinkage int -sys_get_kernel_syms(struct kernel_sym *table) +sys32_get_kernel_syms(struct kernel_sym *table) { return -ENOSYS; } @@ -3327,3 +3340,19 @@ asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, u32 ubuf, { return sys_pwrite(fd, (char *) A(ubuf), count, pos); } + + +extern asmlinkage int sys_personality(unsigned long); + +asmlinkage int sys32_personality(unsigned long personality) +{ + int ret; + lock_kernel(); + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + unlock_kernel(); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 4af388b99..ad7bac534 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.7 1997/12/11 15:15:19 jj Exp $ +/* $Id: sys_sunos32.c,v 1.11 1998/03/29 10:10:55 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -16,6 +16,7 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/resource.h> #include <linux/ipc.h> #include <linux/shm.h> @@ -70,23 +71,30 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of flags &= ~MAP_NORESERVE; } retval = -EBADF; - if(!(flags & MAP_ANONYMOUS)) - if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + if(!(flags & MAP_ANONYMOUS)) { + if(fd >= SUNOS_NR_OPEN) goto out; + file = fget(fd); + if (!file) + goto out; + if (file->f_dentry && file->f_dentry->d_inode) { + struct inode * inode = file->f_dentry->d_inode; + if(MAJOR(inode->i_rdev) == MEM_MAJOR && + MINOR(inode->i_rdev) == 5) { + flags |= MAP_ANONYMOUS; + fput(file); + file = NULL; + } + } + } + retval = -ENOMEM; if(!(flags & MAP_FIXED) && !addr) { unsigned long attempt = get_unmapped_area(addr, len); if(!attempt || (attempt >= 0xf0000000UL)) - goto out; + goto out_putf; addr = (u32) attempt; } - if(file->f_dentry && file->f_dentry->d_inode) { - if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR && - MINOR(file->f_dentry->d_inode->i_rdev) == 5) { - flags |= MAP_ANONYMOUS; - file = 0; - } - } if(!(flags & MAP_FIXED)) addr = 0; ret_type = flags & _MAP_NEW; @@ -98,6 +106,9 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of (unsigned long) off); if(!ret_type) retval = ((retval < 0xf0000000) ? 0 : retval); +out_putf: + if (file) + fput(file); out: unlock_kernel(); return (u32) retval; @@ -372,6 +383,7 @@ static int sunos_filldir(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) { struct file * file; + struct inode * inode; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; @@ -381,32 +393,39 @@ asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) if(fd >= SUNOS_NR_OPEN) goto out; - file = current->files->fd[fd]; + file = fget(fd); if(!file) goto out; error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; error = -EINVAL; if(cnt < (sizeof(struct sunos_dirent) + 255)) - goto out; + goto out_putf; buf.curr = (struct sunos_dirent *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, sunos_filldir); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; + lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = cnt - buf.count; } + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -454,43 +473,51 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent, int cnt, u32 u_basep) { + void *dirent = (void *) A(u_dirent); + unsigned int *basep = (unsigned int *)A(u_basep); struct file * file; + struct inode * inode; struct sunos_direntry * lastdirent; - struct sunos_direntry_callback buf; int error = -EBADF; - void *dirent = (void *) A(u_dirent); - unsigned int *basep = (unsigned int *)A(u_basep); + struct sunos_direntry_callback buf; lock_kernel(); if(fd >= SUNOS_NR_OPEN) goto out; - file = current->files->fd[fd]; + file = fget(fd); if(!file) goto out; error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) - goto out; + goto out_putf; error = -EINVAL; if(cnt < (sizeof(struct sunos_direntry) + 255)) - goto out; + goto out_putf; buf.curr = (struct sunos_direntry *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; + inode = file->f_dentry->d_inode; + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, sunos_filldirentry); + up(&inode->i_sem); if (error < 0) - goto out; + goto out_putf; + lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, basep); error = cnt - buf.count; } + +out_putf: + fput(file); out: unlock_kernel(); return error; @@ -622,14 +649,28 @@ asmlinkage int sunos_pathconf(u32 u_path, int name) extern asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp); -asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp) +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x) { int ret; /* SunOS binaries expect that select won't change the tvp contents */ lock_kernel(); current->personality |= STICKY_TIMEOUTS; - ret = sys32_select (width, inp, outp, exp, tvp); + ret = sys32_select (width, inp, outp, exp, tvp_x); + if (ret == -EINTR && tvp_x) { + struct timeval32 *tvp = (struct timeval32 *)A(tvp_x); + time_t sec, usec; + + __get_user(sec, &tvp->tv_sec); + __get_user(usec, &tvp->tv_usec); + if (sec == 0 && usec == 0) + ret = 0; + } unlock_kernel(); return ret; } @@ -1297,8 +1338,11 @@ asmlinkage int sunos_open(u32 filename, int flags, int mode) static inline int check_nonblock(int ret, int fd) { - if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY)) - return -SUNOS_EWOULDBLOCK; + if (ret == -EAGAIN) { + struct file * file = fcheck(fd); + if (file && (file->f_flags & O_NDELAY)) + ret = -SUNOS_EWOULDBLOCK; + } return ret; } @@ -1370,12 +1414,42 @@ asmlinkage int sunos_send(int fd, u32 buff, int len, unsigned flags) return ret; } +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +asmlinkage int sunos_socket(int family, int type, int protocol) +{ + int ret, one = 1; + + lock_kernel(); + ret = sys_socket(family, type, protocol); + if (ret < 0) + goto out; + + sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT, + (char *)&one, sizeof(one)); +out: + unlock_kernel(); + return ret; +} + asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen) { - int ret; + int ret, one = 1; lock_kernel(); - ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), (int *)A(addrlen)), fd); + while (1) { + ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), + (int *)A(addrlen)), fd); + if (ret != -ENETUNREACH && ret != -EHOSTUNREACH) + break; + } + if (ret < 0) + goto out; + + sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT, + (char *)&one, sizeof(one)); +out: unlock_kernel(); return ret; } diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 48ae0ecdf..6389681dd 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.37 1997/12/24 17:27:31 ecd Exp $ +/* $Id: systbls.S,v 1.41 1998/03/24 05:57:57 ecd Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -19,8 +19,8 @@ sys_call_table32: /*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write /*5*/ .word sys_open, sys_close, sys32_wait4, sys_creat, sys_link -/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys32_mknod -/*15*/ .word sys32_chmod, sys32_chown, sparc_brk, sys_nis_syscall, sys32_lseek +/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_xstat, sys32_mknod +/*15*/ .word sys32_chmod, sys32_lchown, sparc_brk, sys_xmknod, sys32_lseek /*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid /*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys32_pause /*30*/ .word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice @@ -55,7 +55,7 @@ sys_call_table32: .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall /*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .word sys32_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*190*/ .word sys32_init_module, sys32_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir .word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall @@ -78,8 +78,8 @@ sys_call_table64: sys_call_table: /*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write /*5*/ .word sys_open, sys_close, sys_wait4, sys_creat, sys_link -/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod -/*15*/ .word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek +/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_xstat, sys_mknod +/*15*/ .word sys_chmod, sys_lchown, sparc_brk, sys_xmknod, sys_lseek /*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid /*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_nis_syscall /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice @@ -117,7 +117,7 @@ sys_call_table: /*190*/ .word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall - .word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall /*210*/ .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo .word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex /*220*/ .word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid @@ -139,7 +139,7 @@ sunos_sys_table: .word sys_close, sunos_wait4, sys_creat .word sys_link, sys_unlink, sunos_execv .word sys_chdir, sunos_nosys, sys32_mknod - .word sys32_chmod, sys32_chown, sunos_brk + .word sys32_chmod, sys32_lchown, sunos_brk .word sunos_nosys, sys32_lseek, sunos_getpid .word sunos_nosys, sunos_nosys, sunos_nosys .word sunos_getuid, sunos_nosys, sys_ptrace @@ -166,7 +166,7 @@ sunos_sys_table: .word sys32_getitimer, sys_gethostname, sys_sethostname .word sunos_getdtablesize, sys_dup2, sunos_nop .word sys32_fcntl, sunos_select, sunos_nop - .word sys_fsync, sys_setpriority, sys_socket + .word sys_fsync, sys_setpriority, sunos_socket .word sys_connect, sunos_accept /*100*/ .word sys_getpriority, sunos_send, sunos_recv .word sunos_nosys, sys_bind, sunos_setsockopt diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 8b0152231..debb08888 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,7 +1,8 @@ -/* $Id: time.c,v 1.12 1997/08/22 20:12:13 davem Exp $ +/* $Id: time.c,v 1.13 1998/03/15 17:23:47 ecd Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * * Based largely on code which is: * @@ -42,29 +43,64 @@ static int set_rtc_mmss(unsigned long); * NOTE: On SUN5 systems the ticker interrupt comes in using 2 * interrupts, one at level14 and one with softint bit 0. */ -extern struct sun5_timer *linux_timers; +unsigned long timer_tick_offset; +static unsigned long timer_tick_compare; +static unsigned long timer_ticks_per_usec; -static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static __inline__ void timer_check_rtc(void) { /* last time the cmos clock got updated */ static long last_rtc_update=0; - __asm__ __volatile__("ldx [%0], %%g0" - : /* no outputs */ - : "r" (&((linux_timers)->limit0))); - - do_timer(regs); - /* Determine when to update the Mostek clock. */ if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + xtime.tv_usec < 500000 + (tick >> 1)) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; + /* do it again in 60 s */ + } +} + +static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + unsigned long ticks; + + do { + do_timer(regs); + + __asm__ __volatile__(" + rd %%tick_cmpr, %0 + add %0, %2, %0 + wr %0, 0, %%tick_cmpr + rd %%tick, %1" + : "=&r" (timer_tick_compare), "=r" (ticks) + : "r" (timer_tick_offset)); + } while (ticks >= timer_tick_compare); + + timer_check_rtc(); } +#ifdef __SMP__ +void timer_tick_interrupt(struct pt_regs *regs) +{ + do_timer(regs); + + /* + * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR. + */ + __asm__ __volatile__(" + rd %%tick_cmpr, %0 + add %0, %1, %0" + : "=&r" (timer_tick_compare) + : "r" (timer_tick_offset)); + + timer_check_rtc(); +} +#endif + /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. @@ -318,29 +354,32 @@ __initfunc(void time_init(void)) */ } -extern void init_timers(void (*func)(int, void *, struct pt_regs *)); +extern void init_timers(void (*func)(int, void *, struct pt_regs *), + unsigned long *); __initfunc(void sun4u_start_timers(void)) { - init_timers(timer_interrupt); + unsigned long clock; + + init_timers(timer_interrupt, &clock); + timer_tick_offset = clock / HZ; + timer_ticks_per_usec = clock / 1000000; } static __inline__ unsigned long do_gettimeoffset(void) { - unsigned long offset = 0; - unsigned int count; - - /* XXX -DaveM */ -#if 0 - count = (*master_l10_counter >> 10) & 0x1fffff; -#else - count = 0; -#endif + unsigned long ticks; - if(test_bit(TIMER_BH, &bh_active)) - offset = 1000000; - - return offset + count; + __asm__ __volatile__(" + rd %%tick, %%g1 + add %1, %%g1, %0 + sub %0, %2, %0 +" + : "=r" (ticks) + : "r" (timer_tick_offset), "r" (timer_tick_compare) + : "g1", "g2"); + + return ticks / timer_ticks_per_usec; } void do_gettimeofday(struct timeval *tv) @@ -353,13 +392,16 @@ void do_gettimeofday(struct timeval *tv) * nucleus atomic quad 128-bit loads. */ __asm__ __volatile__(" - sethi %hi(linux_timers), %o1 + sethi %hi(timer_tick_offset), %g3 sethi %hi(xtime), %g2 - ldx [%o1 + %lo(linux_timers)], %g3 + sethi %hi(timer_tick_compare), %g1 + ldx [%g3 + %lo(timer_tick_offset)], %g3 or %g2, %lo(xtime), %g2 + or %g1, %lo(timer_tick_compare), %g1 1: ldda [%g2] 0x24, %o4 membar #LoadLoad | #MemIssue - ldx [%g3], %o1 + rd %tick, %o1 + ldx [%g1], %g7 membar #LoadLoad | #MemIssue ldda [%g2] 0x24, %o2 membar #LoadLoad @@ -367,24 +409,28 @@ void do_gettimeofday(struct timeval *tv) xor %o5, %o3, %o3 orcc %o2, %o3, %g0 bne,pn %xcc, 1b - cmp %o1, 0 - bge,pt %icc, 1f - sethi %hi(tick), %o3 - ldx [%o3 + %lo(tick)], %o3 - sethi %hi(0x1fffff), %o2 - or %o2, %lo(0x1fffff), %o2 - add %o5, %o3, %o5 - and %o1, %o2, %o1 -1: add %o5, %o1, %o5 - sethi %hi(1000000), %o2 + sethi %hi(lost_ticks), %o2 + sethi %hi(timer_ticks_per_usec), %o3 + ldx [%o2 + %lo(lost_ticks)], %o2 + add %g3, %o1, %o1 + ldx [%o3 + %lo(timer_ticks_per_usec)], %o3 + sub %o1, %g7, %o1 + brz,pt %o2, 1f + udivx %o1, %o3, %o1 + sethi %hi(10000), %g2 + or %g2, %lo(10000), %g2 + add %o1, %g2, %o1 +1: sethi %hi(1000000), %o2 + srlx %o5, 32, %o5 or %o2, %lo(1000000), %o2 + add %o5, %o1, %o5 cmp %o5, %o2 bl,a,pn %xcc, 1f stx %o4, [%o0 + 0x0] add %o4, 0x1, %o4 sub %o5, %o2, %o5 stx %o4, [%o0 + 0x0] -1: stx %o5, [%o0 + 0x8]"); +1: st %o5, [%o0 + 0x8]"); } void do_settimeofday(struct timeval *tv) @@ -401,6 +447,7 @@ void do_settimeofday(struct timeval *tv) time_state = TIME_BAD; time_maxerror = 0x70000000; time_esterror = 0x70000000; + sti(); } diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S index b5ca851ac..8604b3301 100644 --- a/arch/sparc64/kernel/trampoline.S +++ b/arch/sparc64/kernel/trampoline.S @@ -1,4 +1,4 @@ -/* $Id: trampoline.S,v 1.2 1997/07/28 02:57:32 davem Exp $ +/* $Id: trampoline.S,v 1.3 1998/02/22 21:06:11 jj Exp $ * trampoline.S: Jump start slave processors on sparc64. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -172,8 +172,10 @@ bounce: mov %o2, %g6 wrpr %o1, (PSTATE_MG | PSTATE_IE), %pstate - sethi %hi(0x1ff8), %g2 - or %g2, %lo(0x1ff8), %g2 +#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) + sethi %uhi(KERN_HIGHBITS), %g2 + sllx %g2, 32, %g2 +#undef KERN_HIGHBITS ldx [%o2 + AOFF_task_mm], %g6 ldx [%g6 + AOFF_mm_pgd], %g6 clr %g7 diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 6e1d30990..b255c7623 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.44 1998/01/09 16:39:35 jj Exp $ +/* $Id: traps.c,v 1.49 1998/04/06 16:09:38 jj Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -191,7 +191,7 @@ void bad_trap (struct pt_regs *regs, long lvl) die_if_kernel ("Kernel bad trap", regs); current->tss.sig_desc = SUBSIG_BADTRAP(lvl - 0x100); current->tss.sig_address = regs->tpc; - send_sig(SIGILL, current, 1); + force_sig(SIGILL, current); unlock_kernel (); } @@ -225,7 +225,9 @@ void data_access_exception (struct pt_regs *regs) return; } } - send_sig(SIGSEGV, current, 1); + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } #ifdef CONFIG_PCI @@ -235,16 +237,35 @@ extern volatile int pci_poke_in_progress; extern volatile int pci_poke_faulted; #endif +/* When access exceptions happen, we must do this. */ +static __inline__ void clean_and_reenable_l1_caches(void) +{ + unsigned long va; + + /* Clean 'em. */ + for(va = 0; va < (PAGE_SIZE << 1); va += 32) { + spitfire_put_icache_tag(va, 0x0); + spitfire_put_dcache_tag(va, 0x0); + } + + /* Re-enable. */ + __asm__ __volatile__("flush %%g6\n\t" + "membar #Sync\n\t" + "stxa %0, [%%g0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | + LSU_CONTROL_IM | LSU_CONTROL_DM), + "i" (ASI_LSU_CONTROL) + : "memory"); +} + void do_dae(struct pt_regs *regs) { #ifdef CONFIG_PCI -#ifdef DEBUG_PCI_POKES - prom_printf(" (POKE "); -#endif if(pci_poke_in_progress) { - unsigned long va; #ifdef DEBUG_PCI_POKES - prom_printf("tpc[%016lx] tnpc[%016lx] ", + prom_printf(" (POKE tpc[%016lx] tnpc[%016lx] ", regs->tpc, regs->tnpc); #endif pci_poke_faulted = 1; @@ -255,39 +276,30 @@ void do_dae(struct pt_regs *regs) prom_printf("PCI) "); /* prom_halt(); */ #endif - /* Re-enable I/D caches, Ultra turned them off. */ - for(va = 0; va < (PAGE_SIZE << 1); va += 32) { - spitfire_put_icache_tag(va, 0x0); - spitfire_put_dcache_tag(va, 0x0); - } - __asm__ __volatile__("flush %%g6\n\t" - "membar #Sync\n\t" - "stxa %0, [%%g0] %1\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | - LSU_CONTROL_IM | LSU_CONTROL_DM), - "i" (ASI_LSU_CONTROL) - : "memory"); + clean_and_reenable_l1_caches(); return; } -#ifdef DEBUG_PCI_POKES - prom_printf("USER) "); - prom_printf("tpc[%016lx] tnpc[%016lx]\n"); - prom_halt(); #endif -#endif - send_sig(SIGSEGV, current, 1); + clean_and_reenable_l1_caches(); + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } void instruction_access_exception (struct pt_regs *regs) { - send_sig(SIGSEGV, current, 1); + clean_and_reenable_l1_caches(); + + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } void do_iae(struct pt_regs *regs) { - send_sig(SIGSEGV, current, 1); + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); } void do_fpe_common(struct pt_regs *regs) @@ -312,11 +324,7 @@ void do_fpieee(struct pt_regs *regs) do_fpe_common(regs); } -#ifdef CONFIG_MATHEMU_MODULE -volatile int (*handle_mathemu)(struct pt_regs *, struct fpustate *) = NULL; -#else extern int do_mathemu(struct pt_regs *, struct fpustate *); -#endif void do_fpother(struct pt_regs *regs) { @@ -326,18 +334,7 @@ void do_fpother(struct pt_regs *regs) switch ((f->fsr & 0x1c000)) { case (2 << 14): /* unfinished_FPop */ case (3 << 14): /* unimplemented_FPop */ -#ifdef CONFIG_MATHEMU_MODULE -#ifdef CONFIG_KMOD - if (!handle_mathemu) - request_module("math-emu"); -#endif - if (handle_mathemu) - ret = handle_mathemu(regs, f); -#else -#ifdef CONFIG_MATHEMU ret = do_mathemu(regs, f); -#endif -#endif break; } if (ret) return; @@ -576,27 +573,33 @@ void cache_flush_trap(struct pt_regs *regs) #else #error SMP not supported on sparc64 yet #endif + +#if 0 +/* Broken */ int size = prom_getintdefault(node, "ecache-size", 512*1024); int i, j; - unsigned long addr, page_nr; + unsigned long addr; + struct page *page, *end; regs->tpc = regs->tnpc; regs->tnpc = regs->tnpc + 4; if (!suser()) return; size >>= PAGE_SHIFT; addr = PAGE_OFFSET - PAGE_SIZE; + page = mem_map - 1; + end = mem_map + max_mapnr; for (i = 0; i < size; i++) { do { addr += PAGE_SIZE; - page_nr = MAP_NR(addr); - if (page_nr >= max_mapnr) { + page++; + if (page >= end) return; - } - } while (!PageReserved (mem_map + page_nr)); + } while (!PageReserved(page)); /* E-Cache line size is 64B. Let us pollute it :)) */ for (j = 0; j < PAGE_SIZE; j += 64) __asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1"); } +#endif } #endif diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index b22cf82f7..3d17fb3cb 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.22 1997/10/16 07:07:46 jj Exp $ +/* $Id: ttable.S,v 1.23 1998/03/15 17:23:48 ecd Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -6,7 +6,7 @@ #include <linux/config.h> - .globl sparc64_ttable_tl0, sparc64_ttable_tl1 + .globl sparc64_ttable_tl0, tl0_itick, sparc64_ttable_tl1, sparc64_ttable_tl0: tl0_resv000: BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3) @@ -45,7 +45,7 @@ tl0_irq7: TRAP_IRQ(handler_irq, 7) TRAP_IRQ(handler_irq, 8) tl0_irq9: TRAP_IRQ(handler_irq, 9) TRAP_IRQ(handler_irq, 10) tl0_irq11: TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12) tl0_irq13: TRAP_IRQ(handler_irq, 13) -tl0_itick: TRAP_TICK +tl0_itick: TRAP_IRQ(handler_irq, 14) tl0_irq15: TRAP_IRQ(handler_irq, 15) tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55) tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b) |