diff options
Diffstat (limited to 'arch')
318 files changed, 18664 insertions, 7464 deletions
diff --git a/arch/alpha/boot/main.c b/arch/alpha/boot/main.c index c2d4e657e..dd4e47ab5 100644 --- a/arch/alpha/boot/main.c +++ b/arch/alpha/boot/main.c @@ -22,61 +22,27 @@ extern int vsprintf(char *, const char *, va_list); extern unsigned long switch_to_osf_pal(unsigned long nr, struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, - unsigned long vptb, unsigned long *kstk); - -int printk(const char * fmt, ...) -{ - va_list args; - int i, j, written, remaining, num_nl; - static char buf[1024]; - char * str; - - va_start(args, fmt); - i = vsprintf(buf, fmt, args); - va_end(args); - - /* expand \n into \r\n: */ - - num_nl = 0; - for (j = 0; j < i; ++j) { - if (buf[j] == '\n') - ++num_nl; - } - remaining = i + num_nl; - for (j = i - 1; j >= 0; --j) { - buf[j + num_nl] = buf[j]; - if (buf[j] == '\n') { - --num_nl; - buf[j + num_nl] = '\r'; - } - } - - str = buf; - do { - written = puts(str, remaining); - remaining -= written; - str += written; - } while (remaining > 0); - return i; -} - -#define hwrpb (*INIT_HWRPB) + unsigned long *vptb); +struct hwrpb_struct *hwrpb = INIT_HWRPB; +static struct pcb_struct pcb_va[1]; /* * Find a physical address of a virtual object.. * * This is easy using the virtual page table address. */ -struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb) + +static inline void * +find_pa(unsigned long *vptb, void *ptr) { - unsigned long address = (unsigned long) pcb; + unsigned long address = (unsigned long) ptr; unsigned long result; result = vptb[address >> 13]; result >>= 32; result <<= 13; result |= address & 0x1fff; - return (struct pcb_struct *) result; + return (void *) result; } /* @@ -88,30 +54,19 @@ struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb) * code has the L1 page table identity-map itself in the second PTE * in the L1 page table. Thus the L1-page is virtually addressable * itself (through three levels) at virtual address 0x200802000. - * - * As we don't want it there anyway, we also move the L1 self-map - * up as high as we can, so that the last entry in the L1 page table - * maps the page tables. - * - * As a result, the OSF/1 pal-code will instead use a virtual page table - * map located at 0xffffffe00000000. */ -#define pcb_va ((struct pcb_struct *) 0x20000000) -#define old_vptb (0x0000000200000000UL) -#define new_vptb (0xfffffffe00000000UL) -void pal_init(void) + +#define VPTB ((unsigned long *) 0x200000000) +#define L1 ((unsigned long *) 0x200802000) + +void +pal_init(void) { - unsigned long i, rev, sum; - unsigned long *L1, *l; + unsigned long i, rev; struct percpu_struct * percpu; struct pcb_struct * pcb_pa; - /* Find the level 1 page table and duplicate it in high memory */ - L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */ - L1[1023] = L1[1]; - - percpu = (struct percpu_struct *) (hwrpb.processor_offset + (unsigned long) &hwrpb), - + /* Create the dummy PCB. */ pcb_va->ksp = 0; pcb_va->usp = 0; pcb_va->ptbr = L1[1] >> 32; @@ -119,39 +74,32 @@ void pal_init(void) pcb_va->pcc = 0; pcb_va->unique = 0; pcb_va->flags = 1; - pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va); - printk("Switching to OSF PAL-code .. "); + pcb_va->res1 = 0; + pcb_va->res2 = 0; + pcb_pa = find_pa(VPTB, pcb_va); + /* * a0 = 2 (OSF) - * a1 = return address, but we give the asm the virtual addr of the PCB + * a1 = return address, but we give the asm the vaddr of the PCB * a2 = physical addr of PCB * a3 = new virtual page table pointer - * a4 = KSP (but we give it 0, asm sets it) + * a4 = KSP (but the asm sets it) */ - i = switch_to_osf_pal( - 2, - pcb_va, - pcb_pa, - new_vptb, - 0); + srm_printk("Switching to OSF PAL-code .. "); + + i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); if (i) { - printk("failed, code %ld\n", i); + srm_printk("failed, code %ld\n", i); halt(); } - rev = percpu->pal_revision = percpu->palcode_avail[2]; - hwrpb.vptb = new_vptb; + percpu = (struct percpu_struct *) + (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); + rev = percpu->pal_revision = percpu->palcode_avail[2]; - /* update checksum: */ - sum = 0; - for (l = (unsigned long *) &hwrpb; l < (unsigned long *) &hwrpb.chksum; ++l) - sum += *l; - hwrpb.chksum = sum; + srm_printk("Ok (rev %lx)\n", rev); - printk("Ok (rev %lx)\n", rev); - /* remove the old virtual page-table mapping */ - L1[1] = 0; - flush_tlb_all(); + tbia(); /* do it directly in case we are SMP */ } static inline long openboot(void) @@ -159,15 +107,15 @@ static inline long openboot(void) char bootdev[256]; long result; - result = dispatch(CCB_GET_ENV, ENV_BOOTED_DEV, bootdev, 255); + result = srm_dispatch(CCB_GET_ENV, ENV_BOOTED_DEV, bootdev, 255); if (result < 0) return result; - return dispatch(CCB_OPEN, bootdev, result & 255); + return srm_dispatch(CCB_OPEN, bootdev, result & 255); } static inline long close(long dev) { - return dispatch(CCB_CLOSE, dev); + return srm_dispatch(CCB_CLOSE, dev); } static inline long load(long dev, unsigned long addr, unsigned long count) @@ -176,15 +124,15 @@ static inline long load(long dev, unsigned long addr, unsigned long count) extern char _end; long result, boot_size = &_end - (char *) BOOT_ADDR; - result = dispatch(CCB_GET_ENV, ENV_BOOTED_FILE, bootfile, 255); + result = srm_dispatch(CCB_GET_ENV, ENV_BOOTED_FILE, bootfile, 255); if (result < 0) return result; result &= 255; bootfile[result] = '\0'; if (result) - printk("Boot file specification (%s) not implemented\n", + srm_printk("Boot file specification (%s) not implemented\n", bootfile); - return dispatch(CCB_READ, dev, count, addr, boot_size/512 + 1); + return srm_dispatch(CCB_READ, dev, count, addr, boot_size/512 + 1); } /* @@ -208,27 +156,27 @@ void start_kernel(void) int nbytes; char envval[256]; - printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); - if (hwrpb.pagesize != 8192) { - printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10); + srm_printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); + if (INIT_HWRPB->pagesize != 8192) { + srm_printk("Expected 8kB pages, got %ldkB\n", INIT_HWRPB->pagesize >> 10); return; } pal_init(); dev = openboot(); if (dev < 0) { - printk("Unable to open boot device: %016lx\n", dev); + srm_printk("Unable to open boot device: %016lx\n", dev); return; } dev &= 0xffffffff; - printk("Loading vmlinux ..."); + srm_printk("Loading vmlinux ..."); i = load(dev, START_ADDR, KERNEL_SIZE); close(dev); if (i != KERNEL_SIZE) { - printk("Failed (%lx)\n", i); + srm_printk("Failed (%lx)\n", i); return; } - nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS, + nbytes = srm_dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); if (nbytes < 0) { nbytes = 0; @@ -236,7 +184,7 @@ void start_kernel(void) envval[nbytes] = '\0'; strcpy((char*)ZERO_PAGE, envval); - printk(" Ok\nNow booting the kernel\n"); + srm_printk(" Ok\nNow booting the kernel\n"); runkernel(); for (i = 0 ; i < 0x100000000 ; i++) /* nothing */; diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 4e64ee826..d632cdbb9 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -54,12 +54,10 @@ unset CONFIG_PCI CONFIG_ALPHA_EISA unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS CONFIG_ALPHA_POLARIS unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA -unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION if [ "$CONFIG_ALPHA_GENERIC" = "y" ] then define_bool CONFIG_PCI y - define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y fi if [ "$CONFIG_ALPHA_BOOK1" = "y" ] then @@ -108,7 +106,7 @@ then then define_bool CONFIG_ALPHA_EV5 y else - define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_EV4 y fi define_bool CONFIG_ALPHA_T2 y fi @@ -141,11 +139,6 @@ if [ "$CONFIG_ALPHA_JENSEN" = "y" ] then define_bool CONFIG_ALPHA_EV4 y fi -if [ "$CONFIG_ALPHA_EV4" = "y" ] -then - # EV45 and older do not support all rounding modes in hw: - define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y -fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \ @@ -243,7 +236,7 @@ fi endmenu mainmenu_option next_comment -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' +comment 'Old CD-ROM drivers (not SCSI, not IDE)' bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index cfd366eb5..c557d66ec 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -91,7 +91,6 @@ CONFIG_PARIDE_PARPORT=y # CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index 380af2c9c..b4e71bf56 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -6,8 +6,6 @@ #include <asm/system.h> -#define halt .long PAL_halt -#define rti .long PAL_rti #define SIGCHLD 20 #define NR_SYSCALLS 371 @@ -98,7 +96,7 @@ call_pal PAL_swpipl; \ stq $21,HAE_CACHE($19); \ stq $21,0($20); \ - bis $0,$0,$16; \ + mov $0,$16; \ call_pal PAL_swpipl; \ ldq $0,0($30); \ ldq $1,8($30); \ @@ -127,8 +125,8 @@ entInt: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entInt .end entInt @@ -170,8 +168,8 @@ entMM: entArith: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entArith .end entArith @@ -181,11 +179,23 @@ entArith: entIF: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entIF .end entIF +.align 3 +.globl entDbg +.ent entDbg +entDbg: + SAVE_ALL + lda $8,0x3fff + lda $26,ret_from_sys_call + bic $30,$8,$8 + jsr $31,do_entDbg +.end entDbg + + /* * Fork() is one of the special system calls: it needs to * save the callee-saved regs so that the regs can be found @@ -200,18 +210,18 @@ entIF: kernel_clone: .frame $30, 0, $26 .prologue 0 - subq $30,6*8,$30 - stq $31,0($30) - stq $26,8($30) - stq $29,16($30) - stq $16,24($30) - stq $17,32($30) - stq $18,40($30) - bis $31,2,$0 /* Register v0: syscall nr for fork() */ + subq $30,6*8,$30 + stq $31,0($30) + stq $26,8($30) + stq $29,16($30) + stq $16,24($30) + stq $17,32($30) + stq $18,40($30) + bis $31,2,$0 /* Register v0: syscall nr for fork() */ SAVE_ALL - bsr $26,sys_clone - stq $0,0($30) - br $31,ret_from_sys_call + bsr $26,sys_clone + stq $0,0($30) + br ret_from_sys_call .end kernel_clone /* @@ -221,32 +231,32 @@ kernel_clone: .globl __kernel_thread .ent __kernel_thread __kernel_thread: - ldgp $29,0($27) /* we can be called from a module */ + ldgp $29,0($27) /* we can be called from a module */ .frame $30, 4*8, $26 - subq $30,4*8,$30 - stq $10,16($30) - stq $9,8($30) - stq $26,0($30) + subq $30,4*8,$30 + stq $10,16($30) + stq $9,8($30) + stq $26,0($30) .prologue 1 - bis $17,$17,$9 /* save fn */ - bis $18,$18,$10 /* save arg */ - bsr $26,kernel_clone - bne $20,1f /* $20 is non-zero in child */ - ldq $26,0($30) - ldq $9,8($30) - ldq $10,16($30) - addq $30,4*8,$30 - ret $31,($26),1 + mov $17,$9 /* save fn */ + mov $18,$10 /* save arg */ + bsr $26,kernel_clone + bne $20,1f /* $20 is non-zero in child */ + ldq $26,0($30) + ldq $9,8($30) + ldq $10,16($30) + addq $30,4*8,$30 + ret $31,($26),1 /* this is in child: look out as we don't have any stack here.. */ -1: bis $9,$9,$27 /* get fn */ - lda $8,0x3fff - bis $10,$10,$16 /* get arg */ - bic $30,$8,$8 /* get current */ - jsr $26,($27) +1: mov $9,$27 /* get fn */ + lda $8,0x3fff + mov $10,$16 /* get arg */ + bic $30,$8,$8 /* get current */ + jsr $26,($27) ldgp $29,0($26) - bis $0,$0,$16 - jsr $26,sys_exit - call_pal PAL_halt + mov $0,$16 + mov $31,$26 + jsr $31,sys_exit .end __kernel_thread /* @@ -274,202 +284,199 @@ __kernel_execve: .align 3 .ent do_switch_stack do_switch_stack: - lda $30,-SWITCH_STACK_SIZE($30) - stq $9,0($30) - stq $10,8($30) - stq $11,16($30) - stq $12,24($30) - stq $13,32($30) - stq $14,40($30) - stq $15,48($30) - stq $26,56($30) - stt $f0,64($30) - stt $f1,72($30) - stt $f2,80($30) - stt $f3,88($30) - stt $f4,96($30) - stt $f5,104($30) - stt $f6,112($30) - stt $f7,120($30) - stt $f8,128($30) - stt $f9,136($30) - stt $f10,144($30) - stt $f11,152($30) - stt $f12,160($30) - stt $f13,168($30) - stt $f14,176($30) - stt $f15,184($30) - stt $f16,192($30) - stt $f17,200($30) - stt $f18,208($30) - stt $f19,216($30) - stt $f20,224($30) - stt $f21,232($30) - stt $f22,240($30) - stt $f23,248($30) - stt $f24,256($30) - stt $f25,264($30) - stt $f26,272($30) - stt $f27,280($30) - mf_fpcr $f0 # get fpcr - stt $f28,288($30) - stt $f29,296($30) - stt $f30,304($30) - stt $f0,312($30) # save fpcr in slot of $f31 - ldt $f0,64($30) # dont let "do_switch_stack" change fp state. - ret $31,($1),1 + lda $30,-SWITCH_STACK_SIZE($30) + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + stq $26,56($30) + stt $f0,64($30) + stt $f1,72($30) + stt $f2,80($30) + stt $f3,88($30) + stt $f4,96($30) + stt $f5,104($30) + stt $f6,112($30) + stt $f7,120($30) + stt $f8,128($30) + stt $f9,136($30) + stt $f10,144($30) + stt $f11,152($30) + stt $f12,160($30) + stt $f13,168($30) + stt $f14,176($30) + stt $f15,184($30) + stt $f16,192($30) + stt $f17,200($30) + stt $f18,208($30) + stt $f19,216($30) + stt $f20,224($30) + stt $f21,232($30) + stt $f22,240($30) + stt $f23,248($30) + stt $f24,256($30) + stt $f25,264($30) + stt $f26,272($30) + stt $f27,280($30) + mf_fpcr $f0 # get fpcr + stt $f28,288($30) + stt $f29,296($30) + stt $f30,304($30) + stt $f0,312($30) # save fpcr in slot of $f31 + ldt $f0,64($30) # dont let "do_switch_stack" change fp state. + ret $31,($1),1 .end do_switch_stack .align 3 .ent undo_switch_stack undo_switch_stack: - ldq $9,0($30) - ldq $10,8($30) - ldq $11,16($30) - ldq $12,24($30) - ldq $13,32($30) - ldq $14,40($30) - ldq $15,48($30) - ldq $26,56($30) - ldt $f30,312($30) # get saved fpcr - ldt $f0,64($30) - ldt $f1,72($30) - ldt $f2,80($30) - ldt $f3,88($30) - mt_fpcr $f30 # install saved fpcr - ldt $f4,96($30) - ldt $f5,104($30) - ldt $f6,112($30) - ldt $f7,120($30) - ldt $f8,128($30) - ldt $f9,136($30) - ldt $f10,144($30) - ldt $f11,152($30) - ldt $f12,160($30) - ldt $f13,168($30) - ldt $f14,176($30) - ldt $f15,184($30) - ldt $f16,192($30) - ldt $f17,200($30) - ldt $f18,208($30) - ldt $f19,216($30) - ldt $f20,224($30) - ldt $f21,232($30) - ldt $f22,240($30) - ldt $f23,248($30) - ldt $f24,256($30) - ldt $f25,264($30) - ldt $f26,272($30) - ldt $f27,280($30) - ldt $f28,288($30) - ldt $f29,296($30) - ldt $f30,304($30) - lda $30,SWITCH_STACK_SIZE($30) - ret $31,($1),1 + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + ldq $26,56($30) + ldt $f30,312($30) # get saved fpcr + ldt $f0,64($30) + ldt $f1,72($30) + ldt $f2,80($30) + ldt $f3,88($30) + mt_fpcr $f30 # install saved fpcr + ldt $f4,96($30) + ldt $f5,104($30) + ldt $f6,112($30) + ldt $f7,120($30) + ldt $f8,128($30) + ldt $f9,136($30) + ldt $f10,144($30) + ldt $f11,152($30) + ldt $f12,160($30) + ldt $f13,168($30) + ldt $f14,176($30) + ldt $f15,184($30) + ldt $f16,192($30) + ldt $f17,200($30) + ldt $f18,208($30) + ldt $f19,216($30) + ldt $f20,224($30) + ldt $f21,232($30) + ldt $f22,240($30) + ldt $f23,248($30) + ldt $f24,256($30) + ldt $f25,264($30) + ldt $f26,272($30) + ldt $f27,280($30) + ldt $f28,288($30) + ldt $f29,296($30) + ldt $f30,304($30) + lda $30,SWITCH_STACK_SIZE($30) + ret $31,($1),1 .end undo_switch_stack .align 3 .globl entUna .ent entUna entUna: - lda $30,-256($30) - stq $0,0($30) - ldq $0,256($30) /* get PS */ - stq $1,8($30) - stq $2,16($30) - stq $3,24($30) - and $0,8,$0 /* user mode? */ - stq $4,32($30) - bne $0,entUnaUser /* yup -> do user-level unaligned fault */ - stq $5,40($30) - stq $6,48($30) - stq $7,56($30) - stq $8,64($30) - stq $9,72($30) - stq $10,80($30) - stq $11,88($30) - stq $12,96($30) - stq $13,104($30) - stq $14,112($30) - stq $15,120($30) + lda $30,-256($30) + stq $0,0($30) + ldq $0,256($30) /* get PS */ + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + and $0,8,$0 /* user mode? */ + stq $4,32($30) + bne $0,entUnaUser /* yup -> do user-level unaligned fault */ + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $9,72($30) + stq $10,80($30) + stq $11,88($30) + stq $12,96($30) + stq $13,104($30) + stq $14,112($30) + stq $15,120($30) /* 16-18 PAL-saved */ - stq $19,152($30) - stq $20,160($30) - stq $21,168($30) - stq $22,176($30) - stq $23,184($30) - stq $24,192($30) - stq $25,200($30) - stq $26,208($30) - stq $27,216($30) - stq $28,224($30) - stq $29,232($30) - stq $30,240($30) - stq $31,248($30) - lda $8,0x3fff - bic $30,$8,$8 - jsr $26,do_entUna - ldq $0,0($30) - ldq $1,8($30) - ldq $2,16($30) - ldq $3,24($30) - ldq $4,32($30) - ldq $5,40($30) - ldq $6,48($30) - ldq $7,56($30) - ldq $8,64($30) - ldq $9,72($30) - ldq $10,80($30) - ldq $11,88($30) - ldq $12,96($30) - ldq $13,104($30) - ldq $14,112($30) - ldq $15,120($30) + stq $19,152($30) + stq $20,160($30) + stq $21,168($30) + stq $22,176($30) + stq $23,184($30) + stq $24,192($30) + stq $25,200($30) + stq $26,208($30) + stq $27,216($30) + stq $28,224($30) + stq $29,232($30) + lda $8,0x3fff + stq $31,248($30) + bic $30,$8,$8 + jsr $26,do_entUna + ldq $0,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $9,72($30) + ldq $10,80($30) + ldq $11,88($30) + ldq $12,96($30) + ldq $13,104($30) + ldq $14,112($30) + ldq $15,120($30) /* 16-18 PAL-saved */ - ldq $19,152($30) - ldq $20,160($30) - ldq $21,168($30) - ldq $22,176($30) - ldq $23,184($30) - ldq $24,192($30) - ldq $25,200($30) - ldq $26,208($30) - ldq $27,216($30) - ldq $28,224($30) - ldq $29,232($30) - ldq $30,240($30) - lda $30,256($30) - rti + ldq $19,152($30) + ldq $20,160($30) + ldq $21,168($30) + ldq $22,176($30) + ldq $23,184($30) + ldq $24,192($30) + ldq $25,200($30) + ldq $26,208($30) + ldq $27,216($30) + ldq $28,224($30) + ldq $29,232($30) + lda $30,256($30) + call_pal PAL_rti .end entUna .align 3 .ent entUnaUser entUnaUser: - ldq $0,0($30) /* restore original $0 */ - lda $30,256($30) /* pop entUna's stack frame */ - SAVE_ALL /* setup normal kernel stack */ - lda $30,-56($30) - stq $9,0($30) - stq $10,8($30) - stq $11,16($30) - stq $12,24($30) - stq $13,32($30) - stq $14,40($30) - stq $15,48($30) - lda $8,0x3fff - addq $30,56,$19 - bic $30,$8,$8 - jsr $26,do_entUnaUser - ldq $9,0($30) - ldq $10,8($30) - ldq $11,16($30) - ldq $12,24($30) - ldq $13,32($30) - ldq $14,40($30) - ldq $15,48($30) - lda $30,56($30) - br $31,ret_from_sys_call - + ldq $0,0($30) /* restore original $0 */ + lda $30,256($30) /* pop entUna's stack frame */ + SAVE_ALL /* setup normal kernel stack */ + lda $30,-56($30) + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + lda $8,0x3fff + addq $30,56,$19 + bic $30,$8,$8 + jsr $26,do_entUnaUser + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + lda $30,56($30) + br ret_from_sys_call .end entUnaUser /* @@ -479,36 +486,36 @@ entUnaUser: .globl sys_fork .ent sys_fork sys_fork: - bsr $1,do_switch_stack - bis $31,SIGCHLD,$16 - bis $31,$31,$17 - bis $30,$30,$18 - jsr $26,alpha_clone - bsr $1,undo_switch_stack - ret $31,($26),1 + bsr $1,do_switch_stack + bis $31,SIGCHLD,$16 + mov $31,$17 + mov $30,$18 + jsr $26,alpha_clone + bsr $1,undo_switch_stack + ret $31,($26),1 .end sys_fork .align 3 .globl sys_clone .ent sys_clone sys_clone: - bsr $1,do_switch_stack + bsr $1,do_switch_stack /* arg1 and arg2 come from the user */ - bis $30,$30,$18 - jsr $26,alpha_clone - bsr $1,undo_switch_stack - ret $31,($26),1 + mov $30,$18 + jsr $26,alpha_clone + bsr $1,undo_switch_stack + ret $31,($26),1 .end sys_clone .align 3 .globl sys_vfork .ent sys_vfork sys_vfork: - bsr $1,do_switch_stack - bis $30,$30,$16 - jsr $26,alpha_vfork - bsr $1,undo_switch_stack - ret $31,($26),1 + bsr $1,do_switch_stack + mov $30,$16 + jsr $26,alpha_vfork + bsr $1,undo_switch_stack + ret $31,($26),1 .end sys_vfork .align 3 @@ -516,12 +523,12 @@ sys_vfork: .ent alpha_switch_to alpha_switch_to: .prologue 0 - bsr $1,do_switch_stack + bsr $1,do_switch_stack call_pal PAL_swpctx - lda $16,-2($31) - call_pal PAL_tbi - bsr $1,undo_switch_stack - ret $31,($26),1 + unop + bsr $1,undo_switch_stack + mov $17,$0 + ret $31,($26),1 .end alpha_switch_to /* @@ -580,7 +587,7 @@ ret_from_reschedule: bne $5,signal_return restore_all: RESTORE_ALL - rti + call_pal PAL_rti /* PTRACE syscall handler */ @@ -608,7 +615,7 @@ strace: s8addq $0,$2,$2 beq $1,1f ldq $27,0($2) -1: jsr $26,($27),alpha_ni_syscall +1: jsr $26,($27),sys_gettimeofday ldgp $29,0($26) /* check return.. */ @@ -634,15 +641,15 @@ strace_error: stq $1,72($30) /* a3 for return */ bsr $1,do_switch_stack - bis $19,$19,$9 /* save old syscall number */ - bis $20,$20,$10 /* save old a3 */ + mov $19,$9 /* save old syscall number */ + mov $20,$10 /* save old a3 */ jsr $26,syscall_trace - bis $9,$9,$19 - bis $10,$10,$20 + mov $9,$19 + mov $10,$20 bsr $1,undo_switch_stack - bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */ - br $31,ret_from_sys_call + mov $31,$26 /* tell "ret_from_sys_call" we can restart */ + br ret_from_sys_call .align 3 handle_bottom_half: @@ -653,7 +660,7 @@ handle_bottom_half: ldq $19,0($30) ldq $20,8($30) addq $30,16,$30 - br $31,ret_from_handle_bh + br ret_from_handle_bh .align 3 syscall_error: @@ -671,38 +678,35 @@ syscall_error: subq $31,$0,$0 /* with error in v0 */ addq $31,1,$1 /* set a3 for errno return */ stq $0,0($30) - bis $31,$31,$26 /* tell "ret_from_sys_call" we can restart */ + mov $31,$26 /* tell "ret_from_sys_call" we can restart */ stq $1,72($30) /* a3 for return */ - br $31,ret_from_sys_call + br ret_from_sys_call ret_success: stq $0,0($30) stq $31,72($30) /* a3=0 => no error */ - br $31,ret_from_sys_call + br ret_from_sys_call .align 3 signal_return: - bis $30,$30,$17 + mov $30,$17 br $1,do_switch_stack - bis $30,$30,$18 - bis $31,$31,$16 + mov $30,$18 + mov $31,$16 jsr $26,do_signal bsr $1,undo_switch_stack - br $31,restore_all + br restore_all .end entSys #ifdef __SMP__ - .globl ret_from_smpfork + .globl ret_from_smp_fork .align 3 -.ent ret_from_smpfork -ret_from_smpfork: - .set at - mb /* Make the changed data visible before the freed lock. */ - stq $31,scheduler_lock +.ent ret_from_smp_fork +ret_from_smp_fork: lda $26,ret_from_sys_call + mov $17,$16 jsr $31,schedule_tail - .set noat -.end ret_from_smpfork +.end ret_from_smp_fork #endif /* __SMP__ */ .align 3 @@ -715,51 +719,51 @@ reschedule: ldq $19,0($30) ldq $20,8($30) addq $30,16,$30 - br $31,ret_from_reschedule + br ret_from_reschedule .end reschedule .align 3 .ent sys_sigreturn sys_sigreturn: - bis $30,$30,$17 + mov $30,$17 + lda $18,-SWITCH_STACK_SIZE($30) lda $30,-SWITCH_STACK_SIZE($30) - bis $30,$30,$18 jsr $26,do_sigreturn br $1,undo_switch_stack - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_sigreturn .align 3 .ent sys_rt_sigreturn sys_rt_sigreturn: - bis $30,$30,$17 + mov $30,$17 + lda $18,-SWITCH_STACK_SIZE($30) lda $30,-SWITCH_STACK_SIZE($30) - bis $30,$30,$18 jsr $26,do_rt_sigreturn br $1,undo_switch_stack - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_rt_sigreturn .align 3 .ent sys_sigsuspend sys_sigsuspend: - bis $30,$30,$17 + mov $30,$17 br $1,do_switch_stack - bis $30,$30,$18 + mov $30,$18 jsr $26,do_sigsuspend lda $30,SWITCH_STACK_SIZE($30) - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_sigsuspend .align 3 .ent sys_rt_sigsuspend sys_rt_sigsuspend: - bis $30,$30,$18 + mov $30,$18 br $1,do_switch_stack - bis $30,$30,$19 + mov $30,$19 jsr $26,do_rt_sigsuspend lda $30,SWITCH_STACK_SIZE($30) - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_rt_sigsuspend .data diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 67c08778d..38bd43947 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -37,6 +37,7 @@ #include <asm/uaccess.h> #include <asm/system.h> #include <asm/sysinfo.h> +#include <asm/hwrpb.h> extern int do_mount(kdev_t, const char *, const char *, char *, int, void *); extern int do_pipe(int *); @@ -140,6 +141,7 @@ asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent *dirent, struct inode *inode; struct osf_dirent_callback buf; + lock_kernel(); error = -EBADF; file = fget(fd); if (!file) @@ -172,6 +174,7 @@ asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent *dirent, out_putf: fput(file); out: + unlock_kernel(); return error; } @@ -317,8 +320,8 @@ static int do_osf_statfs(struct dentry * dentry, struct osf_statfs *buffer, unsi struct super_block * sb = inode->i_sb; int error; - error = -ENOSYS; - if (sb->s_op->statfs) { + error = -ENODEV; + if (sb && sb->s_op && sb->s_op->statfs) { set_fs(KERNEL_DS); error = sb->s_op->statfs(sb, &linux_stat, sizeof(linux_stat)); set_fs(USER_DS); @@ -762,14 +765,10 @@ asmlinkage long osf_proplist_syscall(enum pl_code code, union pl_args *args) asmlinkage int osf_sigstack(struct sigstack *uss, struct sigstack *uoss) { unsigned long usp = rdusp(); - unsigned long oss_sp, oss_os; + unsigned long oss_sp = current->sas_ss_sp + current->sas_ss_size; + unsigned long oss_os = on_sig_stack(usp); int error; - if (uoss) { - oss_sp = current->sas_ss_sp + current->sas_ss_size; - oss_os = on_sig_stack(usp); - } - if (uss) { void *ss_sp; @@ -880,11 +879,27 @@ asmlinkage unsigned long osf_getsysinfo(unsigned long op, void *buffer, int *start, void *arg) { unsigned long w; + struct percpu_struct *cpu; switch (op) { case GSI_IEEE_FP_CONTROL: /* Return current software fp control & status bits. */ - w = current->tss.flags & IEEE_SW_MASK; + /* Note that DU doesn't verify available space here. */ + + /* EV6 implements most of the bits in hardware. If + UNDZ is not set, UNFD is maintained in software. */ + if (implver() == IMPLVER_EV6) { + unsigned long fpcr = rdfpcr(); + w = ieee_fpcr_to_swcr(fpcr); + if (!(fpcr & FPCR_UNDZ)) { + w &= ~IEEE_TRAP_ENABLE_UNF; + w |= current->tss.flags & IEEE_TRAP_ENABLE_UNF; + } + } else { + /* Otherwise we are forced to do everything in sw. */ + w = current->tss.flags & IEEE_SW_MASK; + } + if (put_user(w, (unsigned long *) buffer)) return -EFAULT; return 0; @@ -898,10 +913,28 @@ asmlinkage unsigned long osf_getsysinfo(unsigned long op, void *buffer, break; case GSI_UACPROC: + if (nbytes < sizeof(unsigned int)) + return -EINVAL; w = (current->tss.flags >> UAC_SHIFT) & UAC_BITMASK; if (put_user(w, (unsigned int *)buffer)) return -EFAULT; - return 0; + return 1; + + case GSI_PROC_TYPE: + if (nbytes < sizeof(unsigned long)) + return -EINVAL; + cpu = (struct percpu_struct*) + ((char*)hwrpb + hwrpb->processor_offset); + if (put_user(w, (unsigned long *)buffer)) + return -EFAULT; + return 1; + + case GSI_GET_HWRPB: + if (nbytes < sizeof(*hwrpb)) + return -EINVAL; + if (copy_to_user(buffer, hwrpb, nbytes) != 0) + return -EFAULT; + return 1; default: break; @@ -916,7 +949,7 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, { switch (op) { case SSI_IEEE_FP_CONTROL: { - unsigned long swcr, fpcr; + unsigned long swcr, fpcr, undz; /* * Alpha Architecture Handbook 4.7.7.3: @@ -931,11 +964,12 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, current->tss.flags &= ~IEEE_SW_MASK; current->tss.flags |= swcr & IEEE_SW_MASK; - /* Update the real fpcr. For exceptions that are disabled in - software but have not been seen, enable the exception in - hardware so that we can update our software status mask. */ - fpcr = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK); - fpcr |= ieee_swcr_to_fpcr(swcr | (~swcr & IEEE_STATUS_MASK)>>16); + /* Update the real fpcr. Keep UNFD off if not UNDZ. */ + fpcr = rdfpcr(); + undz = (fpcr & FPCR_UNDZ); + fpcr &= ~(FPCR_MASK | FPCR_DYN_MASK | FPCR_UNDZ); + fpcr |= ieee_swcr_to_fpcr(swcr); + fpcr &= ~(undz << 1); wrfpcr(fpcr); return 0; @@ -1390,8 +1424,9 @@ asmlinkage int sys_old_adjtimex(struct timex32 *txc_p) copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - offsetof(struct timex32, time))) return -EFAULT; - - if ((ret = do_adjtimex(&txc))) + + ret = do_adjtimex(&txc); + if (ret < 0) return ret; /* copy back to timex32 */ @@ -1401,5 +1436,5 @@ asmlinkage int sys_old_adjtimex(struct timex32 *txc_p) (put_tv32(&txc_p->time, &txc.time))) return -EFAULT; - return 0; + return ret; } diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 55e211a23..93d8db602 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -237,10 +237,13 @@ void exit_thread(void) void flush_thread(void) { - /* Arrange for each exec'ed process to start off with a - clean slate with respect to the FPU. */ + /* Arrange for each exec'ed process to start off with a clean slate + with respect to the FPU. This is all exceptions disabled. Note + that EV6 defines UNFD valid only with UNDZ, which we don't want + for IEEE conformance -- so that disabled bit remains in software. */ + current->tss.flags &= ~IEEE_SW_MASK; - wrfpcr(FPCR_DYN_NORMAL); + wrfpcr(FPCR_DYN_NORMAL | FPCR_INVD | FPCR_DZED | FPCR_OVFD | FPCR_INED); } void release_thread(struct task_struct *dead_task) @@ -270,8 +273,6 @@ int alpha_vfork(struct switch_stack * swstack) (struct pt_regs *) (swstack+1)); } -extern void ret_from_sys_call(void); -extern void ret_from_smpfork(void); /* * Copy an alpha thread.. * @@ -282,9 +283,13 @@ extern void ret_from_smpfork(void); * Use the passed "regs" pointer to determine how much space we need * for a kernel fork(). */ + int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { + extern void ret_from_sys_call(void); + extern void ret_from_smp_fork(void); + struct pt_regs * childregs; struct switch_stack * childstack, *stack; unsigned long stack_offset; @@ -292,18 +297,18 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, stack_offset = PAGE_SIZE - sizeof(struct pt_regs); if (!(regs->ps & 8)) stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; - childregs = (struct pt_regs *) (stack_offset + PAGE_SIZE + (unsigned long)p); + childregs = (struct pt_regs *) (stack_offset + PAGE_SIZE + (long)p); *childregs = *regs; childregs->r0 = 0; childregs->r19 = 0; - childregs->r20 = 1; /* OSF/1 has some strange fork() semantics.. */ + childregs->r20 = 1; /* OSF/1 has some strange fork() semantics. */ regs->r20 = 0; stack = ((struct switch_stack *) regs) - 1; childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; #ifdef __SMP__ - childstack->r26 = (unsigned long) ret_from_smpfork; + childstack->r26 = (unsigned long) ret_from_smp_fork; #else childstack->r26 = (unsigned long) ret_from_sys_call; #endif @@ -328,10 +333,12 @@ void dump_thread(struct pt_regs * pt, struct user * dump) dump->start_code = current->mm->start_code; dump->start_data = current->mm->start_data; dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); - dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT; - dump->u_dsize = (current->mm->brk + (PAGE_SIZE - 1) - dump->start_data) >> PAGE_SHIFT; - dump->u_ssize = - (current->mm->start_stack - dump->start_stack + PAGE_SIZE - 1) >> PAGE_SHIFT; + dump->u_tsize = ((current->mm->end_code - dump->start_code) + >> PAGE_SHIFT); + dump->u_dsize = ((current->mm->brk + PAGE_SIZE-1 - dump->start_data) + >> PAGE_SHIFT); + dump->u_ssize = (current->mm->start_stack - dump->start_stack + + PAGE_SIZE-1) >> PAGE_SHIFT; /* * We store the registers in an order/format that is diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index bee005933..8a0efe52d 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -165,7 +165,7 @@ extern unsigned long est_cycle_freq; extern void SMC93x_Init(void); /* smc37c669.c */ -extern void SMC669_Init(void); +extern void SMC669_Init(int); /* es1888.c */ extern void es1888_init(void); @@ -187,6 +187,7 @@ extern void entInt(void); extern void entMM(void); extern void entSys(void); extern void entUna(void); +extern void entDbg(void); /* process.c */ extern void generic_kill_arch (int mode, char *reboot_cmd); diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 42821d903..80b4454e1 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -183,6 +183,11 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, vec = get_sysvec_byname(p+9); continue; } + + if (strncmp(p, "cycle=", 6) == 0) { + est_cycle_freq = simple_strtol(p+6, NULL, 0); + continue; + } } /* Replace the command line, not that we've killed it with strtok. */ @@ -721,8 +726,8 @@ int get_cpuinfo(char *buffer) (char*)cpu->serial_no, systype_name, sysvariation_name, hwrpb->sys_revision, (char*)hwrpb->ssn, - hwrpb->cycle_freq ? : est_cycle_freq, - hwrpb->cycle_freq ? "" : "est.", + est_cycle_freq ? : hwrpb->cycle_freq, + est_cycle_freq ? "est." : "", hwrpb->intr_freq / 4096, (100 * hwrpb->intr_freq / 4096) % 100, hwrpb->pagesize, diff --git a/arch/alpha/kernel/smc37c669.c b/arch/alpha/kernel/smc37c669.c index 1202b7b90..caab7accc 100644 --- a/arch/alpha/kernel/smc37c669.c +++ b/arch/alpha/kernel/smc37c669.c @@ -862,7 +862,7 @@ typedef struct _SMC37c669_DRQ_TRANSLATION_ENTRY { */ SMC37c669_CONFIG_REGS *SMC37c669_detect( - void + int ); unsigned int SMC37c669_enable_device( @@ -1015,6 +1015,29 @@ __initdata = }; /* +** The following definition is for the MONET (XP1000) IRQ +** translation table. +*/ +static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_monet_irq_table[] +__initdata = + { + { SMC37c669_DEVICE_IRQ_A, -1 }, + { SMC37c669_DEVICE_IRQ_B, -1 }, + { SMC37c669_DEVICE_IRQ_C, 6 }, + { SMC37c669_DEVICE_IRQ_D, 7 }, + { SMC37c669_DEVICE_IRQ_E, 4 }, + { SMC37c669_DEVICE_IRQ_F, 3 }, + { SMC37c669_DEVICE_IRQ_H, -1 }, + { -1, -1 } /* End of table */ + }; + +static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_tables[] __initdata = + { + SMC37c669_default_irq_table, + SMC37c669_monet_irq_table + }; + +/* ** DRQ Translation Table ** ** The DRQ translation table is a list of SMC37c669 device and @@ -1163,7 +1186,7 @@ struct DDB smc_ddb = { ** **-- */ -SMC37c669_CONFIG_REGS * __init SMC37c669_detect( void ) +SMC37c669_CONFIG_REGS * __init SMC37c669_detect( int index ) { int i; SMC37c669_DEVICE_ID_REGISTER id; @@ -1196,7 +1219,7 @@ SMC37c669_CONFIG_REGS * __init SMC37c669_detect( void ) /* ** Initialize the IRQ and DRQ translation tables. */ - SMC37c669_irq_table = SMC37c669_default_irq_table; + SMC37c669_irq_table = SMC37c669_irq_tables[ index ]; SMC37c669_drq_table = SMC37c669_default_drq_table; /* ** erfix @@ -2516,13 +2539,13 @@ SMC37c669_dump_registers(void) * None * */ -void __init SMC669_Init ( void ) +void __init SMC669_Init ( int index ) { SMC37c669_CONFIG_REGS *SMC_base; unsigned long flags; __save_and_cli(flags); - if ( ( SMC_base = SMC37c669_detect( ) ) != NULL ) { + if ( ( SMC_base = SMC37c669_detect( index ) ) != NULL ) { #if SMC_DEBUG SMC37c669_config_mode( TRUE ); SMC37c669_dump_registers( ); diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 25615e43d..aa1eaf363 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -37,7 +37,18 @@ #define DBGS(args) #endif -struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; +struct ipi_msg_flush_tb_struct { + volatile unsigned int flush_tb_mask; + union { + struct mm_struct * flush_mm; + struct vm_area_struct * flush_vma; + } p; + unsigned long flush_addr; + unsigned long flush_end; +}; + +static struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; +static spinlock_t flush_tb_lock = SPIN_LOCK_UNLOCKED; struct cpuinfo_alpha cpu_data[NR_CPUS]; @@ -499,6 +510,7 @@ secondary_cpu_start(int cpuid, struct task_struct *idle) return; } mdelay(1); + barrier(); } DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); } @@ -541,6 +553,7 @@ delay1: if (!(hwrpb->txrdy & cpumask)) goto ready1; udelay(100); + barrier(); } goto timeout; @@ -549,6 +562,7 @@ delay2: if (!(hwrpb->txrdy & cpumask)) goto ready2; udelay(100); + barrier(); } goto timeout; @@ -783,7 +797,7 @@ flush_tlb_all(void) unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); long timeout = 1000000; - spin_lock_own(&kernel_flag, "flush_tlb_all"); + spin_lock(&flush_tb_lock); ipi_msg_flush_tb.flush_tb_mask = to_whom; send_ipi_message(to_whom, IPI_TLB_ALL); @@ -800,6 +814,8 @@ flush_tlb_all(void) ipi_msg_flush_tb.flush_tb_mask); ipi_msg_flush_tb.flush_tb_mask = 0; } + + spin_unlock(&flush_tb_lock); } void @@ -808,7 +824,7 @@ flush_tlb_mm(struct mm_struct *mm) unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); long timeout = 1000000; - spin_lock_own(&kernel_flag, "flush_tlb_mm"); + spin_lock(&flush_tb_lock); ipi_msg_flush_tb.flush_tb_mask = to_whom; ipi_msg_flush_tb.p.flush_mm = mm; @@ -830,6 +846,8 @@ flush_tlb_mm(struct mm_struct *mm) ipi_msg_flush_tb.flush_tb_mask); ipi_msg_flush_tb.flush_tb_mask = 0; } + + spin_unlock(&flush_tb_lock); } void @@ -840,7 +858,7 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) struct mm_struct * mm = vma->vm_mm; int timeout = 1000000; - spin_lock_own(&kernel_flag, "flush_tlb_page"); + spin_lock(&flush_tb_lock); ipi_msg_flush_tb.flush_tb_mask = to_whom; ipi_msg_flush_tb.p.flush_vma = vma; @@ -863,6 +881,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) ipi_msg_flush_tb.flush_tb_mask); ipi_msg_flush_tb.flush_tb_mask = 0; } + + spin_unlock(&flush_tb_lock); } void diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index 656e5d868..c464c37ec 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -316,7 +316,7 @@ dp264_pci_fixup(void) { layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); common_pci_fixup(dp264_map_irq, common_swizzle); - SMC669_Init(); + SMC669_Init(0); } static void __init @@ -325,7 +325,7 @@ monet_pci_fixup(void) layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); common_pci_fixup(monet_map_irq, monet_swizzle); /* es1888_init(); */ /* later? */ - SMC669_Init(); + SMC669_Init(1); } static void __init @@ -333,7 +333,7 @@ webbrick_pci_fixup(void) { layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); common_pci_fixup(webbrick_map_irq, common_swizzle); - SMC669_Init(); + SMC669_Init(0); } diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c index c6f908618..f9c4b64c5 100644 --- a/arch/alpha/kernel/sys_miata.c +++ b/arch/alpha/kernel/sys_miata.c @@ -267,7 +267,7 @@ miata_pci_fixup(void) { layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); common_pci_fixup(miata_map_irq, miata_swizzle); - SMC669_Init(); /* it might be a GL (fails harmlessly if not) */ + SMC669_Init(0); /* it might be a GL (fails harmlessly if not) */ es1888_init(); } diff --git a/arch/alpha/kernel/sys_sx164.c b/arch/alpha/kernel/sys_sx164.c index a03f451d0..a35fdd219 100644 --- a/arch/alpha/kernel/sys_sx164.c +++ b/arch/alpha/kernel/sys_sx164.c @@ -183,7 +183,7 @@ sx164_pci_fixup(void) { layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); common_pci_fixup(sx164_map_irq, common_swizzle); - SMC669_Init(); + SMC669_Init(0); } diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index 2f9363113..a84378926 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -18,6 +18,9 @@ * fixed tick loss calculation in timer_interrupt * (round system clock to nearest tick instead of truncating) * fixed algorithm in time_init for getting time from CMOS clock + * 1999-04-16 Thorsten Kranzkowski (dl8bcu@gmx.net) + * fixed algorithm in do_gettimeofday() for calculating the precise time + * from processor cycle counter (now taking lost_ticks into account) */ #include <linux/config.h> #include <linux/errno.h> @@ -223,7 +226,7 @@ time_init(void) { void (*irq_handler)(int, void *, struct pt_regs *); unsigned int year, mon, day, hour, min, sec, cc1, cc2; - unsigned long cycle_freq; + unsigned long cycle_freq, diff, one_percent; /* * The Linux interpretation of the CMOS clock register contents: @@ -237,19 +240,30 @@ time_init(void) /* Read cycle counter exactly on falling edge of update flag */ cc1 = rpcc(); - /* If our cycle frequency isn't valid, go another round and give - a guess at what it should be. */ - cycle_freq = hwrpb->cycle_freq; - if (cycle_freq == 0) { - printk("HWRPB cycle frequency bogus. Estimating... "); - + if (!est_cycle_freq) { + /* Sometimes the hwrpb->cycle_freq value is bogus. + Go another round to check up on it and see. */ do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); cc2 = rpcc(); - est_cycle_freq = cycle_freq = cc2 - cc1; + est_cycle_freq = cc2 - cc1; cc1 = cc2; + } - printk("%lu Hz\n", cycle_freq); + /* If the given value is within 1% of what we calculated, + accept it. Otherwise, use what we found. */ + cycle_freq = hwrpb->cycle_freq; + one_percent = cycle_freq / 100; + diff = cycle_freq - est_cycle_freq; + if (diff < 0) + diff = -diff; + if (diff > one_percent) { + cycle_freq = est_cycle_freq; + printk("HWRPB cycle frequency bogus. Estimated %lu Hz\n", + cycle_freq); + } + else { + est_cycle_freq = 0; } /* From John Bowman <bowman@math.ualberta.ca>: allow the values @@ -314,8 +328,10 @@ time_init(void) void do_gettimeofday(struct timeval *tv) { - unsigned long flags, now, delta_cycles, delta_usec; + unsigned long flags, delta_cycles, delta_usec; unsigned long sec, usec; + __u32 now; + extern volatile unsigned long lost_ticks; /*kernel/sched.c*/ now = rpcc(); save_and_cli(flags); @@ -337,9 +353,15 @@ do_gettimeofday(struct timeval *tv) * with no clear gain. */ - delta_usec = delta_cycles * state.scaled_ticks_per_cycle * 15625; + delta_usec = (delta_cycles * state.scaled_ticks_per_cycle + + state.partial_tick + + (lost_ticks << FIX_SHIFT) ) * 15625; delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; + /* the 'lost_tics' term above implements this: + * delta_usec += lost_ticks * (1000000 / HZ); + */ + usec += delta_usec; if (usec >= 1000000) { sec += 1; @@ -357,7 +379,6 @@ do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti(); diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index e925cf6ff..2548f0914 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -121,17 +121,20 @@ long alpha_fp_emul (unsigned long pc); #endif asmlinkage void -do_entArith(unsigned long summary, unsigned long write_mask, unsigned long a2, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) +do_entArith(unsigned long summary, unsigned long write_mask, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, struct pt_regs regs) { - if ((summary & 1)) { - /* - * Software-completion summary bit is set, so try to - * emulate the instruction. - */ - if (alpha_fp_emul_imprecise(®s, write_mask)) { - return; /* emulation was successful */ + if (summary & 1) { + /* Software-completion summary bit is set, so try to + emulate the instruction. */ + if (implver() == IMPLVER_EV6) { + /* Whee! EV6 has precice exceptions. */ + if (alpha_fp_emul(regs.pc - 4)) + return; + } else { + if (alpha_fp_emul_imprecise(®s, write_mask)) + return; } } @@ -141,22 +144,26 @@ do_entArith(unsigned long summary, unsigned long write_mask, unsigned long a2, current->comm, regs.pc, summary, write_mask); #endif die_if_kernel("Arithmetic fault", ®s, 0, 0); - force_sig(SIGFPE, current); + send_sig(SIGFPE, current, 1); unlock_kernel(); } -asmlinkage void do_entIF(unsigned long type, unsigned long a1, - unsigned long a2, unsigned long a3, unsigned long a4, - unsigned long a5, struct pt_regs regs) +asmlinkage void +do_entIF(unsigned long type, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, struct pt_regs regs) { - lock_kernel(); die_if_kernel("Instruction fault", ®s, type, 0); switch (type) { case 0: /* breakpoint */ if (ptrace_cancel_bpt(current)) { regs.pc -= 4; /* make pc point to former bpt */ } - force_sig(SIGTRAP, current); + send_sig(SIGTRAP, current, 1); + break; + + case 1: /* bugcheck */ + send_sig(SIGTRAP, current, 1); break; case 2: /* gentrap */ @@ -170,14 +177,13 @@ asmlinkage void do_entIF(unsigned long type, unsigned long a1, switch ((long) regs.r16) { case GEN_INTOVF: case GEN_INTDIV: case GEN_FLTOVF: case GEN_FLTDIV: case GEN_FLTUND: case GEN_FLTINV: - case GEN_FLTINE: - force_sig(SIGFPE, current); + case GEN_FLTINE: case GEN_ROPRAND: + send_sig(SIGFPE, current, 1); break; case GEN_DECOVF: case GEN_DECDIV: case GEN_DECINV: - case GEN_ROPRAND: case GEN_ASSERTERR: case GEN_NULPTRERR: case GEN_STKOVF: @@ -192,44 +198,50 @@ asmlinkage void do_entIF(unsigned long type, unsigned long a1, case GEN_SUBRNG5: case GEN_SUBRNG6: case GEN_SUBRNG7: - force_sig(SIGILL, current); + send_sig(SIGTRAP, current, 1); break; } break; - case 1: /* bugcheck */ case 3: /* FEN fault */ - force_sig(SIGILL, current); + send_sig(SIGILL, current, 1); break; case 4: /* opDEC */ -#ifdef CONFIG_ALPHA_NEED_ROUNDING_EMULATION - { - unsigned int opcode; - - /* get opcode of faulting instruction: */ - get_user(opcode, (__u32*)(regs.pc - 4)); - opcode >>= 26; - if (opcode == 0x16) { - /* - * It's a FLTI instruction, emulate it - * (we don't do no stinkin' VAX fp...) - */ - if (!alpha_fp_emul(regs.pc - 4)) - force_sig(SIGFPE, current); - break; - } + if (implver() == IMPLVER_EV4) { + /* EV4 does not implement anything except normal + rounding. Everything else will come here as + an illegal instruction. Emulate them. */ + if (alpha_fp_emul(regs.pc - 4)) + return; } -#endif - force_sig(SIGILL, current); + send_sig(SIGILL, current, 1); break; default: panic("do_entIF: unexpected instruction-fault type"); } +} + +/* There is an ifdef in the PALcode in MILO that enables a + "kernel debugging entry point" as an unprivilaged call_pal. + + We don't want to have anything to do with it, but unfortunately + several versions of MILO included in distributions have it enabled, + and if we don't put something on the entry point we'll oops. */ + +asmlinkage void +do_entDbg(unsigned long type, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, struct pt_regs regs) +{ + lock_kernel(); + die_if_kernel("Instruction fault", ®s, type, 0); + force_sig(SIGILL, current); unlock_kernel(); } + /* * entUna has a different register layout to be reasonably simple. It * needs access to all the integer registers (the kernel doesn't use @@ -857,14 +869,14 @@ do_entUnaUser(void * va, unsigned long opcode, give_sigsegv: regs->pc -= 4; /* make pc point to faulting insn */ lock_kernel(); - force_sig(SIGSEGV, current); + send_sig(SIGSEGV, current, 1); unlock_kernel(); return; give_sigbus: regs->pc -= 4; lock_kernel(); - force_sig(SIGBUS, current); + send_sig(SIGBUS, current, 1); unlock_kernel(); return; } @@ -895,4 +907,5 @@ trap_init(void) wrent(entIF, 3); wrent(entUna, 4); wrent(entSys, 5); + wrent(entDbg, 6); } diff --git a/arch/alpha/lib/clear_user.S b/arch/alpha/lib/clear_user.S index 65d7e2f28..21d86eb78 100644 --- a/arch/alpha/lib/clear_user.S +++ b/arch/alpha/lib/clear_user.S @@ -80,6 +80,7 @@ $tail: ret $31, ($28), 1 # .. e1 : __do_clear_user: + ldgp $29,0($27) # we do exceptions -- we need the gp. and $6, 7, $4 # e0 : find dest misalignment beq $0, $zerolength # .. e1 : addq $0, $4, $1 # e0 : bias counter diff --git a/arch/alpha/lib/copy_user.S b/arch/alpha/lib/copy_user.S index aa309b9f5..7cc7382ab 100644 --- a/arch/alpha/lib/copy_user.S +++ b/arch/alpha/lib/copy_user.S @@ -46,6 +46,8 @@ .globl __copy_user .ent __copy_user __copy_user: + ldgp $29,0($27) # we do exceptions -- we need the gp. + .prologue 1 and $6,7,$3 beq $0,$35 beq $3,$36 @@ -107,7 +109,7 @@ $43: $66: EXI( ldq $1,0($7) ) subq $4,8,$4 - stq $1,0($6) + EXO( stq $1,0($6) ) addq $7,8,$7 subq $0,8,$0 addq $6,8,$6 diff --git a/arch/alpha/lib/io.c b/arch/alpha/lib/io.c index 4172216d8..71ba9d4bf 100644 --- a/arch/alpha/lib/io.c +++ b/arch/alpha/lib/io.c @@ -405,15 +405,16 @@ void _memcpy_fromio(void * to, unsigned long from, long count) * Copy data from "real" memory space to IO memory space. * This needs to be optimized. */ -void _memcpy_toio(unsigned long to, void * from, long count) +void _memcpy_toio(unsigned long to, const void * from, long count) { /* Optimize co-aligned transfers. Everything else gets handled a byte at a time. */ + /* FIXME -- align FROM. */ if (count >= 8 && (to & 7) == ((long)from & 7)) { count -= 8; do { - writeq(*(u64 *)from, to); + writeq(*(const u64 *)from, to); count -= 8; to += 8; from += 8; @@ -424,7 +425,7 @@ void _memcpy_toio(unsigned long to, void * from, long count) if (count >= 4 && (to & 3) == ((long)from & 3)) { count -= 4; do { - writel(*(u32 *)from, to); + writel(*(const u32 *)from, to); count -= 4; to += 4; from += 4; @@ -435,7 +436,7 @@ void _memcpy_toio(unsigned long to, void * from, long count) if (count >= 2 && (to & 1) == ((long)from & 1)) { count -= 2; do { - writew(*(u16 *)from, to); + writew(*(const u16 *)from, to); count -= 2; to += 2; from += 2; @@ -444,7 +445,7 @@ void _memcpy_toio(unsigned long to, void * from, long count) } while (count > 0) { - writeb(*(u8 *) from, to); + writeb(*(const u8 *) from, to); count--; to++; from++; diff --git a/arch/alpha/lib/strlen_user.S b/arch/alpha/lib/strlen_user.S index cdf71158f..1f4fb07f5 100644 --- a/arch/alpha/lib/strlen_user.S +++ b/arch/alpha/lib/strlen_user.S @@ -27,7 +27,8 @@ .align 3 __strlen_user: - .prologue 0 + ldgp $29,0($27) # we do exceptions -- we need the gp. + .prologue 1 EX( ldq_u t0, 0(a0) ) # load first quadword (a0 may be misaligned) lda t1, -1(zero) diff --git a/arch/alpha/lib/strncpy_from_user.S b/arch/alpha/lib/strncpy_from_user.S index aff7c1d2b..998450196 100644 --- a/arch/alpha/lib/strncpy_from_user.S +++ b/arch/alpha/lib/strncpy_from_user.S @@ -31,6 +31,7 @@ .globl __strncpy_from_user .ent __strncpy_from_user .frame $30, 0, $26 + .prologue 1 .align 3 $aligned: @@ -99,6 +100,7 @@ $a_eoc: /*** The Function Entry Point ***/ .align 3 __strncpy_from_user: + ldgp $29, 0($27) # we do exceptions -- we need the gp. mov a0, v0 # save the string start beq a2, $zerolength diff --git a/arch/alpha/math-emu/fp-emul.c b/arch/alpha/math-emu/fp-emul.c index 97f4bc326..122ec85ac 100644 --- a/arch/alpha/math-emu/fp-emul.c +++ b/arch/alpha/math-emu/fp-emul.c @@ -13,6 +13,7 @@ #define OPC_INTL 0x11 #define OPC_INTS 0x12 #define OPC_INTM 0x13 +#define OPC_FLTC 0x14 #define OPC_FLTV 0x15 #define OPC_FLTI 0x16 #define OPC_FLTL 0x17 @@ -21,33 +22,37 @@ #define OPC_JSR 0x1a +#define OP_FUN(OP,FUN) ((OP << 26) | (FUN << 5)) + /* - * "Base" function codes for the FLTI-class instructions. These - * instructions all have opcode 0x16. Note that in most cases these - * actually correspond to the "chopped" form of the instruction. Not - * to worry---we extract the qualifier bits separately and deal with - * them separately. Notice that base function code 0x2c is used for - * both CVTTS and CVTST. The other bits in the function code are used - * to distinguish the two. + * "Base" function codes for the FLTI-class instructions. + * Note that in most cases these actually correspond to the "chopped" + * form of the instruction. Not to worry---we extract the qualifier + * bits separately and deal with them separately. Notice that base + * function code 0x2c is used for both CVTTS and CVTST. The other bits + * in the function code are used to distinguish the two. */ -#define FLTI_FUNC_ADDS 0x000 -#define FLTI_FUNC_ADDT 0x020 -#define FLTI_FUNC_CMPTEQ 0x025 -#define FLTI_FUNC_CMPTLT 0x026 -#define FLTI_FUNC_CMPTLE 0x027 -#define FLTI_FUNC_CMPTUN 0x024 -#define FLTI_FUNC_CVTTS_or_CVTST 0x02c -#define FLTI_FUNC_CVTTQ 0x02f -#define FLTI_FUNC_CVTQS 0x03c -#define FLTI_FUNC_CVTQT 0x03e -#define FLTI_FUNC_DIVS 0x003 -#define FLTI_FUNC_DIVT 0x023 -#define FLTI_FUNC_MULS 0x002 -#define FLTI_FUNC_MULT 0x022 -#define FLTI_FUNC_SUBS 0x001 -#define FLTI_FUNC_SUBT 0x021 - -#define FLTI_FUNC_CVTQL 0x030 /* opcode 0x17 */ +#define FLTI_FUNC_ADDS OP_FUN(OPC_FLTI, 0x000) +#define FLTI_FUNC_ADDT OP_FUN(OPC_FLTI, 0x020) +#define FLTI_FUNC_CMPTEQ OP_FUN(OPC_FLTI, 0x025) +#define FLTI_FUNC_CMPTLT OP_FUN(OPC_FLTI, 0x026) +#define FLTI_FUNC_CMPTLE OP_FUN(OPC_FLTI, 0x027) +#define FLTI_FUNC_CMPTUN OP_FUN(OPC_FLTI, 0x024) +#define FLTI_FUNC_CVTTS_or_CVTST OP_FUN(OPC_FLTI, 0x02c) +#define FLTI_FUNC_CVTTQ OP_FUN(OPC_FLTI, 0x02f) +#define FLTI_FUNC_CVTQS OP_FUN(OPC_FLTI, 0x03c) +#define FLTI_FUNC_CVTQT OP_FUN(OPC_FLTI, 0x03e) +#define FLTI_FUNC_DIVS OP_FUN(OPC_FLTI, 0x003) +#define FLTI_FUNC_DIVT OP_FUN(OPC_FLTI, 0x023) +#define FLTI_FUNC_MULS OP_FUN(OPC_FLTI, 0x002) +#define FLTI_FUNC_MULT OP_FUN(OPC_FLTI, 0x022) +#define FLTI_FUNC_SUBS OP_FUN(OPC_FLTI, 0x001) +#define FLTI_FUNC_SUBT OP_FUN(OPC_FLTI, 0x021) + +#define FLTC_FUNC_SQRTS OP_FUN(OPC_FLTC, 0x00B) +#define FLTC_FUNC_SQRTT OP_FUN(OPC_FLTC, 0x02B) + +#define FLTL_FUNC_CVTQL OP_FUN(OPC_FLTL, 0x030) #define MISC_TRAPB 0x0000 #define MISC_EXCB 0x0400 @@ -101,7 +106,7 @@ void cleanup_module(void) long alpha_fp_emul (unsigned long pc) { - unsigned long opcode, fa, fb, fc, func, mode; + unsigned long op_fun, fa, fb, fc, func, mode; unsigned long fpcw = current->tss.flags; unsigned long va, vb, vc, res, fpcr; __u32 insn; @@ -110,10 +115,11 @@ alpha_fp_emul (unsigned long pc) get_user(insn, (__u32*)pc); fc = (insn >> 0) & 0x1f; /* destination register */ - func = (insn >> 5) & 0x7ff; fb = (insn >> 16) & 0x1f; fa = (insn >> 21) & 0x1f; - opcode = insn >> 26; + func = (insn >> 5) & 0x7ff; + mode = (insn >> 5) & 0xc0; + op_fun = insn & OP_FUN(0x3f, 0x3f); va = alpha_read_fp_reg(fa); vb = alpha_read_fp_reg(fb); @@ -123,7 +129,6 @@ alpha_fp_emul (unsigned long pc) * Try the operation in software. First, obtain the rounding * mode... */ - mode = func & 0xc0; if (mode == 0xc0) { /* dynamic---get rounding mode from fpcr: */ mode = ((fpcr & FPCR_DYN_MASK) >> FPCR_DYN_SHIFT) << ROUND_SHIFT; @@ -135,8 +140,7 @@ alpha_fp_emul (unsigned long pc) something_is_wrong(); } - /* least 6 bits contain operation code: */ - switch (func & 0x3f) { + switch (op_fun) { case FLTI_FUNC_CMPTEQ: res = ieee_CMPTEQ(va, vb, &vc); break; @@ -153,7 +157,7 @@ alpha_fp_emul (unsigned long pc) res = ieee_CMPTUN(va, vb, &vc); break; - case FLTI_FUNC_CVTQL: + case FLTL_FUNC_CVTQL: /* * Notice: We can get here only due to an integer * overflow. Such overflows are reported as invalid @@ -222,6 +226,14 @@ alpha_fp_emul (unsigned long pc) res = ieee_CVTTQ(mode, vb, &vc); break; + case FLTC_FUNC_SQRTS: + res = ieee_SQRTS(mode, vb, &vc); + break; + + case FLTC_FUNC_SQRTT: + res = ieee_SQRTT(mode, vb, &vc); + break; + default: printk("alpha_fp_emul: unexpected function code %#lx at %#lx\n", func & 0x3f, pc); @@ -247,7 +259,7 @@ alpha_fp_emul (unsigned long pc) /* Update hardware control register */ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); - fpcr |= ieee_swcr_to_fpcr(fpcw | (~fpcw&IEEE_STATUS_MASK)>>16); + fpcr |= ieee_swcr_to_fpcr(fpcw); wrfpcr(fpcr); /* Do we generate a signal? */ @@ -319,6 +331,7 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) write_mask &= ~(1UL << rc); break; + case OPC_FLTC: case OPC_FLTV: case OPC_FLTI: case OPC_FLTL: @@ -326,13 +339,11 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) break; } if (!write_mask) { - if ((opcode == OPC_FLTI || opcode == OPC_FLTL) - && alpha_fp_emul(trigger_pc)) - { - /* re-execute insns in trap-shadow: */ - regs->pc = trigger_pc + 4; - MOD_DEC_USE_COUNT; - return 1; + if (alpha_fp_emul(trigger_pc)) { + /* re-execute insns in trap-shadow: */ + regs->pc = trigger_pc + 4; + MOD_DEC_USE_COUNT; + return 1; } break; } diff --git a/arch/alpha/math-emu/ieee-math.c b/arch/alpha/math-emu/ieee-math.c index 4208b742c..224fdd2b7 100644 --- a/arch/alpha/math-emu/ieee-math.c +++ b/arch/alpha/math-emu/ieee-math.c @@ -22,6 +22,7 @@ * functions are used on exceptional numbers only (well, assuming you * don't turn on the "trap on inexact"...). */ +#include <linux/sched.h> #include "ieee-math.h" #define STICKY_S 0x20000000 /* both in longword 0 of fraction */ @@ -1340,3 +1341,41 @@ ieee_DIVT (int f, unsigned long a, unsigned long b, unsigned long *c) op_c.e -= 9; /* remove excess exp from original shift */ return round_t_ieee(f, &op_c, c); } + +/* + * Sqrt a = b, where a and b are ieee s-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_SQRTS (int f, unsigned long a, unsigned long *b) +{ + fpclass_t a_type; + EXTENDED op_a, op_b; + + *b = IEEE_QNaN; + a_type = extend_ieee(a, &op_a, SINGLE); + if (op_a.s == 0) { + /* FIXME -- handle positive denormals. */ + send_sig(SIGFPE, current, 1); + } + return FPCR_INV; +} + +/* + * Sqrt a = b, where a and b are ieee t-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_SQRTT (int f, unsigned long a, unsigned long *b) +{ + fpclass_t a_type; + EXTENDED op_a, op_b; + + *b = IEEE_QNaN; + a_type = extend_ieee(a, &op_a, DOUBLE); + if (op_a.s == 0) { + /* FIXME -- handle positive denormals. */ + send_sig(SIGFPE, current, 1); + } + return FPCR_INV; +} diff --git a/arch/alpha/math-emu/ieee-math.h b/arch/alpha/math-emu/ieee-math.h index 35ce1afb7..1b3cf2694 100644 --- a/arch/alpha/math-emu/ieee-math.h +++ b/arch/alpha/math-emu/ieee-math.h @@ -48,5 +48,7 @@ extern unsigned long ieee_DIVS (int rm, unsigned long a, unsigned long b, unsigned long *c); extern unsigned long ieee_DIVT (int rm, unsigned long a, unsigned long b, unsigned long *c); +extern unsigned long ieee_SQRTS (int rm, unsigned long a, unsigned long *b); +extern unsigned long ieee_SQRTT (int rm, unsigned long a, unsigned long *b); #endif /* __ieee_math_h__ */ diff --git a/arch/arm/defconfig b/arch/arm/defconfig index 64fb0d7ab..db89599be 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -80,7 +80,6 @@ CONFIG_BLK_DEV_PART=y # CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index 5996398f8..d50b90f8d 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -77,12 +77,14 @@ asmlinkage int old_mmap(struct mmap_arg_struct *arg) goto out; if (!(a.flags & MAP_ANONYMOUS)) { error = -EBADF; - if (a.fd >= current->files->max_fds || - !(file = current->files->fd[a.fd])) + file = fget(a.fd); + if (!file) goto out; } a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset); + if (file) + fput(file); out: unlock_kernel(); return error; diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 22c3639da..b6448e942 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -127,7 +127,6 @@ void do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti (); diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index fc9809144..665c7c0b1 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -8,37 +8,30 @@ # Note 2! The CFLAGS definition is now in the main makefile... all: lib first_rule -ifeq ($(MACHINE),a5k) -MMARCH=arc -else -MMARCH=$(MACHINE) -endif O_TARGET := mm.o -O_OBJS := init.o extable.o fault-$(PROCESSOR).o mm-$(MMARCH).o +O_OBJS := init.o extable.o fault-$(PROCESSOR).o small_page.o ifeq ($(PROCESSOR),armo) O_OBJS += proc-arm2,3.o endif ifeq ($(PROCESSOR),armv) - O_OBJS += small_page.o proc-arm6,7.o proc-sa110.o + O_OBJS += mm-$(MACHINE).o proc-arm6,7.o proc-sa110.o ioremap.o endif include $(TOPDIR)/Rules.make -proc-arm2,3.o: ../lib/constants.h -proc-arm6,7.o: ../lib/constants.h -proc-sa110.o: ../lib/constants.h - %.o: %.S -ifneq ($(CONFIG_BINUTILS_NEW),y) - $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..$@.tmp.s - $(CC) $(CFLAGS:-pipe=) -c -o $@ ..$@.tmp.s - $(RM) ..$@.tmp.s -else $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< -endif .PHONY: lib lib:; @$(MAKE) -C ../lib constants.h + +# Special dependencies +fault-armv.o: fault-common.c +fault-armo.o: fault-common.c +proc-arm2,3.o: ../lib/constants.h +proc-arm6,7.o: ../lib/constants.h +proc-sa110.o: ../lib/constants.h + diff --git a/arch/arm/mm/fault-armo.c b/arch/arm/mm/fault-armo.c index 6fe1f30ff..c51980771 100644 --- a/arch/arm/mm/fault-armo.c +++ b/arch/arm/mm/fault-armo.c @@ -1,11 +1,10 @@ /* - * linux/arch/arm/mm/fault.c + * linux/arch/arm/mm/fault-armo.c * * Copyright (C) 1995 Linus Torvalds - * Modifications for ARM processor (c) 1995, 1996 Russell King + * Modifications for ARM processor (c) 1995-1999 Russell King */ -#include <linux/config.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -15,8 +14,7 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> +#include <linux/interrupt.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -27,35 +25,32 @@ #define FAULT_CODE_WRITE 0x02 #define FAULT_CODE_USER 0x01 -struct pgtable_cache_struct quicklists; +#define DO_COW(m) ((m) & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW)) +#define READ_FAULT(m) (!((m) & FAULT_CODE_WRITE)) -void __bad_pmd(pmd_t *pmd) +#include "fault-common.c" + +static void *alloc_table(int size, int prio) { - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); -#ifdef CONFIG_DEBUG_ERRORS - __backtrace(); -#endif - set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); + if (size != 128) + printk("invalid table size\n"); + return (void *)get_page_8k(prio); } -void __bad_pmd_kernel(pmd_t *pmd) +void free_table(void *table) { - printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd)); -#ifdef CONFIG_DEBUG_ERRORS - __backtrace(); -#endif - set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); + free_page_8k((unsigned long)table); } pgd_t *get_pgd_slow(void) { - pgd_t *pgd = (pgd_t *) kmalloc(PTRS_PER_PGD * BYTES_PER_PTR, GFP_KERNEL); + pgd_t *pgd = (pgd_t *)alloc_table(PTRS_PER_PGD * BYTES_PER_PTR, GFP_KERNEL); pgd_t *init; - + if (pgd) { init = pgd_offset(&init_mm, 0); - memzero (pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); - memcpy (pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + memzero(pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); + memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); } return pgd; @@ -65,17 +60,17 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; - pte = (pte_t *) kmalloc (PTRS_PER_PTE * BYTES_PER_PTR, GFP_KERNEL); + pte = (pte_t *)alloc_table(PTRS_PER_PTE * BYTES_PER_PTR, GFP_KERNEL); if (pmd_none(*pmd)) { if (pte) { - memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR); + memzero(pte, PTRS_PER_PTE * BYTES_PER_PTR); set_pmd(pmd, mk_pmd(pte)); return pte + offset; } set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); return NULL; } - kfree (pte); + free_table((void *)pte); if (pmd_bad(*pmd)) { __bad_pmd(pmd); return NULL; @@ -83,126 +78,22 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) return (pte_t *) pmd_page(*pmd) + offset; } -extern void die_if_kernel(char *msg, struct pt_regs *regs, unsigned int err, unsigned int ret); - -static void kernel_page_fault (unsigned long addr, int mode, struct pt_regs *regs, - struct task_struct *tsk, struct mm_struct *mm) -{ - /* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - pgd_t *pgd; - if (addr < PAGE_SIZE) - printk (KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - else - printk (KERN_ALERT "Unable to handle kernel paging request"); - printk (" at virtual address %08lx\n", addr); - printk (KERN_ALERT "current->tss.memmap = %08lX\n", tsk->tss.memmap); - pgd = pgd_offset (mm, addr); - printk (KERN_ALERT "*pgd = %08lx", pgd_val (*pgd)); - if (!pgd_none (*pgd)) { - pmd_t *pmd; - pmd = pmd_offset (pgd, addr); - printk (", *pmd = %08lx", pmd_val (*pmd)); - if (!pmd_none (*pmd)) - printk (", *pte = %08lx", pte_val (*pte_offset (pmd, addr))); - } - printk ("\n"); - die_if_kernel ("Oops", regs, mode, SIGKILL); - do_exit (SIGKILL); -} - -static void -handle_dataabort (unsigned long addr, int mode, struct pt_regs *regs) -{ - struct task_struct *tsk; - struct mm_struct *mm; - struct vm_area_struct *vma; - unsigned long fixup; - - lock_kernel(); - tsk = current; - mm = tsk->mm; - - down(&mm->mmap_sem); - vma = find_vma (mm, addr); - if (!vma) - goto bad_area; - if (addr >= vma->vm_start) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack (vma, addr)) - goto bad_area; - - /* - * Ok, we have a good vm_area for this memory access, so - * we can handle it.. - */ -good_area: - if (!(mode & FAULT_CODE_WRITE)) { /* write? */ - if (!(vma->vm_flags & (VM_READ|VM_EXEC))) - goto bad_area; - } else { - if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; - } - handle_mm_fault (tsk, vma, addr, mode & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW)); - up(&mm->mmap_sem); - goto out; - - /* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. - */ -bad_area: - up(&mm->mmap_sem); - if (mode & FAULT_CODE_USER) { -//extern int console_loglevel; -//cli(); - tsk->tss.error_code = mode; - tsk->tss.trap_no = 14; -//console_loglevel = 9; - printk ("%s: memory violation at pc=0x%08lx, lr=0x%08lx (bad address=0x%08lx, code %d)\n", - tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode); -//#ifdef DEBUG - show_regs (regs); - c_backtrace (regs->ARM_fp, 0); -//#endif - force_sig(SIGSEGV, tsk); -//while (1); - goto out; - } - - /* Are we prepared to handle this kernel fault? */ - if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) { - printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n", - tsk->comm, regs->ARM_pc, addr, fixup); - regs->ARM_pc = fixup; - goto out; - } - - - kernel_page_fault (addr, mode, regs, tsk, mm); -out: - unlock_kernel(); -} - /* * Handle a data abort. Note that we have to handle a range of addresses * on ARM2/3 for ldm. If both pages are zero-mapped, then we have to force - * a copy-on-write + * a copy-on-write. However, on the second page, we always force COW. */ asmlinkage void -do_DataAbort (unsigned long min_addr, unsigned long max_addr, int mode, struct pt_regs *regs) +do_DataAbort(unsigned long min_addr, unsigned long max_addr, int mode, struct pt_regs *regs) { - handle_dataabort (min_addr, mode, regs); + do_page_fault(min_addr, mode, regs); if ((min_addr ^ max_addr) >> PAGE_SHIFT) - handle_dataabort (max_addr, mode | FAULT_CODE_FORCECOW, regs); + do_page_fault(max_addr, mode | FAULT_CODE_FORCECOW, regs); } asmlinkage int -do_PrefetchAbort (unsigned long addr, int mode, struct pt_regs *regs) +do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { #if 0 if (the memc mapping for this page exists - can check now...) { @@ -210,6 +101,6 @@ do_PrefetchAbort (unsigned long addr, int mode, struct pt_regs *regs) return 0; } #endif - handle_dataabort (addr, mode, regs); + do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_PREFETCH, regs); return 1; } diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index f090c5f2c..d57d4fb20 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -1,10 +1,11 @@ /* - * linux/arch/arm/mm/fault.c + * linux/arch/arm/mm/fault-armv.c * * Copyright (C) 1995 Linus Torvalds - * Modifications for ARM processor (c) 1995, 1996 Russell King + * Modifications for ARM processor (c) 1995-1999 Russell King */ +#include <linux/config.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -14,43 +15,37 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/init.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/pgtable.h> +#include <asm/unaligned.h> #define FAULT_CODE_READ 0x02 #define FAULT_CODE_USER 0x01 -struct pgtable_cache_struct quicklists; +#define DO_COW(m) (!((m) & FAULT_CODE_READ)) +#define READ_FAULT(m) ((m) & FAULT_CODE_READ) -void __bad_pmd(pmd_t *pmd) -{ - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); -} - -void __bad_pmd_kernel(pmd_t *pmd) -{ - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); -} +#include "fault-common.c" pgd_t *get_pgd_slow(void) { /* * need to get a 16k page for level 1 */ - pgd_t *pgd = (pgd_t *) __get_free_pages(GFP_KERNEL,2); + pgd_t *pgd = (pgd_t *)__get_free_pages(GFP_KERNEL,2); pgd_t *init; - + if (pgd) { init = pgd_offset(&init_mm, 0); - memzero ((void *)pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); - memcpy (pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + memzero(pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); + memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); + clean_cache_area(pgd, PTRS_PER_PGD * BYTES_PER_PTR); } return pgd; } @@ -59,17 +54,19 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; - pte = (pte_t *) get_small_page(GFP_KERNEL); + pte = (pte_t *)get_page_2k(GFP_KERNEL); if (pmd_none(*pmd)) { if (pte) { - memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR); + memzero(pte, 2 * PTRS_PER_PTE * BYTES_PER_PTR); + clean_cache_area(pte, PTRS_PER_PTE * BYTES_PER_PTR); + pte += PTRS_PER_PTE; set_pmd(pmd, mk_user_pmd(pte)); return pte + offset; } set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); return NULL; } - free_small_page ((unsigned long) pte); + free_page_2k((unsigned long)pte); if (pmd_bad(*pmd)) { __bad_pmd(pmd); return NULL; @@ -81,17 +78,19 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; - pte = (pte_t *) get_small_page(GFP_KERNEL); + pte = (pte_t *)get_page_2k(GFP_KERNEL); if (pmd_none(*pmd)) { if (pte) { - memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR); + memzero(pte, 2 * PTRS_PER_PTE * BYTES_PER_PTR); + clean_cache_area(pte, PTRS_PER_PTE * BYTES_PER_PTR); + pte += PTRS_PER_PTE; set_pmd(pmd, mk_kernel_pmd(pte)); return pte + offset; } set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); return NULL; } - free_small_page ((unsigned long) pte); + free_page_2k((unsigned long)pte); if (pmd_bad(*pmd)) { __bad_pmd_kernel(pmd); return NULL; @@ -99,10 +98,8 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) return (pte_t *) pmd_page(*pmd) + offset; } -extern void die_if_kernel(char *msg, struct pt_regs *regs, unsigned int err, unsigned int ret); - #ifdef DEBUG -static int sp_valid (unsigned long *sp) +static int sp_valid(unsigned long *sp) { unsigned long addr = (unsigned long) sp; @@ -114,187 +111,371 @@ static int sp_valid (unsigned long *sp) } #endif -static void kernel_page_fault (unsigned long addr, int mode, struct pt_regs *regs, - struct task_struct *tsk, struct mm_struct *mm) +#ifdef CONFIG_ALIGNMENT_TRAP +/* + * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 + * /proc/sys/debug/alignment, modified and integrated into + * Linux 2.1 by Russell King + * + * NOTE!!! This is not portable onto the ARM6/ARM7 processors yet. Also, + * it seems to give a severe performance impact (1 abort/ms - NW runs at + * ARM6 speeds) with GCC 2.7.2.2 - needs checking with a later GCC/EGCS. + * + * IMHO, I don't think that the trap handler is advantageous on ARM6,7 + * processors (they'll run like an ARM3). We'll see. + */ +#define CODING_BITS(i) (i & 0x0e000000) + +#define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */ +#define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */ +#define LDST_U_BIT(i) (i & (1 << 23)) /* Add offset */ +#define LDST_W_BIT(i) (i & (1 << 21)) /* Writeback */ +#define LDST_L_BIT(i) (i & (1 << 20)) /* Load */ + +#define LDSTH_I_BIT(i) (i & (1 << 22)) /* half-word immed */ +#define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */ + +#define RN_BITS(i) ((i >> 16) & 15) /* Rn */ +#define RD_BITS(i) ((i >> 12) & 15) /* Rd */ +#define RM_BITS(i) (i & 15) /* Rm */ + +#define REGMASK_BITS(i) (i & 0xffff) +#define OFFSET_BITS(i) (i & 0x0fff) + +#define IS_SHIFT(i) (i & 0x0ff0) +#define SHIFT_BITS(i) ((i >> 7) & 0x1f) +#define SHIFT_TYPE(i) (i & 0x60) +#define SHIFT_LSL 0x00 +#define SHIFT_LSR 0x20 +#define SHIFT_ASR 0x40 +#define SHIFT_RORRRX 0x60 + +static unsigned long ai_user; +static unsigned long ai_sys; +static unsigned long ai_skipped; +static unsigned long ai_half; +static unsigned long ai_word; +static unsigned long ai_multi; + +static int proc_alignment_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { - /* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - pgd_t *pgd; - if (addr < PAGE_SIZE) - printk (KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - else - printk (KERN_ALERT "Unable to handle kernel paging request"); - printk (" at virtual address %08lx\n", addr); - printk (KERN_ALERT "current->tss.memmap = %08lX\n", tsk->tss.memmap); - pgd = pgd_offset (mm, addr); - printk (KERN_ALERT "*pgd = %08lx", pgd_val (*pgd)); - if (!pgd_none (*pgd)) { - pmd_t *pmd; - pmd = pmd_offset (pgd, addr); - printk (", *pmd = %08lx", pmd_val (*pmd)); - if (!pmd_none (*pmd)) - printk (", *pte = %08lx", pte_val (*pte_offset (pmd, addr))); - } - printk ("\n"); - die_if_kernel ("Oops", regs, mode, SIGKILL); - do_exit (SIGKILL); + char *p = page; + int len; + + p += sprintf(p, "User:\t\t%li\n", ai_user); + p += sprintf(p, "System:\t\t%li\n", ai_sys); + p += sprintf(p, "Skipped:\t%li\n", ai_skipped); + p += sprintf(p, "Half:\t\t%li\n", ai_half); + p += sprintf(p, "Word:\t\t%li\n", ai_word); + p += sprintf(p, "Multi:\t\t%li\n", ai_multi); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; } -static void page_fault (unsigned long addr, int mode, struct pt_regs *regs) +/* + * This needs to be done after sysctl_init, otherwise sys/ + * will be overwritten. + */ +void __init alignment_init(void) { - struct task_struct *tsk; - struct mm_struct *mm; - struct vm_area_struct *vma; - unsigned long fixup; - - lock_kernel(); - tsk = current; - mm = tsk->mm; - - down(&mm->mmap_sem); - vma = find_vma (mm, addr); - if (!vma) - goto bad_area; - if (vma->vm_start <= addr) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack (vma, addr)) - goto bad_area; + struct proc_dir_entry *e; - /* - * Ok, we have a good vm_area for this memory access, so - * we can handle it.. - */ -good_area: - if (mode & FAULT_CODE_READ) { /* read? */ - if (!(vma->vm_flags & (VM_READ|VM_EXEC))) - goto bad_area; - } else { - if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; + e = create_proc_entry("sys/debug/alignment", S_IFREG | S_IRUGO, NULL); + + if (e) + e->read_proc = proc_alignment_read; +} + +static int +do_alignment_exception(struct pt_regs *regs) +{ + unsigned int instr, rd, rn, correction, nr_regs, regbits; + unsigned long eaddr; + union { unsigned long un; signed long sn; } offset; + + if (user_mode(regs)) { + set_cr(cr_no_alignment); + ai_user += 1; + return 0; } - handle_mm_fault (tsk, vma, addr & PAGE_MASK, !(mode & FAULT_CODE_READ)); - up(&mm->mmap_sem); - goto out; - /* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. - */ -bad_area: - up(&mm->mmap_sem); - if (mode & FAULT_CODE_USER) { - tsk->tss.error_code = mode; - tsk->tss.trap_no = 14; - printk ("%s: memory violation at pc=0x%08lx, lr=0x%08lx (bad address=0x%08lx, code %d)\n", - tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode); -#ifdef DEBUG - { - unsigned int i, j; - unsigned long *sp = (unsigned long *) (regs->ARM_sp - 128); - for (j = 0; j < 20 && sp_valid (sp); j++) { - printk ("%p: ", sp); - for (i = 0; i < 8 && sp_valid (sp); i += 1, sp++) - printk ("%08lx ", *sp); - printk ("\n"); + ai_sys += 1; + + instr = *(unsigned long *)instruction_pointer(regs); + correction = 4; /* sometimes 8 on ARMv3 */ + regs->ARM_pc += correction + 4; + + rd = RD_BITS(instr); + rn = RN_BITS(instr); + eaddr = regs->uregs[rn]; + + switch(CODING_BITS(instr)) { + case 0x00000000: + if ((instr & 0x0ff00ff0) == 0x01000090) { + ai_skipped += 1; + printk(KERN_ERR "Unaligned trap: not handling swp instruction\n"); + return 1; + } + + if (((instr & 0x0e000090) == 0x00000090) && (instr & 0x60) != 0) { + ai_half += 1; + if (LDSTH_I_BIT(instr)) + offset.un = (instr & 0xf00) >> 4 | (instr & 15); + else + offset.un = regs->uregs[RM_BITS(instr)]; + + if (LDST_P_BIT(instr)) { + if (LDST_U_BIT(instr)) + eaddr += offset.un; + else + eaddr -= offset.un; } + + if (LDST_L_BIT(instr)) + regs->uregs[rd] = get_unaligned((unsigned short *)eaddr); + else + put_unaligned(regs->uregs[rd], (unsigned short *)eaddr); + + /* signed half-word? */ + if (instr & 0x40) + regs->uregs[rd] = (long)((short) regs->uregs[rd]); + + if (!LDST_P_BIT(instr)) { + if (LDST_U_BIT(instr)) + eaddr += offset.un; + else + eaddr -= offset.un; + regs->uregs[rn] = eaddr; + } else if (LDST_W_BIT(instr)) + regs->uregs[rn] = eaddr; + break; } - show_regs (regs); - c_backtrace (regs->ARM_fp, regs->ARM_cpsr); -#endif - force_sig(SIGSEGV, tsk); - goto out; - } - /* Are we prepared to handle this kernel fault? */ - if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) { - printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n", - tsk->comm, regs->ARM_pc, addr, fixup); - regs->ARM_pc = fixup; - goto out; + default: + ai_skipped += 1; + panic("Alignment trap: not handling instruction %08X at %08lX", + instr, regs->ARM_pc - correction - 4); + break; + + case 0x04000000: + offset.un = OFFSET_BITS(instr); + goto ldr_str; + + case 0x06000000: + offset.un = regs->uregs[RM_BITS(instr)]; + + if (IS_SHIFT(instr)) { + unsigned int shiftval = SHIFT_BITS(instr); + + switch(SHIFT_TYPE(instr)) { + case SHIFT_LSL: + offset.un <<= shiftval; + break; + + case SHIFT_LSR: + offset.un >>= shiftval; + break; + + case SHIFT_ASR: + offset.sn >>= shiftval; + break; + + case SHIFT_RORRRX: + if (shiftval == 0) { + offset.un >>= 1; + if (regs->ARM_cpsr & CC_C_BIT) + offset.un |= 1 << 31; + } else + offset.un = offset.un >> shiftval | + offset.un << (32 - shiftval); + break; + } + } + + ldr_str: + ai_word += 1; + if (LDST_P_BIT(instr)) { + if (LDST_U_BIT(instr)) + eaddr += offset.un; + else + eaddr -= offset.un; + } else { + if (LDST_W_BIT(instr)) { + printk(KERN_ERR "Not handling ldrt/strt correctly\n"); + return 1; + } + } + + if (LDST_L_BIT(instr)) { + regs->uregs[rd] = get_unaligned((unsigned long *)eaddr); + if (rd == 15) + correction = 0; + } else + put_unaligned(regs->uregs[rd], (unsigned long *)eaddr); + + if (!LDST_P_BIT(instr)) { + if (LDST_U_BIT(instr)) + eaddr += offset.un; + else + eaddr -= offset.un; + + regs->uregs[rn] = eaddr; + } else if (LDST_W_BIT(instr)) + regs->uregs[rn] = eaddr; + break; + + case 0x08000000: + if (LDM_S_BIT(instr)) + panic("Alignment trap: not handling LDM with s-bit\n"); + ai_multi += 1; + + for (regbits = REGMASK_BITS(instr), nr_regs = 0; regbits; regbits >>= 1) + nr_regs += 4; + + if (!LDST_U_BIT(instr)) + eaddr -= nr_regs; + + if ((LDST_U_BIT(instr) == 0 && LDST_P_BIT(instr) == 0) || + (LDST_U_BIT(instr) && LDST_P_BIT(instr))) + eaddr += 4; + + for (regbits = REGMASK_BITS(instr), rd = 0; regbits; regbits >>= 1, rd += 1) + if (regbits & 1) { + if (LDST_L_BIT(instr)) { + regs->uregs[rd] = get_unaligned((unsigned long *)eaddr); + if (rd == 15) + correction = 0; + } else + put_unaligned(regs->uregs[rd], (unsigned long *)eaddr); + eaddr += 4; + } + + if (LDST_W_BIT(instr)) { + if (LDST_P_BIT(instr) && !LDST_U_BIT(instr)) + eaddr -= nr_regs; + else if (LDST_P_BIT(instr)) + eaddr -= 4; + else if (!LDST_U_BIT(instr)) + eaddr -= 4 + nr_regs; + regs->uregs[rn] = eaddr; + } + break; } - kernel_page_fault (addr, mode, regs, tsk, mm); -out: - unlock_kernel(); + regs->ARM_pc -= correction; + + return 0; } -/* - * Handle a data abort. Note that we have to handle a range of addresses - * on ARM2/3 for ldm. If both pages are zero-mapped, then we have to force - * a copy-on-write - */ +#endif + asmlinkage void -do_DataAbort (unsigned long addr, int fsr, int error_code, struct pt_regs *regs) +do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs) { if (user_mode(regs)) error_code |= FAULT_CODE_USER; + #define DIE(signr,nam)\ force_sig(signr, current);\ - die_if_kernel(nam, regs, fsr, signr);\ - break; + die(nam, regs, fsr);\ + do_exit(signr);\ + break switch (fsr & 15) { - case 2: - DIE(SIGKILL, "Terminal exception") + /* + * 0 - vector exception + */ case 0: - DIE(SIGSEGV, "Vector exception") + force_sig(SIGSEGV, current); + if (!user_mode(regs)) { + die("vector exception", regs, fsr); + do_exit(SIGSEGV); + } + break; + + /* + * 15 - permission fault on page + * 5 - page-table entry descriptor fault + * 7 - first-level descriptor fault + */ + case 15: case 5: case 7: + do_page_fault(addr, error_code, regs); + break; + + /* + * 13 - permission fault on section + */ + case 13: + force_sig(SIGSEGV, current); + if (!user_mode(regs)) { + die("section permission fault", regs, fsr); + do_exit(SIGSEGV); + } else { +#ifdef CONFIG_DEBUG_USER + printk("%s: permission fault on section, " + "address=0x%08lx, code %d\n", + current->comm, addr, error_code); +#ifdef DEBUG + { + unsigned int i, j; + unsigned long *sp; + + sp = (unsigned long *) (regs->ARM_sp - 128); + for (j = 0; j < 20 && sp_valid(sp); j++) { + printk("%p: ", sp); + for (i = 0; i < 8 && sp_valid(sp); i += 1, sp++) + printk("%08lx ", *sp); + printk("\n"); + } + show_regs(regs); + c_backtrace(regs->ARM_fp, regs->ARM_cpsr); + } +#endif +#endif + } + break; + case 1: case 3: - DIE(SIGBUS, "Alignment exception") +#ifdef CONFIG_ALIGNMENT_TRAP + if (!do_alignment_exception(regs)) + break; +#endif + /* + * this should never happen + */ + DIE(SIGBUS, "Alignment exception"); + break; + + case 2: + DIE(SIGKILL, "Terminal exception"); case 12: case 14: - DIE(SIGBUS, "External abort on translation") + DIE(SIGBUS, "External abort on translation"); case 9: case 11: - DIE(SIGSEGV, "Domain fault") - case 13:/* permission fault on section */ -#ifdef DEBUG - { - unsigned int i, j; - unsigned long *sp; - - printk ("%s: section permission fault (bad address=0x%08lx, code %d)\n", - current->comm, addr, error_code); - sp = (unsigned long *) (regs->ARM_sp - 128); - for (j = 0; j < 20 && sp_valid (sp); j++) { - printk ("%p: ", sp); - for (i = 0; i < 8 && sp_valid (sp); i += 1, sp++) - printk ("%08lx ", *sp); - printk ("\n"); - } - show_regs (regs); - c_backtrace(regs->ARM_fp, regs->ARM_cpsr); - } -#endif - DIE(SIGSEGV, "Permission fault") + DIE(SIGSEGV, "Domain fault"); - case 15:/* permission fault on page */ - case 5: /* page-table entry descriptor fault */ - case 7: /* first-level descriptor fault */ - page_fault (addr, error_code, regs); - break; case 4: case 6: - DIE(SIGBUS, "External abort on linefetch") + DIE(SIGBUS, "External abort on linefetch"); case 8: case 10: - DIE(SIGBUS, "External abort on non-linefetch") + DIE(SIGBUS, "External abort on non-linefetch"); } } asmlinkage int -do_PrefetchAbort (unsigned long addr, struct pt_regs *regs) +do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { -#if 0 - /* does this still apply ? */ - if (the memc mapping for this page exists - can check now...) { - printk ("Page in, but got abort (undefined instruction?)\n"); - return 0; - } -#endif - page_fault (addr, FAULT_CODE_USER|FAULT_CODE_READ, regs); + do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_READ, regs); return 1; } - diff --git a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c new file mode 100644 index 000000000..810dea699 --- /dev/null +++ b/arch/arm/mm/fault-common.c @@ -0,0 +1,188 @@ +/* + * linux/arch/arm/mm/fault-common.c + * + * Copyright (C) 1995 Linus Torvalds + * Modifications for ARM processor (c) 1995-1999 Russell King + */ +#include <linux/config.h> + +extern void die(char *msg, struct pt_regs *regs, unsigned int err); + +void __bad_pmd(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); +#ifdef CONFIG_DEBUG_ERRORS + __backtrace(); +#endif + set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); +} + +void __bad_pmd_kernel(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd)); +#ifdef CONFIG_DEBUG_ERRORS + __backtrace(); +#endif + set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); +} + +static void +kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs, + struct task_struct *tsk, struct mm_struct *mm) +{ + char *reason; + /* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + pgd_t *pgd; + + if (addr < PAGE_SIZE) + reason = "NULL pointer dereference"; + else + reason = "paging request"; + + printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n", + reason, addr); + printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd); + pgd = pgd_offset(mm, addr); + printk(KERN_ALERT "*pgd = %08lx", pgd_val(*pgd)); + + do { + pmd_t *pmd; + pte_t *pte; + + if (pgd_none(*pgd)) + break; + + if (pgd_bad(*pgd)) { + printk("(bad)\n"); + break; + } + + pmd = pmd_offset(pgd, addr); + printk(", *pmd = %08lx", pmd_val(*pmd)); + + if (pmd_none(*pmd)) + break; + + if (pmd_bad(*pmd)) { + printk("(bad)\n"); + break; + } + + pte = pte_offset(pmd, addr); + printk(", *pte = %08lx", pte_val(*pte)); + printk(", *ppte = %08lx", pte_val(pte[-PTRS_PER_PTE])); + } while(0); + + printk("\n"); + die("Oops", regs, mode); + + do_exit(SIGKILL); +} + +static void do_page_fault(unsigned long addr, int mode, struct pt_regs *regs) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct *vma; + unsigned long fixup; + + tsk = current; + mm = tsk->mm; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto no_context; + + down(&mm->mmap_sem); + vma = find_vma(mm, addr); + if (!vma) + goto bad_area; + if (vma->vm_start <= addr) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack(vma, addr)) + goto bad_area; + + /* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + if (READ_FAULT(mode)) { /* read? */ + if (!(vma->vm_flags & (VM_READ|VM_EXEC))) + goto bad_area; + } else { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + if (!handle_mm_fault(tsk, vma, addr & PAGE_MASK, DO_COW(mode))) + goto do_sigbus; + + up(&mm->mmap_sem); + return; + + /* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up(&mm->mmap_sem); + + /* User mode accesses just cause a SIGSEGV */ + if (mode & FAULT_CODE_USER) { + tsk->tss.error_code = mode; + tsk->tss.trap_no = 14; +#ifdef CONFIG_DEBUG_USER + printk("%s: memory violation at pc=0x%08lx, lr=0x%08lx (bad address=0x%08lx, code %d)\n", + tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode); +#endif + force_sig(SIGSEGV, tsk); + return; + } + +no_context: + /* Are we prepared to handle this kernel fault? */ + if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) { +#ifdef DEBUG + printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n", + tsk->comm, regs->ARM_pc, addr, fixup); +#endif + regs->ARM_pc = fixup; + return; + } + + kernel_page_fault(addr, mode, regs, tsk, mm); + return; + +do_sigbus: + /* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ + up(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + tsk->tss.error_code = mode; + tsk->tss.trap_no = 14; + force_sig(SIGBUS, tsk); + + /* Kernel mode? Handle exceptions or die */ + if (!(mode & FAULT_CODE_USER)) + goto no_context; +} + + diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index b3b0ecf56..47a2cfde7 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -29,6 +29,9 @@ #include <asm/proc/mm-init.h> pgd_t swapper_pg_dir[PTRS_PER_PGD]; +#ifndef CONFIG_NO_PGT_CACHE +struct pgtable_cache_struct quicklists; +#endif extern char _etext, _stext, _edata, __bss_start, _end; extern char __init_begin, __init_end; @@ -36,6 +39,7 @@ extern char __init_begin, __init_end; int do_check_pgt_cache(int low, int high) { int freed = 0; +#ifndef CONFIG_NO_PGT_CACHE if(pgtable_cache_size > high) { do { if(pgd_quicklist) @@ -46,6 +50,7 @@ int do_check_pgt_cache(int low, int high) free_pte_slow(get_pte_fast()), freed++; } while(pgtable_cache_size > low); } +#endif return freed; } @@ -63,17 +68,18 @@ int do_check_pgt_cache(int low, int high) * data and COW. */ #if PTRS_PER_PTE != 1 -unsigned long *empty_bad_page_table; +pte_t *empty_bad_page_table; pte_t *__bad_pagetable(void) { - int i; pte_t bad_page; + int i; bad_page = BAD_PAGE; for (i = 0; i < PTRS_PER_PTE; i++) - empty_bad_page_table[i] = (unsigned long)pte_val(bad_page); - return (pte_t *) empty_bad_page_table; + set_pte(empty_bad_page_table + i, bad_page); + + return empty_bad_page_table; } #endif @@ -128,8 +134,11 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ empty_bad_page = (unsigned long *)start_mem; start_mem += PAGE_SIZE; #if PTRS_PER_PTE != 1 - empty_bad_page_table = (unsigned long *)start_mem; - start_mem += PTRS_PER_PTE * sizeof (void *); +#ifdef CONFIG_CPU_32 + start_mem += PTRS_PER_PTE * BYTES_PER_PTR; +#endif + empty_bad_page_table = (pte_t *)start_mem; + start_mem += PTRS_PER_PTE * BYTES_PER_PTR; #endif memzero (empty_zero_page, PAGE_SIZE); start_mem = setup_pagetables (start_mem, end_mem); @@ -137,6 +146,9 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ flush_tlb_all(); update_memc_all(); + end_mem &= PAGE_MASK; + high_memory = (void *)end_mem; + return free_area_init(start_mem, end_mem); } @@ -161,19 +173,18 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) /* mark usable pages in the mem_map[] */ mark_usable_memory_areas(&start_mem, end_mem); +#define BETWEEN(w,min,max) ((w) >= (unsigned long)(min) && \ + (w) < (unsigned long)(max)) + for (tmp = PAGE_OFFSET; tmp < end_mem ; tmp += PAGE_SIZE) { if (PageReserved(mem_map+MAP_NR(tmp))) { - if (tmp >= KERNTOPHYS(_stext) && - tmp < KERNTOPHYS(_edata)) { - if (tmp < KERNTOPHYS(_etext)) - codepages++; - else - datapages++; - } else if (tmp >= KERNTOPHYS(__init_begin) - && tmp < KERNTOPHYS(__init_end)) + if (BETWEEN(tmp, &__init_begin, &__init_end)) initpages++; - else if (tmp >= KERNTOPHYS(__bss_start) - && tmp < (unsigned long) start_mem) + else if (BETWEEN(tmp, &_stext, &_etext)) + codepages++; + else if (BETWEEN(tmp, &_etext, &_edata)) + datapages++; + else if (BETWEEN(tmp, &__bss_start, start_mem)) datapages++; else reservedpages++; @@ -181,13 +192,16 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) } atomic_set(&mem_map[MAP_NR(tmp)].count, 1); #ifdef CONFIG_BLK_DEV_INITRD - if (!initrd_start || (tmp < initrd_start || tmp >= initrd_end)) + if (!initrd_start || !BETWEEN(tmp, initrd_start, initrd_end)) #endif free_page(tmp); } - printk ("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", + +#undef BETWEEN + + printk ("Memory: %luk/%luM available (%dk code, %dk reserved, %dk data, %dk init)\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), - max_mapnr << (PAGE_SHIFT-10), + max_mapnr >> (20 - PAGE_SHIFT), codepages << (PAGE_SHIFT-10), reservedpages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), @@ -203,17 +217,45 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) #endif } -void free_initmem (void) +static void free_area(unsigned long addr, unsigned long end, char *s) { - unsigned long addr; + unsigned int size = (end - addr) >> 10; - addr = (unsigned long)(&__init_begin); - for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + for (; addr < end; addr += PAGE_SIZE) { mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); atomic_set(&mem_map[MAP_NR(addr)].count, 1); free_page(addr); } - printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); + + if (size) + printk(" %dk %s", size, s); +} + +void free_initmem (void) +{ + printk("Freeing unused kernel memory:"); + + free_area((unsigned long)(&__init_begin), + (unsigned long)(&__init_end), + "init"); + +#ifdef CONFIG_FOOTBRIDGE + { + extern int __netwinder_begin, __netwinder_end, __ebsa285_begin, __ebsa285_end; + + if (!machine_is_netwinder()) + free_area((unsigned long)(&__netwinder_begin), + (unsigned long)(&__netwinder_end), + "netwinder"); + + if (!machine_is_ebsa285() && !machine_is_cats()) + free_area((unsigned long)(&__ebsa285_begin), + (unsigned long)(&__ebsa285_end), + "ebsa285/cats"); + } +#endif + + printk("\n"); } void si_meminfo(struct sysinfo *val) diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c new file mode 100644 index 000000000..70d7c77b9 --- /dev/null +++ b/arch/arm/mm/ioremap.c @@ -0,0 +1,149 @@ +/* + * arch/arm/mm/ioremap.c + * + * Re-map IO memory to kernel address space so that we can access it. + * + * (C) Copyright 1995 1996 Linus Torvalds + * + * Hacked for ARM by Phil Blundell <philb@gnu.org> + * Hacked to allow all architectures to build, and various cleanups + * by Russell King + */ + +/* + * This allows a driver to remap an arbitrary region of bus memory into + * virtual space. One should *only* use readl, writel, memcpy_toio and + * so on with such remapped areas. + * + * Because the ARM only has a 32-bit address space we can't address the + * whole of the (physical) PCI space at once. PCI huge-mode addressing + * allows us to circumvent this restriction by splitting PCI space into + * two 2GB chunks and mapping only one at a time into processor memory. + * We use MMU protection domains to trap any attempt to access the bank + * that is not currently mapped. (This isn't fully implemented yet.) + * + * DC21285 currently has a bug in that the PCI address extension + * register affects the address of any writes waiting in the outbound + * FIFO. Unfortunately, it is not possible to tell the DC21285 to + * flush this - flushing the area causes the bus to lock. + */ + +#include <linux/vmalloc.h> +#include <asm/io.h> + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, pgprot_t pgprot) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + if (!pte_none(*pte)) + printk("remap_area_pte: page already exists\n"); + set_pte(pte, mk_pte_phys(phys_addr, pgprot)); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address < end); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + pgprot_t pgprot; + + address &= ~PGDIR_MASK; + end = address + size; + + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + + phys_addr -= address; + pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags); + do { + pte_t * pte = pte_alloc_kernel(pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, pgprot); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + while (address < end) { + pmd_t *pmd = pmd_alloc_kernel(dir, address); + if (!pmd) + return -ENOMEM; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + return -ENOMEM; + set_pgdir(address, *dir); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } + flush_tlb_all(); + return 0; +} + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + * + * 'flags' are the extra L_PTE_ flags that you want to specify for this + * mapping. See include/asm-arm/proc-armv/pgtable.h for more information. + */ +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + unsigned long offset; + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + size = PAGE_ALIGN(size + offset); + + /* + * Don't allow mappings that wrap.. + */ + if (!size || size > phys_addr + size) + return NULL; + + /* + * Ok, go for it.. + */ + area = get_vm_area(size); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + vfree(addr); + return NULL; + } + return (void *) (offset + (char *)addr); +} + +void iounmap(void *addr) +{ + return vfree((void *) (PAGE_MASK & (unsigned long) addr)); +} diff --git a/arch/arm/mm/mm-arc.c b/arch/arm/mm/mm-arc.c deleted file mode 100644 index 6bb92f037..000000000 --- a/arch/arm/mm/mm-arc.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * arch/arm/mm/mm-arc.c - * - * Extra MM routines for the Archimedes architecture - * - * Copyright (C) 1998 Russell King - */ -#include <linux/init.h> -#include <asm/hardware.h> -#include <asm/pgtable.h> - -unsigned long phys_screen_end; - -/* - * This routine needs more work to make it dynamically release/allocate mem! - */ -__initfunc(unsigned long map_screen_mem(unsigned long log_start, unsigned long kmem, int update)) -{ - static int updated = 0; - - if (updated) - return 0; - - updated = update; - - if (update) { - unsigned long address = log_start, offset; - pgd_t *pgdp; - - kmem = (kmem + 3) & ~3; - - pgdp = pgd_offset (&init_mm, address); /* +31 */ - offset = SCREEN_START; - while (address < SCREEN1_END) { - unsigned long addr_pmd, end_pmd; - pmd_t *pmdp; - - /* if (pgd_none (*pgdp)) alloc pmd */ - pmdp = pmd_offset (pgdp, address); /* +0 */ - addr_pmd = address & ~PGDIR_MASK; /* 088000 */ - end_pmd = addr_pmd + SCREEN1_END - address; /* 100000 */ - if (end_pmd > PGDIR_SIZE) - end_pmd = PGDIR_SIZE; - - do { - unsigned long addr_pte, end_pte; - pte_t *ptep; - - if (pmd_none (*pmdp)) { - pte_t *new_pte = (pte_t *)kmem; - kmem += PTRS_PER_PTE * BYTES_PER_PTR; - memzero (new_pte, PTRS_PER_PTE * BYTES_PER_PTR); - set_pmd (pmdp, mk_pmd(new_pte)); - } - - ptep = pte_offset (pmdp, addr_pmd); /* +11 */ - addr_pte = addr_pmd & ~PMD_MASK; /* 088000 */ - end_pte = addr_pte + end_pmd - addr_pmd; /* 100000 */ - if (end_pte > PMD_SIZE) - end_pte = PMD_SIZE; - - do { - set_pte (ptep, mk_pte(offset, PAGE_KERNEL)); - addr_pte += PAGE_SIZE; - offset += PAGE_SIZE; - ptep++; - } while (addr_pte < end_pte); - - pmdp++; - addr_pmd = (addr_pmd + PMD_SIZE) & PMD_MASK; - } while (addr_pmd < end_pmd); - - address = (address + PGDIR_SIZE) & PGDIR_MASK; - pgdp ++; - } - - phys_screen_end = offset; - flush_tlb_all (); - update_memc_all (); - } - return kmem; -} diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 8a226526b..4481fc32b 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -37,7 +37,8 @@ __initfunc(unsigned long setup_io_pagetables(unsigned long start_mem)) virtual = mp->virtual; physical = mp->physical; length = mp->length; - prot = (mp->prot_read ? PTE_AP_READ : 0) | (mp->prot_write ? PTE_AP_WRITE : 0); + prot = (mp->prot_read ? L_PTE_USER : 0) | (mp->prot_write ? L_PTE_WRITE : 0) + | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY; while ((virtual & 1048575 || physical & 1048575) && length >= PAGE_SIZE) { alloc_init_page(&start_mem, virtual, physical, mp->domain, prot); @@ -56,7 +57,8 @@ __initfunc(unsigned long setup_io_pagetables(unsigned long start_mem)) physical += 1048576; } - prot = (mp->prot_read ? PTE_AP_READ : 0) | (mp->prot_write ? PTE_AP_WRITE : 0); + prot = (mp->prot_read ? L_PTE_USER : 0) | (mp->prot_write ? L_PTE_WRITE : 0) + | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY; while (length >= PAGE_SIZE) { alloc_init_page(&start_mem, virtual, physical, mp->domain, prot); diff --git a/arch/arm/mm/mm-ebsa285.c b/arch/arm/mm/mm-ebsa285.c deleted file mode 100644 index a5b17c6b9..000000000 --- a/arch/arm/mm/mm-ebsa285.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * arch/arm/mm/mm-ebsa285.c - * - * Extra MM routines for the EBSA285 architecture - * - * Copyright (C) 1998 Russell King, Dave Gilbert. - */ -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/init.h> - -#include <asm/pgtable.h> -#include <asm/page.h> -#include <asm/io.h> -#include <asm/proc/mm-init.h> -#include <asm/dec21285.h> - -/* - * This is to allow us to fiddle with the EEPROM - * This entry will go away in time, once the fmu - * can mmap() the flash. - * - * These ones are so that we can fiddle - * with the various cards (eg VGA) - * until we're happy with them... - */ -#define MAPPING \ - { 0xd8000000, DC21285_FLASH, 0x00400000, DOMAIN_USER, 1, 1 }, /* EEPROM */ \ - { 0xdc000000, 0x7c000000, 0x00100000, DOMAIN_USER, 1, 1 }, /* VGA */ \ - { 0xe0000000, DC21285_PCI_MEM, 0x18000000, DOMAIN_USER, 1, 1 }, /* VGA */ \ - { 0xf8000000, DC21285_PCI_TYPE_0_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 0 Config */ \ - { 0xf9000000, DC21285_PCI_TYPE_1_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 1 Config */ \ - { PCI_IACK, DC21285_PCI_IACK, 0x01000000, DOMAIN_IO , 0, 1 }, /* PCI IACK */ \ - { 0xfd000000, DC21285_OUTBOUND_WRITE_FLUSH, 0x01000000, DOMAIN_IO , 0, 1 }, /* Out wrflsh */ \ - { 0xfe000000, DC21285_ARMCSR_BASE, 0x01000000, DOMAIN_IO , 0, 1 }, /* CSR */ \ - { 0xffe00000, DC21285_PCI_IO, 0x00100000, DOMAIN_IO , 0, 1 }, /* PCI I/O */ \ - { 0xfff00000, 0x40000000, 0x00100000, DOMAIN_IO , 0, 1 }, /* X-Bus */ - -#include "mm-armv.c" diff --git a/arch/arm/mm/mm-footbridge.c b/arch/arm/mm/mm-footbridge.c new file mode 100644 index 000000000..ec7e64c90 --- /dev/null +++ b/arch/arm/mm/mm-footbridge.c @@ -0,0 +1,91 @@ +/* + * arch/arm/mm/mm-ebsa285.c + * + * Extra MM routines for the EBSA285 architecture + * + * Copyright (C) 1998 Russell King, Dave Gilbert. + */ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/init.h> + +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/io.h> +#include <asm/proc/mm-init.h> +#include <asm/dec21285.h> + +/* + * The first entry allows us to fiddle with the EEPROM from user-space. + * This entry will go away in time, once the fmu32 can mmap() the + * flash. It can't at the moment. + * + * If you want to fiddle with PCI VGA cards from user space, then + * change the '0, 1 }' for the PCI MEM and PCI IO to '1, 1 }' + * You can then access the PCI bus at 0xe0000000 and 0xffe00000. + */ + +#ifdef CONFIG_HOST_FOOTBRIDGE + +/* + * The mapping when the footbridge is in host mode. + */ +#define MAPPING \ + { FLASH_BASE, DC21285_FLASH, FLASH_SIZE, DOMAIN_IO, 0, 1 }, \ + { PCIMEM_BASE, DC21285_PCI_MEM, PCIMEM_SIZE, DOMAIN_IO, 0, 1 }, \ + { PCICFG0_BASE, DC21285_PCI_TYPE_0_CONFIG, PCICFG0_SIZE, DOMAIN_IO, 0, 1 }, \ + { PCICFG1_BASE, DC21285_PCI_TYPE_1_CONFIG, PCICFG1_SIZE, DOMAIN_IO, 0, 1 }, \ + { PCIIACK_BASE, DC21285_PCI_IACK, PCIIACK_SIZE, DOMAIN_IO, 0, 1 }, \ + { WFLUSH_BASE, DC21285_OUTBOUND_WRITE_FLUSH, WFLUSH_SIZE, DOMAIN_IO, 0, 1 }, \ + { ARMCSR_BASE, DC21285_ARMCSR_BASE, ARMCSR_SIZE, DOMAIN_IO, 0, 1 }, \ + { PCIO_BASE, DC21285_PCI_IO, PCIO_SIZE, DOMAIN_IO, 0, 1 }, \ + { XBUS_BASE, 0x40000000, XBUS_SIZE, DOMAIN_IO, 0, 1 } + +#else + +/* + * These two functions convert virtual addresses to PCI addresses + * and PCI addresses to virtual addresses. Note that it is only + * legal to use these on memory obtained via get_free_page or + * kmalloc. + */ +unsigned long __virt_to_bus(unsigned long res) +{ +#ifdef CONFIG_DEBUG_ERRORS + if (res < PAGE_OFFSET || res >= (unsigned long)high_memory) { + printk("__virt_to_phys: invalid virtual address 0x%08lx\n", res); + __backtrace(); + } +#endif + return (res - PAGE_OFFSET) + (*CSR_PCISDRAMBASE & 0xfffffff0); +} + +unsigned long __bus_to_virt(unsigned long res) +{ + res -= (*CSR_PCISDRAMBASE & 0xfffffff0); + res += PAGE_OFFSET; + +#ifdef CONFIG_DEBUG_ERRORS + if (res < PAGE_OFFSET || res >= (unsigned long)high_memory) { + printk("__phys_to_virt: invalid virtual address 0x%08lx\n", res); + __backtrace(); + } +#endif + return res; +} + +/* + * The mapping when the footbridge is in add-in mode. + */ +#define MAPPING \ + { PCIO_BASE, DC21285_PCI_IO, PCIO_SIZE, DOMAIN_IO, 0, 1 }, \ + { XBUS_BASE, 0x40000000, XBUS_SIZE, DOMAIN_IO, 0, 1 }, \ + { ARMCSR_BASE, DC21285_ARMCSR_BASE, ARMCSR_SIZE, DOMAIN_IO, 0, 1 }, \ + { WFLUSH_BASE, DC21285_OUTBOUND_WRITE_FLUSH, WFLUSH_SIZE, DOMAIN_IO, 0, 1 }, \ + { FLASH_BASE, DC21285_FLASH, FLASH_SIZE, DOMAIN_IO, 0, 1 }, \ + { PCIMEM_BASE, DC21285_PCI_MEM, PCIMEM_SIZE, DOMAIN_IO, 0, 1 } + +#endif + +#include "mm-armv.c" diff --git a/arch/arm/mm/mm-vnc.c b/arch/arm/mm/mm-vnc.c deleted file mode 100644 index 94e037485..000000000 --- a/arch/arm/mm/mm-vnc.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * arch/arm/mm/mm-vnc.c - * - * Extra MM routines for the Corel VNC architecture - * - * Copyright (C) 1998 Russell King - */ -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/init.h> - -#include <asm/pgtable.h> -#include <asm/page.h> -#include <asm/io.h> -#include <asm/proc/mm-init.h> -#include <asm/dec21285.h> - -/* Table describing the MMU translation mapping - * mainly used to set up the I/O mappings. - */ -#define MAPPING \ - { 0xd0000000, DC21285_FLASH, 0x00800000, DOMAIN_IO , 0, 1 }, /* Flash */ \ - { 0xe0000000, DC21285_PCI_MEM, 0x18000000, DOMAIN_IO , 0, 1 }, /* PCI Mem */ \ - { 0xf8000000, DC21285_PCI_TYPE_0_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 0 Config */ \ - { 0xf9000000, DC21285_PCI_TYPE_1_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 1 Config */ \ - { PCI_IACK, DC21285_PCI_IACK, 0x01000000, DOMAIN_IO , 0, 1 }, /* PCI IACK */ \ - { 0xfd000000, DC21285_OUTBOUND_WRITE_FLUSH, 0x01000000, DOMAIN_IO , 0, 1 }, /* Out wrflsh */ \ - { 0xfe000000, DC21285_ARMCSR_BASE, 0x01000000, DOMAIN_IO , 0, 1 }, /* CSR */ \ - { 0xffe00000, DC21285_PCI_IO, 0x00100000, DOMAIN_IO , 0, 1 }, /* PCI I/O */ \ - -#include "mm-armv.c" diff --git a/arch/arm/mm/proc-arm2,3.S b/arch/arm/mm/proc-arm2,3.S index 86cab564a..263d79708 100644 --- a/arch/arm/mm/proc-arm2,3.S +++ b/arch/arm/mm/proc-arm2,3.S @@ -193,7 +193,7 @@ _arm2_3_data_abort: movs pc, lr _arm2_3_check_bugs: - movs pc, lr + bics pc, lr, #0x04000000 @ Clear FIQ disable bit /* * Processor specific - ARM2 @@ -206,6 +206,8 @@ LC0: .word SYMBOL_NAME(page_nr) * Params : prev Old task structure * : next New task structure for process to run * + * Returns : prev + * * Purpose : Perform a task switch, saving the old processes state, and restoring * the new. * @@ -218,15 +220,15 @@ _arm2_switch_to: str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC mov r4, r1 - add r0, r1, #TSS_MEMCMAP @ Remap MEMC + add r7, r1, #TSS_MEMCMAP @ Remap MEMC ldr r1, LC0 ldr r1, [r1] -1: ldmia r0!, {r2, r3, r5, r6} +1: ldmia r7!, {r2, r3, r5, r6} strb r2, [r2] strb r3, [r3] strb r5, [r5] strb r6, [r6] - ldmia r0!, {r2, r3, r5, r6} + ldmia r7!, {r2, r3, r5, r6} strb r2, [r2] strb r3, [r3] strb r5, [r5] @@ -318,6 +320,8 @@ _arm2_proc_fin: movs pc, lr * Params : prev Old task structure * : next New task structure for process to run * + * Returns : prev + * * Purpose : Perform a task switch, saving the old processes state, and restoring * the new. * @@ -330,22 +334,22 @@ _arm3_switch_to: str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC mov r4, r1 - add r0, r1, #TSS_MEMCMAP @ Remap MEMC + add r7, r1, #TSS_MEMCMAP @ Remap MEMC ldr r1, LC0 ldr r1, [r1] -1: ldmia r0!, {r2, r3, r5, r6} +1: ldmia r7!, {r2, r3, r5, r6} strb r2, [r2] strb r3, [r3] strb r5, [r5] strb r6, [r6] - ldmia r0!, {r2, r3, r5, r6} + ldmia r7!, {r2, r3, r5, r6} strb r2, [r2] strb r3, [r3] strb r5, [r5] strb r6, [r6] subs r1, r1, #8 bhi 1b - mcr p15, 0, r0, c1, c0, 0 @ flush cache + mcr p15, 0, r7, c1, c0, 0 @ flush cache ldmfd sp!, {r4 - r9, fp, pc}^ @ Load all regs saved previously /* * Function: arm3_remap_memc (struct task_struct *tsk) diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S index b7119a330..b817ae2b4 100644 --- a/arch/arm/mm/proc-arm6,7.S +++ b/arch/arm/mm/proc-arm6,7.S @@ -52,13 +52,14 @@ _arm6_7_flush_tlb_area: blt 1b mov pc, lr -@LC0: .word _current /* * Function: arm6_7_switch_to (struct task_struct *prev, struct task_struct *next) * * Params : prev Old task structure * : next New task structure for process to run * + * Returns : prev + * * Purpose : Perform a task switch, saving the old processes state, and restoring * the new. * @@ -72,15 +73,15 @@ _arm6_7_switch_to: stmfd sp!, {ip} @ Save cpsr_SVC str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - ldr r0, [r1, #TSK_ADDR_LIMIT] - teq r0, #0 - moveq r0, #DOM_KERNELDOMAIN - movne r0, #DOM_USERDOMAIN - mcr p15, 0, r0, c3, c0 @ Set domain reg - ldr r0, [r1, #TSS_MEMMAP] @ Page table pointer + ldr r2, [r1, #TSK_ADDR_LIMIT] + teq r2, #0 + moveq r2, #DOM_KERNELDOMAIN + movne r2, #DOM_USERDOMAIN + mcr p15, 0, r2, c3, c0 @ Set domain reg + ldr r2, [r1, #TSS_MEMMAP] @ Page table pointer mov r1, #0 mcr p15, 0, r1, c7, c0, 0 @ flush cache - mcr p15, 0, r0, c2, c0, 0 @ update page table ptr + mcr p15, 0, r2, c2, c0, 0 @ update page table ptr mcr p15, 0, r1, c5, c0, 0 @ flush TLBs ldmfd sp!, {ip} msr spsr, ip @ Save tasks CPSR into SPSR for this return @@ -369,6 +370,35 @@ _arm7_set_pmd: tst r1, #3 mov pc, lr /* + * Function: arm6_7_set_pte(pte_t *ptep, pte_t pte) + * Params : r0 = Address to set + * : r1 = value to set + * Purpose : Set a PTE and flush it out of any WB cache + */ + .align 5 +_arm6_7_set_pte: + str r1, [r0], #-1024 @ linux version + + bic r2, r1, #0xff0 + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + + tst r1, #LPTE_USER | LPTE_EXEC + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE + tstne r1, #LPTE_DIRTY + orrne r2, r2, #HPTE_AP_WRITE + + tst r1, #LPTE_PRESENT + tstne r1, #LPTE_YOUNG + moveq r2, #0 + + str r2, [r0] @ hardware version + mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) + mov pc, lr + +/* * Function: _arm6_7_reset * * Notes : This sets up everything for a reset @@ -405,8 +435,12 @@ ENTRY(arm6_processor_functions) .word _arm6_7_flush_tlb_all @ 44 .word _arm6_7_flush_tlb_area @ 48 .word _arm6_set_pmd @ 52 - .word _arm6_7_reset @ 54 - .word _arm6_7_flush_cache @ 58 + .word _arm6_7_set_pte @ 56 + .word _arm6_7_reset @ 60 + .word _arm6_7_flush_cache @ 64 + + .word _arm6_7_flush_cache @ 68 + .word _arm6_7_flush_cache @ 72 /* * Purpose : Function pointers used to access above functions - all calls @@ -431,8 +465,9 @@ ENTRY(arm7_processor_functions) .word _arm6_7_flush_tlb_all @ 44 .word _arm6_7_flush_tlb_area @ 48 .word _arm7_set_pmd @ 52 - .word _arm6_7_reset @ 56 - .word _arm6_7_flush_cache @ 60 - + .word _arm6_7_set_pte @ 56 + .word _arm6_7_reset @ 60 .word _arm6_7_flush_cache @ 64 + .word _arm6_7_flush_cache @ 68 + .word _arm6_7_flush_cache @ 72 diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index 221797862..ff55c8ffa 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -8,6 +8,7 @@ */ #include <linux/linkage.h> #include <asm/assembler.h> +#include <asm/hardware.h> #include "../lib/constants.h" /* This is the maximum size of an area which will be flushed. If the area @@ -21,7 +22,6 @@ Lclean_switch: .long 0 /* * Function: sa110_flush_cache_all (void) - * * Purpose : Flush all cache lines */ .align 5 @@ -33,7 +33,7 @@ _sa110_flush_cache_all_r2: ands r1, r1, #1 eor r1, r1, #1 str r1, [r3] - ldr ip, =0xdf000000 + ldr ip, =FLUSH_BASE addne ip, ip, #32768 add r1, ip, #16384 @ only necessary for 16k 1: ldr r3, [ip], #32 @@ -47,11 +47,9 @@ _sa110_flush_cache_all_r2: /* * Function: sa110_flush_cache_area (unsigned long address, int end, int flags) - * * Params : address Area start address * : end Area end address * : flags b0 = I cache as well - * * Purpose : clean & flush all cache lines associated with this area of memory */ .align 5 @@ -74,10 +72,8 @@ _sa110_flush_cache_area: /* * Function: sa110_cache_wback_area(unsigned long address, unsigned long end) - * * Params : address Area start address * : end Area end address - * * Purpose : ensure all dirty cachelines in the specified area have been * written out to memory (for DMA) */ @@ -99,13 +95,10 @@ _sa110_cache_wback_area: /* * Function: sa110_cache_purge_area(unsigned long address, unsigned long end) - * * Params : address Area start address * : end Area end address - * * Purpose : throw away all D-cached data in specified region without - * an obligation to write it ack. - * + * an obligation to write it back. * Note : Must clean the D-cached entries around the boundaries if the * start and/or end address are not cache aligned. */ @@ -124,9 +117,7 @@ _sa110_cache_purge_area: /* * Function: sa110_flush_cache_entry (unsigned long address) - * * Params : address Address of cache line to flush - * * Purpose : clean & flush an entry */ .align 5 @@ -138,24 +129,23 @@ _sa110_flush_cache_entry: mov pc, lr /* - * Function: sa110_flush_cache_pte (unsigned long address) - * + * Function: sa110_clean_cache_area(unsigned long start, unsigned long size) * Params : address Address of cache line to clean - * * Purpose : Ensure that physical memory reflects cache at this location * for page table purposes. */ -_sa110_flush_cache_pte: - mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) +_sa110_clean_cache_area: +1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) + add r0, r0, #32 + subs r1, r1, #32 + bhi 1b mov pc, lr /* * Function: sa110_flush_ram_page (unsigned long page) - * * Params : address Area start address * : size size of area * : flags b0 = I cache as well - * * Purpose : clean & flush all cache lines associated with this area of memory */ .align 5 @@ -176,7 +166,6 @@ _sa110_flush_ram_page: /* * Function: sa110_flush_tlb_all (void) - * * Purpose : flush all TLB entries in all caches */ .align 5 @@ -188,11 +177,9 @@ _sa110_flush_tlb_all: /* * Function: sa110_flush_tlb_area (unsigned long address, unsigned long end, int flags) - * * Params : address Area start address * : end Area end address * : flags b0 = I cache as well - * * Purpose : flush a TLB entry */ .align 5 @@ -212,22 +199,21 @@ _sa110_flush_tlb_area: .align 5 _sa110_flush_icache_area: - mov r3, #0 1: mcr p15, 0, r0, c7, c10, 1 @ Clean D entry add r0, r0, #32 - cmp r0, r1 - blt 1b + subs r1, r1, #32 + bhi 1b + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB mcr p15, 0, r0, c7, c5, 0 @ flush I cache mov pc, lr /* * Function: sa110_switch_to (struct task_struct *prev, struct task_struct *next) - * * Params : prev Old task structure * : next New task structure for process to run - * + * Returns : prev * Purpose : Perform a task switch, saving the old processes state, and restoring * the new. - * * Notes : We don't fiddle with the FP registers here - we postpone this until * the new task actually uses FP. This way, we don't swap FP for tasks * that do not require it. @@ -237,20 +223,30 @@ _sa110_switch_to: stmfd sp!, {r4 - r9, fp, lr} @ Store most regs on stack mrs ip, cpsr stmfd sp!, {ip} @ Save cpsr_SVC + ldr r2, [r0, #TSS_MEMMAP] @ Get old page tables str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - ldr r0, [r1, #TSK_ADDR_LIMIT] - teq r0, #0 - moveq r0, #DOM_KERNELDOMAIN - movne r0, #DOM_USERDOMAIN - mcr p15, 0, r0, c3, c0 @ Set segment - ldr r0, [r1, #TSS_MEMMAP] @ Page table pointer + ldr r4, [r1, #TSK_ADDR_LIMIT] + teq r4, #0 + moveq r4, #DOM_KERNELDOMAIN + movne r4, #DOM_USERDOMAIN + mcr p15, 0, r4, c3, c0 @ Set segment + ldr r4, [r1, #TSS_MEMMAP] @ Page table pointer +/* + * Flushing the cache is nightmarishly slow, so we take any excuse + * to get out of it. If the old page table is the same as the new, + * this is a CLONE_VM relative of the old task and there is no need + * to flush. The overhead of the tests isn't even on the radar + * compared to the cost of the flush itself. + */ + teq r4, r2 + beq 2f ldr r3, =Lclean_switch ldr r2, [r3] ands r2, r2, #1 eor r2, r2, #1 str r2, [r3] - ldr r2, =0xdf000000 + ldr r2, =FLUSH_BASE addne r2, r2, #32768 add r1, r2, #16384 @ only necessary for 16k 1: ldr r3, [r2], #32 @@ -259,19 +255,16 @@ _sa110_switch_to: mov r1, #0 mcr p15, 0, r1, c7, c5, 0 @ flush I cache mcr p15, 0, r1, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c2, c0, 0 @ load page table pointer + mcr p15, 0, r4, c2, c0, 0 @ load page table pointer mcr p15, 0, r1, c8, c7, 0 @ flush TLBs - ldmfd sp!, {ip} +2: ldmfd sp!, {ip} msr spsr, ip @ Save tasks CPSR into SPSR for this return ldmfd sp!, {r4 - r9, fp, pc}^ @ Load all regs saved previously /* * Function: sa110_data_abort () - * * Params : r0 = address of aborted instruction - * * Purpose : obtain information about current aborted instruction - * * Returns : r0 = address of abort * : r1 = FSR * : r2 != 0 if writing @@ -288,12 +281,10 @@ _sa110_data_abort: mov pc, lr /* - * Function: sa110_set_pmd () - * + * Function: sa110_set_pmd(pmd_t *pmdp, pmd_t pmd) * Params : r0 = Address to set * : r1 = value to set - * - * Purpose : Set a PMD and flush it out of any WB cache + * Purpose : Set a PMD and flush it out */ .align 5 _sa110_set_pmd: str r1, [r0] @@ -301,23 +292,51 @@ _sa110_set_pmd: str r1, [r0] mov pc, lr /* + * Function: sa110_set_pte(pte_t *ptep, pte_t pte) + * Params : r0 = Address to set + * : r1 = value to set + * Purpose : Set a PTE and flush it out + */ + .align 5 +_sa110_set_pte: str r1, [r0], #-1024 @ linux version + + eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY + + bic r2, r1, #0xff0 + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + + tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? + orreq r2, r2, #HPTE_AP_WRITE + + tst r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young? + movne r2, #0 + + str r2, [r0] @ hardware version + mov r0, r0 + mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) + mov pc, lr + +/* * Function: sa110_check_bugs (void) * : sa110_proc_init (void) * : sa110_proc_fin (void) - * * Notes : This processor does not require these */ _sa110_check_bugs: mrs ip, cpsr bic ip, ip, #F_BIT msr cpsr, ip + _sa110_proc_init: _sa110_proc_fin: mov pc, lr /* * Function: sa110_reset - * * Notes : This sets up everything for a reset */ _sa110_reset: mrs r1, cpsr @@ -350,14 +369,15 @@ ENTRY(sa110_processor_functions) .word _sa110_flush_cache_all @ 24 .word _sa110_flush_cache_area @ 28 .word _sa110_flush_cache_entry @ 32 - .word _sa110_flush_cache_pte @ 36 + .word _sa110_clean_cache_area @ 36 .word _sa110_flush_ram_page @ 40 .word _sa110_flush_tlb_all @ 44 .word _sa110_flush_tlb_area @ 48 .word _sa110_set_pmd @ 52 - .word _sa110_reset @ 56 - .word _sa110_flush_icache_area @ 60 + .word _sa110_set_pte @ 56 + .word _sa110_reset @ 60 + .word _sa110_flush_icache_area @ 64 - .word _sa110_cache_wback_area @ 64 - .word _sa110_cache_purge_area @ 68 + .word _sa110_cache_wback_area @ 68 + .word _sa110_cache_purge_area @ 72 diff --git a/arch/arm/mm/small_page.c b/arch/arm/mm/small_page.c index 2f8cad9a3..6bdc6cfc7 100644 --- a/arch/arm/mm/small_page.c +++ b/arch/arm/mm/small_page.c @@ -5,6 +5,8 @@ * * Changelog: * 26/01/1996 RMK Cleaned up various areas to make little more generic + * 07/02/1999 RMK Support added for 16K and 32K page sizes + * containing 8K blocks */ #include <linux/signal.h> @@ -19,21 +21,32 @@ #include <linux/swap.h> #include <linux/smp.h> -#define SMALL_ALLOC_SHIFT (10) +#if PAGE_SIZE == 4096 +/* 2K blocks */ +#define SMALL_ALLOC_SHIFT (11) +#define NAME(x) x##_2k +#elif PAGE_SIZE == 32768 || PAGE_SIZE == 16384 +/* 8K blocks */ +#define SMALL_ALLOC_SHIFT (13) +#define NAME(x) x##_8k +#endif + #define SMALL_ALLOC_SIZE (1 << SMALL_ALLOC_SHIFT) #define NR_BLOCKS (PAGE_SIZE / SMALL_ALLOC_SIZE) +#define BLOCK_MASK ((1 << NR_BLOCKS) - 1) -#if NR_BLOCKS != 4 -#error I only support 4 blocks per page! -#endif - -#define USED(pg) ((atomic_read(&(pg)->count) >> 8) & 15) +#define USED(pg) ((atomic_read(&(pg)->count) >> 8) & BLOCK_MASK) #define SET_USED(pg,off) (atomic_read(&(pg)->count) |= 256 << off) #define CLEAR_USED(pg,off) (atomic_read(&(pg)->count) &= ~(256 << off)) +#define ALL_USED BLOCK_MASK #define IS_FREE(pg,off) (!(atomic_read(&(pg)->count) & (256 << off))) -#define PAGE_PTR(page,block) ((struct free_small_page *)((page) + \ +#define SM_PAGE_PTR(page,block) ((struct free_small_page *)((page) + \ ((block) << SMALL_ALLOC_SHIFT))) +#if NR_BLOCKS != 2 && NR_BLOCKS != 4 +#error I only support 2 or 4 blocks per page +#endif + struct free_small_page { unsigned long next; unsigned long prev; @@ -52,6 +65,7 @@ static unsigned char offsets[1<<NR_BLOCKS] = { 1, /* 0001 */ 0, /* 0010 */ 2, /* 0011 */ +#if NR_BLOCKS == 4 0, /* 0100 */ 1, /* 0101 */ 0, /* 0110 */ @@ -64,6 +78,7 @@ static unsigned char offsets[1<<NR_BLOCKS] = { 1, /* 1101 */ 0, /* 1110 */ 4 /* 1111 */ +#endif }; static inline void clear_page_links(unsigned long page) @@ -72,7 +87,7 @@ static inline void clear_page_links(unsigned long page) int i; for (i = 0; i < NR_BLOCKS; i++) { - fsp = PAGE_PTR(page, i); + fsp = SM_PAGE_PTR(page, i); fsp->next = fsp->prev = 0; } } @@ -90,7 +105,7 @@ static inline void set_page_links_prev(unsigned long page, unsigned long prev) for (i = 0; i < NR_BLOCKS; i++) { if (mask & (1 << i)) continue; - fsp = PAGE_PTR(page, i); + fsp = SM_PAGE_PTR(page, i); fsp->prev = prev; } } @@ -108,12 +123,12 @@ static inline void set_page_links_next(unsigned long page, unsigned long next) for (i = 0; i < NR_BLOCKS; i++) { if (mask & (1 << i)) continue; - fsp = PAGE_PTR(page, i); + fsp = SM_PAGE_PTR(page, i); fsp->next = next; } } -unsigned long get_small_page(int priority) +unsigned long NAME(get_page)(int priority) { struct free_small_page *fsp; unsigned long new_page; @@ -129,8 +144,8 @@ again: page = mem_map + MAP_NR(small_page_ptr); offset = offsets[USED(page)]; SET_USED(page, offset); - new_page = (unsigned long)PAGE_PTR(small_page_ptr, offset); - if (USED(page) == 15) { + new_page = (unsigned long)SM_PAGE_PTR(small_page_ptr, offset); + if (USED(page) == ALL_USED) { fsp = (struct free_small_page *)new_page; set_page_links_prev (fsp->next, 0); small_page_ptr = fsp->next; @@ -156,30 +171,31 @@ need_new_page: goto again; } -void free_small_page(unsigned long spage) +void NAME(free_page)(unsigned long spage) { struct free_small_page *ofsp, *cfsp; unsigned long flags; struct page *page; int offset, oldoffset; + if (!spage) + goto none; + offset = (spage >> SMALL_ALLOC_SHIFT) & (NR_BLOCKS - 1); spage -= offset << SMALL_ALLOC_SHIFT; page = mem_map + MAP_NR(spage); - if (!PageReserved(page) || !USED(page)) { - printk ("Trying to free non-small page from %p\n", __builtin_return_address(0)); - return; - } - if (IS_FREE(page, offset)) { - printk ("Trying to free free small page from %p\n", __builtin_return_address(0)); - return; - } + if (!PageReserved(page) || !USED(page)) + goto non_small; + + if (IS_FREE(page, offset)) + goto free; + save_flags_cli (flags); oldoffset = offsets[USED(page)]; CLEAR_USED(page, offset); - ofsp = PAGE_PTR(spage, oldoffset); - cfsp = PAGE_PTR(spage, offset); + ofsp = SM_PAGE_PTR(spage, oldoffset); + cfsp = SM_PAGE_PTR(spage, offset); if (oldoffset == NR_BLOCKS) { /* going from totally used to mostly used */ cfsp->prev = 0; @@ -197,4 +213,13 @@ void free_small_page(unsigned long spage) } else *cfsp = *ofsp; restore_flags(flags); + return; + +non_small: + printk ("Trying to free non-small page from %p\n", __builtin_return_address(0)); + return; +free: + printk ("Trying to free free small page from %p\n", __builtin_return_address(0)); +none: + return; } diff --git a/arch/i386/config.in b/arch/i386/config.in index 3c42052e4..65a3aee0a 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -158,7 +158,7 @@ fi endmenu mainmenu_option next_comment -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' +comment 'Old CD-ROM drivers (not SCSI, not IDE)' bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then @@ -168,6 +168,8 @@ endmenu source drivers/char/Config.in +# source drivers/usb/Config.in + source fs/Config.in if [ "$CONFIG_VT" = "y" ]; then diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 200716f59..5bd7fe5bd 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -104,7 +104,6 @@ CONFIG_PARIDE_PARPORT=y CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -121,7 +120,6 @@ CONFIG_INET=y # (it is safe to leave these untouched) # # CONFIG_INET_RARP is not set -CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y # @@ -172,20 +170,25 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_SYM53C8XX=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=4 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -241,7 +244,7 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_ISDN is not set # -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# Old CD-ROM drivers (not SCSI, not IDE) # # CONFIG_CD_NO_IDESCSI is not set @@ -281,6 +284,7 @@ CONFIG_82C710_MOUSE=y # Joystick support # # CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set # # Ftape, the floppy tape device driver diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index e7383e55b..91d338b2c 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -14,7 +14,7 @@ * Hannover, Germany * hm@ix.de * - * Copyright 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * Copyright 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz> * * For more information, please consult the following manuals (look at * http://www.pcisig.com/ for how to get them): @@ -71,6 +71,10 @@ * a large gallery of common hardware bug workarounds (watch the comments) * -- the PCI specs themselves are sane, but most implementors should be * hit hard with \hammer scaled \magstep5. [mj] + * + * Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj] + * + * Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj] */ #include <linux/config.h> @@ -171,6 +175,7 @@ PCI_STUB(write, dword, u32) #define PCI_NO_SORT 0x100 #define PCI_BIOS_SORT 0x200 #define PCI_NO_CHECKS 0x400 +#define PCI_NO_PEER_FIXUP 0x800 static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; @@ -521,6 +526,8 @@ static struct { unsigned short segment; } pci_indirect = { 0, __KERNEL_CS }; +static int pci_bios_present; + __initfunc(static int check_pcibios(void)) { u32 signature, eax, ebx, ecx; @@ -803,7 +810,7 @@ __initfunc(static struct pci_access *pci_find_bios(void)) * which used BIOS ordering, we are bound to do this... */ -__initfunc(void pcibios_sort(void)) +static void __init pcibios_sort(void) { struct pci_dev *dev = pci_devices; struct pci_dev **last = &pci_devices; @@ -856,7 +863,7 @@ __initfunc(void pcibios_sort(void)) static int pci_last_io_addr __initdata = 0x5800; -__initfunc(void pcibios_fixup_io_addr(struct pci_dev *dev, int idx)) +static void __init pcibios_fixup_io_addr(struct pci_dev *dev, int idx) { unsigned short cmd; unsigned int reg = PCI_BASE_ADDRESS_0 + 4*idx; @@ -868,13 +875,16 @@ __initfunc(void pcibios_fixup_io_addr(struct pci_dev *dev, int idx)) printk("PCI: Unassigned I/O space for %02x:%02x\n", bus, devfn); return; } - if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && idx < 4) { + if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && idx < 4) || + (dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { /* * In case the BIOS didn't assign an address 0--3 to an IDE * controller, we don't try to fix it as it means "use default * addresses" at least with several broken chips and the IDE * driver needs the original settings to recognize which devices * correspond to the primary controller. + * + * We don't assign VGA I/O ranges as well. */ return; } @@ -914,7 +924,7 @@ __initfunc(void pcibios_fixup_io_addr(struct pci_dev *dev, int idx)) * expected to be unique) and remove the ghost devices. */ -__initfunc(void pcibios_fixup_ghosts(struct pci_bus *b)) +static void __init pcibios_fixup_ghosts(struct pci_bus *b) { struct pci_dev *d, *e, **z; int mirror = PCI_DEVFN(16,0); @@ -954,12 +964,17 @@ __initfunc(void pcibios_fixup_ghosts(struct pci_bus *b)) * the reality doesn't pass this test and the bus number is usually * set by BIOS to the first free value. */ -__initfunc(void pcibios_fixup_peer_bridges(void)) +static void __init pcibios_fixup_peer_bridges(void) { struct pci_bus *b = &pci_root; int i, n, cnt=-1; struct pci_dev *d; +#ifdef CONFIG_VISWS + pci_scan_peer_bridge(1); + return; +#endif + #ifdef CONFIG_PCI_DIRECT /* * Don't search for peer host bridges if we use config type 2 @@ -969,6 +984,7 @@ __initfunc(void pcibios_fixup_peer_bridges(void)) if (access_pci == &pci_direct_conf2) return; #endif + for(d=b->devices; d; d=d->sibling) if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) cnt++; @@ -979,6 +995,20 @@ __initfunc(void pcibios_fixup_peer_bridges(void)) for(i=0; i<256; i += 8) if (!pcibios_read_config_word(n, i, PCI_VENDOR_ID, &l) && l != 0x0000 && l != 0xffff) { +#ifdef CONFIG_PCI_BIOS + if (pci_bios_present) { + int err, idx = 0; + u8 bios_bus, bios_dfn; + u16 d; + pcibios_read_config_word(n, i, PCI_DEVICE_ID, &d); + DBG("BIOS test for %02x:%02x (%04x:%04x)\n", n, i, l, d); + while (!(err = pci_bios_find_device(l, d, idx, &bios_bus, &bios_dfn)) && + (bios_bus != n || bios_dfn != i)) + idx++; + if (err) + break; + } +#endif DBG("Found device at %02x:%02x\n", n, i); found++; if (!pcibios_read_config_word(n, i, PCI_CLASS_DEVICE, &l) && @@ -989,13 +1019,7 @@ __initfunc(void pcibios_fixup_peer_bridges(void)) break; if (found) { printk("PCI: Discovered primary peer bus %02x\n", n); - b = kmalloc(sizeof(*b), GFP_KERNEL); - memset(b, 0, sizeof(*b)); - b->next = pci_root.next; - pci_root.next = b; - b->number = b->secondary = n; - b->subordinate = 0xff; - b->subordinate = pci_scan_bus(b); + b = pci_scan_peer_bridge(n); n = b->subordinate; } n++; @@ -1003,11 +1027,77 @@ __initfunc(void pcibios_fixup_peer_bridges(void)) } /* + * Exceptions for specific devices. Usually work-arounds for fatal design flaws. + */ + +static void __init pci_fixup_i450nx(struct pci_dev *d) +{ + /* + * i450NX -- Find and scan all secondary buses on all PXB's. + */ + int pxb, reg; + u8 busno, suba, subb; + reg = 0xd0; + for(pxb=0; pxb<2; pxb++) { + pci_read_config_byte(d, reg++, &busno); + pci_read_config_byte(d, reg++, &suba); + pci_read_config_byte(d, reg++, &subb); + DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); + if (busno) + pci_scan_peer_bridge(busno); /* Bus A */ + if (suba < subb) + pci_scan_peer_bridge(suba+1); /* Bus B */ + } + pci_probe |= PCI_NO_PEER_FIXUP; +} + +static void __init pci_fixup_umc_ide(struct pci_dev *d) +{ + /* + * UM8886BF IDE controller sets region type bits incorrectly, + * therefore they look like memory despite of them being I/O. + */ + int i; + + for(i=0; i<4; i++) + d->base_address[i] |= PCI_BASE_ADDRESS_SPACE_IO; +} + +struct dev_ex { + u16 vendor, device; + void (*handler)(struct pci_dev *); + char *comment; +}; + +static struct dev_ex __initdata dev_ex_table[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx, "Scanning peer host bridges" }, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide, "Working around UM8886BF bugs" } +}; + +static void __init pcibios_scan_buglist(struct pci_bus *b) +{ + struct pci_dev *d; + int i; + + for(d=b->devices; d; d=d->sibling) + for(i=0; i<sizeof(dev_ex_table)/sizeof(dev_ex_table[0]); i++) { + struct dev_ex *e = &dev_ex_table[i]; + if (e->vendor == d->vendor && e->device == d->device) { + printk("PCI: %02x:%02x [%04x/%04x]: %s\n", + b->number, d->devfn, d->vendor, d->device, e->comment); + e->handler(d); + } + } +} + +/* * Fix base addresses, I/O and memory enables and IRQ's (mostly work-arounds * for buggy PCI BIOS'es :-[). */ -__initfunc(void pcibios_fixup_devices(void)) +extern int skip_ioapic_setup; + +static void __init pcibios_fixup_devices(void) { struct pci_dev *dev; int i, has_io, has_mem; @@ -1059,6 +1149,7 @@ __initfunc(void pcibios_fixup_devices(void)) /* * Recalculate IRQ numbers if we use the I/O APIC */ + if(!skip_ioapic_setup) { int irq; unsigned char pin; @@ -1099,7 +1190,8 @@ __initfunc(void pcibios_fixup_devices(void)) __initfunc(void pcibios_fixup(void)) { - pcibios_fixup_peer_bridges(); + if (!(pci_probe & PCI_NO_PEER_FIXUP)) + pcibios_fixup_peer_bridges(); pcibios_fixup_devices(); #ifdef CONFIG_PCI_BIOS @@ -1111,6 +1203,7 @@ __initfunc(void pcibios_fixup(void)) __initfunc(void pcibios_fixup_bus(struct pci_bus *b)) { pcibios_fixup_ghosts(b); + pcibios_scan_buglist(b); } /* @@ -1126,8 +1219,10 @@ __initfunc(void pcibios_init(void)) struct pci_access *dir = NULL; #ifdef CONFIG_PCI_BIOS - if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios()))) + if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios()))) { pci_probe |= PCI_BIOS_SORT; + pci_bios_present = 1; + } #endif #ifdef CONFIG_PCI_DIRECT if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) @@ -1139,10 +1234,6 @@ __initfunc(void pcibios_init(void)) access_pci = bios; } -#if !defined(CONFIG_PCI_BIOS) && !defined(CONFIG_PCI_DIRECT) -#error PCI configured with neither PCI BIOS or PCI direct access support. -#endif - __initfunc(char *pcibios_setup(char *str)) { if (!strcmp(str, "off")) { @@ -1178,5 +1269,9 @@ __initfunc(char *pcibios_setup(char *str)) return NULL; } #endif + else if (!strcmp(str, "nopeer")) { + pci_probe |= PCI_NO_PEER_FIXUP; + return NULL; + } return str; } diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 0153c4b40..3a5fc93a1 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -154,7 +154,9 @@ ENTRY(lcall7) .globl ret_from_fork ret_from_fork: #ifdef __SMP__ + pushl %ebx call SYMBOL_NAME(schedule_tail) + addl $4, %esp #endif /* __SMP__ */ GET_CURRENT(%ebx) jmp ret_from_sys_call diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index cd9074796..f0d5d3378 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -39,10 +39,12 @@ EXPORT_SYMBOL(local_bh_count); EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); +EXPORT_SYMBOL_NOVERS(__down_failed_trylock); EXPORT_SYMBOL_NOVERS(__up_wakeup); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); @@ -74,8 +76,11 @@ EXPORT_SYMBOL(strlen_user); EXPORT_SYMBOL(cpu_data); EXPORT_SYMBOL(kernel_flag); EXPORT_SYMBOL(smp_invalidate_needed); +EXPORT_SYMBOL(cpu_number_map); EXPORT_SYMBOL(__cpu_logical_map); EXPORT_SYMBOL(smp_num_cpus); +EXPORT_SYMBOL(cpu_present_map); +EXPORT_SYMBOL(cpu_online_map); /* Global SMP irq stuff */ EXPORT_SYMBOL(synchronize_irq); @@ -87,7 +92,7 @@ EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); -EXPORT_SYMBOL(mtrr_hook); +EXPORT_SYMBOL(smp_call_function); #endif #ifdef CONFIG_MCA diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 232abf78d..42ebd9643 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -202,7 +202,7 @@ DO_ACTION( enable, 1, |= 0xff000000, ) /* destination = 0xff */ DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync()) /* mask = 1 */ DO_ACTION( unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ -static void __init clear_IO_APIC_pin(unsigned int pin) +static void clear_IO_APIC_pin(unsigned int pin) { struct IO_APIC_route_entry entry; @@ -215,6 +215,13 @@ static void __init clear_IO_APIC_pin(unsigned int pin) io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1)); } +static void clear_IO_APIC (void) +{ + int pin; + + for (pin = 0; pin < nr_ioapic_registers; pin++) + clear_IO_APIC_pin(pin); +} /* * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to @@ -286,7 +293,8 @@ static int __init find_timer_pin(int type) for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; - if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA) && + if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || + mp_bus_id_to_type[lbus] == MP_BUS_EISA) && (mp_irqs[i].mpc_irqtype == type) && (mp_irqs[i].mpc_srcbusirq == 0x00)) @@ -319,20 +327,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) } /* - * Unclear documentation on what a "conforming ISA interrupt" means. - * - * Should we, or should we not, take the ELCR register into account? - * It's part of the EISA specification, but maybe it should only be - * used if the interrupt is actually marked as EISA? - * - * Oh, well. Don't do it until somebody tells us what the right thing - * to do is.. - */ -#undef USE_ELCR_TRIGGER_LEVEL -#ifdef USE_ELCR_TRIGGER_LEVEL - -/* - * ISA Edge/Level control register, ELCR + * EISA Edge/Level control register, ELCR */ static int __init EISA_ELCR(unsigned int irq) { @@ -342,18 +337,22 @@ static int __init EISA_ELCR(unsigned int irq) } printk("Broken MPtable reports ISA irq %d\n", irq); return 0; -} +} -#define default_ISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_dstirq)) -#define default_ISA_polarity(idx) (0) +/* EISA interrupts are always polarity zero and can be edge or level + * trigger depending on the ELCR value. If an interrupt is listed as + * EISA conforming in the MP table, that means its trigger type must + * be read in from the ELCR */ -#else +#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_dstirq)) +#define default_EISA_polarity(idx) (0) + +/* ISA interrupts are always polarity zero edge triggered, even when + * listed as conforming in the MP table. */ #define default_ISA_trigger(idx) (0) #define default_ISA_polarity(idx) (0) -#endif - static int __init MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; @@ -373,6 +372,11 @@ static int __init MPBIOS_polarity(int idx) polarity = default_ISA_polarity(idx); break; } + case MP_BUS_EISA: + { + polarity = default_EISA_polarity(idx); + break; + } case MP_BUS_PCI: /* PCI pin */ { polarity = 1; @@ -432,6 +436,11 @@ static int __init MPBIOS_trigger(int idx) trigger = default_ISA_trigger(idx); break; } + case MP_BUS_EISA: + { + trigger = default_EISA_trigger(idx); + break; + } case MP_BUS_PCI: /* PCI pin, level */ { trigger = 1; @@ -496,6 +505,7 @@ static int __init pin_2_irq(int idx, int pin) switch (mp_bus_id_to_type[bus]) { case MP_BUS_ISA: /* ISA pin */ + case MP_BUS_EISA: { irq = mp_irqs[idx].mpc_srcbusirq; break; @@ -562,6 +572,9 @@ static int __init assign_irq_vector(int irq) printk("WARNING: ASSIGN_IRQ_VECTOR wrapped back to %02X\n", current_vector); } + if (current_vector == SYSCALL_VECTOR) + panic("ran out of interrupt sources!"); + IO_APIC_VECTOR(irq) = current_vector; return current_vector; } @@ -625,7 +638,7 @@ void __init setup_IO_APIC_irqs(void) /* * Set up a certain pin as ExtINT delivered interrupt */ -void __init setup_ExtINT_pin(unsigned int pin) +void __init setup_ExtINT_pin(unsigned int pin, int irq) { struct IO_APIC_route_entry entry; @@ -635,11 +648,16 @@ void __init setup_ExtINT_pin(unsigned int pin) memset(&entry,0,sizeof(entry)); entry.delivery_mode = dest_ExtINT; - entry.dest_mode = 1; /* logical delivery */ + entry.dest_mode = 0; /* physical delivery */ entry.mask = 0; /* unmask IRQ now */ - entry.dest.logical.logical_dest = 0x01; /* logical CPU #0 */ + /* + * We use physical delivery to get the timer IRQ + * to the boot CPU. 'boot_cpu_id' is the physical + * APIC ID of the boot CPU. + */ + entry.dest.physical.physical_dest = boot_cpu_id; - entry.vector = 0; /* it's ignored */ + entry.vector = assign_irq_vector(irq); entry.polarity = 0; entry.trigger = 0; @@ -681,9 +699,11 @@ void __init print_IO_APIC(void) printk(".... register #01: %08X\n", *(int *)®_01); printk("....... : max redirection entries: %04X\n", reg_01.entries); - if ( (reg_01.entries != 0x0f) && /* ISA-only Neptune boards */ - (reg_01.entries != 0x17) && /* ISA+PCI boards */ - (reg_01.entries != 0x3F) /* Xeon boards */ + if ( (reg_01.entries != 0x0f) && /* older (Neptune) boards */ + (reg_01.entries != 0x17) && /* typical ISA+PCI boards */ + (reg_01.entries != 0x1b) && /* Compaq Proliant boards */ + (reg_01.entries != 0x1f) && /* dual Xeon boards */ + (reg_01.entries != 0x3F) /* bigger Xeon boards */ ) UNEXPECTED_IO_APIC(); if (reg_01.entries == 0x0f) @@ -754,7 +774,7 @@ void __init print_IO_APIC(void) static void __init init_sym_mode(void) { - int i, pin; + int i; for (i = 0; i < PIN_MAP_SIZE; i++) { irq_2_pin[i].pin = -1; @@ -784,8 +804,7 @@ static void __init init_sym_mode(void) /* * Do not trust the IO-APIC being empty at bootup */ - for (pin = 0; pin < nr_ioapic_registers; pin++) - clear_IO_APIC_pin(pin); + clear_IO_APIC(); } /* @@ -793,6 +812,15 @@ static void __init init_sym_mode(void) */ void init_pic_mode(void) { + /* + * Clear the IO-APIC before rebooting: + */ + clear_IO_APIC(); + + /* + * Put it back into PIC mode (has an effect only on + * certain boards) + */ printk("disabling symmetric IO mode... "); outb_p(0x70, 0x22); outb_p(0x00, 0x23); @@ -885,6 +913,8 @@ static void __init setup_ioapic_id(void) static void __init construct_default_ISA_mptable(void) { int i, pos = 0; + const int bus_type = (mpc_default_type == 2 || mpc_default_type == 3 || + mpc_default_type == 6) ? MP_BUS_EISA : MP_BUS_ISA; for (i = 0; i < 16; i++) { if (!IO_APIC_IRQ(i)) @@ -892,14 +922,14 @@ static void __init construct_default_ISA_mptable(void) mp_irqs[pos].mpc_irqtype = mp_INT; mp_irqs[pos].mpc_irqflag = 0; /* default */ - mp_irqs[pos].mpc_srcbus = MP_BUS_ISA; + mp_irqs[pos].mpc_srcbus = 0; mp_irqs[pos].mpc_srcbusirq = i; mp_irqs[pos].mpc_dstapic = 0; mp_irqs[pos].mpc_dstirq = i; pos++; } mp_irq_entries = pos; - mp_bus_id_to_type[0] = MP_BUS_ISA; + mp_bus_id_to_type[0] = bus_type; /* * MP specification 1.4 defines some extra rules for default @@ -1019,7 +1049,7 @@ static void do_edge_ioapic_IRQ(unsigned int irq, struct pt_regs * regs) * and do not need to be masked. */ ack_APIC_irq(); - status = desc->status & ~IRQ_REPLAY; + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); status |= IRQ_PENDING; /* @@ -1030,8 +1060,9 @@ static void do_edge_ioapic_IRQ(unsigned int irq, struct pt_regs * regs) if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; status &= ~IRQ_PENDING; + status |= IRQ_INPROGRESS; } - desc->status = status | IRQ_INPROGRESS; + desc->status = status; spin_unlock(&irq_controller_lock); /* @@ -1073,7 +1104,7 @@ static void do_level_ioapic_IRQ(unsigned int irq, struct pt_regs * regs) * So this all has to be within the spinlock. */ mask_IO_APIC_irq(irq); - status = desc->status & ~IRQ_REPLAY; + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); /* * If the IRQ is disabled for whatever reason, we must @@ -1082,8 +1113,9 @@ static void do_level_ioapic_IRQ(unsigned int irq, struct pt_regs * regs) action = NULL; if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; + status |= IRQ_INPROGRESS; } - desc->status = status | IRQ_INPROGRESS; + desc->status = status; ack_APIC_irq(); spin_unlock(&irq_controller_lock); @@ -1143,7 +1175,7 @@ static inline void init_IO_APIC_traps(void) * 0x80, because int 0x80 is hm, kind of importantish. ;) */ for (i = 0; i < NR_IRQS ; i++) { - if (IO_APIC_IRQ(i)) { + if (IO_APIC_VECTOR(i) > 0) { if (IO_APIC_irq_trigger(i)) irq_desc[i].handler = &ioapic_level_irq_type; else @@ -1153,8 +1185,25 @@ static inline void init_IO_APIC_traps(void) */ if (i < 16) disable_8259A_irq(i); + } else { + if (!IO_APIC_IRQ(i)) + continue; + + /* + * Hmm.. We don't have an entry for this, + * so default to an old-fashioned 8259 + * interrupt if we can.. + */ + if (i < 16) { + make_8259A_irq(i); + continue; + } + + /* Strange. Oh, well.. */ + irq_desc[i].handler = &no_irq_type; } } + init_IRQ_SMP(); } /* @@ -1178,7 +1227,7 @@ static inline void check_timer(void) if (pin2 != -1) { printk(".. (found pin %d) ...", pin2); - setup_ExtINT_pin(pin2); + setup_ExtINT_pin(pin2, 0); make_8259A_irq(0); } @@ -1258,14 +1307,12 @@ void __init setup_IO_APIC(void) construct_default_ISA_mptable(); } - init_IO_APIC_traps(); - /* * Set up the IO-APIC IRQ routing table by parsing the MP-BIOS * mptable: */ setup_IO_APIC_irqs(); - init_IRQ_SMP(); + init_IO_APIC_traps(); check_timer(); print_IO_APIC(); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 37878f59f..ea218fe45 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -70,11 +70,34 @@ atomic_t nmi_counter; */ spinlock_t irq_controller_lock; - /* * Dummy controller type for unused interrupts */ -static void do_none(unsigned int irq, struct pt_regs * regs) { } +static void do_none(unsigned int irq, struct pt_regs * regs) +{ + /* + * we are careful. While for ISA irqs it's common to happen + * outside of any driver (think autodetection), this is not + * at all nice for PCI interrupts. So we are stricter and + * print a warning when such spurious interrupts happen. + * Spurious interrupts can confuse other drivers if the PCI + * IRQ line is shared. + * + * Such spurious interrupts are either driver bugs, or + * sometimes hw (chipset) bugs. + */ + printk("unexpected IRQ vector %d on CPU#%d!\n",irq, smp_processor_id()); + +#ifdef __SMP__ + /* + * [currently unexpected vectors happen only on SMP and APIC. + * if we want to have non-APIC and non-8259A controllers + * in the future with unexpected vectors, this ack should + * probably be made controller-specific.] + */ + ack_APIC_irq(); +#endif +} static void enable_none(unsigned int irq) { } static void disable_none(unsigned int irq) { } @@ -82,7 +105,7 @@ static void disable_none(unsigned int irq) { } #define startup_none enable_none #define shutdown_none disable_none -static struct hw_interrupt_type no_irq_type = { +struct hw_interrupt_type no_irq_type = { "none", startup_none, shutdown_none, @@ -128,10 +151,7 @@ irq_desc_t irq_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; */ static unsigned int cached_irq_mask = 0xffff; -#define __byte(x,y) (((unsigned char *)&(y))[x]) -#define __word(x,y) (((unsigned short *)&(y))[x]) -#define __long(x,y) (((unsigned int *)&(y))[x]) - +#define __byte(x,y) (((unsigned char *)&(y))[x]) #define cached_21 (__byte(0,cached_irq_mask)) #define cached_A1 (__byte(1,cached_irq_mask)) @@ -141,10 +161,10 @@ static unsigned int cached_irq_mask = 0xffff; * fed to the CPU IRQ line directly. * * Any '1' bit in this mask means the IRQ is routed through the IO-APIC. - * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ, - * but we have _much_ higher compatibility and robustness this way. + * this 'mixed mode' IRQ handling costs nothing because it's only used + * at IRQ setup time. */ -unsigned long long io_apic_irqs = 0; +unsigned long io_apic_irqs = 0; /* * These have to be protected by the irq controller spinlock @@ -183,8 +203,8 @@ int i8259A_irq_pending(unsigned int irq) void make_8259A_irq(unsigned int irq) { - disable_irq(irq); - __long(0,io_apic_irqs) &= ~(1<<irq); + disable_irq_nosync(irq); + io_apic_irqs &= ~(1<<irq); irq_desc[irq].handler = &i8259A_irq_type; enable_irq(irq); } @@ -219,11 +239,13 @@ static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs) { unsigned int status; mask_and_ack_8259A(irq); - status = desc->status & ~IRQ_REPLAY; + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); action = NULL; - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; - desc->status = status | IRQ_INPROGRESS; + status |= IRQ_INPROGRESS; + } + desc->status = status; } spin_unlock(&irq_controller_lock); @@ -254,32 +276,43 @@ static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs) BUILD_COMMON_IRQ() + +#define BI(x,y) \ + BUILD_IRQ(##x##y) + +#define BUILD_16_IRQS(x) \ + BI(x,0) BI(x,1) BI(x,2) BI(x,3) \ + BI(x,4) BI(x,5) BI(x,6) BI(x,7) \ + BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ + BI(x,c) BI(x,d) BI(x,e) BI(x,f) + /* * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts: + * (these are usually mapped to vectors 0x20-0x30) */ -BUILD_IRQ(0) BUILD_IRQ(1) BUILD_IRQ(2) BUILD_IRQ(3) -BUILD_IRQ(4) BUILD_IRQ(5) BUILD_IRQ(6) BUILD_IRQ(7) -BUILD_IRQ(8) BUILD_IRQ(9) BUILD_IRQ(10) BUILD_IRQ(11) -BUILD_IRQ(12) BUILD_IRQ(13) BUILD_IRQ(14) BUILD_IRQ(15) +BUILD_16_IRQS(0x0) #ifdef CONFIG_X86_IO_APIC /* - * The IO-APIC gives us many more interrupt sources.. + * The IO-APIC gives us many more interrupt sources. Most of these + * are unused but an SMP system is supposed to have enough memory ... + * sometimes (mostly wrt. hw bugs) we get corrupted vectors all + * across the spectrum, so we really want to be prepared to get all + * of these. Plus, more powerful systems might have more than 64 + * IO-APIC registers. + * + * (these are usually mapped into the 0x30-0xff vector range) */ -BUILD_IRQ(16) BUILD_IRQ(17) BUILD_IRQ(18) BUILD_IRQ(19) -BUILD_IRQ(20) BUILD_IRQ(21) BUILD_IRQ(22) BUILD_IRQ(23) -BUILD_IRQ(24) BUILD_IRQ(25) BUILD_IRQ(26) BUILD_IRQ(27) -BUILD_IRQ(28) BUILD_IRQ(29) BUILD_IRQ(30) BUILD_IRQ(31) -BUILD_IRQ(32) BUILD_IRQ(33) BUILD_IRQ(34) BUILD_IRQ(35) -BUILD_IRQ(36) BUILD_IRQ(37) BUILD_IRQ(38) BUILD_IRQ(39) -BUILD_IRQ(40) BUILD_IRQ(41) BUILD_IRQ(42) BUILD_IRQ(43) -BUILD_IRQ(44) BUILD_IRQ(45) BUILD_IRQ(46) BUILD_IRQ(47) -BUILD_IRQ(48) BUILD_IRQ(49) BUILD_IRQ(50) BUILD_IRQ(51) -BUILD_IRQ(52) BUILD_IRQ(53) BUILD_IRQ(54) BUILD_IRQ(55) -BUILD_IRQ(56) BUILD_IRQ(57) BUILD_IRQ(58) BUILD_IRQ(59) -BUILD_IRQ(60) BUILD_IRQ(61) BUILD_IRQ(62) BUILD_IRQ(63) + BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) +BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7) +BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb) +BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) #endif +#undef BUILD_16_IRQS +#undef BI + + #ifdef __SMP__ /* * The following vectors are part of the Linux architecture, there @@ -289,7 +322,7 @@ BUILD_IRQ(60) BUILD_IRQ(61) BUILD_IRQ(62) BUILD_IRQ(63) BUILD_SMP_INTERRUPT(reschedule_interrupt) BUILD_SMP_INTERRUPT(invalidate_interrupt) BUILD_SMP_INTERRUPT(stop_cpu_interrupt) -BUILD_SMP_INTERRUPT(mtrr_interrupt) +BUILD_SMP_INTERRUPT(call_function_interrupt) BUILD_SMP_INTERRUPT(spurious_interrupt) /* @@ -303,37 +336,35 @@ BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt) #endif +#define IRQ(x,y) \ + IRQ##x##y##_interrupt + +#define IRQLIST_16(x) \ + IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \ + IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \ + IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ + IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f) + static void (*interrupt[NR_IRQS])(void) = { - IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt, - IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, - IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, - IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt + IRQLIST_16(0x0), + #ifdef CONFIG_X86_IO_APIC - ,IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt, - IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt, - IRQ24_interrupt, IRQ25_interrupt, IRQ26_interrupt, IRQ27_interrupt, - IRQ28_interrupt, IRQ29_interrupt, - IRQ30_interrupt, IRQ31_interrupt, IRQ32_interrupt, IRQ33_interrupt, - IRQ34_interrupt, IRQ35_interrupt, IRQ36_interrupt, IRQ37_interrupt, - IRQ38_interrupt, IRQ39_interrupt, - IRQ40_interrupt, IRQ41_interrupt, IRQ42_interrupt, IRQ43_interrupt, - IRQ44_interrupt, IRQ45_interrupt, IRQ46_interrupt, IRQ47_interrupt, - IRQ48_interrupt, IRQ49_interrupt, - IRQ50_interrupt, IRQ51_interrupt, IRQ52_interrupt, IRQ53_interrupt, - IRQ54_interrupt, IRQ55_interrupt, IRQ56_interrupt, IRQ57_interrupt, - IRQ58_interrupt, IRQ59_interrupt, - IRQ60_interrupt, IRQ61_interrupt, IRQ62_interrupt, IRQ63_interrupt + IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3), + IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7), + IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb), + IRQLIST_16(0xc), IRQLIST_16(0xd) #endif }; +#undef IRQ +#undef IRQLIST_16 + /* - * Initial irq handlers. + * Special irq handlers. */ -void no_action(int cpl, void *dev_id, struct pt_regs *regs) -{ -} +void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } #ifndef CONFIG_VISWS /* @@ -718,7 +749,7 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * * hardware disable after having gotten the irq * controller lock. */ -void disable_irq(unsigned int irq) +void disable_irq_nosync(unsigned int irq) { unsigned long flags; @@ -728,9 +759,21 @@ void disable_irq(unsigned int irq) irq_desc[irq].handler->disable(irq); } spin_unlock_irqrestore(&irq_controller_lock, flags); +} - if (irq_desc[irq].status & IRQ_INPROGRESS) - synchronize_irq(); +/* + * Synchronous version of the above, making sure the IRQ is + * no longer running on any other IRQ.. + */ +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count[smp_processor_id()]) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } } void enable_irq(unsigned int irq) @@ -740,7 +783,7 @@ void enable_irq(unsigned int irq) spin_lock_irqsave(&irq_controller_lock, flags); switch (irq_desc[irq].depth) { case 1: - irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS); + irq_desc[irq].status &= ~IRQ_DISABLED; irq_desc[irq].handler->enable(irq); /* fall throught */ default: @@ -770,7 +813,7 @@ asmlinkage void do_IRQ(struct pt_regs regs) * 0 return value means that this irq is already being * handled by some other CPU. (or is disabled) */ - unsigned int irq = regs.orig_eax & 0xff; + int irq = regs.orig_eax & 0xff; /* subtle, see irq.h */ int cpu = smp_processor_id(); kstat.irqs[cpu][irq]++; @@ -835,7 +878,7 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new) if (!shared) { irq_desc[irq].depth = 0; - irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS); + irq_desc[irq].status &= ~IRQ_DISABLED; irq_desc[irq].handler->startup(irq); } spin_unlock_irqrestore(&irq_controller_lock,flags); @@ -907,7 +950,7 @@ out: * * This depends on the fact that any interrupt that * comes in on to an unassigned handler will get stuck - * with "IRQ_INPROGRESS" asserted and the interrupt + * with "IRQ_WAITING" cleared and the interrupt * disabled. */ unsigned long probe_irq_on(void) @@ -921,8 +964,7 @@ unsigned long probe_irq_on(void) spin_lock_irq(&irq_controller_lock); for (i = NR_IRQS-1; i > 0; i--) { if (!irq_desc[i].action) { - unsigned int status = irq_desc[i].status | IRQ_AUTODETECT; - irq_desc[i].status = status & ~IRQ_INPROGRESS; + irq_desc[i].status |= IRQ_AUTODETECT | IRQ_WAITING; irq_desc[i].handler->startup(i); } } @@ -945,7 +987,7 @@ unsigned long probe_irq_on(void) continue; /* It triggered already - consider it spurious. */ - if (status & IRQ_INPROGRESS) { + if (!(status & IRQ_WAITING)) { irq_desc[i].status = status & ~IRQ_AUTODETECT; irq_desc[i].handler->shutdown(i); } @@ -971,7 +1013,7 @@ int probe_irq_off(unsigned long unused) if (!(status & IRQ_AUTODETECT)) continue; - if (status & IRQ_INPROGRESS) { + if (!(status & IRQ_WAITING)) { if (!nr_irqs) irq_found = i; nr_irqs++; @@ -986,42 +1028,6 @@ int probe_irq_off(unsigned long unused) return irq_found; } -/* - * Silly, horrible hack - */ -static char uglybuffer[10*256]; - -__asm__("\n" __ALIGN_STR"\n" - "common_unexpected:\n\t" - SAVE_ALL - "pushl $ret_from_intr\n\t" - "jmp strange_interrupt"); - -void strange_interrupt(int irqnum) -{ - printk("Unexpected interrupt %d\n", irqnum & 255); - for (;;); -} - -extern int common_unexpected; -__initfunc(void init_unexpected_irq(void)) -{ - int i; - for (i = 0; i < 256; i++) { - char *code = uglybuffer + 10*i; - unsigned long jumpto = (unsigned long) &common_unexpected; - - jumpto -= (unsigned long)(code+10); - code[0] = 0x68; /* pushl */ - *(int *)(code+1) = i - 512; - code[5] = 0xe9; /* jmp */ - *(int *)(code+6) = jumpto; - - set_intr_gate(i,code); - } -} - - void init_ISA_irqs (void) { int i; @@ -1033,7 +1039,7 @@ void init_ISA_irqs (void) if (i < 16) { /* - * 16 old-style INTA-cycle interrupt gates: + * 16 old-style INTA-cycle interrupts: */ irq_desc[i].handler = &i8259A_irq_type; } else { @@ -1054,9 +1060,16 @@ __initfunc(void init_IRQ(void)) #else init_VISWS_APIC_irqs(); #endif - - for (i = 0; i < 16; i++) - set_intr_gate(0x20+i,interrupt[i]); + /* + * Cover the whole vector space, no vector can escape + * us. (some of these will be overridden and become + * 'special' SMP interrupts) + */ + for (i = 0; i < NR_IRQS; i++) { + int vector = FIRST_EXTERNAL_VECTOR + i; + if (vector != SYSCALL_VECTOR) + set_intr_gate(vector, interrupt[i]); + } #ifdef __SMP__ @@ -1067,13 +1080,9 @@ __initfunc(void init_IRQ(void)) set_intr_gate(IRQ0_TRAP_VECTOR, interrupt[0]); /* - * The reschedule interrupt slowly changes it's functionality, - * while so far it was a kind of broadcasted timer interrupt, - * in the future it should become a CPU-to-CPU rescheduling IPI, - * driven by schedule() ? + * The reschedule interrupt is a CPU-to-CPU reschedule-helper + * IPI, driven by wakeup. */ - - /* IPI for rescheduling */ set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); /* IPI for invalidation */ @@ -1085,8 +1094,8 @@ __initfunc(void init_IRQ(void)) /* self generated IPI for local APIC timer */ set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); - /* IPI for MTRR control */ - set_intr_gate(MTRR_CHANGE_VECTOR, mtrr_interrupt); + /* IPI for generic function call */ + set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); /* IPI vector for APIC spurious interrupts */ set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 982ab101e..6a19d9884 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -16,6 +16,7 @@ struct hw_interrupt_type { void (*disable)(unsigned int irq); }; +extern struct hw_interrupt_type no_irq_type; /* * IRQ line status. @@ -25,6 +26,7 @@ struct hw_interrupt_type { #define IRQ_PENDING 4 /* IRQ pending - replay on enable */ #define IRQ_REPLAY 8 /* IRQ has been replayed but not acked yet */ #define IRQ_AUTODETECT 16 /* IRQ is being autodetected */ +#define IRQ_WAITING 32 /* IRQ not yet seen - for autodetection */ /* * This is the "IRQ descriptor", which contains various information @@ -41,6 +43,18 @@ typedef struct { } irq_desc_t; /* + * IDT vectors usable for external interrupt sources start + * at 0x20: + */ +#define FIRST_EXTERNAL_VECTOR 0x20 + +#define SYSCALL_VECTOR 0x80 + +/* + * Vectors 0x20-0x2f are used for ISA interrupts. + */ + +/* * Special IRQ vectors used by the SMP architecture: * * (some of the following vectors are 'rare', they might be merged @@ -51,10 +65,10 @@ typedef struct { #define INVALIDATE_TLB_VECTOR 0x31 #define STOP_CPU_VECTOR 0x40 #define LOCAL_TIMER_VECTOR 0x41 -#define MTRR_CHANGE_VECTOR 0x50 +#define CALL_FUNCTION_VECTOR 0x50 /* - * First vector available to drivers: (vectors 0x51-0xfe) + * First APIC vector available to drivers: (vectors 0x51-0xfe) */ #define IRQ0_TRAP_VECTOR 0x51 @@ -85,7 +99,6 @@ extern void disable_8259A_irq(unsigned int irq); extern int i8259A_irq_pending(unsigned int irq); extern void ack_APIC_irq(void); extern void FASTCALL(send_IPI_self(int vector)); -extern void smp_send_mtrr(void); extern void init_VISWS_APIC_irqs(void); extern void setup_IO_APIC(void); extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn); @@ -94,12 +107,15 @@ extern void send_IPI(int dest, int vector); extern void init_pic_mode(void); extern void print_IO_APIC(void); -extern unsigned long long io_apic_irqs; +extern unsigned long io_apic_irqs; + +extern char _stext, _etext; #define MAX_IRQ_SOURCES 128 #define MAX_MP_BUSSES 32 enum mp_bustype { MP_BUS_ISA, + MP_BUS_EISA, MP_BUS_PCI }; extern int mp_bus_id_to_type [MAX_MP_BUSSES]; @@ -126,7 +142,7 @@ static inline void irq_exit(int cpu, unsigned int irq) hardirq_exit(cpu); } -#define IO_APIC_IRQ(x) ((1<<x) & io_apic_irqs) +#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs)) #else @@ -201,6 +217,13 @@ __asm__( \ "pushl $ret_from_intr\n\t" \ "jmp "SYMBOL_NAME_STR(do_IRQ)); +/* + * subtle. orig_eax is used by the signal code to distinct between + * system calls and interrupted 'random user-space'. Thus we have + * to put a negative value into orig_eax here. (the problem is that + * both system calls and IRQs want to have small integer numbers in + * orig_eax, and the syscall code has won the optimization conflict ;) + */ #define BUILD_IRQ(nr) \ asmlinkage void IRQ_NAME(nr); \ __asm__( \ @@ -216,7 +239,6 @@ SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ static inline void x86_do_profile (unsigned long eip) { if (prof_buffer && current->pid) { - extern int _stext; eip -= (unsigned long) &_stext; eip >>= prof_shift; /* diff --git a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c index de5a47e72..7c5ad2712 100644 --- a/arch/i386/kernel/mca.c +++ b/arch/i386/kernel/mca.c @@ -26,6 +26,9 @@ * - Added the 'driver_loaded' flag in MCA_adapter * - Added an alternative implemention of ZP Gu's mca_find_unused_adapter * + * David Weinehall March 24th, 1999 + * - Fixed the output of 'Driver Installed' in /proc/mca/pos + * - Made the Integrated Video & SCSI show up even if they have id 0000 */ #include <linux/types.h> @@ -49,12 +52,12 @@ * Other miscellaneous information follows. */ -typedef enum { - MCA_ADAPTER_NORMAL = 0, - MCA_ADAPTER_NONE = 1, - MCA_ADAPTER_DISABLED = 2, - MCA_ADAPTER_ERROR = 3 -} MCA_AdapterStatus; +typedef enum { + MCA_ADAPTER_NORMAL = 0, + MCA_ADAPTER_NONE = 1, + MCA_ADAPTER_DISABLED = 2, + MCA_ADAPTER_ERROR = 3 +} MCA_AdapterStatus; struct MCA_adapter { MCA_AdapterStatus status; /* is there a valid adapter? */ @@ -69,16 +72,17 @@ struct MCA_adapter { }; struct MCA_info { -/* one for each of the 8 possible slots, plus one for integrated SCSI - and one for integrated video. */ + /* one for each of the 8 possible slots, plus one for integrated SCSI + * and one for integrated video. + */ struct MCA_adapter slot[MCA_NUMADAPTERS]; -/* two potential addresses for integrated SCSI adapter - this will - * track which one we think it is - */ + /* two potential addresses for integrated SCSI adapter - this will + * track which one we think it is. + */ - unsigned char which_scsi; + unsigned char which_scsi; }; /* The mca_info structure pointer. If MCA bus is present, the function @@ -88,7 +92,7 @@ struct MCA_info { * is set to zero. */ -static struct MCA_info* mca_info = 0; +static struct MCA_info* mca_info = NULL; /* MCA registers */ @@ -102,10 +106,10 @@ static struct MCA_info* mca_info = 0; #ifdef CONFIG_PROC_FS -static void mca_do_proc_init( void ); -static int mca_default_procfn( char* buf, int slot ); +static void mca_do_proc_init(void); +static int mca_default_procfn(char* buf, int slot); -static ssize_t proc_mca_read( struct file*, char*, size_t, loff_t *); +static ssize_t proc_mca_read(struct file*, char*, size_t, loff_t *); static struct file_operations proc_mca_operations = { NULL, /* array_lseek */ @@ -146,23 +150,26 @@ static struct inode_operations proc_mca_inode_operations = { /* Build the status info for the adapter */ -static void mca_configure_adapter_status( int slot ) { +static void mca_configure_adapter_status(int slot) { mca_info->slot[slot].status = MCA_ADAPTER_NONE; mca_info->slot[slot].id = mca_info->slot[slot].pos[0] + (mca_info->slot[slot].pos[1] << 8); - if( !mca_info->slot[slot].id ) { + if(!mca_info->slot[slot].id && slot < MCA_MAX_SLOT_NR) { /* id = 0x0000 usually indicates hardware failure, * however, ZP Gu (zpg@castle.net> reports that his 9556 - * has 0x0000 as id and everything still works. + * has 0x0000 as id and everything still works. There + * also seem to be an adapter with id = 0x0000; the + * NCR Parallel Bus Memory Card. Until this is confirmed, + * however, this code will stay. */ mca_info->slot[slot].status = MCA_ADAPTER_ERROR; return; - } else if( mca_info->slot[slot].id != 0xffff ) { + } else if(mca_info->slot[slot].id != 0xffff) { /* 0xffff usually indicates that there's no adapter, * however, some integrated adapters may have 0xffff as @@ -174,21 +181,21 @@ static void mca_configure_adapter_status( int slot ) { mca_info->slot[slot].status = MCA_ADAPTER_NORMAL; } - if( (mca_info->slot[slot].id == 0xffff || - mca_info->slot[slot].id == 0x0000) && slot >= MCA_MAX_SLOT_NR ) { + if((mca_info->slot[slot].id == 0xffff || + mca_info->slot[slot].id == 0x0000) && slot >= MCA_MAX_SLOT_NR) { int j; - for( j = 2; j < 8; j++ ) { - if( mca_info->slot[slot].pos[j] != 0xff ) { + for(j = 2; j < 8; j++) { + if(mca_info->slot[slot].pos[j] != 0xff) { mca_info->slot[slot].status = MCA_ADAPTER_NORMAL; break; } } } - if( !(mca_info->slot[slot].pos[2] & MCA_ENABLED) ) { + if(!(mca_info->slot[slot].pos[2] & MCA_ENABLED)) { - /* enabled bit is in pos 2 */ + /* enabled bit is in POS 2 */ mca_info->slot[slot].status = MCA_ADAPTER_DISABLED; } @@ -198,94 +205,101 @@ static void mca_configure_adapter_status( int slot ) { __initfunc(void mca_init(void)) { - unsigned int i, j; + unsigned int i, j; unsigned long flags; /* WARNING: Be careful when making changes here. Putting an adapter - * and the motherboard simultaneously into setup mode may result in - * damage to chips (according to The Indispensible PC Hardware Book - * by Hans-Peter Messmer). Also, we disable system interrupts (so + * and the motherboard simultaneously into setup mode may result in + * damage to chips (according to The Indispensible PC Hardware Book + * by Hans-Peter Messmer). Also, we disable system interrupts (so * that we are not disturbed in the middle of this). */ /* Make sure the MCA bus is present */ - if (!MCA_bus) + if(!MCA_bus) return; - printk( "Micro Channel bus detected.\n" ); - save_flags( flags ); + printk("Micro Channel bus detected.\n"); + save_flags(flags); cli(); /* Allocate MCA_info structure (at address divisible by 8) */ - mca_info = kmalloc(sizeof(struct MCA_info), GFP_ATOMIC); + mca_info = kmalloc(sizeof(struct MCA_info), GFP_KERNEL); + + if(mca_info == NULL) { + printk("Failed to allocate memory for mca_info!"); + restore_flags(flags); + return; + } /* Make sure adapter setup is off */ outb_p(0, MCA_ADAPTER_SETUP_REG); /* Put motherboard into video setup mode, read integrated video - * pos registers, and turn motherboard setup off. + * POS registers, and turn motherboard setup off. */ outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG); mca_info->slot[MCA_INTEGVIDEO].name[0] = 0; - for (j=0; j<8; j++) { - mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j)); + for(j=0; j<8; j++) { + mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j)); } mca_configure_adapter_status(MCA_INTEGVIDEO); /* Put motherboard into scsi setup mode, read integrated scsi - * pos registers, and turn motherboard setup off. + * POS registers, and turn motherboard setup off. * - * It seems there are two possible SCSI registers. Martin says that + * It seems there are two possible SCSI registers. Martin says that * for the 56,57, 0xf7 is the one, but fails on the 76. * Alfredo (apena@vnet.ibm.com) says - * 0xfd works on his machine. We'll try both of them. I figure it's - * a good bet that only one could be valid at a time. This could + * 0xfd works on his machine. We'll try both of them. I figure it's + * a good bet that only one could be valid at a time. This could * screw up though if one is used for something else on the other * machine. */ outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG); mca_info->slot[MCA_INTEGSCSI].name[0] = 0; - for (j=0; j<8; j++) { - if( (mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff ) + for(j=0; j<8; j++) { + if((mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff) { - /* 0xff all across means no device. 0x00 means something's - * broken, but a device is probably there. However, if you get - * 0x00 from a motherboard register it won't matter what we - * find. For the record, on the 57SLC, the integrated SCSI - * adapter has 0xffff for the adapter ID, but nonzero for - * other registers. + /* 0xff all across means no device. 0x00 means + * something's broken, but a device is probably there. + * However, if you get 0x00 from a motherboard + * register it won't matter what we find. For the + * record, on the 57SLC, the integrated SCSI + * adapter has 0xffff for the adapter ID, but + * nonzero for other registers. */ mca_info->which_scsi = 0xf7; } } - if( !mca_info->which_scsi ) { + if(!mca_info->which_scsi) { /* Didn't find it at 0xf7, try somewhere else... */ mca_info->which_scsi = 0xfd; outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG); - for (j=0; j<8; j++) - mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j)); + for(j=0; j<8; j++) + mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j)); } mca_configure_adapter_status(MCA_INTEGSCSI); - /* turn off motherboard setup */ + /* Turn off motherboard setup */ outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); /* Now loop over MCA slots: put each adapter into setup mode, and - * read its pos registers. Then put adapter setup off. + * read its POS registers. Then put adapter setup off. */ - for (i=0; i<MCA_MAX_SLOT_NR; i++) { + for(i=0; i<MCA_MAX_SLOT_NR; i++) { outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG); - for (j=0; j<8; j++) { - mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j)); + for(j=0; j<8; j++) { + mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j)); } mca_info->slot[i].name[0] = 0; mca_info->slot[i].driver_loaded = 0; @@ -295,7 +309,7 @@ __initfunc(void mca_init(void)) /* Enable interrupts and return memory start */ - restore_flags( flags ); + restore_flags(flags); request_region(0x60,0x01,"system control port B (MCA)"); request_region(0x90,0x01,"arbitration (MCA)"); @@ -312,89 +326,90 @@ __initfunc(void mca_init(void)) /*--------------------------------------------------------------------*/ -static void mca_handle_nmi_slot( int slot, int check_flag ) +static void mca_handle_nmi_slot(int slot, int check_flag) { - if( slot < MCA_MAX_SLOT_NR ) { - printk( "NMI: caused by MCA adapter in slot %d (%s)\n", slot+1, - mca_info->slot[slot].name ); - } else if( slot == MCA_INTEGSCSI ) { - printk( "NMI: caused by MCA integrated SCSI adapter (%s)\n", - mca_info->slot[slot].name ); - } else if( slot == MCA_INTEGVIDEO ) { - printk( "NMI: caused by MCA integrated video adapter (%s)\n", - mca_info->slot[slot].name ); - } - - /* more info available in pos 6 and 7? */ - - if( check_flag ) { - unsigned char pos6, pos7; - - pos6 = mca_read_pos( slot, 6 ); - pos7 = mca_read_pos( slot, 7 ); - - printk( "NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7 ); - } - -} /* mca_handle_nmi_slot */ - -/*--------------------------------------------------------------------*/ - -void mca_handle_nmi( void ) + if(slot < MCA_MAX_SLOT_NR) { + printk("NMI: caused by MCA adapter in slot %d (%s)\n", slot+1, + mca_info->slot[slot].name); + } else if(slot == MCA_INTEGSCSI) { + printk("NMI: caused by MCA integrated SCSI adapter (%s)\n", + mca_info->slot[slot].name); + } else if(slot == MCA_INTEGVIDEO) { + printk("NMI: caused by MCA integrated video adapter (%s)\n", + mca_info->slot[slot].name); + } + + /* More info available in POS 6 and 7? */ + + if(check_flag) { + unsigned char pos6, pos7; + + pos6 = mca_read_pos(slot, 6); + pos7 = mca_read_pos(slot, 7); + + printk("NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7); + } + +} /* mca_handle_nmi_slot */ + +/*--------------------------------------------------------------------*/ + +void mca_handle_nmi(void) { int i; - unsigned char pos5; - - /* First try - scan the various adapters and see if a specific - * adapter was responsible for the error - */ - - for( i = 0; i < MCA_NUMADAPTERS; i += 1 ) { - - /* bit 7 of POS 5 is reset when this adapter has a hardware - * error. bit 7 it reset if there's error information - * available in pos 6 and 7. */ - - pos5 = mca_read_pos( i, 5 ); - - if( !(pos5 & 0x80) ) { - mca_handle_nmi_slot( i, !(pos5 & 0x40) ); - return; - } - } - - /* if I recall correctly, there's a whole bunch of other things that - * we can do to check for NMI problems, but that's all I know about + unsigned char pos5; + + /* First try - scan the various adapters and see if a specific + * adapter was responsible for the error. + */ + + for(i = 0; i < MCA_NUMADAPTERS; i++) { + + /* Bit 7 of POS 5 is reset when this adapter has a hardware + * error. Bit 7 it reset if there's error information + * available in POS 6 and 7. + */ + + pos5 = mca_read_pos(i, 5); + + if(!(pos5 & 0x80)) { + mca_handle_nmi_slot(i, !(pos5 & 0x40)); + return; + } + } + + /* If I recall correctly, there's a whole bunch of other things that + * we can do to check for NMI problems, but that's all I know about * at the moment. - */ + */ - printk( "NMI generated from unknown source!\n" ); -} /* mca_handle_nmi */ + printk("NMI generated from unknown source!\n"); +} /* mca_handle_nmi */ /*--------------------------------------------------------------------*/ -int mca_find_adapter( int id, int start ) +int mca_find_adapter(int id, int start) { - if( mca_info == 0 || id == 0 || id == 0xffff ) { + if(mca_info == NULL || id == 0xffff) { return MCA_NOTFOUND; } - for( ; start >= 0 && start < MCA_NUMADAPTERS; start += 1 ) { + for(; start >= 0 && start < MCA_NUMADAPTERS; start++) { - /* not sure about this. There's no point in returning + /* Not sure about this. There's no point in returning * adapters that aren't enabled, since they can't actually - * be used. However, they might be needed for statistical + * be used. However, they might be needed for statistical * purposes or something... But if that is the case, the * user is free to write a routine that manually iterates * through the adapters. */ - if( mca_info->slot[start].status == MCA_ADAPTER_DISABLED ) { + if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED) { continue; } - if( id == mca_info->slot[start].id ) { + if(id == mca_info->slot[start].id) { return start; } } @@ -404,28 +419,28 @@ int mca_find_adapter( int id, int start ) /*--------------------------------------------------------------------*/ -int mca_find_unused_adapter( int id, int start ) +int mca_find_unused_adapter(int id, int start) { - if( mca_info == 0 || id == 0 || id == 0xffff ) { + if(mca_info == NULL || id == 0xffff) { return MCA_NOTFOUND; } - for( ; start >= 0 && start < MCA_NUMADAPTERS; start += 1 ) { + for(; start >= 0 && start < MCA_NUMADAPTERS; start++) { - /* not sure about this. There's no point in returning + /* not sure about this. There's no point in returning * adapters that aren't enabled, since they can't actually - * be used. However, they might be needed for statistical + * be used. However, they might be needed for statistical * purposes or something... But if that is the case, the * user is free to write a routine that manually iterates * through the adapters. */ - if( mca_info->slot[start].status == MCA_ADAPTER_DISABLED || - mca_info->slot[start].driver_loaded ) { + if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED || + mca_info->slot[start].driver_loaded) { continue; } - if( id == mca_info->slot[start].id ) { + if(id == mca_info->slot[start].id) { return start; } } @@ -435,68 +450,68 @@ int mca_find_unused_adapter( int id, int start ) /*--------------------------------------------------------------------*/ -unsigned char mca_read_stored_pos( int slot, int reg ) +unsigned char mca_read_stored_pos(int slot, int reg) { - if( slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == 0 ) return 0; - if( reg < 0 || reg >= 8 ) return 0; + if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0; + if(reg < 0 || reg >= 8) return 0; return mca_info->slot[slot].pos[reg]; } /* mca_read_stored_pos() */ /*--------------------------------------------------------------------*/ -unsigned char mca_read_pos( int slot, int reg ) +unsigned char mca_read_pos(int slot, int reg) { unsigned int byte = 0; unsigned long flags; - if( slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == 0 ) return 0; - if( reg < 0 || reg >= 8 ) return 0; + if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0; + if(reg < 0 || reg >= 8) return 0; - save_flags( flags ); + save_flags(flags); cli(); - /* make sure motherboard setup is off */ + /* Make sure motherboard setup is off */ outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); - /* read in the appropriate register */ + /* Read in the appropriate register */ - if( slot == MCA_INTEGSCSI && mca_info->which_scsi ) { + if(slot == MCA_INTEGSCSI && mca_info->which_scsi) { - /* disable adapter setup, enable motherboard setup */ + /* Disable adapter setup, enable motherboard setup */ outb_p(0, MCA_ADAPTER_SETUP_REG); outb_p(mca_info->which_scsi, MCA_MOTHERBOARD_SETUP_REG); byte = inb_p(MCA_POS_REG(reg)); outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); - } else if( slot == MCA_INTEGVIDEO ) { + } else if(slot == MCA_INTEGVIDEO) { - /* disable adapter setup, enable motherboard setup */ + /* Disable adapter setup, enable motherboard setup */ outb_p(0, MCA_ADAPTER_SETUP_REG); outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG); byte = inb_p(MCA_POS_REG(reg)); outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); - } else if( slot < MCA_MAX_SLOT_NR ) { + } else if(slot < MCA_MAX_SLOT_NR) { - /* make sure motherboard setup is off */ + /* Make sure motherboard setup is off */ outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); - /* read the appropriate register */ + /* Read the appropriate register */ outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG); byte = inb_p(MCA_POS_REG(reg)); outb_p(0, MCA_ADAPTER_SETUP_REG); } - /* make sure the stored values are consistent, while we're here */ + /* Make sure the stored values are consistent, while we're here */ mca_info->slot[slot].pos[reg] = byte; - restore_flags( flags ); + restore_flags(flags); return byte; } /* mca_read_pos() */ @@ -513,44 +528,47 @@ unsigned char mca_read_pos( int slot, int reg ) * screws up. */ -void mca_write_pos( int slot, int reg, unsigned char byte ) +void mca_write_pos(int slot, int reg, unsigned char byte) { unsigned long flags; - if( slot < 0 || slot >= MCA_MAX_SLOT_NR ) return; - if( reg < 0 || reg >= 8 ) return; - if (mca_info == 0 ) return; + if(slot < 0 || slot >= MCA_MAX_SLOT_NR) + return; + if(reg < 0 || reg >= 8) + return; + if(mca_info == NULL) + return; - save_flags( flags ); + save_flags(flags); cli(); - /* make sure motherboard setup is off */ + /* Make sure motherboard setup is off */ outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); - /* read in the appropriate register */ + /* Read in the appropriate register */ outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG); - outb_p( byte, MCA_POS_REG(reg) ); + outb_p(byte, MCA_POS_REG(reg)); outb_p(0, MCA_ADAPTER_SETUP_REG); - restore_flags( flags ); + restore_flags(flags); - /* update the global register list, while we have the byte */ + /* Update the global register list, while we have the byte */ mca_info->slot[slot].pos[reg] = byte; } /* mca_write_pos() */ /*--------------------------------------------------------------------*/ -void mca_set_adapter_name( int slot, char* name ) +void mca_set_adapter_name(int slot, char* name) { - if( mca_info == 0 ) return; + if(mca_info == NULL) return; - if( slot >= 0 && slot < MCA_NUMADAPTERS ) { - if( name != NULL ) { - strncpy( mca_info->slot[slot].name, name, - sizeof(mca_info->slot[slot].name)-1 ); + if(slot >= 0 && slot < MCA_NUMADAPTERS) { + if(name != NULL) { + strncpy(mca_info->slot[slot].name, name, + sizeof(mca_info->slot[slot].name)-1); mca_info->slot[slot].name[ sizeof(mca_info->slot[slot].name)-1] = 0; } else { @@ -559,61 +577,61 @@ void mca_set_adapter_name( int slot, char* name ) } } -void mca_set_adapter_procfn( int slot, MCA_ProcFn procfn, void* dev) +void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev) { - if( mca_info == 0 ) return; + if(mca_info == NULL) return; - if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + if(slot >= 0 && slot < MCA_NUMADAPTERS) { mca_info->slot[slot].procfn = procfn; mca_info->slot[slot].dev = dev; } } -int mca_is_adapter_used( int slot ) +int mca_is_adapter_used(int slot) { return mca_info->slot[slot].driver_loaded; } -int mca_mark_as_used( int slot ) +int mca_mark_as_used(int slot) { if(mca_info->slot[slot].driver_loaded) return 1; mca_info->slot[slot].driver_loaded = 1; return 0; } -void mca_mark_as_unused( int slot ) +void mca_mark_as_unused(int slot) { mca_info->slot[slot].driver_loaded = 0; } -char *mca_get_adapter_name( int slot ) +char *mca_get_adapter_name(int slot) { - if( mca_info == 0 ) return 0; + if(mca_info == NULL) return 0; - if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + if(slot >= 0 && slot < MCA_NUMADAPTERS) { return mca_info->slot[slot].name; } return 0; } -int mca_isadapter( int slot ) +int mca_isadapter(int slot) { - if( mca_info == 0 ) return 0; + if(mca_info == NULL) return 0; - if( slot >= 0 && slot < MCA_NUMADAPTERS ) { - return (( mca_info->slot[slot].status == MCA_ADAPTER_NORMAL ) - || (mca_info->slot[slot].status == MCA_ADAPTER_DISABLED ) ); + if(slot >= 0 && slot < MCA_NUMADAPTERS) { + return ((mca_info->slot[slot].status == MCA_ADAPTER_NORMAL) + || (mca_info->slot[slot].status == MCA_ADAPTER_DISABLED)); } return 0; } -int mca_isenabled( int slot ) +int mca_isenabled(int slot) { - if( mca_info == 0 ) return 0; + if(mca_info == NULL) return 0; - if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + if(slot >= 0 && slot < MCA_NUMADAPTERS) { return (mca_info->slot[slot].status == MCA_ADAPTER_NORMAL); } @@ -624,39 +642,37 @@ int mca_isenabled( int slot ) #ifdef CONFIG_PROC_FS -int get_mca_info(char *buf) +int get_mca_info(char *buf) { - int i, j, len = 0; + int i, j, len = 0; - if( MCA_bus && mca_info != 0 ) + if(MCA_bus && mca_info != NULL) { - /* Format pos registers of eight MCA slots */ + /* Format POS registers of eight MCA slots */ - for (i=0; i<MCA_MAX_SLOT_NR; i++) + for(i=0; i<MCA_MAX_SLOT_NR; i++) { len += sprintf(buf+len, "Slot %d: ", i+1); - for (j=0; j<8; j++) + for(j=0; j<8; j++) len += sprintf(buf+len, "%02x ", mca_info->slot[i].pos[j]); - len += sprintf( buf+len, " %s\n", mca_info->slot[i].name ); - } + len += sprintf(buf+len, " %s\n", mca_info->slot[i].name); + } - /* Format pos registers of integrated video subsystem */ + /* Format POS registers of integrated video subsystem */ len += sprintf(buf+len, "Video : "); - for (j=0; j<8; j++) + for(j=0; j<8; j++) len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]); - len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name ); + len += sprintf(buf+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name); - /* Format pos registers of integrated SCSI subsystem */ + /* Format POS registers of integrated SCSI subsystem */ len += sprintf(buf+len, "SCSI : "); - for (j=0; j<8; j++) + for(j=0; j<8; j++) len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]); - len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name ); - } - else - { - /* Leave it empty if MCA not detected - this should *never* + len += sprintf(buf+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name); + } else { + /* Leave it empty if MCA not detected - this should *never* * happen! */ } @@ -667,119 +683,123 @@ int get_mca_info(char *buf) /*--------------------------------------------------------------------*/ -__initfunc(void mca_do_proc_init( void )) +__initfunc(void mca_do_proc_init(void)) { - int i = 0; - struct proc_dir_entry* node = 0; + int i; + struct proc_dir_entry* node = NULL; - if( mca_info == 0 ) return; /* should never happen */ + if(mca_info == NULL) return; /* Should never happen */ - proc_register( &proc_mca, &(struct proc_dir_entry) { + proc_register(&proc_mca, &(struct proc_dir_entry) { PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO, - 1, 0, 0, 0, &proc_mca_inode_operations,} ); + 1, 0, 0, 0, &proc_mca_inode_operations,}); - proc_register( &proc_mca, &(struct proc_dir_entry) { + proc_register(&proc_mca, &(struct proc_dir_entry) { PROC_MCA_MACHINE, 7, "machine", S_IFREG|S_IRUGO, - 1, 0, 0, 0, &proc_mca_inode_operations,} ); + 1, 0, 0, 0, &proc_mca_inode_operations,}); - /* initialize /proc/mca entries for existing adapters */ + /* Initialize /proc/mca entries for existing adapters */ - for( i = 0; i < MCA_NUMADAPTERS; i += 1 ) { + for(i = 0; i < MCA_NUMADAPTERS; i++) { mca_info->slot[i].procfn = 0; mca_info->slot[i].dev = 0; - if( ! mca_isadapter( i ) ) continue; - node = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC); + if(!mca_isadapter(i)) continue; + node = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL); - if( i < MCA_MAX_SLOT_NR ) { + if(node == NULL) { + printk("Failed to allocate memory for MCA proc-entries!"); + return; + } + if(i < MCA_MAX_SLOT_NR) { node->low_ino = PROC_MCA_SLOT + i; - node->namelen = sprintf( mca_info->slot[i].procname, - "slot%d", i+1 ); - } else if( i == MCA_INTEGVIDEO ) { + node->namelen = sprintf(mca_info->slot[i].procname, + "slot%d", i+1); + } else if(i == MCA_INTEGVIDEO) { node->low_ino = PROC_MCA_VIDEO; - node->namelen = sprintf( mca_info->slot[i].procname, - "video" ); - } else if( i == MCA_INTEGSCSI ) { + node->namelen = sprintf(mca_info->slot[i].procname, + "video"); + } else if(i == MCA_INTEGSCSI) { node->low_ino = PROC_MCA_SCSI; - node->namelen = sprintf( mca_info->slot[i].procname, - "scsi" ); + node->namelen = sprintf(mca_info->slot[i].procname, + "scsi"); } node->name = mca_info->slot[i].procname; node->mode = S_IFREG | S_IRUGO; node->ops = &proc_mca_inode_operations; - proc_register( &proc_mca, node ); + proc_register(&proc_mca, node); } } /* mca_do_proc_init() */ /*--------------------------------------------------------------------*/ -int mca_default_procfn( char* buf, int slot ) +int mca_default_procfn(char* buf, int slot) { int len = 0, i; - /* this really shouldn't happen... */ + /* This really shouldn't happen... */ - if( mca_info == 0 ) { + if(mca_info == NULL) { *buf = 0; return 0; } - /* print out the basic information */ + /* Print out the basic information */ - if( slot < MCA_MAX_SLOT_NR ) { - len += sprintf( buf+len, "Slot: %d\n", slot+1 ); - } else if( slot == MCA_INTEGSCSI ) { - len += sprintf( buf+len, "Integrated SCSI Adapter\n" ); - } else if( slot == MCA_INTEGVIDEO ) { - len += sprintf( buf+len, "Integrated Video Adapter\n" ); + if(slot < MCA_MAX_SLOT_NR) { + len += sprintf(buf+len, "Slot: %d\n", slot+1); + } else if(slot == MCA_INTEGSCSI) { + len += sprintf(buf+len, "Integrated SCSI Adapter\n"); + } else if(slot == MCA_INTEGVIDEO) { + len += sprintf(buf+len, "Integrated Video Adapter\n"); } - if( mca_info->slot[slot].name[0] ) { + if(mca_info->slot[slot].name[0]) { - /* drivers might register a name without /proc handler... */ + /* Drivers might register a name without /proc handler... */ - len += sprintf( buf+len, "Adapter Name: %s\n", - mca_info->slot[slot].name ); + len += sprintf(buf+len, "Adapter Name: %s\n", + mca_info->slot[slot].name); } else { - len += sprintf( buf+len, "Adapter Name: Unknown\n" ); + len += sprintf(buf+len, "Adapter Name: Unknown\n"); } - len += sprintf( buf+len, "Id: %02x%02x\n", - mca_info->slot[slot].pos[1], mca_info->slot[slot].pos[0] ); - len += sprintf( buf+len, "Enabled: %s\nPOS: ", - mca_isenabled(slot) ? "Yes" : "No" ); - len += sprintf( buf+len, "Driver Installed: %s\n", - mca_is_adapter_used(slot) ? "Yes" : "No" ); - for (i=0; i<8; i++) { + len += sprintf(buf+len, "Id: %02x%02x\n", + mca_info->slot[slot].pos[1], mca_info->slot[slot].pos[0]); + len += sprintf(buf+len, "Enabled: %s\nPOS: ", + mca_isenabled(slot) ? "Yes" : "No"); + for(i=0; i<8; i++) { len += sprintf(buf+len, "%02x ", mca_info->slot[slot].pos[i]); } + len += sprintf(buf+len, "\nDriver Installed: %s", + mca_is_adapter_used(slot) ? "Yes" : "No"); buf[len++] = '\n'; buf[len] = 0; return len; } /* mca_default_procfn() */ -static int get_mca_machine_info( char* buf ) +static int get_mca_machine_info(char* buf) { int len = 0; - len += sprintf( buf+len, "Model Id: 0x%x\n", machine_id ); - len += sprintf( buf+len, "Submodel Id: 0x%x\n", machine_submodel_id ); - len += sprintf( buf+len, "BIOS Revision: 0x%x\n", BIOS_revision ); + len += sprintf(buf+len, "Model Id: 0x%x\n", machine_id); + len += sprintf(buf+len, "Submodel Id: 0x%x\n", machine_submodel_id); + len += sprintf(buf+len, "BIOS Revision: 0x%x\n", BIOS_revision); return len; } -static int mca_fill( char* page, int pid, int type, char** start, +static int mca_fill(char* page, int pid, int type, char** start, loff_t *offset, int length) { int len = 0; int slot = 0; - switch( type ) { + switch(type) { case PROC_MCA_REGISTERS: - return get_mca_info( page ); + return get_mca_info(page); case PROC_MCA_MACHINE: - return get_mca_machine_info( page ); + return get_mca_machine_info(page); case PROC_MCA_VIDEO: slot = MCA_INTEGVIDEO; break; @@ -787,24 +807,24 @@ static int mca_fill( char* page, int pid, int type, char** start, slot = MCA_INTEGSCSI; break; default: - if( type < PROC_MCA_SLOT || type >= PROC_MCA_LAST ) { + if(type < PROC_MCA_SLOT || type >= PROC_MCA_LAST) { return -EBADF; } slot = type - PROC_MCA_SLOT; break; } - /* if we made it here, we better have a valid slot */ + /* If we made it here, we better have a valid slot */ - /* get the standard info */ + /* Get the standard info */ - len = mca_default_procfn( page, slot ); + len = mca_default_procfn(page, slot); - /* do any device-specific processing, if there is any */ + /* Do any device-specific processing, if there is any */ - if( mca_info->slot[slot].procfn ) { - len += mca_info->slot[slot].procfn( page+len, slot, - mca_info->slot[slot].dev ); + if(mca_info->slot[slot].procfn) { + len += mca_info->slot[slot].procfn(page+len, slot, + mca_info->slot[slot].dev); } return len; @@ -814,7 +834,7 @@ static int mca_fill( char* page, int pid, int type, char** start, #define PROC_BLOCK_SIZE (3*1024) -static ssize_t proc_mca_read( struct file* file, +static ssize_t proc_mca_read(struct file* file, char* buf, size_t count, loff_t *ppos) { unsigned long page; @@ -825,11 +845,11 @@ static ssize_t proc_mca_read( struct file* file, struct proc_dir_entry *dp; struct inode *inode = file->f_dentry->d_inode; - if (count < 0) + if(count < 0) return -EINVAL; - if (count > PROC_BLOCK_SIZE) + if(count > PROC_BLOCK_SIZE) count = PROC_BLOCK_SIZE; - if (!(page = __get_free_page(GFP_KERNEL))) + if(!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; type = inode->i_ino; pid = type >> 16; @@ -837,12 +857,12 @@ static ssize_t proc_mca_read( struct file* file, start = 0; dp = (struct proc_dir_entry *) inode->u.generic_ip; length = mca_fill((char *) page, pid, type, - &start, ppos, count); - if (length < 0) { + &start, ppos, count); + if(length < 0) { free_page(page); return length; } - if (start != 0) { + if(start != 0) { /* We have had block-adjusting processing! */ copy_to_user(buf, start, length); @@ -851,11 +871,11 @@ static ssize_t proc_mca_read( struct file* file, } else { /* Static 4kB (or whatever) block capacity */ - if (*ppos >= length) { + if(*ppos >= length) { free_page(page); return 0; } - if (count + *ppos > length) + if(count + *ppos > length) count = length - *ppos; end = count + *ppos; copy_to_user(buf, (char *) page + *ppos, count); diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 16c767b4a..0d71d8bb5 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -132,6 +132,70 @@ Fixed harmless compiler warning in include/asm-i386/mtrr.h Fixed version numbering and history for v1.23 -> v1.24. v1.26 + 19990118 Richard Gooch <rgooch@atnf.csiro.au> + PLACEHOLDER. + v1.27 + 19990123 Richard Gooch <rgooch@atnf.csiro.au> + Changed locking to spin with reschedule. + Made use of new <smp_call_function>. + v1.28 + 19990201 Zoltan Boszormenyi <zboszor@mol.hu> + Extended the driver to be able to use Cyrix style ARRs. + 19990204 Richard Gooch <rgooch@atnf.csiro.au> + Restructured Cyrix support. + v1.29 + 19990204 Zoltan Boszormenyi <zboszor@mol.hu> + Refined ARR support: enable MAPEN in set_mtrr_prepare() + and disable MAPEN in set_mtrr_done(). + 19990205 Richard Gooch <rgooch@atnf.csiro.au> + Minor cleanups. + v1.30 + 19990208 Zoltan Boszormenyi <zboszor@mol.hu> + Protect plain 6x86s (and other processors without the + Page Global Enable feature) against accessing CR4 in + set_mtrr_prepare() and set_mtrr_done(). + 19990210 Richard Gooch <rgooch@atnf.csiro.au> + Turned <set_mtrr_up> and <get_mtrr> into function pointers. + v1.31 + 19990212 Zoltan Boszormenyi <zboszor@mol.hu> + Major rewrite of cyrix_arr_init(): do not touch ARRs, + leave them as the BIOS have set them up. + Enable usage of all 8 ARRs. + Avoid multiplications by 3 everywhere and other + code clean ups/speed ups. + 19990213 Zoltan Boszormenyi <zboszor@mol.hu> + Set up other Cyrix processors identical to the boot cpu. + Since Cyrix don't support Intel APIC, this is l'art pour l'art. + Weigh ARRs by size: + If size <= 32M is given, set up ARR# we were given. + If size > 32M is given, set up ARR7 only if it is free, + fail otherwise. + 19990214 Zoltan Boszormenyi <zboszor@mol.hu> + Also check for size >= 256K if we are to set up ARR7, + mtrr_add() returns the value it gets from set_mtrr() + 19990218 Zoltan Boszormenyi <zboszor@mol.hu> + Remove Cyrix "coma bug" workaround from here. + Moved to linux/arch/i386/kernel/setup.c and + linux/include/asm-i386/bugs.h + 19990228 Richard Gooch <rgooch@atnf.csiro.au> + Added #ifdef CONFIG_DEVFS_FS + Added MTRRIOC_KILL_ENTRY ioctl(2) + Trap for counter underflow in <mtrr_file_del>. + Trap for 4 MiB aligned regions for PPro, stepping <= 7. + 19990301 Richard Gooch <rgooch@atnf.csiro.au> + Created <get_free_region> hook. + 19990305 Richard Gooch <rgooch@atnf.csiro.au> + Temporarily disable AMD support now MTRR capability flag is set. + v1.32 + 19990308 Zoltan Boszormenyi <zboszor@mol.hu> + Adjust my changes (19990212-19990218) to Richard Gooch's + latest changes. (19990228-19990305) + v1.33 + 19990309 Richard Gooch <rgooch@atnf.csiro.au> + Fixed typo in <printk> message. + 19990310 Richard Gooch <rgooch@atnf.csiro.au> + Support K6-II/III based on Alan Cox's <alan@redhat.com> patches. + v1.34 */ #include <linux/types.h> #include <linux/errno.h> @@ -163,11 +227,12 @@ #include <asm/segment.h> #include <asm/bitops.h> #include <asm/atomic.h> +#include <asm/msr.h> #include <asm/hardirq.h> #include "irq.h" -#define MTRR_VERSION "1.26 (19981001)" +#define MTRR_VERSION "1.34 (19990310)" #define TRUE 1 #define FALSE 0 @@ -197,7 +262,7 @@ # define MTRR_CHANGE_MASK_DEFTYPE 0x04 #endif -/* In the processor's MTRR interface, the MTRR type is always held in +/* In the Intel processor's MTRR interface, the MTRR type is always held in an 8 bit field: */ typedef u8 mtrr_type; @@ -207,9 +272,12 @@ typedef u8 mtrr_type; #ifdef __SMP__ # define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type) #else -# define set_mtrr(reg,base,size,type) set_mtrr_up (reg, base, size, type,TRUE) +# define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \ + TRUE) #endif +#define spin_lock_reschedule(lock) while (!spin_trylock(lock)) schedule (); + #ifndef CONFIG_PROC_FS # define compute_ascii() while (0) #endif @@ -233,49 +301,30 @@ struct set_mtrr_context unsigned long deftype_lo; unsigned long deftype_hi; unsigned long cr4val; + unsigned long ccr3; }; -/* - * Access to machine-specific registers (available on 586 and better only) - * Note: the rd* operations modify the parameters directly (without using - * pointer indirection), this allows gcc to optimize better - */ -#define rdmsr(msr,val1,val2) \ - __asm__ __volatile__("rdmsr" \ - : "=a" (val1), "=d" (val2) \ - : "c" (msr)) - -#define wrmsr(msr,val1,val2) \ - __asm__ __volatile__("wrmsr" \ - : /* no outputs */ \ - : "c" (msr), "a" (val1), "d" (val2)) -#define rdtsc(low,high) \ - __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) - -#define rdpmc(counter,low,high) \ - __asm__ __volatile__("rdpmc" \ - : "=a" (low), "=d" (high) \ - : "c" (counter)) - - -/* Put the processor into a state where MTRRs can be safely set. */ -static void set_mtrr_prepare(struct set_mtrr_context *ctxt) +/* Put the processor into a state where MTRRs can be safely set */ +static void set_mtrr_prepare (struct set_mtrr_context *ctxt) { unsigned long tmp; - /* disable interrupts locally */ + /* Disable interrupts locally */ __save_flags (ctxt->flags); __cli (); - /* save value of CR4 and clear Page Global Enable (bit 7) */ - asm volatile ("movl %%cr4, %0\n\t" - "movl %0, %1\n\t" - "andb $0x7f, %b1\n\t" - "movl %1, %%cr4\n\t" - : "=r" (ctxt->cr4val), "=q" (tmp) : : "memory"); + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return; + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) + asm volatile ("movl %%cr4, %0\n\t" + "movl %0, %1\n\t" + "andb $0x7f, %b1\n\t" + "movl %1, %%cr4\n\t" + : "=r" (ctxt->cr4val), "=q" (tmp) : : "memory"); - /* disable and flush caches. Note that wbinvd flushes the TLBs as - a side-effect. */ + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ asm volatile ("movl %%cr0, %0\n\t" "orl $0x40000000, %0\n\t" "wbinvd\n\t" @@ -283,64 +332,108 @@ static void set_mtrr_prepare(struct set_mtrr_context *ctxt) "wbinvd\n\t" : "=r" (tmp) : : "memory"); - /* disable MTRRs, and set the default type to uncached. */ - rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); - wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + /* Disable MTRRs, and set the default type to uncached */ + rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); + break; + case X86_VENDOR_CYRIX: + tmp = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (tmp & 0x0f) | 0x10); + ctxt->ccr3 = tmp; + break; + } } /* End Function set_mtrr_prepare */ - -/* Restore the processor after a set_mtrr_prepare */ -static void set_mtrr_done(struct set_mtrr_context *ctxt) +/* Restore the processor after a set_mtrr_prepare */ +static void set_mtrr_done (struct set_mtrr_context *ctxt) { unsigned long tmp; - /* flush caches and TLBs */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + { + __restore_flags (ctxt->flags); + return; + } + + /* Flush caches and TLBs */ asm volatile ("wbinvd" : : : "memory" ); - /* restore MTRRdefType */ - wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + /* Restore MTRRdefType */ + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + break; + case X86_VENDOR_CYRIX: + setCx86 (CX86_CCR3, ctxt->ccr3); + break; + } - /* enable caches */ + /* Enable caches */ asm volatile ("movl %%cr0, %0\n\t" "andl $0xbfffffff, %0\n\t" "movl %0, %%cr0\n\t" : "=r" (tmp) : : "memory"); - /* restore value of CR4 */ - asm volatile ("movl %0, %%cr4" - : : "r" (ctxt->cr4val) : "memory"); + /* Restore value of CR4 */ + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) + asm volatile ("movl %0, %%cr4" + : : "r" (ctxt->cr4val) : "memory"); - /* re-enable interrupts locally (if enabled previously) */ + /* Re-enable interrupts locally (if enabled previously) */ __restore_flags (ctxt->flags); } /* End Function set_mtrr_done */ - -/* this function returns the number of variable MTRRs */ +/* This function returns the number of variable MTRRs */ static unsigned int get_num_var_ranges (void) { unsigned long config, dummy; - rdmsr(MTRRcap_MSR, config, dummy); - return (config & 0xff); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & 0xff); + /*break;*/ + case X86_VENDOR_CYRIX: + /* Cyrix have 8 ARRs */ + return 8; + /*break;*/ + case X86_VENDOR_AMD: + return 2; + /*break;*/ + } + return 0; } /* End Function get_num_var_ranges */ - -/* non-zero if we have the write-combining memory type. */ +/* Returns non-zero if we have the write-combining memory type */ static int have_wrcomb (void) { unsigned long config, dummy; - rdmsr(MTRRcap_MSR, config, dummy); - return (config & (1<<10)); -} - + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & (1<<10)); + /*break;*/ + case X86_VENDOR_CYRIX: + case X86_VENDOR_AMD: + return 1; + /*break;*/ + } + return 0; +} /* End Function have_wrcomb */ -static void get_mtrr (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type) +static void intel_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) { unsigned long dummy, mask_lo, base_lo; - rdmsr(MTRRphysMask_MSR(reg), mask_lo, dummy); + rdmsr (MTRRphysMask_MSR(reg), mask_lo, dummy); if ((mask_lo & 0x800) == 0) { /* Invalid (i.e. free) range. */ *base = 0; @@ -364,11 +457,104 @@ static void get_mtrr (unsigned int reg, unsigned long *base, *base = (base_lo & 0xfffff000UL); *type = (base_lo & 0xff); -} /* End Function get_mtrr */ +} /* End Function intel_get_mtrr */ + +static void cyrix_get_arr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long flags; + unsigned char arr, ccr3, rcr, shift; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* Save flags and disable interrupts */ + __save_flags (flags); __cli (); + ccr3 = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ((unsigned char *) base)[3] = getCx86 (arr); + ((unsigned char *) base)[2] = getCx86 (arr+1); + ((unsigned char *) base)[1] = getCx86 (arr+2); + rcr = getCx86(CX86_RCR_BASE + reg); + setCx86 (CX86_CCR3, ccr3); /* disable MAPEN */ + + /* Enable interrupts if it was enabled previously */ + __restore_flags (flags); + + shift = ((unsigned char *) base)[1] & 0x0f; + *base &= 0xfffff000UL; + + /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 + * Note: shift==0xf means 4G, this is unsupported. + */ + if (shift) + *size = (reg < 7 ? 0x800UL : 0x20000UL) << shift; + else + *size = 0; + + /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ + if (reg < 7) { + switch (rcr) { + case 1: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRBACK; break; + case 9: *type = MTRR_TYPE_WRCOMB; break; + case 24: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } else { + switch (rcr) { + case 0: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRCOMB; break; + case 9: *type = MTRR_TYPE_WRBACK; break; + case 25: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } +} /* End Function cyrix_get_arr */ -static void set_mtrr_up (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type, int do_safe) +static void amd_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long low, high; + + rdmsr (0xC0000085, low, high); + /* Upper dword is region 1, lower is region 0 */ + if (reg == 1) low = high; + /* The base masks off on the right alignment */ + *base = low & 0xFFFE0000; + *type = 0; + if (low & 1) *type = MTRR_TYPE_UNCACHABLE; + if (low & 2) *type = MTRR_TYPE_WRCOMB; + if ( !(low & 3) ) + { + *size = 0; + return; + } + /* + * This needs a little explaining. The size is stored as an + * inverted mask of bits of 128K granularity 15 bits long offset + * 2 bits + * + * So to get a size we do invert the mask and add 1 to the lowest + * mask bit (4 as its 2 bits in). This gives us a size we then shift + * to turn into 128K blocks + * + * eg 111 1111 1111 1100 is 512K + * + * invert 000 0000 0000 0011 + * +1 000 0000 0000 0100 + * *128K ... + */ + low = (~low) & 0x1FFFC; + *size = (low + 4) << 15; + return; +} /* End Function amd_get_mtrr */ + +static void (*get_mtrr) (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) = NULL; + +static void intel_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) /* [SUMMARY] Set variable MTRR register on the local CPU. <reg> The register to set. <base> The base address of the region. @@ -376,6 +562,7 @@ static void set_mtrr_up (unsigned int reg, unsigned long base, <type> The type of the region. <do_safe> If TRUE, do the change safely. If FALSE, safety measures should be done externally. + [RETURNS] Nothing. */ { struct set_mtrr_context ctxt; @@ -393,8 +580,92 @@ static void set_mtrr_up (unsigned int reg, unsigned long base, wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0); } if (do_safe) set_mtrr_done (&ctxt); -} /* End Function set_mtrr_up */ +} /* End Function intel_set_mtrr_up */ + +static void cyrix_set_arr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +{ + struct set_mtrr_context ctxt; + unsigned char arr, arr_type, arr_size; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ + size >>= (reg < 7 ? 12 : 18); + size &= 0x7fff; /* make sure arr_size <= 14 */ + for(arr_size = 0; size; arr_size++, size >>= 1); + + if (reg<7) { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; + case MTRR_TYPE_WRCOMB: arr_type = 9; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; + default: arr_type = 8; break; + } + } else { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; + case MTRR_TYPE_WRCOMB: arr_type = 8; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; + default: arr_type = 9; break; + } + } + + if (do_safe) set_mtrr_prepare (&ctxt); + setCx86(arr, ((unsigned char *) &base)[3]); + setCx86(arr+1, ((unsigned char *) &base)[2]); + setCx86(arr+2, (((unsigned char *) &base)[1]) | arr_size); + setCx86(CX86_RCR_BASE + reg, arr_type); + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function cyrix_set_arr_up */ + +static void amd_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + <reg> The register to set. + <base> The base address of the region. + <size> The size of the region. If this is 0 the region is disabled. + <type> The type of the region. + <do_safe> If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + u32 low, high; + struct set_mtrr_context ctxt; + + if (do_safe) set_mtrr_prepare (&ctxt); + /* + * Low is MTRR0 , High MTRR 1 + */ + rdmsr (0xC0000085, low, high); + /* + * Blank to disable + */ + if (size == 0) + *(reg ? &high : &low) = 0; + else + /* Set the register to the base (already shifted for us), the + type (off by one) and an inverted bitmask of the size + + The size is the only odd bit. We are fed say 512K + We invert this and we get 111 1111 1111 1011 but + if you subtract one and invert you get the desired + 111 1111 1111 1100 mask + */ + *(reg ? &high : &low)=(((~(size-1))>>15)&0x0001FFFC)|base|(type+1); + /* + * The writeback rule is quite specific. See the manual. Its + * disable local interrupts, write back the cache, set the mtrr + */ + __asm__ __volatile__ ("wbinvd" : : : "memory"); + wrmsr (0xC0000085, low, high); + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function amd_set_mtrr_up */ +static void (*set_mtrr_up) (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, + int do_safe) = NULL; #ifdef __SMP__ @@ -407,7 +678,7 @@ struct mtrr_var_range }; -/* Get the MSR pair relating to a var range. */ +/* Get the MSR pair relating to a var range */ __initfunc(static void get_mtrr_var_range (unsigned int index, struct mtrr_var_range *vr)) { @@ -416,8 +687,8 @@ __initfunc(static void get_mtrr_var_range (unsigned int index, } /* End Function get_mtrr_var_range */ -/* Set the MSR pair relating to a var range. Returns TRUE if - changes are made. */ +/* Set the MSR pair relating to a var range. Returns TRUE if + changes are made */ __initfunc(static int set_mtrr_var_range_testing (unsigned int index, struct mtrr_var_range *vr)) { @@ -441,8 +712,7 @@ __initfunc(static int set_mtrr_var_range_testing (unsigned int index, } return changed; -} - +} /* End Function set_mtrr_var_range_testing */ __initfunc(static void get_fixed_ranges(mtrr_type *frs)) { @@ -456,8 +726,7 @@ __initfunc(static void get_fixed_ranges(mtrr_type *frs)) for (i = 0; i < 8; i++) rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); -} - +} /* End Function get_fixed_ranges */ __initfunc(static int set_fixed_ranges_testing(mtrr_type *frs)) { @@ -487,10 +756,8 @@ __initfunc(static int set_fixed_ranges_testing(mtrr_type *frs)) changed = TRUE; } } - return changed; -} - +} /* End Function set_fixed_ranges_testing */ struct mtrr_state { @@ -502,7 +769,7 @@ struct mtrr_state }; -/* Grab all of the MTRR state for this CPU into *state. */ +/* Grab all of the MTRR state for this CPU into *state */ __initfunc(static void get_mtrr_state(struct mtrr_state *state)) { unsigned int nvrs, i; @@ -511,22 +778,22 @@ __initfunc(static void get_mtrr_state(struct mtrr_state *state)) nvrs = state->num_var_ranges = get_num_var_ranges(); vrs = state->var_ranges - = kmalloc(nvrs * sizeof(struct mtrr_var_range), GFP_KERNEL); + = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); if (vrs == NULL) nvrs = state->num_var_ranges = 0; for (i = 0; i < nvrs; i++) - get_mtrr_var_range(i, &vrs[i]); + get_mtrr_var_range (i, &vrs[i]); - get_fixed_ranges(state->fixed_ranges); + get_fixed_ranges (state->fixed_ranges); - rdmsr(MTRRdefType_MSR, lo, dummy); + rdmsr (MTRRdefType_MSR, lo, dummy); state->def_type = (lo & 0xff); state->enabled = (lo & 0xc00) >> 10; } /* End Function get_mtrr_state */ -/* Free resources associated with a struct mtrr_state */ +/* Free resources associated with a struct mtrr_state */ __initfunc(static void finalize_mtrr_state(struct mtrr_state *state)) { if (state->var_ranges) kfree (state->var_ranges); @@ -546,14 +813,14 @@ __initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, unsigned long change_mask = 0; for (i = 0; i < state->num_var_ranges; i++) - if (set_mtrr_var_range_testing(i, &state->var_ranges[i])) + if ( set_mtrr_var_range_testing (i, &state->var_ranges[i]) ) change_mask |= MTRR_CHANGE_MASK_VARIABLE; - if (set_fixed_ranges_testing(state->fixed_ranges)) + if ( set_fixed_ranges_testing(state->fixed_ranges) ) change_mask |= MTRR_CHANGE_MASK_FIXED; - /* set_mtrr_restore restores the old value of MTRRdefType, - so to set it we fiddle with the saved value. */ + /* Set_mtrr_restore restores the old value of MTRRdefType, + so to set it we fiddle with the saved value */ if ((ctxt->deftype_lo & 0xff) != state->def_type || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled) { @@ -566,76 +833,63 @@ __initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, static atomic_t undone_count; -static void (*handler_func) (struct set_mtrr_context *ctxt, void *info); -static void *handler_info; static volatile int wait_barrier_execute = FALSE; static volatile int wait_barrier_cache_enable = FALSE; -static void sync_handler (void) +struct set_mtrr_data +{ + unsigned long smp_base; + unsigned long smp_size; + unsigned int smp_reg; + mtrr_type smp_type; +}; + +static void ipi_handler (void *info) /* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. [RETURNS] Nothing. */ { + struct set_mtrr_data *data = info; struct set_mtrr_context ctxt; set_mtrr_prepare (&ctxt); - /* Notify master CPU that I'm at the barrier and then wait */ + /* Notify master that I've flushed and disabled my cache */ atomic_dec (&undone_count); while (wait_barrier_execute) barrier (); /* The master has cleared me to execute */ - (*handler_func) (&ctxt, handler_info); + (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, + data->smp_type, FALSE); /* Notify master CPU that I've executed the function */ atomic_dec (&undone_count); /* Wait for master to clear me to enable cache and return */ while (wait_barrier_cache_enable) barrier (); set_mtrr_done (&ctxt); -} /* End Function sync_handler */ - -static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt, - void *info), - void *info, int local) -/* [SUMMARY] Execute a function on all CPUs, with caches flushed and disabled. - [PURPOSE] This function will synchronise all CPUs, flush and disable caches - on all CPUs, then call a specified function. When the specified function - finishes on all CPUs, caches are enabled on all CPUs. - <handler> The function to execute. - <info> An arbitrary information pointer which is passed to <<handler>>. - <local> If TRUE <<handler>> is executed locally. - [RETURNS] Nothing. -*/ +} /* End Function ipi_handler */ + +static void set_mtrr_smp (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) { - unsigned long timeout; + struct set_mtrr_data data; struct set_mtrr_context ctxt; - mtrr_hook = sync_handler; - handler_func = handler; - handler_info = info; + data.smp_reg = reg; + data.smp_base = base; + data.smp_size = size; + data.smp_type = type; wait_barrier_execute = TRUE; wait_barrier_cache_enable = TRUE; - /* Send a message to all other CPUs and wait for them to enter the - barrier */ atomic_set (&undone_count, smp_num_cpus - 1); - smp_send_mtrr(); - /* Wait for it to be done */ - timeout = jiffies + JIFFIE_TIMEOUT; - while ( (atomic_read (&undone_count) > 0) && - time_before(jiffies, timeout) ) - barrier (); - if (atomic_read (&undone_count) > 0) - { + /* Flush and disable the local CPU's cache and start the ball rolling on + other CPUs */ + set_mtrr_prepare (&ctxt); + if (smp_call_function (ipi_handler, &data, 1, 0) != 0) panic ("mtrr: timed out waiting for other CPUs\n"); - } - mtrr_hook = NULL; - /* All other CPUs should be waiting for the barrier, with their caches - already flushed and disabled. Prepare for function completion - notification */ + /* Wait for all other CPUs to flush and disable their caches */ + while (atomic_read (&undone_count) > 0) barrier (); + /* Set up for completion wait and then release other CPUs to change MTRRs*/ atomic_set (&undone_count, smp_num_cpus - 1); - /* Flush and disable the local CPU's cache and release the barier, which - should cause the other CPUs to execute the function. Also execute it - locally if required */ - set_mtrr_prepare (&ctxt); wait_barrier_execute = FALSE; - if (local) (*handler) (&ctxt, info); + (*set_mtrr_up) (reg, base, size, type, FALSE); /* Now wait for other CPUs to complete the function */ while (atomic_read (&undone_count) > 0) barrier (); /* Now all CPUs should have finished the function. Release the barrier to @@ -643,41 +897,10 @@ static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt, then enable the local cache and return */ wait_barrier_cache_enable = FALSE; set_mtrr_done (&ctxt); - handler_func = NULL; - handler_info = NULL; -} /* End Function do_all_cpus */ - - -struct set_mtrr_data -{ - unsigned long smp_base; - unsigned long smp_size; - unsigned int smp_reg; - mtrr_type smp_type; -}; - -static void set_mtrr_handler (struct set_mtrr_context *ctxt, void *info) -{ - struct set_mtrr_data *data = info; - - set_mtrr_up (data->smp_reg, data->smp_base, data->smp_size, data->smp_type, - FALSE); -} /* End Function set_mtrr_handler */ - -static void set_mtrr_smp (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type) -{ - struct set_mtrr_data data; - - data.smp_reg = reg; - data.smp_base = base; - data.smp_size = size; - data.smp_type = type; - do_all_cpus (set_mtrr_handler, &data, TRUE); } /* End Function set_mtrr_smp */ -/* Some BIOS's are fucked and don't set all MTRRs the same! */ +/* Some BIOS's are fucked and don't set all MTRRs the same! */ __initfunc(static void mtrr_state_warn (unsigned long mask)) { if (!mask) return; @@ -720,6 +943,58 @@ static void init_table (void) #endif } /* End Function init_table */ +static int generic_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + <base> The starting (base) address of the region. + <size> The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (lsize < 1) return i; + } + return -ENOSPC; +} /* End Function generic_get_free_region */ + +static int cyrix_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free ARR. + <base> The starting (base) address of the region. + <size> The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i; + mtrr_type ltype; + unsigned long lbase, lsize; + + /* If we are to set up a region >32M then look at ARR7 immediately */ + if (size > 0x2000000UL) { + cyrix_get_arr (7, &lbase, &lsize, <ype); + if (lsize < 1) return 7; + /* else try ARR0-ARR6 first */ + } else { + for (i = 0; i < 7; i++) + { + cyrix_get_arr (i, &lbase, &lsize, <ype); + if (lsize < 1) return i; + } + /* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ + cyrix_get_arr (i, &lbase, &lsize, <ype); + if ((lsize < 1) && (size >= 0x40000)) return i; + } + return -ENOSPC; +} /* End Function cyrix_get_free_region */ + +static int (*get_free_region) (unsigned long base, + unsigned long size) = generic_get_free_region; + int mtrr_add (unsigned long base, unsigned long size, unsigned int type, char increment) /* [SUMMARY] Add an MTRR entry. @@ -738,28 +1013,57 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, unsigned long lbase, lsize, last; if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; - if ( (base & 0xfff) || (size & 0xfff) ) + switch (boot_cpu_data.x86_vendor) { - printk ("mtrr: size and base must be multiples of 4kB\n"); - printk ("mtrr: size: %lx base: %lx\n", size, base); - return -EINVAL; - } - if (base + size < 0x100000) - { - printk ("mtrr: cannot set region below 1 MByte (0x%lx,0x%lx)\n", - base, size); - return -EINVAL; - } - /* Check upper bits of base and last are equal and lower bits are 0 for - base and 1 for last */ - last = base + size - 1; - for (lbase = base; !(lbase & 1) && (last & 1); - lbase = lbase >> 1, last = last >> 1); - if (lbase != last) - { - printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n", - base, size); + case X86_VENDOR_INTEL: + /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ + if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) && + (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) ) + { + printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base); + return -EINVAL; + } + /* Fall through */ + case X86_VENDOR_CYRIX: + if ( (base & 0xfff) || (size & 0xfff) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: %lx base: %lx\n", size, base); + return -EINVAL; + } + if (base + size < 0x100000) + { + printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n", + base, size); + return -EINVAL; + } + /* Check upper bits of base and last are equal and lower bits are 0 + for base and 1 for last */ + last = base + size - 1; + for (lbase = base; !(lbase & 1) && (last & 1); + lbase = lbase >> 1, last = last >> 1); + if (lbase != last) + { + printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n", + base, size); + return -EINVAL; + } + break; + case X86_VENDOR_AMD: + /* Apply the K6 block alignment and size rules + In order + o Uncached or gathering only + o 128K or bigger block + o Power of 2 block + o base suitably aligned to the power + */ + if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) || + (size & ~(size-1))-size || (base & (size-1))) + return -EINVAL; + break; + default: return -EINVAL; + /*break;*/ } if (type >= MTRR_NUM_TYPES) { @@ -775,10 +1079,10 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, increment = increment ? 1 : 0; max = get_num_var_ranges (); /* Search for existing MTRR */ - spin_lock (&main_lock); + spin_lock_reschedule (&main_lock); for (i = 0; i < max; ++i) { - get_mtrr (i, &lbase, &lsize, <ype); + (*get_mtrr) (i, &lbase, &lsize, <ype); if (base >= lbase + lsize) continue; if ( (base < lbase) && (base + size <= lbase) ) continue; /* At this point we know there is some kind of overlap/enclosure */ @@ -804,19 +1108,18 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, return i; } /* Search for an empty MTRR */ - for (i = 0; i < max; ++i) + i = (*get_free_region) (base, size); + if (i < 0) { - get_mtrr (i, &lbase, &lsize, <ype); - if (lsize > 0) continue; - set_mtrr (i, base, size, type); - usage_table[i] = 1; - compute_ascii (); spin_unlock (&main_lock); + printk ("mtrr: no more MTRRs available\n"); return i; } + set_mtrr (i, base, size, type); + usage_table[i] = 1; + compute_ascii (); spin_unlock (&main_lock); - printk ("mtrr: no more MTRRs available\n"); - return -ENOSPC; + return i; } /* End Function mtrr_add */ int mtrr_del (int reg, unsigned long base, unsigned long size) @@ -836,13 +1139,13 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; max = get_num_var_ranges (); - spin_lock (&main_lock); + spin_lock_reschedule (&main_lock); if (reg < 0) { /* Search for existing MTRR */ for (i = 0; i < max; ++i) { - get_mtrr (i, &lbase, &lsize, <ype); + (*get_mtrr) (i, &lbase, &lsize, <ype); if ( (lbase == base) && (lsize == size) ) { reg = i; @@ -862,7 +1165,7 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) printk ("mtrr: register: %d too big\n", reg); return -EINVAL; } - get_mtrr (reg, &lbase, &lsize, <ype); + (*get_mtrr) (reg, &lbase, &lsize, <ype); if (lsize < 1) { spin_unlock (&main_lock); @@ -913,7 +1216,9 @@ static int mtrr_file_del (unsigned long base, unsigned long size, reg = mtrr_del (-1, base, size); if (reg < 0) return reg; - if (fcount != NULL) --fcount[reg]; + if (fcount == NULL) return reg; + if (fcount[reg] < 1) return -EINVAL; + --fcount[reg]; return reg; } /* End Function mtrr_file_del */ @@ -1019,11 +1324,18 @@ static int mtrr_ioctl (struct inode *inode, struct file *file, err = mtrr_file_del (sentry.base, sentry.size, file); if (err < 0) return err; break; + case MTRRIOC_KILL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_del (-1, sentry.base, sentry.size); + if (err < 0) return err; + break; case MTRRIOC_GET_ENTRY: if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) return -EFAULT; if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; - get_mtrr (gentry.regnum, &gentry.base, &gentry.size, &type); + (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); gentry.type = type; if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) return -EFAULT; @@ -1115,7 +1427,7 @@ static void compute_ascii (void) max = get_num_var_ranges (); for (i = 0; i < max; i++) { - get_mtrr (i, &base, &size, &type); + (*get_mtrr) (i, &base, &size, &type); if (size < 1) usage_table[i] = 0; else { @@ -1148,23 +1460,165 @@ EXPORT_SYMBOL(mtrr_del); #ifdef __SMP__ +typedef struct { + unsigned long base; + unsigned long size; + mtrr_type type; +} arr_state_t; + +arr_state_t arr_state[8] __initdata = { + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} +}; + +unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; + +__initfunc(static void cyrix_arr_init_secondary(void)) +{ + struct set_mtrr_context ctxt; + int i; + + set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + + /* the CCRs are not contiguous */ + for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]); + for( ; i<7; i++) setCx86(CX86_CCR4 + i, ccr_state[i]); + for(i=0; i<8; i++) + cyrix_set_arr_up(i, + arr_state[i].base, arr_state[i].size, arr_state[i].type, FALSE); + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ +} /* End Function cyrix_arr_init_secondary */ + +#endif + +/* + * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection + * with the SMM (System Management Mode) mode. So we need the following: + * Check whether SMI_LOCK (CCR3 bit 0) is set + * if it is set, write a warning message: ARR3 cannot be changed! + * (it cannot be changed until the next processor reset) + * if it is reset, then we can change it, set all the needed bits: + * - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) + * - disable access to SMM memory (CCR1 bit 2 reset) + * - disable SMM mode (CCR1 bit 1 reset) + * - disable write protection of ARR3 (CCR6 bit 1 reset) + * - (maybe) disable ARR3 + * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) + */ +__initfunc(static void cyrix_arr_init(void)) +{ + struct set_mtrr_context ctxt; + unsigned char ccr[7]; + int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; +#ifdef __SMP__ + int i; +#endif + + set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + + /* Save all CCRs locally */ + ccr[0] = getCx86 (CX86_CCR0); + ccr[1] = getCx86 (CX86_CCR1); + ccr[2] = getCx86 (CX86_CCR2); + ccr[3] = ctxt.ccr3; + ccr[4] = getCx86 (CX86_CCR4); + ccr[5] = getCx86 (CX86_CCR5); + ccr[6] = getCx86 (CX86_CCR6); + + if (ccr[3] & 1) + ccrc[3] = 1; + else { + /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and + * access to SMM memory through ARR3 (bit 7). + */ +/* + if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } + if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } + if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } +*/ + if (ccr[6] & 0x02) { + ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3. */ + setCx86 (CX86_CCR6, ccr[6]); + } + /* Disable ARR3. */ + /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ + } + /* If we changed CCR1 in memory, change it in the processor, too. */ + if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]); + + /* Enable ARR usage by the processor */ + if (!(ccr[5] & 0x20)) { + ccr[5] |= 0x20; ccrc[5] = 1; + setCx86 (CX86_CCR5, ccr[5]); + } + +#ifdef __SMP__ + for(i=0; i<7; i++) ccr_state[i] = ccr[i]; + for(i=0; i<8; i++) + cyrix_get_arr(i, + &arr_state[i].base, &arr_state[i].size, &arr_state[i].type); +#endif + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ + + if ( ccrc[5] ) printk ("mtrr: ARR usage was not enabled, enabled manually\n"); + if ( ccrc[3] ) printk ("mtrr: ARR3 cannot be changed\n"); +/* + if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n"); + if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n"); + if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n"); +*/ + if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n"); +} /* End Function cyrix_arr_init */ + +__initfunc(static void mtrr_setup (void)) +{ + printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + get_mtrr = intel_get_mtrr; + set_mtrr_up = intel_set_mtrr_up; + break; + case X86_VENDOR_CYRIX: + printk ("mtrr: Using Cyrix style ARRs\n"); + get_mtrr = cyrix_get_arr; + set_mtrr_up = cyrix_set_arr_up; + get_free_region = cyrix_get_free_region; + break; + case X86_VENDOR_AMD: + get_mtrr = amd_get_mtrr; + set_mtrr_up = amd_set_mtrr_up; + break; + } +} /* End Function mtrr_setup */ + +#ifdef __SMP__ + static volatile unsigned long smp_changes_mask __initdata = 0; static struct mtrr_state smp_mtrr_state __initdata = {0, 0}; __initfunc(void mtrr_init_boot_cpu (void)) { if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; - printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); - - get_mtrr_state (&smp_mtrr_state); + mtrr_setup (); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + get_mtrr_state (&smp_mtrr_state); + break; + case X86_VENDOR_CYRIX: + cyrix_arr_init (); + break; + } } /* End Function mtrr_init_boot_cpu */ -__initfunc(void mtrr_init_secondary_cpu (void)) +__initfunc(static void intel_mtrr_init_secondary_cpu (void)) { unsigned long mask, count; struct set_mtrr_context ctxt; - if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; /* Note that this is not ideal, since the cache is only flushed/disabled for this CPU while the MTRRs are changed, but changing this requires more invasive changes to the way the kernel boots */ @@ -1177,21 +1631,52 @@ __initfunc(void mtrr_init_secondary_cpu (void)) if (mask & 0x01) set_bit (count, &smp_changes_mask); mask >>= 1; } -} /* End Function mtrr_init_secondary_cpu */ +} /* End Function intel_mtrr_init_secondary_cpu */ +__initfunc(void mtrr_init_secondary_cpu (void)) +{ + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + intel_mtrr_init_secondary_cpu (); + break; + case X86_VENDOR_CYRIX: + /* This is _completely theoretical_! + * I assume here that one day Cyrix will support Intel APIC. + * In reality on non-Intel CPUs we won't even get to this routine. + * Hopefully no one will plug two Cyrix processors in a dual P5 board. + * :-) + */ + cyrix_arr_init_secondary (); + break; + default: + printk ("mtrr: SMP support incomplete for this vendor\n"); + break; + } +} /* End Function mtrr_init_secondary_cpu */ #endif /* __SMP__ */ __initfunc(int mtrr_init(void)) { if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0; -# ifndef __SMP__ - printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); -# endif - # ifdef __SMP__ - finalize_mtrr_state (&smp_mtrr_state); - mtrr_state_warn (smp_changes_mask); -# endif /* __SMP__ */ + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + finalize_mtrr_state (&smp_mtrr_state); + mtrr_state_warn (smp_changes_mask); + break; + } +# else /* __SMP__ */ + mtrr_setup (); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_CYRIX: + cyrix_arr_init (); + break; + } +# endif /* !__SMP__ */ # ifdef CONFIG_PROC_FS proc_register (&proc_root, &proc_root_mtrr); diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 00f7e0ba2..ad745f58a 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -111,6 +111,8 @@ static int cpu_idle(void *unused) /* endless idle loop with no priority at all */ current->priority = 0; current->counter = -100; + init_idle(); + for (;;) { if (work) start_idle = jiffies; @@ -139,6 +141,8 @@ int cpu_idle(void *unused) /* endless idle loop with no priority at all */ current->priority = 0; current->counter = -100; + init_idle(); + while(1) { if (current_cpu_data.hlt_works_ok && !hlt_counter && !current->need_resched) @@ -316,7 +320,7 @@ void machine_restart(char * __unused) /* Make sure the first page is mapped to the start of physical memory. It is normally not mapped, to trap kernel NULL pointer dereferences. */ - pg0[0] = 7; + pg0[0] = _PAGE_RW | _PAGE_PRESENT; /* * Use `swapper_pg_dir' as our page directory. We bother with diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index b0eca4345..9f5ce58f1 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -354,6 +354,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; struct user * dummy = NULL; + unsigned long flags; int i, ret; lock_kernel(); @@ -385,21 +386,22 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ if (child->flags & PF_PTRACED) goto out; child->flags |= PF_PTRACED; - if (child->p_pptr != current) { - unsigned long flags; - write_lock_irqsave(&tasklist_lock, flags); + write_lock_irqsave(&tasklist_lock, flags); + if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); + send_sig(SIGSTOP, child, 1); ret = 0; goto out; @@ -559,7 +561,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } case PTRACE_DETACH: { /* detach a process that was attached. */ - unsigned long flags; long tmp; ret = -EIO; diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index c3f34270a..af6df1065 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -5,6 +5,10 @@ * * Enhanced CPU type detection by Mike Jagdis, Patrick St. Jean * and Martin Mares, November 1997. + * + * Force Cyrix 6x86(MX) and M II processors to report MTRR capability + * and fix against Cyrix "coma bug" by + * Zoltan Boszormenyi <zboszor@mol.hu> February 1999. */ /* @@ -39,6 +43,7 @@ #include <asm/io.h> #include <asm/smp.h> #include <asm/cobalt.h> +#include <asm/msr.h> /* * Machine setup.. @@ -57,6 +62,7 @@ int MCA_bus = 0; unsigned int machine_id = 0; unsigned int machine_submodel_id = 0; unsigned int BIOS_revision = 0; +unsigned int mca_pentium_flag = 0; /* * Setup options @@ -244,11 +250,6 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long memory_start, memory_end; char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; - static unsigned char smptrap=0; - - if (smptrap) - return; - smptrap=1; #ifdef CONFIG_VISWS visws_get_board_type_and_rev(); @@ -381,7 +382,7 @@ __initfunc(void setup_arch(char **cmdline_p, } -__initfunc(static int amd_model(struct cpuinfo_x86 *c)) +__initfunc(static int get_model_name(struct cpuinfo_x86 *c)) { unsigned int n, dummy, *v; @@ -398,9 +399,87 @@ __initfunc(static int amd_model(struct cpuinfo_x86 *c)) cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); c->x86_model_id[48] = 0; + /* Set MTRR capability flag if appropriate */ + if(boot_cpu_data.x86 !=5) + return 1; + if((boot_cpu_data.x86_model == 9) || + ((boot_cpu_data.x86_model == 8) && + (boot_cpu_data.x86_mask >= 8))) + c->x86_capability |= X86_FEATURE_MTRR; + return 1; } +__initfunc(static int amd_model(struct cpuinfo_x86 *c)) +{ + u32 l, h; + unsigned long flags; + int mbytes = max_mapnr >> (20-PAGE_SHIFT); + + int r=get_model_name(c); + + /* + * Now do the cache operations. + */ + + switch(c->x86) + { + case 5: + if( c->x86_model < 6 ) + { + /* Anyone with a K5 want to fill this in */ + break; + } + + /* K6 with old style WHCR */ + if( c->x86_model < 8 || + (c->x86_model== 8 && c->x86_mask < 8)) + { + /* We can only write allocate on the low 508Mb */ + if(mbytes>508) + mbytes=508; + + rdmsr(0xC0000082, l, h); + if((l&0x0000FFFF)==0) + { + l=(1<<0)|(mbytes/4); + save_flags(flags); + __cli(); + __asm__ __volatile__ ("wbinvd": : :"memory"); + wrmsr(0xC0000082, l, h); + restore_flags(flags); + printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n", + mbytes); + + } + break; + } + if (c->x86_model == 8 || c->x86_model == 9) + { + /* The more serious chips .. */ + + if(mbytes>4092) + mbytes=4092; + rdmsr(0xC0000082, l, h); + if((l&0xFFFF0000)==0) + { + l=((mbytes>>2)<<22)|(1<<16); + save_flags(flags); + __cli(); + __asm__ __volatile__ ("wbinvd": : :"memory"); + wrmsr(0xC0000082, l, h); + restore_flags(flags); + printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n", + mbytes); + } + break; + } + break; + } + return r; +} + + /* * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU */ @@ -507,6 +586,10 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) (c->x86_model)++; } else /* 686 */ p = Cx86_cb+1; + /* Emulate MTRRs using Cyrix's ARRs. */ + c->x86_capability |= X86_FEATURE_MTRR; + /* 6x86's contain this bug */ + c->coma_bug = 1; break; case 4: /* MediaGX/GXm */ @@ -517,7 +600,7 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) /* GXm supports extended cpuid levels 'ala' AMD */ if (c->cpuid_level == 2) { - amd_model(c); /* get CPU marketing name */ + get_model_name(c); /* get CPU marketing name */ c->x86_capability&=~X86_FEATURE_TSC; return; } @@ -531,11 +614,14 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) case 5: /* 6x86MX/M II */ if (dir1 > 7) dir0_msn++; /* M II */ + else c->coma_bug = 1; /* 6x86MX, it has the bug. */ tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0; Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7]; p = Cx86_cb+tmp; if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20)) (c->x86_model)++; + /* Emulate MTRRs using Cyrix's ARRs. */ + c->x86_capability |= X86_FEATURE_MTRR; break; case 0xf: /* Cyrix 486 without DEVID registers */ @@ -642,6 +728,20 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c)) if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) return; + + if (c->cpuid_level > 0 && c->x86_vendor == X86_VENDOR_INTEL) + { + if(c->x86_capability&(1<<18)) + { + /* Disable processor serial number on Intel Pentium III + from code by Phil Karn */ + unsigned long lo,hi; + rdmsr(0x119,lo,hi); + lo |= 0x200000; + wrmsr(0x119,lo,hi); + printk(KERN_INFO "Pentium-III serial number disabled.\n"); + } + } for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { if (c->cpuid_level > 1) { @@ -726,15 +826,6 @@ __initfunc(void dodgy_tsc(void)) } -#define rdmsr(msr,val1,val2) \ - __asm__ __volatile__("rdmsr" \ - : "=a" (val1), "=d" (val2) \ - : "c" (msr)) - -#define wrmsr(msr,val1,val2) \ - __asm__ __volatile__("wrmsr" \ - : /* no outputs */ \ - : "c" (msr), "a" (val1), "d" (val2)) static char *cpu_vendor_names[] __initdata = { "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" }; @@ -784,9 +875,9 @@ int get_cpuinfo(char * buffer) int sep_bug; static char *x86_cap_flags[] = { "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce", - "cx8", "9", "10", "sep", "12", "pge", "14", "cmov", - "16", "17", "18", "19", "20", "21", "22", "mmx", - "24", "25", "26", "27", "28", "29", "30", "31" + "cx8", "9", "10", "sep", "mtrr", "pge", "14", "cmov", + "16", "17", "psn", "19", "20", "21", "22", "mmx", + "24", "kni", "26", "27", "28", "29", "30", "31" }; struct cpuinfo_x86 *c = cpu_data; int i, n; @@ -807,7 +898,7 @@ int get_cpuinfo(char * buffer) c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); - if (c->x86_mask) + if (c->x86_mask || c->cpuid_level >= 0) p += sprintf(p, "stepping\t: %d\n", c->x86_mask); else p += sprintf(p, "stepping\t: unknown\n"); @@ -832,10 +923,10 @@ int get_cpuinfo(char * buffer) } else if (c->x86_vendor == X86_VENDOR_INTEL) { x86_cap_flags[6] = "pae"; x86_cap_flags[9] = "apic"; - x86_cap_flags[12] = "mtrr"; x86_cap_flags[14] = "mca"; x86_cap_flags[16] = "pat"; x86_cap_flags[17] = "pse36"; + x86_cap_flags[18] = "psn"; x86_cap_flags[24] = "osfxsr"; } @@ -850,6 +941,7 @@ int get_cpuinfo(char * buffer) "hlt_bug\t\t: %s\n" "sep_bug\t\t: %s\n" "f00f_bug\t: %s\n" + "coma_bug\t: %s\n" "fpu\t\t: %s\n" "fpu_exception\t: %s\n" "cpuid level\t: %d\n" @@ -859,6 +951,7 @@ int get_cpuinfo(char * buffer) c->hlt_works_ok ? "no" : "yes", sep_bug ? "yes" : "no", c->f00f_bug ? "yes" : "no", + c->coma_bug ? "yes" : "no", c->hard_math ? "yes" : "no", (c->hard_math && ignore_irq13) ? "yes" : "no", c->cpuid_level, diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 2960d521c..4d13635ae 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -29,6 +29,7 @@ * from Jose Renau * Alan Cox : Added EBDA scanning * Ingo Molnar : various cleanups and rewrites + * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. */ #include <linux/config.h> @@ -39,10 +40,12 @@ #include <linux/smp_lock.h> #include <linux/init.h> #include <asm/mtrr.h> +#include <asm/msr.h> #include "irq.h" -extern unsigned long start_kernel, _etext; +#define JIFFIE_TIMEOUT 100 + extern void update_one_process( struct task_struct *p, unsigned long ticks, unsigned long user, unsigned long system, int cpu); @@ -146,6 +149,8 @@ int skip_ioapic_setup = 0; /* 1 if "noapic" boot option passed */ */ #define APIC_DEFAULT_PHYS_BASE 0xfee00000 +#define CLEAR_TSC wrmsr(0x10, 0x00001000, 0x00001000) + /* * Setup routine for controlling SMP activation * @@ -308,8 +313,17 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) printk("Processor #%d unused. (Max %d processors).\n",m->mpc_apicid, NR_CPUS); else { + int ver = m->mpc_apicver; + cpu_present_map|=(1<<m->mpc_apicid); - apic_version[m->mpc_apicid]=m->mpc_apicver; + /* + * Validate version + */ + if (ver == 0x0) { + printk("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->mpc_apicid); + ver = 0x10; + } + apic_version[m->mpc_apicid] = ver; } } mpt+=sizeof(*m); @@ -325,11 +339,13 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) SMP_PRINTK(("Bus #%d is %s\n", m->mpc_busid, str)); - if ((strncmp(m->mpc_bustype,"ISA",3) == 0) || - (strncmp(m->mpc_bustype,"EISA",4) == 0)) + if (strncmp(m->mpc_bustype,"ISA",3) == 0) mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; else + if (strncmp(m->mpc_bustype,"EISA",4) == 0) + mp_bus_id_to_type[m->mpc_busid] = + MP_BUS_EISA; if (strncmp(m->mpc_bustype,"PCI",3) == 0) { mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; @@ -454,7 +470,7 @@ static int __init smp_scan_config(unsigned long base, unsigned long length) */ cfg=pg0[0]; - pg0[0] = (mp_lapic_addr | 7); + pg0[0] = (mp_lapic_addr | _PAGE_RW | _PAGE_PRESENT); local_flush_tlb(); boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID)); @@ -710,24 +726,19 @@ void __init enable_local_APIC(void) value |= 0xff; /* Set spurious IRQ vector to 0xff */ apic_write(APIC_SPIV,value); - value = apic_read(APIC_TASKPRI); - value &= ~APIC_TPRI_MASK; /* Set Task Priority to 'accept all' */ - apic_write(APIC_TASKPRI,value); - /* - * Set arbitrarion priority to 0 + * Set Task Priority to 'accept all' */ - value = apic_read(APIC_ARBPRI); - value &= ~APIC_ARBPRI_MASK; - apic_write(APIC_ARBPRI, value); + value = apic_read(APIC_TASKPRI); + value &= ~APIC_TPRI_MASK; + apic_write(APIC_TASKPRI,value); /* - * Set the logical destination ID to 'all', just to be safe. + * Clear the logical destination ID, just to be safe. * also, put the APIC into flat delivery mode. */ value = apic_read(APIC_LDR); value &= ~APIC_LDR_MASK; - value |= SET_APIC_LOGICAL_ID(0xff); apic_write(APIC_LDR,value); value = apic_read(APIC_DFR); @@ -735,8 +746,6 @@ void __init enable_local_APIC(void) apic_write(APIC_DFR, value); udelay(100); /* B safe */ - ack_APIC_irq(); - udelay(100); } unsigned long __init init_smp_mappings(unsigned long memory_start) @@ -883,6 +892,7 @@ int __init start_secondary(void *unused) * Everything has been set up for the secondary * CPUs - they just need to reload everything * from the task structure + * This function must not return. */ void __init initialize_secondary(void) { @@ -924,7 +934,6 @@ static void __init do_boot_cpu(int i) /* * We need an idle process for each processor. */ - kernel_thread(start_secondary, NULL, CLONE_PID); cpucount++; @@ -935,6 +944,8 @@ static void __init do_boot_cpu(int i) idle->processor = i; __cpu_logical_map[cpucount] = i; cpu_number_map[i] = cpucount; + idle->has_cpu = 1; /* we schedule the first task manually */ + idle->tss.eip = (unsigned long) start_secondary; /* start_eip had better be page-aligned! */ start_eip = setup_trampoline(); @@ -1167,6 +1178,7 @@ void __init smp_boot_cpus(void) /* Must be done before other processors booted */ mtrr_init_boot_cpu (); #endif + init_idle(); /* * Initialize the logical to physical CPU number mapping * and the per-CPU profiling counter/multiplier @@ -1316,7 +1328,7 @@ void __init smp_boot_cpus(void) * Install writable page 0 entry. */ cfg = pg0[0]; - pg0[0] = 3; /* writeable, present, addr 0 */ + pg0[0] = _PAGE_RW | _PAGE_PRESENT; /* writeable, present, addr 0 */ local_flush_tlb(); /* @@ -1641,15 +1653,84 @@ void smp_send_stop(void) send_IPI_allbutself(STOP_CPU_VECTOR); } +/* Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +struct smp_call_function_struct { + void (*func) (void *info); + void *info; + atomic_t unstarted_count; + atomic_t unfinished_count; + int wait; +}; +static volatile struct smp_call_function_struct *smp_call_function_data = NULL; + /* - * this function sends an 'reload MTRR state' IPI to all other CPUs - * in the system. it goes straight through, completion processing - * is done on the mttr.c level. + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. */ -void smp_send_mtrr(void) +int smp_call_function (void (*func) (void *info), void *info, int retry, + int wait) +/* [SUMMARY] Run a function on all other CPUs. + <func> The function to run. This must be fast and non-blocking. + <info> An arbitrary pointer to pass to the function. + <retry> If true, keep retrying until ready. + <wait> If true, wait until function has completed on other CPUs. + [RETURNS] 0 on success, else a negative status code. Does not return until + remote CPUs are nearly ready to execute <<func>> or are or have executed. +*/ { - send_IPI_allbutself(MTRR_CHANGE_VECTOR); + unsigned long timeout; + struct smp_call_function_struct data; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + + if (retry) { + while (1) { + if (smp_call_function_data) { + schedule (); /* Give a mate a go */ + continue; + } + spin_lock (&lock); + if (smp_call_function_data) { + spin_unlock (&lock); /* Bad luck */ + continue; + } + /* Mine, all mine! */ + break; + } + } + else { + if (smp_call_function_data) return -EBUSY; + spin_lock (&lock); + if (smp_call_function_data) { + spin_unlock (&lock); + return -EBUSY; + } + } + smp_call_function_data = &data; + spin_unlock (&lock); + data.func = func; + data.info = info; + atomic_set (&data.unstarted_count, smp_num_cpus - 1); + data.wait = wait; + if (wait) atomic_set (&data.unfinished_count, smp_num_cpus - 1); + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself (CALL_FUNCTION_VECTOR); + /* Wait for response */ + timeout = jiffies + JIFFIE_TIMEOUT; + while ( (atomic_read (&data.unstarted_count) > 0) && + time_before (jiffies, timeout) ) + barrier (); + if (atomic_read (&data.unstarted_count) > 0) { + smp_call_function_data = NULL; + return -ETIMEDOUT; + } + if (wait) + while (atomic_read (&data.unfinished_count) > 0) + barrier (); + smp_call_function_data = NULL; + return 0; } /* @@ -1692,9 +1773,8 @@ void smp_local_timer_interrupt(struct pt_regs * regs) system=1; irq_enter(cpu, 0); + update_one_process(p, 1, user, system, cpu); if (p->pid) { - update_one_process(p, 1, user, system, cpu); - p->counter -= 1; if (p->counter < 0) { p->counter = 0; @@ -1707,7 +1787,6 @@ void smp_local_timer_interrupt(struct pt_regs * regs) kstat.cpu_user += user; kstat.per_cpu_user[cpu] += user; } - kstat.cpu_system += system; kstat.per_cpu_system[cpu] += system; @@ -1767,6 +1846,7 @@ asmlinkage void smp_invalidate_interrupt(void) local_flush_tlb(); ack_APIC_irq(); + } static void stop_this_cpu (void) @@ -1789,12 +1869,19 @@ asmlinkage void smp_stop_cpu_interrupt(void) stop_this_cpu(); } -void (*mtrr_hook) (void) = NULL; - -asmlinkage void smp_mtrr_interrupt(void) +asmlinkage void smp_call_function_interrupt(void) { - ack_APIC_irq(); - if (mtrr_hook) (*mtrr_hook)(); + void (*func) (void *info) = smp_call_function_data->func; + void *info = smp_call_function_data->info; + int wait = smp_call_function_data->wait; + + ack_APIC_irq (); + /* Notify initiating CPU that I've grabbed the data and am about to + execute the function */ + atomic_dec (&smp_call_function_data->unstarted_count); + /* At this point the structure may be out of scope unless wait==1 */ + (*func) (info); + if (wait) atomic_dec (&smp_call_function_data->unfinished_count); } /* @@ -1802,8 +1889,10 @@ asmlinkage void smp_mtrr_interrupt(void) */ asmlinkage void smp_spurious_interrupt(void) { - /* ack_APIC_irq(); see sw-dev-man vol 3, chapter 7.4.13.5 */ - printk("spurious APIC interrupt, ayiee, should never happen.\n"); + ack_APIC_irq(); + /* see sw-dev-man vol 3, chapter 7.4.13.5 */ + printk("spurious APIC interrupt on CPU#%d, should never happen.\n", + smp_processor_id()); } /* @@ -1815,10 +1904,6 @@ asmlinkage void smp_spurious_interrupt(void) * closely follows bus clocks. */ -#define RDTSC(x) __asm__ __volatile__ ( "rdtsc" \ - :"=a" (((unsigned long*)&x)[0]), \ - "=d" (((unsigned long*)&x)[1])) - /* * The timer chip is already set up at HZ interrupts per second here, * but we do not accept timer interrupts yet. We only allow the BP @@ -1937,7 +2022,7 @@ int __init calibrate_APIC_clock(void) /* * We wrapped around just now. Let's start: */ - RDTSC(t1); + rdtscll(t1); tt1=apic_read(APIC_TMCCT); #define LOOPS (HZ/10) @@ -1948,7 +2033,7 @@ int __init calibrate_APIC_clock(void) wait_8254_wraparound (); tt2=apic_read(APIC_TMCCT); - RDTSC(t2); + rdtscll(t2); /* * The APIC bus clock counter is 32 bits only, it @@ -2058,3 +2143,4 @@ int setup_profiling_timer(unsigned int multiplier) } #undef APIC_DIVISOR + diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index ec2ea5d60..2ab29d479 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -30,19 +30,6 @@ * serialize accesses to xtime/lost_ticks). */ -/* What about the "updated NTP code" stuff in 2.0 time.c? It's not in - * 2.1, perhaps it should be ported, too. - * - * What about the BUGGY_NEPTUN_TIMER stuff in do_slow_gettimeoffset()? - * Whatever it fixes, is it also fixed in the new code from the Jumbo - * patch, so that that code can be used instead? - * - * The CPU Hz should probably be displayed in check_bugs() together - * with the CPU vendor and type. Perhaps even only in MHz, though that - * takes away some of the fun of the new code :) - * - * - Michael Krause */ - #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -60,6 +47,7 @@ #include <asm/io.h> #include <asm/irq.h> #include <asm/delay.h> +#include <asm/msr.h> #include <linux/mc146818rtc.h> #include <linux/timex.h> @@ -96,8 +84,8 @@ static inline unsigned long do_fast_gettimeoffset(void) register unsigned long edx asm("dx"); /* Read the Time Stamp Counter */ - __asm__("rdtsc" - :"=a" (eax), "=d" (edx)); + + rdtsc(eax,edx); /* .. relative to previous jiffy (32 bits is enough) */ eax -= last_tsc_low; /* tsc_low delta */ @@ -292,7 +280,6 @@ void do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; write_unlock_irq(&xtime_lock); @@ -457,7 +444,8 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) */ /* read Pentium cycle counter */ - __asm__("rdtsc" : "=a" (last_tsc_low) : : "edx"); + + rdtscl(last_tsc_low); outb_p(0x00, 0x43); /* latch the count ASAP */ @@ -556,70 +544,72 @@ static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NUL * device. */ +#define CALIBRATE_LATCH (5 * LATCH) +#define CALIBRATE_TIME (5 * 1000020/HZ) + __initfunc(static unsigned long calibrate_tsc(void)) { - unsigned long retval; + /* Set the Gate high, disable speaker */ + outb((inb(0x61) & ~0x02) | 0x01, 0x61); - __asm__( /* set the Gate high, program CTC channel 2 for mode 0 - * (interrupt on terminal count mode), binary count, - * load 5 * LATCH count, (LSB and MSB) - * to begin countdown, read the TSC and busy wait. - * BTW LATCH is calculated in timex.h from the HZ value - */ + /* + * Now let's take care of CTC channel 2 + * + * Set the Gate high, program CTC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + */ + outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ + outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ + + { + unsigned long startlow, starthigh; + unsigned long endlow, endhigh; + unsigned long count; + + rdtsc(startlow,starthigh); + count = 0; + do { + count++; + } while ((inb(0x61) & 0x20) == 0); + rdtsc(endlow,endhigh); + + last_tsc_low = endlow; + + /* Error: ECTCNEVERSET */ + if (count <= 1) + goto bad_ctc; + + /* 64-bit subtract - gcc just messes up with long longs */ + __asm__("subl %2,%0\n\t" + "sbbl %3,%1" + :"=a" (endlow), "=d" (endhigh) + :"g" (startlow), "g" (starthigh), + "0" (endlow), "1" (endhigh)); + + /* Error: ECPUTOOFAST */ + if (endhigh) + goto bad_ctc; + + /* Error: ECPUTOOSLOW */ + if (endlow <= CALIBRATE_TIME) + goto bad_ctc; + + __asm__("divl %2" + :"=a" (endlow), "=d" (endhigh) + :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME)); + + return endlow; + } - /* Set the Gate high, disable speaker */ - "inb $0x61, %%al\n\t" /* Read port */ - "andb $0xfd, %%al\n\t" /* Turn off speaker Data */ - "orb $0x01, %%al\n\t" /* Set Gate high */ - "outb %%al, $0x61\n\t" /* Write port */ - - /* Now let's take care of CTC channel 2 */ - "movb $0xb0, %%al\n\t" /* binary, mode 0, LSB/MSB, ch 2*/ - "outb %%al, $0x43\n\t" /* Write to CTC command port */ - "movl %1, %%eax\n\t" - "outb %%al, $0x42\n\t" /* LSB of count */ - "shrl $8, %%eax\n\t" - "outb %%al, $0x42\n\t" /* MSB of count */ - - /* Read the TSC; counting has just started */ - "rdtsc\n\t" - /* Move the value for safe-keeping. */ - "movl %%eax, %%ebx\n\t" - "movl %%edx, %%ecx\n\t" - - /* Busy wait. Only 50 ms wasted at boot time. */ - "0: inb $0x61, %%al\n\t" /* Read Speaker Output Port */ - "testb $0x20, %%al\n\t" /* Check CTC channel 2 output (bit 5) */ - "jz 0b\n\t" - - /* And read the TSC. 5 jiffies (50.00077ms) have elapsed. */ - "rdtsc\n\t" - - /* Great. So far so good. Store last TSC reading in - * last_tsc_low (only 32 lsb bits needed) */ - "movl %%eax, last_tsc_low\n\t" - /* And now calculate the difference between the readings. */ - "subl %%ebx, %%eax\n\t" - "sbbl %%ecx, %%edx\n\t" /* 64-bit subtract */ - /* but probably edx = 0 at this point (see below). */ - /* Now we have 5 * (TSC counts per jiffy) in eax. We want - * to calculate TSC->microsecond conversion factor. */ - - /* Note that edx (high 32-bits of difference) will now be - * zero iff CPU clock speed is less than 85 GHz. Moore's - * law says that this is likely to be true for the next - * 12 years or so. You will have to change this code to - * do a real 64-by-64 divide before that time's up. */ - "movl %%eax, %%ecx\n\t" - "xorl %%eax, %%eax\n\t" - "movl %2, %%edx\n\t" - "divl %%ecx\n\t" /* eax= 2^32 / (1 * TSC counts per microsecond) */ - /* Return eax for the use of fast_gettimeoffset */ - "movl %%eax, %0\n\t" - : "=r" (retval) - : "r" (5 * LATCH), "r" (5 * 1000020/HZ) - : /* we clobber: */ "ax", "bx", "cx", "dx", "cc", "memory"); - return retval; + /* + * The CTC wasn't reliable: we got a hit on the very first read, + * or the CPU was so fast/slow that the quotient wouldn't fit in + * 32 bits.. + */ +bad_ctc: + return 0; } __initfunc(void time_init(void)) @@ -655,23 +645,26 @@ __initfunc(void time_init(void)) dodgy_tsc(); if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) { + unsigned long tsc_quotient = calibrate_tsc(); + if (tsc_quotient) { + fast_gettimeoffset_quotient = tsc_quotient; + use_tsc = 1; #ifndef do_gettimeoffset - do_gettimeoffset = do_fast_gettimeoffset; + do_gettimeoffset = do_fast_gettimeoffset; #endif - do_get_fast_time = do_gettimeofday; - use_tsc = 1; - fast_gettimeoffset_quotient = calibrate_tsc(); - - /* report CPU clock rate in Hz. - * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = - * clock/second. Our precision is about 100 ppm. - */ - { unsigned long eax=0, edx=1000000; - __asm__("divl %2" - :"=a" (cpu_hz), "=d" (edx) - :"r" (fast_gettimeoffset_quotient), - "0" (eax), "1" (edx)); - printk("Detected %ld Hz processor.\n", cpu_hz); + do_get_fast_time = do_gettimeofday; + + /* report CPU clock rate in Hz. + * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = + * clock/second. Our precision is about 100 ppm. + */ + { unsigned long eax=0, edx=1000000; + __asm__("divl %2" + :"=a" (cpu_hz), "=d" (edx) + :"r" (tsc_quotient), + "0" (eax), "1" (edx)); + printk("Detected %ld Hz processor.\n", cpu_hz); + } } } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index f0dc06092..cce35ac80 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -42,6 +42,8 @@ #include <asm/lithium.h> #endif +#include "irq.h" + asmlinkage int system_call(void); asmlinkage void lcall7(void); @@ -125,7 +127,6 @@ static void show_registers(struct pt_regs *regs) unsigned long esp; unsigned short ss; unsigned long *stack, addr, module_start, module_end; - extern char _stext, _etext; esp = (unsigned long) (1+regs); ss = __KERNEL_DS; @@ -669,9 +670,6 @@ cobalt_init(void) #endif void __init trap_init(void) { - /* Initially up all of the IDT to jump to unexpected */ - init_unexpected_irq(); - if (readl(0x0FFFD9) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24)) EISA_bus = 1; set_call_gate(&default_ldt,lcall7); @@ -693,7 +691,7 @@ void __init trap_init(void) set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); - set_system_gate(0x80,&system_call); + set_system_gate(SYSCALL_VECTOR,&system_call); /* set up GDT task & ldt entries */ set_tss_desc(0, &init_task.tss); diff --git a/arch/i386/kernel/visws_apic.c b/arch/i386/kernel/visws_apic.c index f7dabc15d..c12054689 100644 --- a/arch/i386/kernel/visws_apic.c +++ b/arch/i386/kernel/visws_apic.c @@ -201,11 +201,13 @@ static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs) { unsigned int status; /* XXX APIC EOI? */ - status = desc->status & ~IRQ_REPLAY; + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); action = NULL; - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; - desc->status = status | IRQ_INPROGRESS; + status |= IRQ_INPROGRESS; + } + desc->status = status; } spin_unlock(&irq_controller_lock); diff --git a/arch/i386/lib/semaphore.S b/arch/i386/lib/semaphore.S index faddbf4ec..3f6e27fcc 100644 --- a/arch/i386/lib/semaphore.S +++ b/arch/i386/lib/semaphore.S @@ -31,6 +31,15 @@ ENTRY(__down_failed_interruptible) popl %edx /* restore %edx */ ret +/* Don't save/restore %eax, because that will be our return value */ +ENTRY(__down_failed_trylock) + pushl %edx /* save %edx */ + pushl %ecx /* save %ecx (and argument) */ + call SYMBOL_NAME(__down_trylock) + popl %ecx /* restore %ecx (count on __down_trylock not changing it) */ + popl %edx /* restore %edx */ + ret + ENTRY(__up_wakeup) pushl %eax /* save %eax */ pushl %edx /* save %edx */ diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index cb60ea268..3a5957b09 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -56,6 +56,11 @@ SUBDIRS += arch/m68k/kernel arch/m68k/mm arch/m68k/lib CORE_FILES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(CORE_FILES) LIBS += arch/m68k/lib/lib.a +ifdef CONFIG_Q40 +CORE_FILES := $(CORE_FILES) arch/m68k/q40/q40.o +SUBDIRS := $(SUBDIRS) arch/m68k/q40 +endif + ifdef CONFIG_AMIGA CORE_FILES := $(CORE_FILES) arch/m68k/amiga/amiga.o SUBDIRS := $(SUBDIRS) arch/m68k/amiga @@ -81,6 +86,11 @@ CORE_FILES := $(CORE_FILES) arch/m68k/apollo/apollo.o SUBDIRS := $(SUBDIRS) arch/m68k/apollo endif +ifdef CONFIG_MVME147 +CORE_FILES := $(CORE_FILES) arch/m68k/mvme147/mvme147.o +SUBDIRS := $(SUBDIRS) arch/m68k/mvme147 +endif + ifdef CONFIG_MVME16x CORE_FILES := $(CORE_FILES) arch/m68k/mvme16x/mvme16x.o SUBDIRS := $(SUBDIRS) arch/m68k/mvme16x @@ -91,6 +101,11 @@ CORE_FILES := $(CORE_FILES) arch/m68k/bvme6000/bvme6000.o SUBDIRS := $(SUBDIRS) arch/m68k/bvme6000 endif +ifdef CONFIG_SUN3X +CORE_FILES := $(CORE_FILES) arch/m68k/sun3x/sun3x.o +SUBDIRS := $(SUBDIRS) arch/m68k/sun3x +endif + ifdef CONFIG_M68040 CORE_FILES := $(CORE_FILES) arch/m68k/fpsp040/fpsp.o SUBDIRS := $(SUBDIRS) arch/m68k/fpsp040 diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index 79ef5f9fd..6de1fd686 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -291,7 +291,7 @@ static void atakeyb_rep( unsigned long ignore ) atakeyb_rep_timer.prev = atakeyb_rep_timer.next = NULL; add_timer( &atakeyb_rep_timer ); - handle_scancode(rep_scancode); + handle_scancode(rep_scancode, 1); } atari_enable_irq( IRQ_MFP_ACIA ); @@ -448,7 +448,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) add_timer( &atakeyb_rep_timer ); } - handle_scancode(break_flag | scancode); + handle_scancode(scancode, !break_flag); break; } break; diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index 543b04c74..fc1b5627f 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -417,10 +417,10 @@ void bvme6000_init_console_port (struct console *co, int cflag) static void scc_delay (void) { int n; - char i; + volatile int trash; for (n = 0; n < 20; n++) - i = *(volatile char *)0; + trash = n; } static void scc_write (char ch) diff --git a/arch/m68k/config.in b/arch/m68k/config.in index a67227d60..a8dacc904 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -37,6 +37,7 @@ fi bool 'Apollo support' CONFIG_APOLLO bool 'VME (Motorola and BVM) support' CONFIG_VME if [ "$CONFIG_VME" = "y" ]; then + bool 'MVME147 support' CONFIG_MVME147 bool 'MVME162, 166 and 167 support' CONFIG_MVME16x bool 'BVME4000 and BVME6000 support' CONFIG_BVME6000 fi @@ -44,10 +45,13 @@ bool 'HP9000/300 support' CONFIG_HP300 if [ "$CONFIG_HP300" = "y" ]; then bool 'DIO bus support' CONFIG_DIO fi +bool 'Sun3x support' CONFIG_SUN3X + define_bool CONFIG_SUN3 n if [ "$CONFIG_PCI" = "y" ]; then bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC fi +bool 'Q40/Q60 support' CONFIG_Q40 comment 'Processor type' bool '68020 support' CONFIG_M68020 @@ -94,6 +98,29 @@ else fi fi bool '/proc/hardware support' CONFIG_PROC_HARDWARE + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Parallel port support (EXPERIMENTAL, disables old lp driver!)' CONFIG_PARPORT + if [ "$CONFIG_PARPORT" != "n" ]; then + if [ "$CONFIG_AMIGA" != "n" ]; then + dep_tristate ' Amiga builtin port' CONFIG_PARPORT_AMIGA $CONFIG_PARPORT + if [ "$CONFIG_ZORRO" != "n" ]; then + dep_tristate ' Multiface III parallel port' CONFIG_PARPORT_MFC3 $CONFIG_PARPORT + fi + fi + if [ "$CONFIG_Q40" != "n" ]; then + tristate ' Q40 Parallel port' CONFIG_PARPORT + if [ "$CONFIG_PARPORT" != "n" ]; then + define_bool CONFIG_PARPORT_PC y + fi + fi + fi + if [ "$CONFIG_ATARI" == "y" ]; then + dep_tristate ' Atari builtin port' CONFIG_PARPORT_ATARI $CONFIG_PARPORT + fi +fi + + endmenu source drivers/block/Config.in @@ -147,6 +174,7 @@ if [ "$CONFIG_ZORRO" = "y" ]; then bool 'A4091 SCSI support' CONFIG_A4091_SCSI bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI bool 'Blizzard PowerUP 603e+ SCSI' CONFIG_BLZ603EPLUS_SCSI + dep_tristate 'BSC Oktagon SCSI support' CONFIG_OKTAGON_SCSI $CONFIG_SCSI bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI # bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI fi @@ -167,6 +195,10 @@ if [ "$CONFIG_MAC" = "y" ]; then fi #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI +if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME147" = "y" ]; then + bool 'WD33C93 SCSI driver for MVME147' CONFIG_MVME147_SCSI +fi + if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME16x" = "y" ]; then bool 'NCR53C710 SCSI driver for MVME16x' CONFIG_MVME16x_SCSI fi @@ -175,6 +207,10 @@ if [ "$CONFIG_VME" = "y" -a "$CONFIG_BVME6000" = "y" ]; then bool 'NCR53C710 SCSI driver for BVME6000' CONFIG_BVME6000_SCSI fi +if [ "$CONFIG_SUN3X" = "y" ]; then + bool 'ESP SCSI driver' CONFIG_SUN3X_ESP +fi + endmenu fi @@ -219,6 +255,9 @@ if [ "$CONFIG_MAC" = "y" ]; then # bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE bool 'Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC fi +if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME147" = "y" ]; then + tristate 'MVME147 (Lance) Ethernet support' CONFIG_MVME147_NET +fi if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME16x" = "y" ]; then tristate 'MVME16x Ethernet support' CONFIG_MVME16x_NET fi @@ -232,9 +271,17 @@ if [ "$CONFIG_ATARI" = "y" ]; then tristate 'PAMsNet support' CONFIG_ATARI_PAMSNET fi fi +if [ "$CONFIG_SUN3X" = "y" ]; then + bool 'Sun3x Lance support' CONFIG_SUNLANCE +fi if [ "$CONFIG_HP300" = "y" ]; then bool 'HP on-board LANCE support' CONFIG_HPLANCE fi +if [ "$CONFIG_Q40" = "y" ]; then + if [ ! "$CONFIG_PARPORT" = "n" ]; then + dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT + fi +fi fi endmenu @@ -243,6 +290,23 @@ fi mainmenu_option next_comment comment 'Character devices' +if [ "$CONFIG_Q40" = "y" ]; then + tristate 'Q40 Standard/generic serial support' CONFIG_SERIAL +fi + +if [ "$CONFIG_SERIAL" = "y" ]; then + bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE + bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED +fi + +if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then + bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS + bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ +# bool ' Autodetect IRQ - do not yet enable !!' CONFIG_SERIAL_DETECT_IRQ + bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT + bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6 +fi + if [ "$CONFIG_VME" = "n" ]; then define_bool CONFIG_VT y if [ "$CONFIG_VT" = "y" ]; then @@ -254,10 +318,17 @@ if [ "$CONFIG_ATARI" = "y" ]; then define_bool CONFIG_NVRAM y fi -tristate 'Parallel printer support' CONFIG_M68K_PRINTER -if [ "$CONFIG_ZORRO" = "y" ]; then - dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_M68K_PRINTER -fi +if [ "$CONFIG_PARPORT" = "n" ]; then + tristate 'Parallel printer support' CONFIG_M68K_PRINTER + if [ "$CONFIG_ZORRO" = "y" ]; then + dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_M68K_PRINTER + fi +else + dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT + if [ "$CONFIG_PRINTER" != "n" ]; then + bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK + fi +fi if [ "$CONFIG_AMIGA" = "y" ]; then tristate 'Amiga mouse support' CONFIG_AMIGAMOUSE fi @@ -280,13 +351,17 @@ if [ "$CONFIG_ATARI" = "y" ]; then fi if [ "$CONFIG_AMIGA" = "y" ]; then tristate 'Amiga builtin serial support' CONFIG_AMIGA_BUILTIN_SERIAL - bool 'Hisoft Whippet PCMCIA serial support' CONFIG_WHIPPET + if [ "$CONFIG_AMIGA_PCMCIA" = "y" ]; then + tristate 'Hisoft Whippet PCMCIA serial support' CONFIG_WHIPPET_SERIAL + fi fi -if [ "$CONFIG_ZORRO" = "y" ]; then - tristate 'GVP IO-Extender support' CONFIG_GVPIOEXT - dep_tristate 'GVP IO-Extender parallel printer support' CONFIG_GVPIOEXT_LP $CONFIG_GVPIOEXT - dep_tristate 'GVP IO-Extender PLIP support' CONFIG_GVPIOEXT_PLIP $CONFIG_GVPIOEXT - tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY +if [ "$CONFIG_PARPORT" = "n" ]; then + if [ "$CONFIG_ZORRO" = "y" ]; then + tristate 'GVP IO-Extender support' CONFIG_GVPIOEXT + dep_tristate 'GVP IO-Extender parallel printer support' CONFIG_GVPIOEXT_LP $CONFIG_GVPIOEXT + dep_tristate 'GVP IO-Extender PLIP support' CONFIG_GVPIOEXT_PLIP $CONFIG_GVPIOEXT + tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY + fi fi if [ "$CONFIG_MAC" = "y" ]; then bool 'Mac SCC serial support' CONFIG_MAC_SCC @@ -294,18 +369,32 @@ fi if [ "$CONFIG_HP300" = "y" -a "$CONFIG_DIO" = "y" ]; then tristate 'HP DCA serial support' CONFIG_HPDCA fi +if [ "$CONFIG_SUN3X" = "y" ]; then + bool 'Sun3x builtin serial support' CONFIG_SUN3X_ZS + if [ "$CONFIG_SUN3X_ZS" = "y" ]; then + bool 'Sun keyboard support' CONFIG_SUN_KEYBOARD + bool 'Sun mouse support' CONFIG_SUN_MOUSE + define_bool CONFIG_SBUS y + define_bool CONFIG_SBUSCHAR y + define_bool CONFIG_SUN_SERIAL y + fi +fi if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_ATARI" = "y" -o \ - "$CONFIG_MAC" = "y" -o "$CONFIG_HP300" = "y" ]; then + "$CONFIG_MAC" = "y" -o "$CONFIG_HP300" = "y" -o \ + "$CONFIG_SUN3X" = "y" ]; then if [ "$CONFIG_ATARI_MFPSER" = "y" -o "$CONFIG_ATARI_SCC" = "y" -o \ "$CONFIG_ATARI_MIDI" = "y" -o "$CONFIG_MAC_SCC" = "y" -o \ "$CONFIG_AMIGA_BUILTIN_SERIAL" = "y" -o \ "$CONFIG_GVPIOEXT" = "y" -o "$CONFIG_MULTIFACE_III_TTY" = "y" -o \ - "$CONFIG_HPDCA" = "y" ]; then + "$CONFIG_HPDCA" = "y" -o "$CONFIG_SUN3X_ZS" = "y" ]; then bool 'Support for serial port console' CONFIG_SERIAL_CONSOLE fi fi if [ "$CONFIG_VME" = "y" ]; then define_bool CONFIG_SERIAL_CONSOLE y + if [ "$CONFIG_MVME147" = "y" ]; then + bool 'SCC support for MVME147 serial ports' CONFIG_MVME147_SCC + fi if [ "$CONFIG_MVME16x" = "y" ]; then bool 'CD2401 support for MVME166/7 serial ports' CONFIG_SERIAL167 bool 'SCC support for MVME162 serial ports' CONFIG_MVME162_SCC @@ -346,13 +435,13 @@ endmenu source fs/Config.in if [ "$CONFIG_VME" = "n" ]; then + mainmenu_option next_comment + comment 'Console drivers' if [ "$CONFIG_HP300" = "y" ]; then bool 'Frame buffer support' CONFIG_FB else define_bool CONFIG_FB y fi - mainmenu_option next_comment - comment 'Console drivers' source drivers/video/Config.in endmenu fi diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig index ab9152f5d..109ce19fa 100644 --- a/arch/m68k/defconfig +++ b/arch/m68k/defconfig @@ -78,7 +78,6 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y diff --git a/arch/m68k/hp300/hil.c b/arch/m68k/hp300/hil.c index 3be0d488a..d7fe1867c 100644 --- a/arch/m68k/hp300/hil.c +++ b/arch/m68k/hp300/hil.c @@ -223,12 +223,13 @@ static void poll_finished(void) { case 0x40: { - unsigned char scode = (poll.data[1] >> 1) | ((poll.data[1] & 1)?0x80:0); + int down = (poll.data[1] & 1) == 0; + unsigned char scode = poll.data[1] >> 1; #if 0 - if (scode & 0x80) - printk("[%02x]", scode & 0x7f); + if (down) + printk("[%02x]", scode); #endif - handle_scancode(scode); + handle_scancode(scode, down); } break; } diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index e21e4b21c..61482c3a8 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -164,8 +164,17 @@ SYMBOL_NAME_LABEL(inthandler) movel %sp,%sp@- movel %d0,%sp@- | put vector # on stack +#if defined(MACH_Q40_ONLY) && defined(CONFIG_BLK_DEV_FD) + btstb #4,0xff000000 | Q40 floppy needs very special treatment ... + jbeq 1f + btstb #3,0xff000004 + jbeq 1f + jbsr SYMBOL_NAME(floppy_hardint) + jbra 3f +1: +#endif jbsr SYMBOL_NAME(process_int)| process the IRQ - addql #8,%sp | pop parameters off stack +3: addql #8,%sp | pop parameters off stack SYMBOL_NAME_LABEL(ret_from_interrupt) subql #1,SYMBOL_NAME(local_irq_count) @@ -295,6 +304,8 @@ SYMBOL_NAME_LABEL(resume) 2: fmovemx %fp0-%fp7,%a0@(TASK_TSS+TSS_FPREG) fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_TSS+TSS_FPCNTL) 3: + /* Return previous task in %d1 */ + movel %curptr,%d1 /* switch to new task (a1 contains new task) */ movel %a1,%curptr @@ -320,8 +331,15 @@ SYMBOL_NAME_LABEL(resume) movec %d0,%cacr /* switch the root pointer */ +#ifdef CPU_M68030_ONLY + .chip 68030 + pmovefd %a1@(TASK_TSS+TSS_CRP),%crp + .chip 68k + pflush #0,#4 +#else pmove %a1@(TASK_TSS+TSS_CRP),%crp #endif +#endif #if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) jra 2f /* skip m68040 stuff */ diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index 482c15f50..361b10cb6 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -23,6 +23,7 @@ ** 98/04/25 Phil Blundell: added HP300 support ** 1998/08/30 David Kilzer: Added support for fbcon_font_desc structures ** for linux-2.1.115 +** 9/02/11 Richard Zidlicky: added Q40 support (initial vesion 99/01/01) ** ** This file is subject to the terms and conditions of the GNU General Public ** License. See the file README.legal in the main directory of this archive @@ -303,6 +304,12 @@ .globl SYMBOL_NAME(availmem) .globl SYMBOL_NAME(m68k_pgtable_cachemode) .globl SYMBOL_NAME(m68k_supervisor_cachemode) +#ifdef CONFIG_MVME16x +.globl SYMBOL_NAME(mvme_bdid_ptr) +#endif +#ifdef CONFIG_Q40 +.globl SYMBOL_NAME(q40_mem_cptr) +#endif CPUTYPE_040 = 1 /* indicates an 040 */ CPUTYPE_060 = 2 /* indicates an 060 */ @@ -512,13 +519,15 @@ func_define putn,1 #endif .endm - #define is_not_amiga(lab) cmpl &MACH_AMIGA,%pc@(m68k_machtype); jne lab #define is_not_atari(lab) cmpl &MACH_ATARI,%pc@(m68k_machtype); jne lab #define is_not_mac(lab) cmpl &MACH_MAC,%pc@(m68k_machtype); jne lab +#define is_not_mvme147(lab) cmpl &MACH_MVME147,%pc@(m68k_machtype); jne lab #define is_not_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jne lab #define is_not_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jne lab #define is_not_hp300(lab) cmpl &MACH_HP300,%pc@(m68k_machtype); jne lab +#define is_not_q40(lab) cmpl &MACH_Q40,%pc@(m68k_machtype); jne lab +#define is_not_sun3x(lab) cmpl &MACH_SUN3X,%pc@(m68k_machtype); jne lab #define is_040_or_060(lab) btst &CPUTYPE_0460,%pc@(L(cputype)+3); jne lab #define is_not_040_or_060(lab) btst &CPUTYPE_0460,%pc@(L(cputype)+3); jeq lab @@ -552,9 +561,11 @@ ENTRY(_stext) .long BOOTINFOV_MAGIC .long MACH_AMIGA, AMIGA_BOOTI_VERSION .long MACH_ATARI, ATARI_BOOTI_VERSION + .long MACH_MVME147, MVME147_BOOTI_VERSION .long MACH_MVME16x, MVME16x_BOOTI_VERSION .long MACH_BVME6000, BVME6000_BOOTI_VERSION .long MACH_MAC, MAC_BOOTI_VERSION + .long MACH_Q40, Q40_BOOTI_VERSION .long 0 1: jra SYMBOL_NAME(__start) @@ -859,7 +870,12 @@ L(mmu_init_amiga): /* * 040: Map the 16Meg range physical 0x0 upto logical 0x8000.0000 */ - mmu_map #0x80000000,#0,#0x01000000,#_PAGE_NOCACHE_S + mmu_map #0x80000000,#0,#0x01000000,#_PAGE_NOCACHE_S + /* + * Map the Zorro III I/O space with transparent translation + * for frame buffer memory etc. + */ + mmu_map_tt #1,#0x40000000,#0x20000000,#_PAGE_NOCACHE_S jbra L(mmu_init_done) @@ -867,7 +883,8 @@ L(mmu_init_amiga): /* * 030: Map the 32Meg range physical 0x0 upto logical 0x8000.0000 */ - mmu_map #0x80000000,#0,#0x02000000,#_PAGE_NOCACHE030 + mmu_map #0x80000000,#0,#0x02000000,#_PAGE_NOCACHE030 + mmu_map_tt #1,#0x40000000,#0x20000000,#_PAGE_NOCACHE030 jbra L(mmu_init_done) @@ -926,6 +943,25 @@ L(spata68040): L(mmu_init_not_atari): #endif +#ifdef CONFIG_Q40 + is_not_q40(L(notq40)) + /* + * add transparent mapping for 0xff00 0000 - 0xffff ffff + * non-cached serialized etc.. + * this includes master chip, DAC, RTC and ISA ports + * 0xfe000000-0xfeffffff is for screen and ROM + */ + + putc 'Q' + + mmu_map_tt #0,#0xfe000000,#0x01000000,#_PAGE_CACHE040W + mmu_map_tt #1,#0xff000000,#0x01000000,#_PAGE_NOCACHE_S + + jbra L(mmu_init_done) + +L(notq40): +#endif + #ifdef CONFIG_HP300 is_not_hp300(L(nothp300)) @@ -940,6 +976,24 @@ L(nothp300): #endif +#ifdef CONFIG_MVME147 + + is_not_mvme147(L(not147)) + + /* + * On MVME147 we have already created kernel page tables for + * 4MB of RAM at address 0, so now need to do a transparent + * mapping of the top of memory space. Make it 0.5GByte for now, + * so we can access on-board i/o areas. + */ + + mmu_map_tt #1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE030 + + jbra L(mmu_init_done) + +L(not147): +#endif /* CONFIG_MVME147 */ + #ifdef CONFIG_MVME16x is_not_mvme16x(L(not16x)) @@ -965,7 +1019,7 @@ L(nothp300): * 0xffe00000->0xffe1ffff. */ - mmu_map_tt 1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE_S + mmu_map_tt #1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE_S jbra L(mmu_init_done) @@ -985,7 +1039,7 @@ L(not16x): * clash with User code virtual address space. */ - mmu_map_tt 1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE_S + mmu_map_tt #1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE_S jbra L(mmu_init_done) @@ -1052,13 +1106,23 @@ L(mmu_init_mac): mmu_map_eq #0x50000000,#0x02000000,%d3 mmu_map_eq #0x60000000,#0x00400000,%d3 mmu_map_eq #0x9c000000,#0x00400000,%d3 - mmu_map_tt 1,#0xf8000000,#0x08000000,%d3 + mmu_map_tt #1,#0xf8000000,#0x08000000,%d3 jbra L(mmu_init_done) L(mmu_init_not_mac): #endif +#ifdef CONFIG_SUN3X + is_not_sun3x(L(notsun3x)) + + /* setup tt1 for I/O */ + mmu_map_tt #1,#0x40000000,#0x40000000,#_PAGE_NOCACHE_S + +L(notsun3x): + jbra L(mmu_init_done) +#endif + L(mmu_init_done): putc 'G' @@ -1214,6 +1278,14 @@ L(mmu_fixup_done): 1: #endif +#ifdef CONFIG_SUN3X + is_not_sun3x(1f) + + /* enable copro */ + oriw #0x4000,0x61000000 +1: +#endif + /* * Fixup the addresses for the kernel pointer table and availmem. * Convert them from physical addresses to virtual addresses. @@ -1780,7 +1852,7 @@ func_start mmu_map_tt,%d0/%d1/%a0,4 /* Extract the highest bit set */ bfffo ARG3{#0,#32},%d1 - cmpw #8,%d0 + cmpw #8,%d1 jcc L(do_map) /* And get the mask @@ -2155,7 +2227,9 @@ func_start mmu_temp_map,%d0/%d1/%a0/%a1 /* Temporary allocate a page table and insert it into the ptr table */ movel %a1@,%d0 - addl #PTR_TABLE_SIZE*4,%a1@ + /* The 512 should be PAGE_TABLE_SIZE*4, but that violates the + alignment restriction for pointer tables on the '0[46]0. */ + addl #512,%a1@ orw #_PAGE_TABLE+_PAGE_ACCESSED,%d0 movel %d0,%a0@ dputs " (new)" @@ -2703,6 +2777,31 @@ func_start serial_init,%d0/%d1/%a0/%a1 L(serial_init_not_mac): #endif /* CONFIG_MAC */ +#ifdef CONFIG_Q40 + is_not_q40(2f) +/* debug output goes into SRAM, so we don't do it unless requested + - check for '%LX$' signature in SRAM */ + lea %pc@(SYMBOL_NAME(q40_mem_cptr)),%a1 + move.l #0xff020010,%a1@ /* must be inited - also used by debug=mem */ + move.l #0xff020000,%a1 + cmp.b #'%',%a1@ + bne 2f /*nodbg*/ + addq.w #4,%a1 + cmp.b #'L',%a1@ + bne 2f /*nodbg*/ + addq.w #4,%a1 + cmp.b #'X',%a1@ + bne 2f /*nodbg*/ + addq.w #4,%a1 + cmp.b #'$',%a1@ + bne 2f /*nodbg*/ + /* signature OK */ + lea %pc@(L(q40_do_debug)),%a1 + tas %a1@ +/*nodbg: q40_do_debug is 0 by default*/ +2: +#endif + L(serial_init_done): func_return serial_init @@ -2792,6 +2891,15 @@ func_start serial_putc,%d0/%d1/%a0/%a1 4: #endif /* CONFIG_ATARI */ +#ifdef CONFIG_MVME147 + is_not_mvme147(2f) +1: btst #2,M147_SCC_CTRL_A + jeq 1b + moveb %d0,M147_SCC_DATA_A + jbra L(serial_putc_done) +2: +#endif + #ifdef CONFIG_MVME16x is_not_mvme16x(2f) /* @@ -2805,7 +2913,7 @@ func_start serial_putc,%d0/%d1/%a0/%a1 moveml %sp@+,%d0-%d7/%a2-%a6 jbra L(serial_putc_done) 2: -#endif CONFIG_MVME162 | CONFIG_MVME167 +#endif CONFIG_MVME16x #ifdef CONFIG_BVME6000 is_not_bvme6000(2f) @@ -2819,6 +2927,18 @@ func_start serial_putc,%d0/%d1/%a0/%a1 2: #endif +#ifdef CONFIG_Q40 + is_not_q40(2f) + tst.l %pc@(L(q40_do_debug)) /* only debug if requested */ + beq 2f + lea %pc@(SYMBOL_NAME(q40_mem_cptr)),%a1 + move.l %a1@,%a0 + move.b %d0,%a0@ + addq.l #4,%a0 + move.l %a0,%a1@ +2: +#endif + L(serial_putc_done): func_return serial_putc @@ -3472,6 +3592,10 @@ L(kernel_pgdir_ptr): L(temp_mmap_mem): .long 0 +#if defined (CONFIG_MVME147) +M147_SCC_CTRL_A = 0xfffe3002 +M147_SCC_DATA_A = 0xfffe3003 +#endif #if defined (CONFIG_BVME6000) BVME_SCC_CTRL_A = 0xffb0000b @@ -3509,3 +3633,9 @@ SYMBOL_NAME_LABEL(m68k_supervisor_cachemode) SYMBOL_NAME_LABEL(mvme_bdid_ptr) .long 0 #endif +#if defined(CONFIG_Q40) +SYMBOL_NAME_LABEL(q40_mem_cptr) + .long 0 +L(q40_do_debug): + .long 0 +#endif diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index 20e39f17f..66fc41064 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -38,6 +38,10 @@ #include <asm/page.h> #include <asm/machdep.h> +#ifdef CONFIG_Q40 +#include <asm/q40ints.h> +#endif + /* table for system interrupt handlers */ static irq_handler_t irq_list[SYS_IRQS]; @@ -177,14 +181,24 @@ void sys_free_irq(unsigned int irq, void *dev_id) /* * Do we need these probe functions on the m68k? + * + * ... may be usefull with ISA devices */ unsigned long probe_irq_on (void) { +#ifdef CONFIG_Q40 + if (MACH_IS_Q40) + return q40_probe_irq_on(); +#endif return 0; } int probe_irq_off (unsigned long irqs) { +#ifdef CONFIG_Q40 + if (MACH_IS_Q40) + return q40_probe_irq_off(irqs); +#endif return 0; } diff --git a/arch/m68k/kernel/m68k_defs.h b/arch/m68k/kernel/m68k_defs.h index b32e6a1c9..4926a6dad 100644 --- a/arch/m68k/kernel/m68k_defs.h +++ b/arch/m68k/kernel/m68k_defs.h @@ -3,6 +3,47 @@ */ #define TS_MAGICKEY 0x5a5a5a5a -#define TS_TSS 482 -#define TS_ESP0 502 -#define TS_FPU 506 +#define TASK_STATE 0 +#define TASK_FLAGS 4 +#define TASK_SIGPENDING 8 +#define TASK_NEEDRESCHED 20 +#define TASK_TSS 470 +#define TASK_MM 622 +#define TSS_KSP 0 +#define TSS_USP 4 +#define TSS_SR 8 +#define TSS_FS 10 +#define TSS_CRP 12 +#define TSS_ESP0 20 +#define TSS_FPREG 24 +#define TSS_FPCNTL 120 +#define TSS_FPSTATE 132 +#define PT_D0 32 +#define PT_ORIG_D0 36 +#define PT_SR 44 +#define PT_VECTOR 50 +#define IRQ_HANDLER 0 +#define IRQ_DEVID 8 +#define IRQ_NEXT 16 +#define STAT_IRQ 120 +#define BIR_TAG 0 +#define BIR_SIZE 2 +#define BIR_DATA 4 +#define FBCON_FONT_DESC_IDX 0 +#define FBCON_FONT_DESC_NAME 4 +#define FBCON_FONT_DESC_WIDTH 8 +#define FBCON_FONT_DESC_HEIGHT 12 +#define FBCON_FONT_DESC_DATA 16 +#define FBCON_FONT_DESC_PREF 20 +#define CUSTOMBASE -2132807680 +#define C_INTENAR 28 +#define C_INTREQR 30 +#define C_INTENA 154 +#define C_INTREQ 156 +#define C_SERDATR 24 +#define C_SERDAT 48 +#define C_SERPER 50 +#define CIAABASE -2134908927 +#define CIABBASE -2134913024 +#define C_PRA 0 +#define ZTWOBASE -2147483648 diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 793ca6ec7..5aae5953c 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -18,6 +18,7 @@ #include <asm/checksum.h> #include <asm/hardirq.h> #include <asm/softirq.h> +#include <asm/m68kserial.h> asmlinkage long long __ashrdi3 (long long, int); extern char m68k_debug_device[]; @@ -54,6 +55,8 @@ EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(kernel_set_cachemode); EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); @@ -69,4 +72,5 @@ EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); +EXPORT_SYMBOL_NOVERS(__down_failed_trylock); EXPORT_SYMBOL_NOVERS(__up_wakeup); diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index c7c3f458e..97f3bd151 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -169,15 +169,7 @@ asmlinkage int m68k_fork(struct pt_regs *regs) asmlinkage int m68k_vfork(struct pt_regs *regs) { - int child; - struct semaphore sem = MUTEX_LOCKED; - - child = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs); - - if (child > 0) - down(&sem); - - return child; + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs); } asmlinkage int m68k_clone(struct pt_regs *regs) @@ -190,7 +182,7 @@ asmlinkage int m68k_clone(struct pt_regs *regs) newsp = regs->d2; if (!newsp) newsp = rdusp(); - return do_fork(clone_flags & ~CLONE_VFORK, newsp, regs); + return do_fork(clone_flags, newsp, regs); } int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 5aeb2534b..e9254cfa7 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -312,6 +312,7 @@ static int write_long(struct task_struct * tsk, unsigned long addr, asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; + unsigned long flags; int ret; lock_kernel(); @@ -343,21 +344,22 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ if (child->flags & PF_PTRACED) goto out; child->flags |= PF_PTRACED; - if (child->p_pptr != current) { - unsigned long flags; - write_lock_irqsave(&tasklist_lock, flags); + write_lock_irqsave(&tasklist_lock, flags); + if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); + send_sig(SIGSTOP, child, 1); ret = 0; goto out; @@ -502,7 +504,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } case PTRACE_DETACH: { /* detach a process that was attached. */ - unsigned long flags; long tmp; ret = -EIO; diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index ade12c46d..39dcd9c15 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -77,10 +77,26 @@ int (*mach_hwclk) (int, struct hwclk_time*) = NULL; int (*mach_set_clock_mmss) (unsigned long) = NULL; void (*mach_reset)( void ); long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ -#if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) +#if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) || defined(CONFIG_BLK_DEV_FD) void (*mach_floppy_setup) (char *, int *) __initdata = NULL; void (*mach_floppy_eject) (void) = NULL; #endif +struct serial_struct; +#ifdef CONFIG_SERIAL +long serial_rs_init(void); +int serial_register_serial(struct serial_struct *); +void serial_unregister_serial(int); +long ser_console_init(long, long ); +#endif +#if defined(CONFIG_USERIAL)||defined(CONFIG_BVME6000_SCC)||defined(CONFIG_MVME162_SCC)||defined(CONFIG_HPDCA)||defined(CONFIG_WHIPPET_SERIAL)||defined(CONFIG_MULTIFACE_III_TTY)||defined(CONFIG_GVPIOEXT)||defined(CONFIG_AMIGA_BUILTIN_SERIAL)||defined(CONFIG_MAC_SCC)||defined(CONFIG_ATARI_MIDI)||defined(CONFIG_ATARI_SCC)||defined(CONFIG_ATARI_MFPSER) +#define M68K_SERIAL +#endif +#ifdef M68K_SERIAL +long m68k_rs_init(void); +int m68k_register_serial(struct serial_struct *); +void m68k_unregister_serial(int); +long m68k_serial_console_init(long, long ); +#endif #ifdef CONFIG_HEARTBEAT void (*mach_heartbeat) (int) = NULL; #endif @@ -97,15 +113,19 @@ char *mach_sysrq_xlate = NULL; extern int amiga_parse_bootinfo(const struct bi_record *); extern int atari_parse_bootinfo(const struct bi_record *); extern int mac_parse_bootinfo(const struct bi_record *); +extern int q40_parse_bootinfo(const struct bi_record *); extern void config_amiga(void); extern void config_atari(void); extern void config_mac(void); extern void config_sun3(void); extern void config_apollo(void); +extern void config_mvme147(void); extern void config_mvme16x(void); extern void config_bvme6000(void); extern void config_hp300(void); +extern void config_q40(void); +extern void config_sun3x(void); #define MASK_256K 0xfffc0000 @@ -149,6 +169,8 @@ __initfunc(static void m68k_parse_bootinfo(const struct bi_record *record)) unknown = atari_parse_bootinfo(record); else if (MACH_IS_MAC) unknown = mac_parse_bootinfo(record); + else if (MACH_IS_Q40) + unknown = q40_parse_bootinfo(record); else unknown = 1; } @@ -258,6 +280,11 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, config_apollo(); break; #endif +#ifdef CONFIG_MVME147 + case MACH_MVME147: + config_mvme147(); + break; +#endif #ifdef CONFIG_MVME16x case MACH_MVME16x: config_mvme16x(); @@ -273,6 +300,16 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, config_hp300(); break; #endif +#ifdef CONFIG_Q40 + case MACH_Q40: + config_q40(); + break; +#endif +#ifdef CONFIG_SUN3X + case MACH_SUN3X: + config_sun3x(); + break; +#endif default: panic ("No configuration setup"); } @@ -384,7 +421,52 @@ int get_hardware_list(char *buffer) return(len); } -#if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) +#if defined(CONFIG_SERIAL) || defined(M68K_SERIAL) +int rs_init(void) +{ +#ifdef CONFIG_SERIAL + if (MACH_IS_Q40) + return serial_rs_init(); +#endif +#ifdef M68K_SERIAL + return m68k_rs_init(); +#endif +} +int register_serial(struct serial_struct *p) +{ +#ifdef CONFIG_SERIAL + if (MACH_IS_Q40) + return serial_register_serial(p); +#endif +#ifdef M68K_SERIAL + return m68k_register_serial(p); +#endif +} +void unregister_serial(int i) +{ +#ifdef CONFIG_SERIAL + if (MACH_IS_Q40) + serial_unregister_serial(i); +#endif +#ifdef M68K_SERIAL + m68k_unregister_serial(i); +#endif +} +#ifdef CONFIG_SERIAL_CONSOLE +long serial_console_init(long kmem_start, long kmem_end) +{ +#ifdef CONFIG_SERIAL + if (MACH_IS_Q40) + return ser_console_init(kmem_start, kmem_end); +#endif +#if defined(M68K_SERIAL) && defined(CONFIG_SERIAL_CONSOLE) + return m68k_serial_console_init(kmem_start, kmem_end); +#endif +} +#endif +#endif + +#if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) || defined(CONFIG_BLK_DEV_FD) __initfunc(void floppy_setup(char *str, int *ints)) { if (mach_floppy_setup) @@ -399,7 +481,7 @@ void floppy_eject(void) #endif /* for "kbd-reset" cmdline param */ -void __init kbd_reset_setup(char *str, int *ints) +__initfunc(void kbd_reset_setup(char *str, int *ints)) { } diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c index 107d0de7f..9b98fdb75 100644 --- a/arch/m68k/kernel/time.c +++ b/arch/m68k/kernel/time.c @@ -198,8 +198,7 @@ void do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + write_unlock_irq(&xtime_lock); } diff --git a/arch/m68k/lib/semaphore.S b/arch/m68k/lib/semaphore.S index 8e4141149..842f83f64 100644 --- a/arch/m68k/lib/semaphore.S +++ b/arch/m68k/lib/semaphore.S @@ -32,6 +32,16 @@ ENTRY(__down_failed_interruptible) movel (%sp)+,%a0 rts +ENTRY(__down_failed_trylock) + movel %a0,-(%sp) + movel %d1,-(%sp) + movel %a1,-(%sp) + jbsr SYMBOL_NAME(__down_trylock) + movel (%sp)+,%a1 + movel (%sp)+,%d1 + movel (%sp)+,%a0 + rts + ENTRY(__up_wakeup) moveml %a0/%d0/%d1,-(%sp) movel %a1,-(%sp) diff --git a/arch/m68k/mac/mackeyb.c b/arch/m68k/mac/mackeyb.c index 5a6ae7c75..a128a6314 100644 --- a/arch/m68k/mac/mackeyb.c +++ b/arch/m68k/mac/mackeyb.c @@ -60,7 +60,7 @@ static void input_keycode(int, int); extern struct kbd_struct kbd_table[]; extern void adb_bus_init(void); -extern void handle_scancode(unsigned char); +extern void handle_scancode(unsigned char, int); extern void put_queue(int); /* keyb */ @@ -387,7 +387,7 @@ input_keycode(int keycode, int repeat) */ switch (keycode) { case 0x39: - handle_scancode(keycode); /* down */ + handle_scancode(keycode, 1); /* down */ up_flag = 0x80; /* see below ... */ mark_bh(KEYBOARD_BH); break; @@ -397,7 +397,7 @@ input_keycode(int keycode, int repeat) } } - handle_scancode(keycode + up_flag); + handle_scancode(keycode, !up_flag); } static void diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 8e520702f..9a41dc279 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -123,7 +123,7 @@ void show_mem(void) unsigned long mm_cachebits = 0; #endif -static pte_t *__init kernel_page_table(unsigned long *memavailp) +__initfunc(static pte_t * kernel_page_table(unsigned long *memavailp)) { pte_t *ptablep; @@ -140,15 +140,19 @@ static pte_t *__init kernel_page_table(unsigned long *memavailp) static pmd_t *last_pgtable __initdata = NULL; -static pmd_t *__init kernel_ptr_table(unsigned long *memavailp) +__initfunc(static pmd_t * kernel_ptr_table(unsigned long *memavailp)) { if (!last_pgtable) { unsigned long pmd, last; int i; + /* Find the last ptr table that was used in head.S and + * reuse the remaining space in that page for further + * ptr tables. + */ last = (unsigned long)kernel_pg_dir; for (i = 0; i < PTRS_PER_PGD; i++) { - if (!pgd_val(kernel_pg_dir[i])) + if (!pgd_present(kernel_pg_dir[i])) continue; pmd = pgd_page(kernel_pg_dir[i]); if (pmd > last) @@ -175,8 +179,8 @@ static pmd_t *__init kernel_ptr_table(unsigned long *memavailp) return last_pgtable; } -static unsigned long __init -map_chunk (unsigned long addr, long size, unsigned long *memavailp) +__initfunc(static unsigned long +map_chunk (unsigned long addr, long size, unsigned long *memavailp)) { #define PTRTREESIZE (256*1024) #define ROOTTREESIZE (32*1024*1024) @@ -282,8 +286,8 @@ extern char __init_begin, __init_end; * paging_init() continues the virtual memory environment setup which * was begun by the code in arch/head.S. */ -unsigned long __init paging_init(unsigned long start_mem, - unsigned long end_mem) +__initfunc(unsigned long paging_init(unsigned long start_mem, + unsigned long end_mem)) { int chunk; unsigned long mem_avail = 0; @@ -392,7 +396,7 @@ unsigned long __init paging_init(unsigned long start_mem, return PAGE_ALIGN(free_area_init(start_mem, end_mem)); } -void __init mem_init(unsigned long start_mem, unsigned long end_mem) +__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) { int codepages = 0; int datapages = 0; @@ -443,7 +447,7 @@ void __init mem_init(unsigned long start_mem, unsigned long end_mem) /* insert pointer tables allocated so far into the tablelist */ init_pointer_table((unsigned long)kernel_pg_dir); for (i = 0; i < PTRS_PER_PGD; i++) { - if (pgd_val(kernel_pg_dir[i])) + if (pgd_present(kernel_pg_dir[i])) init_pointer_table(pgd_page(kernel_pg_dir[i])); } diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c index d2cd29011..3fefe4e03 100644 --- a/arch/m68k/mm/kmap.c +++ b/arch/m68k/mm/kmap.c @@ -116,6 +116,14 @@ void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) if (!size || size > physaddr + size) return NULL; +#ifdef CONFIG_AMIGA + if (MACH_IS_AMIGA) { + if ((physaddr >= 0x40000000) && (physaddr + size < 0x60000000) + && (cacheflag == IOMAP_NOCACHE_SER)) + return (void *)physaddr; + } +#endif + #ifdef DEBUG printk("ioremap: 0x%lx,0x%lx(%d) - ", physaddr, size, cacheflag); #endif @@ -174,7 +182,7 @@ void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) } } - while (size > 0) { + while ((long)size > 0) { #ifdef DEBUG if (!(virtaddr & (PTRTREESIZE-1))) printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr); @@ -187,7 +195,7 @@ void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) } if (CPU_IS_020_OR_030) { - pmd_dir->pmd[(virtaddr/PTRTREESIZE)&-16] = physaddr; + pmd_dir->pmd[(virtaddr/PTRTREESIZE) & 15] = physaddr; physaddr += PTRTREESIZE; virtaddr += PTRTREESIZE; size -= PTRTREESIZE; @@ -217,7 +225,14 @@ void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) */ void iounmap(void *addr) { +#ifdef CONFIG_AMIGA + if ((!MACH_IS_AMIGA) || + (((unsigned long)addr < 0x40000000) || + ((unsigned long)addr > 0x60000000))) + free_io_area(addr); +#else free_io_area(addr); +#endif } /* @@ -232,7 +247,7 @@ void __iounmap(void *addr, unsigned long size) pmd_t *pmd_dir; pte_t *pte_dir; - while (size > 0) { + while ((long)size > 0) { pgd_dir = pgd_offset_k(virtaddr); if (pgd_bad(*pgd_dir)) { printk("iounmap: bad pgd(%08lx)\n", pgd_val(*pgd_dir)); @@ -242,7 +257,7 @@ void __iounmap(void *addr, unsigned long size) pmd_dir = pmd_offset(pgd_dir, virtaddr); if (CPU_IS_020_OR_030) { - int pmd_off = (virtaddr/PTRTREESIZE) & -16; + int pmd_off = (virtaddr/PTRTREESIZE) & 15; if ((pmd_dir->pmd[pmd_off] & _DESCTYPE_MASK) == _PAGE_PRESENT) { pmd_dir->pmd[pmd_off] = 0; @@ -308,7 +323,7 @@ void kernel_set_cachemode(void *addr, unsigned long size, int cmode) } } - while (size > 0) { + while ((long)size > 0) { pgd_dir = pgd_offset_k(virtaddr); if (pgd_bad(*pgd_dir)) { printk("iocachemode: bad pgd(%08lx)\n", pgd_val(*pgd_dir)); @@ -318,7 +333,7 @@ void kernel_set_cachemode(void *addr, unsigned long size, int cmode) pmd_dir = pmd_offset(pgd_dir, virtaddr); if (CPU_IS_020_OR_030) { - int pmd_off = (virtaddr/PTRTREESIZE) & -16; + int pmd_off = (virtaddr/PTRTREESIZE) & 15; if ((pmd_dir->pmd[pmd_off] & _DESCTYPE_MASK) == _PAGE_PRESENT) { pmd_dir->pmd[pmd_off] = (pmd_dir->pmd[pmd_off] & diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index a97578ec2..a6d496571 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -11,6 +11,7 @@ #include <linux/types.h> #include <linux/malloc.h> #include <linux/init.h> +#include <linux/pagemap.h> #include <asm/setup.h> #include <asm/segment.h> @@ -93,12 +94,11 @@ typedef struct page ptable_desc; static ptable_desc ptable_list = { &ptable_list, &ptable_list }; #define PD_MARKBITS(dp) (*(unsigned char *)&(dp)->offset) -#define PD_PAGE(dp) (PAGE_OFFSET + ((dp)->map_nr << PAGE_SHIFT)) #define PAGE_PD(page) ((ptable_desc *)&mem_map[MAP_NR(page)]) #define PTABLE_SIZE (PTRS_PER_PMD * sizeof(pmd_t)) -void __init init_pointer_table(unsigned long ptable) +__initfunc(void init_pointer_table(unsigned long ptable)) { ptable_desc *dp; unsigned long page = ptable & PAGE_MASK; @@ -166,7 +166,7 @@ pmd_t *get_pointer_table (void) (dp->next = last->next)->prev = dp; (dp->prev = last)->next = dp; } - return (pmd_t *) (PD_PAGE(dp) + off); + return (pmd_t *) (page_address(dp) + off); } int free_pointer_table (pmd_t *ptable) @@ -215,8 +215,8 @@ static unsigned long transp_transl_matches( unsigned long regval, /* function code match? */ base = (regval >> 4) & 7; mask = ~(regval & 7); - if ((SUPER_DATA & mask) != (base & mask)) - return( 0 ); + if (((SUPER_DATA ^ base) & mask) != 0) + return 0; } else { /* must not be user-only */ @@ -226,8 +226,8 @@ static unsigned long transp_transl_matches( unsigned long regval, /* address match? */ base = regval & 0xff000000; - mask = ~((regval << 8) & 0xff000000); - return( (vaddr & mask) == (base & mask) ); + mask = ~(regval << 8) & 0xff000000; + return ((vaddr ^ base) & mask) == 0; } #ifndef CONFIG_SINGLE_MEMORY_CHUNK diff --git a/arch/m68k/mvme147/147ints.c b/arch/m68k/mvme147/147ints.c new file mode 100644 index 000000000..3121ad94f --- /dev/null +++ b/arch/m68k/mvme147/147ints.c @@ -0,0 +1,142 @@ +/* + * arch/m68k/mvme147/147ints.c + * + * Copyright (C) 1997 Richard Hirst [richard@sleepie.demon.co.uk] + * + * based on amiints.c -- Amiga Linux interrupt handling code + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> + +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/traps.h> + +static void mvme147_defhand (int irq, void *dev_id, struct pt_regs *fp); + +/* + * This should ideally be 4 elements only, for speed. + */ + +static struct { + void (*handler)(int, void *, struct pt_regs *); + unsigned long flags; + void *dev_id; + const char *devname; + unsigned count; +} irq_tab[256]; + +/* + * void mvme147_init_IRQ (void) + * + * Parameters: None + * + * Returns: Nothing + * + * This function is called during kernel startup to initialize + * the mvme147 IRQ handling routines. + */ + +void mvme147_init_IRQ (void) +{ + int i; + + for (i = 0; i < 256; i++) { + irq_tab[i].handler = mvme147_defhand; + irq_tab[i].flags = IRQ_FLG_STD; + irq_tab[i].dev_id = NULL; + irq_tab[i].devname = NULL; + irq_tab[i].count = 0; + } +} + +int mvme147_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + if (irq > 255) { + printk("%s: Incorrect IRQ %d from %s\n", __FUNCTION__, irq, devname); + return -ENXIO; + } + if (!(irq_tab[irq].flags & IRQ_FLG_STD)) { + if (irq_tab[irq].flags & IRQ_FLG_LOCK) { + printk("%s: IRQ %d from %s is not replaceable\n", + __FUNCTION__, irq, irq_tab[irq].devname); + return -EBUSY; + } + if (flags & IRQ_FLG_REPLACE) { + printk("%s: %s can't replace IRQ %d from %s\n", + __FUNCTION__, devname, irq, irq_tab[irq].devname); + return -EBUSY; + } + } + irq_tab[irq].handler = handler; + irq_tab[irq].flags = flags; + irq_tab[irq].dev_id = dev_id; + irq_tab[irq].devname = devname; + return 0; +} + +void mvme147_free_irq(unsigned int irq, void *dev_id) +{ + if (irq > 255) { + printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq); + return; + } + if (irq_tab[irq].dev_id != dev_id) + printk("%s: Removing probably wrong IRQ %d from %s\n", + __FUNCTION__, irq, irq_tab[irq].devname); + + irq_tab[irq].handler = mvme147_defhand; + irq_tab[irq].flags = IRQ_FLG_STD; + irq_tab[irq].dev_id = NULL; + irq_tab[irq].devname = NULL; +} + +void mvme147_process_int (unsigned long vec, struct pt_regs *fp) +{ + if (vec > 255) + printk ("mvme147_process_int: Illegal vector %ld\n", vec); + else + { + irq_tab[vec].count++; + irq_tab[vec].handler(vec, irq_tab[vec].dev_id, fp); + } +} + +int mvme147_get_irq_list (char *buf) +{ + int i, len = 0; + + for (i = 0; i < 256; i++) { + if (irq_tab[i].count) + len += sprintf (buf+len, "Vec 0x%02x: %8d %s\n", + i, irq_tab[i].count, + irq_tab[i].devname ? irq_tab[i].devname : "free"); + } + return len; +} + + +static void mvme147_defhand (int irq, void *dev_id, struct pt_regs *fp) +{ + printk ("Unknown interrupt 0x%02x\n", irq); +} + +void mvme147_enable_irq (unsigned int irq) +{ +} + + +void mvme147_disable_irq (unsigned int irq) +{ +} + diff --git a/arch/m68k/mvme147/Makefile b/arch/m68k/mvme147/Makefile new file mode 100644 index 000000000..c0f064118 --- /dev/null +++ b/arch/m68k/mvme147/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Linux arch/m68k/mvme147 source directory +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +O_TARGET := mvme147.o +O_OBJS := config.o 147ints.o + + +include $(TOPDIR)/Rules.make + diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c new file mode 100644 index 000000000..639d5f640 --- /dev/null +++ b/arch/m68k/mvme147/config.c @@ -0,0 +1,240 @@ +/* + * arch/m68k/mvme147/config.c + * + * Copyright (C) 1996 Dave Frascone [chaos@mindspring.com] + * Cloned from Richard Hirst [richard@sleepie.demon.co.uk] + * + * Based on: + * + * Copyright (C) 1993 Hamish Macdonald + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +#include <linux/config.h> +#include <stdarg.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/major.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/machdep.h> +#include <asm/mvme147hw.h> + + +extern void mvme147_process_int (int level, struct pt_regs *regs); +extern void mvme147_init_IRQ (void); +extern void mvme147_free_irq (unsigned int, void *); +extern int mvme147_get_irq_list (char *); +extern void mvme147_enable_irq (unsigned int); +extern void mvme147_disable_irq (unsigned int); +static void mvme147_get_model(char *model); +static int mvme147_get_hardware_list(char *buffer); +extern int mvme147_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id); +extern void mvme147_sched_init(void (*handler)(int, void *, struct pt_regs *)); +extern int mvme147_keyb_init(void); +extern int mvme147_kbdrate (struct kbd_repeat *); +extern unsigned long mvme147_gettimeoffset (void); +extern void mvme147_gettod (int *year, int *mon, int *day, int *hour, + int *min, int *sec); +extern int mvme147_hwclk (int, struct hwclk_time *); +extern int mvme147_set_clock_mmss (unsigned long); +extern void mvme147_check_partition (struct gendisk *hd, unsigned int dev); +extern void mvme147_reset (void); +extern void mvme147_waitbut(void); + + +static int bcd2int (unsigned char b); + +/* Save tick handler routine pointer, will point to do_timer() in + * kernel/sched.c, called via mvme147_process_int() */ + +void (*tick_handler)(int, void *, struct pt_regs *); + + +int mvme147_kbdrate (struct kbd_repeat *k) +{ + return 0; +} + +void mvme147_reset() +{ + printk ("\r\n\nCalled mvme147_reset\r\n"); + m147_pcc->watchdog = 0x0a; /* Clear timer */ + m147_pcc->watchdog = 0xa5; /* Enable watchdog - 100ms to reset */ + while (1) + ; +} + +static void mvme147_get_model(char *model) +{ + sprintf(model, "Motorola MVME147"); +} + + +static int mvme147_get_hardware_list(char *buffer) +{ + *buffer = '\0'; + + return 0; +} + + +__initfunc(void config_mvme147(void)) +{ + mach_sched_init = mvme147_sched_init; + mach_keyb_init = mvme147_keyb_init; + mach_kbdrate = mvme147_kbdrate; + mach_init_IRQ = mvme147_init_IRQ; + mach_gettimeoffset = mvme147_gettimeoffset; + mach_gettod = mvme147_gettod; + mach_hwclk = mvme147_hwclk; + mach_set_clock_mmss = mvme147_set_clock_mmss; + mach_reset = mvme147_reset; + mach_free_irq = mvme147_free_irq; + mach_process_int = mvme147_process_int; + mach_get_irq_list = mvme147_get_irq_list; + mach_request_irq = mvme147_request_irq; + enable_irq = mvme147_enable_irq; + disable_irq = mvme147_disable_irq; + mach_get_model = mvme147_get_model; + mach_get_hardware_list = mvme147_get_hardware_list; +} + + +/* Using pcc tick timer 1 */ + +static void mvme147_timer_int (int irq, void *dev_id, struct pt_regs *fp) +{ + m147_pcc->t1_int_cntrl = PCC_TIMER_INT_CLR; + m147_pcc->t1_int_cntrl = PCC_INT_ENAB|PCC_LEVEL_TIMER1; + tick_handler(irq, dev_id, fp); +} + + +void mvme147_sched_init (void (*timer_routine)(int, void *, struct pt_regs *)) +{ + tick_handler = timer_routine; + request_irq (PCC_IRQ_TIMER1, mvme147_timer_int, + IRQ_FLG_REPLACE, "timer 1", NULL); + + /* Init the clock with a value */ + /* our clock goes off every 6.25us */ + m147_pcc->t1_preload = PCC_TIMER_PRELOAD; + m147_pcc->t1_cntrl = 0x0; /* clear timer */ + m147_pcc->t1_cntrl = 0x3; /* start timer */ + m147_pcc->t1_int_cntrl = PCC_TIMER_INT_CLR; /* clear pending ints */ + m147_pcc->t1_int_cntrl = PCC_INT_ENAB|PCC_LEVEL_TIMER1; +} + +/* This is always executed with interrupts disabled. */ +/* XXX There are race hazards in this code XXX */ +unsigned long mvme147_gettimeoffset (void) +{ + volatile unsigned short *cp = (volatile unsigned short *)0xfffe1012; + unsigned short n; + + n = *cp; + while (n != *cp) + n = *cp; + + n -= PCC_TIMER_PRELOAD; + return (unsigned long)n * 25 / 4; +} + +extern void mvme147_gettod (int *year, int *mon, int *day, int *hour, + int *min, int *sec) +{ + m147_rtc->ctrl = RTC_READ; + *year = bcd2int (m147_rtc->bcd_year); + *mon = bcd2int (m147_rtc->bcd_mth); + *day = bcd2int (m147_rtc->bcd_dom); + *hour = bcd2int (m147_rtc->bcd_hr); + *min = bcd2int (m147_rtc->bcd_min); + *sec = bcd2int (m147_rtc->bcd_sec); + m147_rtc->ctrl = 0; +} + +static int bcd2int (unsigned char b) +{ + return ((b>>4)*10 + (b&15)); +} + +int mvme147_hwclk(int op, struct hwclk_time *t) +{ + return 0; +} + +int mvme147_set_clock_mmss (unsigned long nowtime) +{ + return 0; +} + +int mvme147_keyb_init (void) +{ + return 0; +} + +/*------------------- Serial console stuff ------------------------*/ + +void m147_scc_write(struct console *co, const char *str, unsigned cnt); + + +void mvme147_init_console_port (struct console *co, int cflag) +{ + co->write = m147_scc_write; +} + + +static void scc_delay (void) +{ + int n; + volatile int trash; + + for (n = 0; n < 20; n++) + trash = n; +} + +static void scc_write (char ch) +{ + volatile char *p = (volatile char *)M147_SCC_A_ADDR; + + do { + scc_delay(); + } + while (!(*p & 4)); + scc_delay(); + *p = 8; + scc_delay(); + *p = ch; +} + + +void m147_scc_write (struct console *co, const char *str, unsigned count) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + while (count--) + { + if (*str == '\n') + scc_write ('\r'); + scc_write (*str++); + } + restore_flags(flags); +} + diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c index 838c3bbac..db7eb3137 100644 --- a/arch/m68k/mvme16x/config.c +++ b/arch/m68k/mvme16x/config.c @@ -335,10 +335,10 @@ void mvme16x_init_console_port (struct console *co, int cflag) static void scc_delay (void) { int n; - char i; + volatile int trash; for (n = 0; n < 20; n++) - i = *(volatile char *)0; + trash = n; } static void scc_write (char ch) diff --git a/arch/m68k/q40/Makefile b/arch/m68k/q40/Makefile new file mode 100644 index 000000000..90858884f --- /dev/null +++ b/arch/m68k/q40/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Linux arch/m68k/q40 source directory +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := q40.o +O_OBJS := config.o q40ints.o + + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/q40/README b/arch/m68k/q40/README new file mode 100644 index 000000000..5433796cb --- /dev/null +++ b/arch/m68k/q40/README @@ -0,0 +1,121 @@ +Linux for the Q40 +================= + +You may try http://www.geocities.com/SiliconValley/Bay/2602/ for +some up to date information. Booter and other tools will be also +available from this place and ftp.uni-erlangen.de/linux/680x0/q40/ +and mirrors. + +Hints to documentation usually refer to the linux source tree in +/usr/src/linux unless URL given. + +It seems IRQ unmasking can't be safely done on a Q40. Autoprobing is +not yet implemented - do not try it! (See below) + +For a list of kernel commandline options read the documentation for the +particular device drivers. + +The floppy imposes a very high interrupt load on the CPU, approx 30K/s. +When something blocks interrupts (HD) it will loose some of them, so far +this is not known to have caused any data loss. On hihgly loaded systems +it can make the floppy very slow. Other Q40 OS' simply poll the floppy +for this reason - something that can't be done in Linux. +Only possible cure is getting a 82072 contoler with fifo instead of +the 8272A + +drivers used by the Q40, appart from the very obvious (console etc.): + drivers/char/q40_keyb.c # use PC keymaps for national keyboards + serial.c # normal PC driver - any speed + lp.c # printer driver + char/joystick/* # most of this should work + block/q40ide.c # startup for ide + ide* # see Documentation/ide.txt + floppy.c # normal PC driver, DMA emu in asm/floppy.h + # and arch/m68k/kernel/entry.S + # see drivers/block/README.fd + video/q40fb.c + misc/parport_pc.c + +Various other PC drivers can be enabled simply by adding them to +arch/m68k/config.in, especially 8 bit devices should be without any +problems. For cards using 16bit io/mem more care is required, like +checking byteorder issues, hacking memcpy_*_io etc. + + +Debugging +========= + +Upon startup the kernel will usually output "ABCQGHIJ" into the SRAM, +preceded by the booter signature. This is a trace just in case something +went wrong during earliest setup stages. +*Changed* to preserve SRAM contents by default, this is only done when +requested - SRAM must start with '%LX$' signature to do this. '-d' option +to 'lxx' loader enables this. + +SRAM can also be used as additional console device, use debug=mem. +This will save kernel startup msgs into SRAM, the screen will display +only the penguin - and shell prompt if it gets that far.. + +Serial console works and can also be used for debugging, provided serial +initialisation works. + +Most problems seem to be caused by fawlty or badly configured io-cards or +harddrives anyway..there are so many things that can go wrong here. +Make sure to configure the parallel port as SPP for first testing..the +Q40 may have trouble with parallel interrupts. + + +Q40 Hardware Description +======================== + +This is just an overview, see asm-m68k/* for details ask if you have any +questions. + +The Q40 consists of a 68040@40 MHz, 1MB video RAM, up to 32MB RAM, AT-style +keyboard interface, 1 Programmable LED, 2 8bit DACs and up to 1MB ROM, 1MB +shadow ROM. + +Most interfacing like floppy, hd, serial, parallel ports is done via ISA +slots. The ISA io and mem range is mapped (sparse&byteswapped!) into separate +regions of the memory. +The main interrupt register IIRQ_REG will indicate whether an IRQ was internal +or from some ISA devices, EIRQ_REG can distinguish up to 8 ISA IRQs. + +The Q40 custom chip is programmable to provide 2 periodic timers: + - 50 or 200 Hz - level 2, !!THIS CANT BE DISABLED!! + - 10 or 20 KHz - level 4 (and possibly 6 - hardware decoding..) + +Linux uses the 200 Hz interrupt for timer and beep by default. + + +Interrupts +========== + +q40 master chip handles only level triggered interrupts :-(( +further limitation is no disabling etc. Unless someone finds +some ingenious clue this means autoprobing will never work. +Parallel port interrupts cause most trouble.. + +IRQ sharing is not yet implemented. + + +Keyboard +======== + +q40 receives AT make/break codes from the keyboard, these are translated to +the PC scancodes x86 Linux uses. So by theory every national keyboard should +work just by loading the apropriate x86 keytable - see any national-HOWTO. + +Unfortunately the AT->PC translation isn't quite trivial and even worse, my +documentation of it is absolutely minimal - thus some exotic keys may not +behave exactly as expected. + +There is still hope that it can be fixed completely though. If you encounter +problems, email me idealy this: + - exact keypress/release sequence + - 'showkey -s' run on q40, non-X session + - 'showkey -s' run on a PC, non-X session + - AT codes as displayed by the q40 debuging ROM +btw if the showkey output from PC and Q40 doesn't differ then you have some +classic configuration problem - don't send me anything in this case + diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c new file mode 100644 index 000000000..e3c35c60c --- /dev/null +++ b/arch/m68k/q40/config.c @@ -0,0 +1,425 @@ +/* + * arch/m68k/q40/config.c + * + * originally based on: + * + * linux/bvme/config.c + * + * Copyright (C) 1993 Hamish Macdonald + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +#include <stdarg.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/major.h> + +#include <asm/bootinfo.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/machdep.h> +#include <asm/q40_master.h> +#include <asm/keyboard.h> + +extern void fd_floppy_eject(void); +extern void fd_floppy_setup(char *str, int *ints); + +extern void q40_process_int (int level, struct pt_regs *regs); +extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */ +extern void q40_init_IRQ (void); +extern void q40_free_irq (unsigned int, void *); +extern int q40_get_irq_list (char *); +extern void q40_enable_irq (unsigned int); +extern void q40_disable_irq (unsigned int); +static void q40_get_model(char *model); +static int q40_get_hardware_list(char *buffer); +extern int q40_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id); +extern void q40_sched_init(void (*handler)(int, void *, struct pt_regs *)); +extern int q40_keyb_init(void); +extern int q40_kbdrate (struct kbd_repeat *); +extern unsigned long q40_gettimeoffset (void); +extern void q40_gettod (int *year, int *mon, int *day, int *hour, + int *min, int *sec); +extern int q40_hwclk (int, struct hwclk_time *); +extern int q40_set_clock_mmss (unsigned long); +extern void q40_reset (void); +extern void q40_waitbut(void); +void q40_set_vectors (void); +extern void (*kd_mksound)(unsigned int, unsigned int); +void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/ ); + +extern char *saved_command_line; +extern char m68k_debug_device[]; +static void q40_mem_console_write(struct console *co, const char *b, + unsigned int count); + +static int ql_ticks=0; +static int sound_ticks=0; + +static unsigned char bcd2bin (unsigned char b); +static unsigned char bin2bcd (unsigned char b); + +static int q40_wait_key(struct console *co){return 0;} +static struct console q40_console_driver = { + "debug", + NULL, /* write */ + NULL, /* read */ + NULL, /* device */ + q40_wait_key, /* wait_key */ + NULL, /* unblank */ + NULL, /* setup */ + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + + +/* Save tick handler routine pointer, will point to do_timer() in + * kernel/sched.c */ + +/* static void (*tick_handler)(int, void *, struct pt_regs *); */ + + +/* early debugging function:*/ +extern char *q40_mem_cptr; /*=(char *)0xff020000;*/ +static int _cpleft; + +static void q40_mem_console_write(struct console *co, const char *s, + unsigned int count) +{ + char *p=(char *)s; + + if (count<_cpleft) + while (count-- >0){ + *q40_mem_cptr=*p++; + q40_mem_cptr+=4; + _cpleft--; + } +} +#if 0 +void printq40(char *str) +{ + int l=strlen(str); + char *p=q40_mem_cptr; + + while (l-- >0 && _cpleft-- >0) + { + *p=*str++; + p+=4; + } + q40_mem_cptr=p; +} +#endif + +#if 0 +int q40_kbdrate (struct kbd_repeat *k) +{ + return 0; +} +#endif + +void q40_reset() +{ + + printk ("\n\n*******************************************\n" + "Called q40_reset : press the RESET button!! \n"); + printk( "*******************************************\n"); + + while(1) + ; +} + +static void q40_get_model(char *model) +{ + sprintf(model, "Q40"); +} + + +/* No hardware options on Q40? */ + +static int q40_get_hardware_list(char *buffer) +{ + *buffer = '\0'; + return 0; +} + + +__initfunc(void config_q40(void)) +{ + mach_sched_init = q40_sched_init; /* ok */ + /*mach_kbdrate = q40_kbdrate;*/ /* unneeded ?*/ + mach_keyb_init = q40_keyb_init; /* OK */ + mach_init_IRQ = q40_init_IRQ; + mach_gettimeoffset = q40_gettimeoffset; + mach_gettod = q40_gettod; + mach_hwclk = q40_hwclk; + mach_set_clock_mmss = q40_set_clock_mmss; +/* mach_mksound = q40_mksound; */ + mach_reset = q40_reset; /* use reset button instead !*/ + mach_free_irq = q40_free_irq; + mach_process_int = q40_process_int; + mach_get_irq_list = q40_get_irq_list; + mach_request_irq = q40_request_irq; + enable_irq = q40_enable_irq; + disable_irq = q40_disable_irq; + mach_default_handler = &q40_sys_default_handler; + mach_get_model = q40_get_model; /* no use..*/ + mach_get_hardware_list = q40_get_hardware_list; /* no use */ + kd_mksound = q40_mksound; + /*mach_kbd_leds = q40kbd_leds;*/ +#ifdef CONFIG_MAGIC_SYSRQ + mach_sysrq_key = 0x54; +#endif + conswitchp = &dummy_con; +#ifdef CONFIG_BLK_DEV_FD + mach_floppy_setup = fd_floppy_setup; + mach_floppy_eject = fd_floppy_eject; + /**/ +#endif + + mach_max_dma_address = 0; /* no DMA at all */ + + +/* userfull for early debuging stages writes kernel messages into SRAM */ + + if (!strncmp( m68k_debug_device,"mem",3 )) + { + /*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/ + _cpleft=2000-((long)q40_mem_cptr-0xff020000)/4; + q40_console_driver.write = q40_mem_console_write; + register_console(&q40_console_driver); + } +} + + +int q40_parse_bootinfo(const struct bi_record *rec) +{ + return 1; /* unknown */ +} + +#define DAC_LEFT ((unsigned char *)0xff008000) +#define DAC_RIGHT ((unsigned char *)0xff008004) +void q40_mksound(unsigned int hz, unsigned int ticks) +{ + /* for now ignore hz, except that hz==0 switches off sound */ + /* simply alternate the ampl 0-255-0-.. at 200Hz */ + if (hz==0) + { + if (sound_ticks) + sound_ticks=1; /* atomic - no irq spinlock used */ + + *DAC_LEFT=0; + *DAC_RIGHT=0; + + return; + } + /* sound itself is done in q40_timer_int */ + if (sound_ticks == 0) sound_ticks=1000; /* pretty long beep */ + sound_ticks=ticks<<1; +} + +static void (*q40_timer_routine)(int, void *, struct pt_regs *); + +static void q40_timer_int (int irq, void *dev_id, struct pt_regs *fp) +{ +#if (HZ==10000) + master_outb(-1,SAMPLE_CLEAR_REG); +#else /* must be 50 or 100 */ + master_outb(-1,FRAME_CLEAR_REG); +#endif + +#if (HZ==100) + ql_ticks = ql_ticks ? 0 : 1; + if (sound_ticks) + { + unsigned char sval=(sound_ticks & 1) ? 0 : 255; + sound_ticks--; + *DAC_LEFT=sval; + *DAC_RIGHT=sval; + } + if (ql_ticks) return; +#endif + q40_timer_routine(irq, dev_id, fp); +} + + +void q40_sched_init (void (*timer_routine)(int, void *, struct pt_regs *)) +{ + int timer_irq; + + q40_timer_routine = timer_routine; + +#if (HZ==10000) + timer_irq=Q40_IRQ_TIMER; +#else + timer_irq=Q40_IRQ_FRAME; +#endif + + /*printk("registering sched/timer IRQ %d\n", timer_irq);*/ + + if (request_irq(timer_irq, q40_timer_int, 0, + "timer", q40_timer_int)) + panic ("Couldn't register timer int"); + +#if (HZ==10000) + master_outb(SAMPLE_LOW,SAMPLE_RATE_REG); + master_outb(-1,SAMPLE_CLEAR_REG); + master_outb(1,SAMPLE_ENABLE_REG); +#else + master_outb(-1,FRAME_CLEAR_REG); /* not necessary ? */ +#if (HZ==100) + master_outb( 1,FRAME_RATE_REG); +#endif +#endif +} + + +unsigned long q40_gettimeoffset (void) +{ +#if (HZ==100) + return 5000*(ql_ticks!=0); +#else + return 0; +#endif +} + +extern void q40_gettod (int *year, int *mon, int *day, int *hour, + int *min, int *sec) +{ + RTC_CTRL |= RTC_READ; + *year = bcd2bin (RTC_YEAR); + *mon = bcd2bin (RTC_MNTH)-1; + *day = bcd2bin (RTC_DATE); + *hour = bcd2bin (RTC_HOUR); + *min = bcd2bin (RTC_MINS); + *sec = bcd2bin (RTC_SECS); + RTC_CTRL &= ~(RTC_READ); + +} + +static unsigned char bcd2bin (unsigned char b) +{ + return ((b>>4)*10 + (b&15)); +} + +static unsigned char bin2bcd (unsigned char b) +{ + return (((b/10)*16) + (b%10)); +} + + +/* + * Looks like op is non-zero for setting the clock, and zero for + * reading the clock. + * + * struct hwclk_time { + * unsigned sec; 0..59 + * unsigned min; 0..59 + * unsigned hour; 0..23 + * unsigned day; 1..31 + * unsigned mon; 0..11 + * unsigned year; 00... + * int wday; 0..6, 0 is Sunday, -1 means unknown/don't set + * }; + */ + +int q40_hwclk(int op, struct hwclk_time *t) +{ + if (op) + { /* Write.... */ + RTC_CTRL |= RTC_WRITE; + + RTC_SECS = bin2bcd(t->sec); + RTC_MINS = bin2bcd(t->min); + RTC_HOUR = bin2bcd(t->hour); + RTC_DATE = bin2bcd(t->day); + RTC_MNTH = bin2bcd(t->mon + 1); + RTC_YEAR = bin2bcd(t->year%100); + if (t->wday >= 0) + RTC_DOW = bin2bcd(t->wday+1); + + RTC_CTRL &= ~(RTC_WRITE); + } + else + { /* Read.... */ + RTC_CTRL |= RTC_READ; + + t->year = bcd2bin (RTC_YEAR); + t->mon = bcd2bin (RTC_MNTH)-1; + t->day = bcd2bin (RTC_DATE); + t->hour = bcd2bin (RTC_HOUR); + t->min = bcd2bin (RTC_MINS); + t->sec = bcd2bin (RTC_SECS); + + RTC_CTRL &= ~(RTC_READ); + + if (t->year < 70) + t->year += 100; + t->wday = bcd2bin(RTC_DOW)-1; + + } + + return 0; +} + +/* + * Set the minutes and seconds from seconds value 'nowtime'. Fail if + * clock is out by > 30 minutes. Logic lifted from atari code. + * Algorithm is to wait for the 10ms register to change, and then to + * wait a short while, and then set it. + */ + +int q40_set_clock_mmss (unsigned long nowtime) +{ + int retval = 0; + short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; + + int rtc_minutes; + + + rtc_minutes = bcd2bin (RTC_MINS); + + if ((rtc_minutes < real_minutes + ? real_minutes - rtc_minutes + : rtc_minutes - real_minutes) < 30) + { + RTC_CTRL |= RTC_WRITE; + RTC_MINS = bin2bcd(real_minutes); + RTC_SECS = bin2bcd(real_seconds); + RTC_CTRL &= ~(RTC_WRITE); + } + else + retval = -1; + + + return retval; +} + +extern void q40kbd_init_hw(void); + +int q40_keyb_init (void) +{ + q40kbd_init_hw(); + return 0; +} + +#if 0 +/* dummy to cause */ +void q40_slow_io() +{ + return; +} +#endif diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c new file mode 100644 index 000000000..e9b5a5b1f --- /dev/null +++ b/arch/m68k/q40/q40ints.c @@ -0,0 +1,347 @@ +/* + * arch/m68k/q40/q40ints.c + * + * Copyright (C) 1999 Richard Zidlicky + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * losely based on bvme6000ints.c + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> + +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/traps.h> + +#include <asm/q40_master.h> +#include <asm/q40ints.h> + +/* + * Q40 IRQs are defined as follows: + * 3,4,5,6,7,10,11,14,15 : ISA dev IRQs + * 16-31: reserved + * 32 : keyboard int + * 33 : frame int (50 Hz periodic timer) + * 34 : sample int (10/20 KHz periodic timer) + * +*/ + +extern int ints_inited; + + +void q40_irq2_handler (int, void *, struct pt_regs *fp); + + +extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */ + +static void q40_defhand (int irq, void *dev_id, struct pt_regs *fp); +static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs); + +/* + * This should ideally be 4 elements only, for speed. + */ + +#define DEVNAME_SIZE 24 + +static struct { + void (*handler)(int, void *, struct pt_regs *); + unsigned long flags; + void *dev_id; + char devname[DEVNAME_SIZE]; + unsigned count; +} irq_tab[Q40_IRQ_MAX+1]; + +/* + * void q40_init_IRQ (void) + * + * Parameters: None + * + * Returns: Nothing + * + * This function is called during kernel startup to initialize + * the q40 IRQ handling routines. + */ + +void q40_init_IRQ (void) +{ + int i; + + for (i = 0; i <= Q40_IRQ_MAX; i++) { + irq_tab[i].handler = q40_defhand; + irq_tab[i].flags = IRQ_FLG_STD; + irq_tab[i].dev_id = NULL; + irq_tab[i].devname[0] = 0; + irq_tab[i].count = 0; + } + + /* setup handler for ISA ints */ + sys_request_irq(IRQ2,q40_irq2_handler, IRQ_FLG_LOCK, "q40 ISA and master chip", NULL); + + /* now enable some ints.. */ + +#if 0 /* has been abandoned */ + master_outb(1,SER_ENABLE_REG); +#endif + master_outb(1,EXT_ENABLE_REG); + + /* would be spurious ints by now, q40kbd_init_hw() does that */ + master_outb(0,KEY_IRQ_ENABLE_REG); +} + +int q40_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + /*printk("q40_request_irq %d, %s\n",irq,devname);*/ + + if (irq > Q40_IRQ_MAX || (irq>15 && irq<32)) { + printk("%s: Incorrect IRQ %d from %s\n", __FUNCTION__, irq, devname); + return -ENXIO; + } + + /* test for ISA ints not implemented by HW */ + if (irq<15) + { + switch (irq){ + case 1: case 2: case 8: case 9: + case 12: case 13: + printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname); + return -ENXIO; + default: + } + } + + if (irq<Q40_IRQ_TIMER) + { + if (irq==11) { + printk("warning IRQ 10 and 11 not distinguishable\n"); + irq=10; + } + if (!(irq_tab[irq].flags & IRQ_FLG_STD)) + { + if (irq_tab[irq].flags & IRQ_FLG_LOCK) + { + printk("%s: IRQ %d from %s is not replaceable\n", + __FUNCTION__, irq, irq_tab[irq].devname); + return -EBUSY; + } + if (flags & IRQ_FLG_REPLACE) + { + printk("%s: %s can't replace IRQ %d from %s\n", + __FUNCTION__, devname, irq, irq_tab[irq].devname); + return -EBUSY; + } + } + /*printk("IRQ %d set to handler %p\n",irq,handler);*/ + irq_tab[irq].handler = handler; + irq_tab[irq].flags = flags; + irq_tab[irq].dev_id = dev_id; + strncpy(irq_tab[irq].devname,devname,DEVNAME_SIZE); + return 0; + } + else { + /* Q40_IRQ_TIMER :somewhat special actions required here ..*/ + sys_request_irq(4,handler,flags,devname,dev_id); + sys_request_irq(6,handler,flags,devname,dev_id); + return 0; + } +} + +void q40_free_irq(unsigned int irq, void *dev_id) +{ + if (irq > Q40_IRQ_MAX || (irq>15 && irq<32)) { + printk("%s: Incorrect IRQ %d, dev_id %x \n", __FUNCTION__, irq, (unsigned)dev_id); + return; + } + + /* test for ISA ints not implemented by HW */ + if (irq<15) { + switch (irq){ + case 1: case 2: case 8: case 9: + case 12: case 13: + printk("%s: ISA IRQ %d from %x illegal\n", __FUNCTION__, irq, (unsigned)dev_id); + return; + default: + } + } + + if (irq<Q40_IRQ_TIMER){ + if (irq==11) irq=10; + if (irq_tab[irq].dev_id != dev_id) + printk("%s: Removing probably wrong IRQ %d from %s\n", + __FUNCTION__, irq, irq_tab[irq].devname); + + irq_tab[irq].handler = q40_defhand; + irq_tab[irq].flags = IRQ_FLG_STD; + irq_tab[irq].dev_id = NULL; + /* irq_tab[irq].devname = NULL; */ + } else { /* == Q40_IRQ_TIMER */ + sys_free_irq(4,dev_id); + sys_free_irq(6,dev_id); + } +} + +#if 1 +void q40_process_int (int level, struct pt_regs *fp) +{ + printk("unexpected interrupt %x\n",level); +} +#endif + +/* + * tables to translate bits into IRQ numbers + * it is a good idea to order the entries by priority + * +*/ + +struct IRQ_TABLE{ unsigned mask; int irq ;}; + +static struct IRQ_TABLE iirqs[]={ + {IRQ_FRAME_MASK,Q40_IRQ_FRAME}, + {IRQ_KEYB_MASK,Q40_IRQ_KEYBOARD}, + {0,0}}; +static struct IRQ_TABLE eirqs[]={ + {IRQ3_MASK,3}, /* ser 1 */ + {IRQ4_MASK,4}, /* ser 2 */ + {IRQ14_MASK,14}, /* IDE 1 */ + {IRQ15_MASK,15}, /* IDE 2 */ + {IRQ6_MASK,6}, /* floppy */ + {IRQ7_MASK,7}, /* par */ + + {IRQ5_MASK,5}, + {IRQ10_MASK,10}, + + + + + {0,0}}; + +/* complaiun only this many times about spurious ints : */ +static int ccleirq=60; /* ISA dev IRQ's*/ +static int cclirq=60; /* internal */ + +/* FIX: add IRQ_INPROGRESS,mask,unmask,probing.... */ + +void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) +{ + /* got level 2 interrupt, dispatch to ISA or keyboard IRQs */ + + unsigned mir=master_inb(IIRQ_REG); + unsigned mer; + int irq,i; + + /* + * more than 1 bit might be set, must handle atmost 1 int source, + * - handle only those with explicitly set handler + */ + + if ((mir&IRQ_SER_MASK) || (mir&IRQ_EXT_MASK)) + { + + /* some ISA dev caused the int */ + + mer=master_inb(EIRQ_REG); + + for (i=0; eirqs[i].mask; i++) + { + if (mer&(eirqs[i].mask)) + { + irq=eirqs[i].irq; + irq_tab[irq].count++; + if (irq_tab[irq].handler == q40_defhand ) + continue; /* ignore uninited INTs :-( */ + + irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); + return; + } + } + if (ccleirq>0) + printk("ISA interrupt from unknown source? EIRQ_REG = %x\n",mer),ccleirq--; + } + else + { + /* internal */ + + for (i=0; iirqs[i].mask; i++) + { + if (mir&(iirqs[i].mask)) + { + irq=iirqs[i].irq; + irq_tab[irq].count++; + if (irq_tab[irq].handler == q40_defhand ) + continue; /* ignore uninited INTs :-( */ + + irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); + return; + } + } + if (cclirq>0) + printk("internal level 2 interrupt from unknown source ? IIRQ_REG=%x\n",mir),cclirq--; + } +} + +int q40_get_irq_list (char *buf) +{ + int i, len = 0; + + for (i = 0; i <= Q40_IRQ_MAX; i++) { + if (irq_tab[i].count) + len += sprintf (buf+len, "Vec 0x%02x: %8d %s%s\n", + i, irq_tab[i].count, + irq_tab[i].devname[0] ? irq_tab[i].devname : "?", + irq_tab[i].handler == q40_defhand ? + " (now unassigned)" : ""); + } + return len; +} + + +static void q40_defhand (int irq, void *dev_id, struct pt_regs *fp) +{ +#if 0 + printk ("Unknown q40 interrupt 0x%02x\n", irq); +#endif +} +static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs) +{ +#if 0 + if (ints_inited) +#endif + printk ("Uninitialised interrupt level %d\n", lev); +#if 0 + else + printk ("Interrupt before interrupt initialisation\n"); +#endif +} + + void (*q40_sys_default_handler[SYS_IRQS]) (int, void *, struct pt_regs *) = { + sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler, + sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler + }; + +void q40_enable_irq (unsigned int irq) +{ +} + + +void q40_disable_irq (unsigned int irq) +{ +} + +unsigned long q40_probe_irq_on (void) +{ + printk("sorry, irq probing not yet implemented - reconfigure the driver to avoid this\n"); + return 0; +} +int q40_probe_irq_off (unsigned long irqs) +{ + return -1; +} diff --git a/arch/m68k/sun3x/Makefile b/arch/m68k/sun3x/Makefile new file mode 100644 index 000000000..ba1f2bbd5 --- /dev/null +++ b/arch/m68k/sun3x/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Linux arch/m68k/sun3x source directory +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := sun3x.o +O_OBJS := config.o time.o dvma.o sbus.o +OX_OBJS := + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/sun3x/config.c b/arch/m68k/sun3x/config.c new file mode 100644 index 000000000..5054cd888 --- /dev/null +++ b/arch/m68k/sun3x/config.c @@ -0,0 +1,128 @@ +/* + * Setup kernel for a Sun3x machine + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + * + * based on code from Oliver Jowett <oliver@jowett.manawatu.gen.nz> + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/machdep.h> +#include <asm/irq.h> +#include <asm/sun3x.h> + +#include "time.h" + +static volatile unsigned char *sun3x_intreg = (unsigned char *)SUN3X_INTREG; +extern int serial_console; + +void sun3x_halt(void) +{ + /* Disable interrupts */ + cli(); + + /* we can't drop back to PROM, so we loop here */ + for (;;); +} + +void sun3x_reboot(void) +{ + /* This never returns, don't bother saving things */ + cli(); + + /* no idea, whether this works */ + asm ("reset"); +} + +__initfunc(int sun3x_keyb_init(void)) +{ + return 0; +} + +int sun3x_kbdrate(struct kbd_repeat *r) +{ + return 0; +} + +void sun3x_kbd_leds(unsigned int i) +{ + +} + +static void sun3x_badint (int irq, void *dev_id, struct pt_regs *fp) +{ + printk ("received spurious interrupt %d\n",irq); + num_spurious += 1; +} + +void (*sun3x_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { + sun3x_badint, sun3x_badint, sun3x_badint, sun3x_badint, + sun3x_badint, sun3x_badint, sun3x_badint, sun3x_badint +}; + +void sun3x_enable_irq(unsigned int irq) +{ + *sun3x_intreg |= (1 << irq); +} + +void sun3x_disable_irq(unsigned int irq) +{ + *sun3x_intreg &= ~(1 << irq); +} + +__initfunc(void sun3x_init_IRQ(void)) +{ + /* disable all interrupts initially */ + *sun3x_intreg = 1; /* master enable only */ +} + +int sun3x_get_irq_list(char *buf) +{ + return 0; +} + +/* + * Setup the sun3x configuration info + */ +__initfunc(void config_sun3x(void)) +{ + mach_get_irq_list = sun3x_get_irq_list; + mach_max_dma_address = 0xffffffff; /* we can DMA anywhere, whee */ + + mach_keyb_init = sun3x_keyb_init; + mach_kbdrate = sun3x_kbdrate; + mach_kbd_leds = sun3x_kbd_leds; + + mach_sched_init = sun3x_sched_init; + mach_init_IRQ = sun3x_init_IRQ; + enable_irq = sun3x_enable_irq; + disable_irq = sun3x_disable_irq; + mach_request_irq = sys_request_irq; + mach_free_irq = sys_free_irq; + mach_default_handler = &sun3x_default_handler; + mach_gettimeoffset = sun3x_gettimeoffset; + mach_reset = sun3x_reboot; + + mach_gettod = sun3x_gettod; + + switch (*(unsigned char *)SUN3X_EEPROM_CONS) { + case 0x10: + serial_console = 1; + conswitchp = NULL; + break; + case 0x11: + serial_console = 2; + conswitchp = NULL; + break; + default: + serial_console = 0; + conswitchp = &dummy_con; + break; + } + +} diff --git a/arch/m68k/sun3x/dvma.c b/arch/m68k/sun3x/dvma.c new file mode 100644 index 000000000..9ef3471a2 --- /dev/null +++ b/arch/m68k/sun3x/dvma.c @@ -0,0 +1,162 @@ +/* + * Virtual DMA allocation + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/mm.h> + +#include <asm/sun3x.h> +#include <asm/dvma.h> +#include <asm/io.h> +#include <asm/page.h> + +/* IOMMU support */ + +#define IOMMU_ENTRIES 2048 +#define IOMMU_ADDR_MASK 0x03ffe000 +#define IOMMU_CACHE_INHIBIT 0x00000040 +#define IOMMU_FULL_BLOCK 0x00000020 +#define IOMMU_MODIFIED 0x00000010 +#define IOMMU_USED 0x00000008 +#define IOMMU_WRITE_PROTECT 0x00000004 +#define IOMMU_DT_MASK 0x00000003 +#define IOMMU_DT_INVALID 0x00000000 +#define IOMMU_DT_VALID 0x00000001 +#define IOMMU_DT_BAD 0x00000002 + +#define DVMA_PAGE_SHIFT 13 +#define DVMA_PAGE_SIZE (1UL << DVMA_PAGE_SHIFT) +#define DVMA_PAGE_MASK (~(DVMA_PAGE_SIZE-1)) + + +static volatile unsigned long *iommu_pte = (unsigned long *)SUN3X_IOMMU; +static unsigned long iommu_use[IOMMU_ENTRIES]; +static unsigned long iommu_bitmap[IOMMU_ENTRIES/32]; + + +#define dvma_entry_paddr(index) (iommu_pte[index] & IOMMU_ADDR_MASK) +#define dvma_entry_vaddr(index,paddr) ((index << DVMA_PAGE_SHIFT) | \ + (paddr & (DVMA_PAGE_SIZE-1))) +#define dvma_entry_set(index,addr) (iommu_pte[index] = \ + (addr & IOMMU_ADDR_MASK) | \ + IOMMU_DT_VALID) +#define dvma_entry_clr(index) (iommu_pte[index] = IOMMU_DT_INVALID) +#define dvma_entry_use(index) (iommu_use[index]) +#define dvma_entry_inc(index) (iommu_use[index]++) +#define dvma_entry_dec(index) (iommu_use[index]--) +#define dvma_entry_hash(addr) ((addr >> DVMA_PAGE_SHIFT) ^ \ + ((addr & 0x03c00000) >> \ + (DVMA_PAGE_SHIFT+4))) +#define dvma_map iommu_bitmap +#define dvma_map_size (IOMMU_ENTRIES/2) +#define dvma_slow_offset (IOMMU_ENTRIES/2) +#define dvma_is_slow(addr) ((addr) & \ + (dvma_slow_offset << DVMA_PAGE_SHIFT)) + +static int fixed_dvma; + +void __init dvma_init(void) +{ + unsigned long tmp; + + if ((unsigned long)high_memory < (IOMMU_ENTRIES << DVMA_PAGE_SHIFT)) { + printk ("Sun3x fixed DVMA mapping\n"); + fixed_dvma = 1; + for (tmp = 0; tmp < (unsigned long)high_memory; tmp += DVMA_PAGE_SIZE) + dvma_entry_set (tmp >> DVMA_PAGE_SHIFT, virt_to_phys((void *)tmp)); + fixed_dvma = 1; + } else { + printk ("Sun3x variable DVMA mapping\n"); + for (tmp = 0; tmp < IOMMU_ENTRIES; tmp++) + dvma_entry_clr (tmp); + fixed_dvma = 0; + } +} + +unsigned long dvma_slow_alloc (unsigned long paddr, int npages) +{ + int scan, base; + + scan = 0; + for (;;) { + scan = find_next_zero_bit(dvma_map, dvma_map_size, scan); + if ((base = scan) + npages > dvma_map_size) { + printk ("dvma_slow_alloc failed for %d pages\n",npages); + return 0; + } + for (;;) { + if (scan >= base + npages) goto found; + if (test_bit(scan, dvma_map)) break; + scan++; + } + } + +found: + for (scan = base; scan < base+npages; scan++) { + dvma_entry_set(scan+dvma_slow_offset, paddr); + paddr += DVMA_PAGE_SIZE; + set_bit(scan, dvma_map); + } + return (dvma_entry_vaddr((base+dvma_slow_offset),paddr)); +} + +unsigned long dvma_alloc (unsigned long paddr, unsigned long size) +{ + int index; + int pages = ((paddr & ~DVMA_PAGE_MASK) + size + (DVMA_PAGE_SIZE-1)) >> + DVMA_PAGE_SHIFT; + + if (fixed_dvma) + return ((unsigned long)phys_to_virt (paddr)); + + if (pages > 1) /* multi page, allocate from slow pool */ + return dvma_slow_alloc (paddr, pages); + + index = dvma_entry_hash (paddr); + + if (dvma_entry_use(index)) { + if (dvma_entry_paddr(index) == (paddr & DVMA_PAGE_MASK)) { + dvma_entry_inc(index); + return dvma_entry_vaddr(index,paddr); + } + /* collision, allocate from slow pool */ + return dvma_slow_alloc (paddr, pages); + } + + dvma_entry_set(index,paddr); + dvma_entry_inc(index); + return dvma_entry_vaddr(index,paddr); +} + +void dvma_free (unsigned long dvma_addr, unsigned long size) +{ + int npages; + int index; + + if (fixed_dvma) + return; + + if (!dvma_is_slow(dvma_addr)) { + index = (dvma_addr >> DVMA_PAGE_SHIFT); + if (dvma_entry_use(index) == 0) { + printk ("dvma_free: %lx entry already free\n",dvma_addr); + return; + } + dvma_entry_dec(index); + if (dvma_entry_use(index) == 0) + dvma_entry_clr(index); + return; + } + + /* free in slow pool */ + npages = ((dvma_addr & ~DVMA_PAGE_MASK) + size + (DVMA_PAGE_SIZE-1)) >> + DVMA_PAGE_SHIFT; + for (index = (dvma_addr >> DVMA_PAGE_SHIFT); npages--; index++) { + dvma_entry_clr(index); + clear_bit (index,dvma_map); + } +} diff --git a/arch/m68k/sun3x/sbus.c b/arch/m68k/sun3x/sbus.c new file mode 100644 index 000000000..f1a89df65 --- /dev/null +++ b/arch/m68k/sun3x/sbus.c @@ -0,0 +1,44 @@ +/* + * SBus helper functions + * + * Sun3x don't have a sbus, but many of the used devices are also + * used on Sparc machines with sbus. To avoid having a lot of + * duplicate code, we provide necessary glue stuff to make using + * of the sbus driver code possible. + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + */ + +#include <linux/types.h> +#include <linux/init.h> + +__initfunc(void sbus_init(void)) +{ + +} + +void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, + u32 bus_type, int rdonly) +{ + return (void *)address; +} + +int prom_getintdefault(int node, char *property, int deflt) +{ + return deflt; +} + +int prom_getbool (int node, char *prop) +{ + return 1; +} + +void prom_printf(char *fmt, ...) +{ + +} + +void prom_halt (void) +{ + +} diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c new file mode 100644 index 000000000..39fa5f696 --- /dev/null +++ b/arch/m68k/sun3x/time.c @@ -0,0 +1,83 @@ +/* + * linux/arch/m68k/sun3x/time.c + * + * Sun3x-specific time handling + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/traps.h> +#include <asm/sun3x.h> + +#include "time.h" + +#define M_CONTROL 0xf8 +#define M_SEC 0xf9 +#define M_MIN 0xfa +#define M_HOUR 0xfb +#define M_DAY 0xfc +#define M_DATE 0xfd +#define M_MONTH 0xfe +#define M_YEAR 0xff + +#define C_WRITE 0x80 +#define C_READ 0x40 +#define C_SIGN 0x20 +#define C_CALIB 0x1f + +#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) + +/* Read the Mostek */ +void sun3x_gettod (int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + volatile unsigned char *eeprom = (unsigned char *)SUN3X_EEPROM; + + /* Stop updates */ + *(eeprom + M_CONTROL) |= C_READ; + + /* Read values */ + *yearp = BCD_TO_BIN(*(eeprom + M_YEAR)); + *monp = BCD_TO_BIN(*(eeprom + M_MONTH)); + *dayp = BCD_TO_BIN(*(eeprom + M_DATE)); + *hourp = BCD_TO_BIN(*(eeprom + M_HOUR)); + *minp = BCD_TO_BIN(*(eeprom + M_MIN)); + *secp = BCD_TO_BIN(*(eeprom + M_SEC)); + + /* Restart updates */ + *(eeprom + M_CONTROL) &= ~C_READ; +} + +/* Not much we can do here */ +unsigned long sun3x_gettimeoffset (void) +{ + return 0L; +} + +static void sun3x_timer_tick(int irq, void *dev_id, struct pt_regs *regs) +{ + void (*vector)(int, void *, struct pt_regs *) = dev_id; + + /* Clear the pending interrupt - pulse the enable line low */ + disable_irq(5); + enable_irq(5); + + vector(irq, NULL, regs); +} + +__initfunc(void sun3x_sched_init(void (*vector)(int, void *, struct pt_regs *))) +{ + sys_request_irq(5, sun3x_timer_tick, IRQ_FLG_STD, "timer tick", vector); + + /* Pulse enable low to get the clock started */ + disable_irq(5); + enable_irq(5); +} diff --git a/arch/m68k/sun3x/time.h b/arch/m68k/sun3x/time.h new file mode 100644 index 000000000..873742d70 --- /dev/null +++ b/arch/m68k/sun3x/time.h @@ -0,0 +1,9 @@ +#ifndef SUN3X_TIME_H +#define SUN3X_TIME_H + +void sun3x_gettod (int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp); +unsigned long sun3x_gettimeoffset (void); +void sun3x_sched_init(void (*vector)(int, void *, struct pt_regs *)); + +#endif diff --git a/arch/mips/config.in b/arch/mips/config.in index b1a6310b6..aa1aba32f 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -217,7 +217,7 @@ if [ "$CONFIG_SGI" != "y" -a "$CONFIG_DECSTATION" != "y" -a "$CONFIG_BAGET_MIPS" mainmenu_option next_comment - comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' + comment comment 'Old CD-ROM drivers (not SCSI, not IDE)' bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then @@ -257,6 +257,8 @@ else endmenu fi +# source drivers/usb/Config.in + source fs/Config.in if [ "$CONFIG_VT" = "y" ]; then diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 0b3de2ffe..a24fa46f5 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -11,7 +11,6 @@ # Machine selection # # CONFIG_ACER_PICA_61 is not set -# CONFIG_COBALT_MICRO_SERVER is not set # CONFIG_MIPS_MAGNUM_4000 is not set # CONFIG_OLIVETTI_M700 is not set # CONFIG_SGI is not set @@ -35,8 +34,8 @@ CONFIG_CPU_R4X00=y # CONFIG_PCI_QUIRKS=y CONFIG_PCI_OLD_PROC=y -CONFIG_ELF_KERNEL=y CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_ELF_KERNEL=y # CONFIG_BINFMT_AOUT is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set @@ -91,7 +90,6 @@ CONFIG_PARIDE_PARPORT=y # CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -108,7 +106,6 @@ CONFIG_INET=y # (it is safe to leave these untouched) # # CONFIG_INET_RARP is not set -CONFIG_IP_NOSR=y # CONFIG_SKB_LARGE is not set # @@ -160,9 +157,12 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_NCR53C7xx is not set CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_SYM53C8XX is not set CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 CONFIG_SCSI_NCR53C8XX_SYNC=20 @@ -174,6 +174,7 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -239,7 +240,7 @@ CONFIG_PCNET32=y # CONFIG_ISDN is not set # -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# comment # # CONFIG_CD_NO_IDESCSI is not set @@ -267,6 +268,7 @@ CONFIG_RTC=y # Joystick support # # CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set # # Ftape, the floppy tape device driver @@ -311,6 +313,7 @@ CONFIG_LOCKD=m # # CONFIG_BSD_DISKLABEL is not set # CONFIG_MAC_PARTITION is not set +# CONFIG_OSF_PARTITION is not set # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set CONFIG_NLS=y @@ -351,6 +354,10 @@ CONFIG_NLS_KOI8_R=m # CONFIG_VGA_CONSOLE=y # CONFIG_FB is not set +# CONFIG_FBCON_FONTWIDTH8_ONLY is not set +# CONFIG_FBCON_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y # # Sound diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index a0e141759..38fab1e16 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -1,4 +1,4 @@ -/* $Id: irixelf.c,v 1.16 1998/10/28 12:38:11 ralf Exp $ +/* $Id: irixelf.c,v 1.15 1999/01/04 16:03:48 ralf Exp $ * * irixelf.c: Code to load IRIX ELF executables which conform to * the MIPS ABI. @@ -1123,7 +1123,7 @@ static int writenote(struct memelfnote *men, struct file *file) static int irix_core_dump(long signr, struct pt_regs * regs) { int has_dumped = 0; - struct file file; + struct file *file; struct dentry *dentry; struct inode *inode; mm_segment_t fs; @@ -1192,30 +1192,28 @@ static int irix_core_dump(long signr, struct pt_regs * regs) fs = get_fs(); set_fs(KERNEL_DS); + memcpy(corefile,"core.", 5); #if 0 memcpy(corefile+5,current->comm,sizeof(current->comm)); #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(dentry)) { - inode = NULL; + file = filp_open(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); + if (IS_ERR(file)) goto end_coredump; - } + dentry = file->f_dentry; inode = dentry->d_inode; - - if(inode->i_nlink > 1) - goto end_coredump; /* multiple links - don't dump */ + if (inode->i_nlink > 1) + goto close_coredump; /* multiple links - don't dump */ if (!S_ISREG(inode->i_mode)) - goto end_coredump; + goto close_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) - goto end_coredump; - if (init_private_file(&file, dentry, 3)) - goto end_coredump; - if (!file.f_op->write) goto close_coredump; + if (!file->f_op->write) + goto close_coredump; + has_dumped = 1; current->flags |= PF_DUMPCORE; @@ -1351,7 +1349,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs) } for(i = 0; i < numnote; i++) - if (!writenote(¬es[i], &file)) + if (!writenote(¬es[i], file)) goto close_coredump; set_fs(fs); @@ -1373,19 +1371,17 @@ static int irix_core_dump(long signr, struct pt_regs * regs) DUMP_WRITE((void *)addr, len); } - if ((off_t) file.f_pos != offset) { + if ((off_t) file->f_pos != offset) { /* Sanity check. */ - printk("elf_core_dump: file.f_pos (%ld) != offset (%ld)\n", - (off_t) file.f_pos, offset); + printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n", + (off_t) file->f_pos, offset); } close_coredump: - if (file.f_op->release) - file.f_op->release(inode, &file); + filp_close(file, NULL); end_coredump: set_fs(fs); - dput(dentry); #ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; #endif diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 6fb6735fd..71dd05e75 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -1,4 +1,4 @@ -/* $Id: ptrace.c,v 1.10 1999/01/03 17:50:52 ralf Exp $ +/* $Id: ptrace.c,v 1.11 1999/02/15 02:16:51 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -259,6 +259,7 @@ static int write_long(struct task_struct * tsk, unsigned long addr, asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; + unsigned int flags; int res; lock_kernel(); @@ -297,22 +298,26 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || - (current->gid != child->gid)) && - !capable(CAP_SYS_PTRACE)) { + (current->gid != child->gid) || + (!cap_issubset(child->cap_permitted, + current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)){ res = -EPERM; goto out; } /* the same process cannot be attached many times */ - if (child->flags & PF_PTRACED) { - res = -EPERM; + if (child->flags & PF_PTRACED) goto out; - } child->flags |= PF_PTRACED; + + write_lock_irqsave(&tasklist_lock, flags); if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); } + write_unlock_irqrestore(&tasklist_lock, flags); + send_sig(SIGSTOP, child, 1); res = 0; goto out; diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S index 4b8698d3b..faac180d1 100644 --- a/arch/mips/kernel/r2300_switch.S +++ b/arch/mips/kernel/r2300_switch.S @@ -1,7 +1,9 @@ -/* $Id: $ +/* $Id: r2300_switch.S,v 1.5 1999/04/11 17:13:56 harald Exp $ + * * r2300_switch.S: R2300 specific task switching code. * - * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse + * Copyright (C) 1994, 1995, 1996, 1999 by Ralf Baechle + * Copyright (C) 1994, 1995, 1996 by Andreas Busse * * Multi-cpu abstraction and macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -28,19 +30,23 @@ .set mips1 .align 5 +/* + * task_struct *r4xx0_resume(task_struct *prev, + * task_struct *next) + */ LEAF(r2300_resume) .set reorder mfc0 t1, CP0_STATUS .set noreorder - sw t1, THREAD_STATUS($28) - CPU_SAVE_NONSCRATCH($28) - sw ra, THREAD_REG31($28) + sw t1, THREAD_STATUS(a0) + CPU_SAVE_NONSCRATCH(a0) + sw ra, THREAD_REG31(a0) /* * The order of restoring the registers takes care of the race * updating $28, $29 and kernelsp without disabling ints. */ - move $28, a0 + move $28, a1 CPU_RESTORE_NONSCRATCH($28) addiu t0, $28, KERNEL_STACK_SIZE-32 sw t0, kernelsp @@ -55,8 +61,9 @@ LEAF(r2300_resume) lw a3, MM_CONTEXT(a3) mtc0 a2, CP0_STATUS andi a3, 0xfc0 + mtc0 a3, CP0_ENTRYHI jr ra - mtc0 a3, CP0_ENTRYHI + move v0, a0 END(r2300_resume) /* diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S index afc1591f5..3ba84bb9a 100644 --- a/arch/mips/kernel/r4k_switch.S +++ b/arch/mips/kernel/r4k_switch.S @@ -1,10 +1,10 @@ -/* $Id: r4k_switch.S,v 1.5 1999/05/01 10:08:18 harald Exp $ +/* $Id: r4k_switch.S,v 1.6 1999/05/01 22:40:37 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1994, 1995, 1996, 1998 by Ralf Baechle + * Copyright (C) 1994, 1995, 1996, 1998, 1999 by Ralf Baechle * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * Copyright (C) 1994, 1995, 1996, by Andreas Busse */ @@ -24,20 +24,24 @@ #include <asm/asmmacro.h> +/* + * task_struct *r4xx0_resume(task_struct *prev, + * task_struct *next) + */ .set noreorder .set mips3 .align 5 LEAF(r4xx0_resume) mfc0 t1, CP0_STATUS - sw t1, THREAD_STATUS($28) - CPU_SAVE_NONSCRATCH($28) - sw ra, THREAD_REG31($28) + sw t1, THREAD_STATUS(a0) + CPU_SAVE_NONSCRATCH(a0) + sw ra, THREAD_REG31(a0) /* * The order of restoring the registers takes care of the race * updating $28, $29 and kernelsp without disabling ints. */ - move $28, a0 + move $28, a1 CPU_RESTORE_NONSCRATCH($28) addiu t0, $28, KERNEL_STACK_SIZE-32 sw t0, kernelsp @@ -52,8 +56,9 @@ lw a3, MM_CONTEXT(a3) mtc0 a2, CP0_STATUS andi a3, a3, 0xff + mtc0 a3, CP0_ENTRYHI jr ra - mtc0 a3, CP0_ENTRYHI + move v0, a0 END(r4xx0_resume) /* diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index b3336d864..5464524f6 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -1,4 +1,4 @@ -/* $Id: sysirix.c,v 1.17 1999/05/01 21:54:19 ralf Exp $ +/* $Id: sysirix.c,v 1.18 1999/05/01 22:40:38 ralf Exp $ * * sysirix.c: IRIX system call emulation. * @@ -628,7 +628,6 @@ asmlinkage int irix_stime(int value) cli(); xtime.tv_sec = value; xtime.tv_usec = 0; - time_state = TIME_ERROR; time_maxerror = MAXPHASE; time_esterror = MAXPHASE; sti(); diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index 1491da82d..ecb89b755 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.10 1999/02/01 01:26:32 ralf Exp $ +/* $Id: time.c,v 1.11 1999/02/15 02:16:53 ralf Exp $ * * Copyright (C) 1991, 1992, 1995 Linus Torvalds * Copyright (C) 1996, 1997, 1998 Ralf Baechle @@ -255,7 +255,6 @@ void do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti(); diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 1d2cd9647..b6c219169 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.18 1999/04/12 19:13:21 harald Exp $ +/* $Id: traps.c,v 1.19 1999/05/01 22:40:38 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -464,8 +464,8 @@ extern asmlinkage void r4k_restore_fp_context(struct sigcontext *sc); extern asmlinkage void r2300_restore_fp_context(struct sigcontext *sc); extern asmlinkage void r6000_restore_fp_context(struct sigcontext *sc); -extern asmlinkage void r4xx0_resume(void *tsk); -extern asmlinkage void r2300_resume(void *tsk); +extern asmlinkage void *r4xx0_resume(void *last, void *next); +extern asmlinkage void *r2300_resume(void *last, void *next); __initfunc(void trap_init(void)) { diff --git a/arch/mips/mm/loadmmu.c b/arch/mips/mm/loadmmu.c index 82f6793c1..6142d82c5 100644 --- a/arch/mips/mm/loadmmu.c +++ b/arch/mips/mm/loadmmu.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: loadmmu.c,v 1.7 1998/04/05 11:23:55 ralf Exp $ + * $Id: loadmmu.c,v 1.8 1999/04/11 17:13:56 harald Exp $ */ #include <linux/init.h> #include <linux/kernel.h> @@ -53,7 +53,7 @@ void (*add_wired_entry)(unsigned long entrylo0, unsigned long entrylo1, int (*user_mode)(struct pt_regs *); -asmlinkage void (*resume)(void *tsk); +asmlinkage void *(*resume)(void *last, void *next); extern void ld_mmu_r2300(void); extern void ld_mmu_r4xx0(void); diff --git a/arch/mips/sgi/kernel/indy_timer.c b/arch/mips/sgi/kernel/indy_timer.c index 0920b9d2c..11980d75a 100644 --- a/arch/mips/sgi/kernel/indy_timer.c +++ b/arch/mips/sgi/kernel/indy_timer.c @@ -1,4 +1,4 @@ -/* $Id: indy_timer.c,v 1.10 1998/08/25 09:14:49 ralf Exp $ +/* $Id: indy_timer.c,v 1.11 1999/01/04 16:03:56 ralf Exp $ * * indy_timer.c: Setting up the clock on the INDY 8254 controller. * @@ -104,9 +104,10 @@ void indy_timer_interrupt(struct pt_regs *regs) * absolutely sure we do this update within 500ms before the * next second starts, thus the following code. */ - 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 ((time_status & STA_UNSYNC) == 0 && + 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 diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c index b1ea955ed..707f3c97c 100644 --- a/arch/ppc/8xx_io/commproc.c +++ b/arch/ppc/8xx_io/commproc.c @@ -111,7 +111,12 @@ m8xx_cpm_reset(uint host_page_addr) */ ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr = (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | - ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK; + (((5)/2) << 13) | CICR_HP_MASK; + /* I hard coded the CPM interrupt to 5 above + * since the CPM_INTERRUPT define is relative to + * the linux irq structure not what the hardware + * belives. -- Cort + */ ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr = 0; /* Set our interrupt handler with the core CPU. */ diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c index 126b724be..1c71f473f 100644 --- a/arch/ppc/8xx_io/enet.c +++ b/arch/ppc/8xx_io/enet.c @@ -1,5 +1,4 @@ /* - * $Id: enet.c,v 1.8 1998/11/15 19:58:07 cort Exp $ * Ethernet driver for Motorola MPC8xx. * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * @@ -22,6 +21,7 @@ * small packets. * */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/string.h> @@ -39,7 +39,7 @@ #include <asm/8xx_immap.h> #include <asm/pgtable.h> -#include <asm/mbx.h> +#include <asm/fads.h> #include <asm/bitops.h> #include <asm/uaccess.h> #include "commproc.h" @@ -49,7 +49,7 @@ * * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use * an aribtrary number of buffers on byte boundaries, but must have at - * least two receive buffers to prevent constand overrun conditions. + * least two receive buffers to prevent constant overrun conditions. * * The buffer descriptors are allocated from the CPM dual port memory * with the data buffers allocated from host memory, just like all other @@ -94,6 +94,17 @@ * Port C, 15 - SCC1 Ethernet Tx Enable * Port C, 11 - SCC1 Ethernet Collision * Port C, 10 - SCC1 Ethernet Rx Enable + * + * The RPX-Lite (that I had :-), was the MPC850SAR. It has a control + * register to enable Ethernet functions in the 68160, and the Ethernet + * was controlled by SCC2. So, the pin I/O was like this: + * Port A, 13 - SCC2 Ethernet Rx + * Port A, 12 - SCC2 Ethernet Tx + * Port A, 6 (CLK2) - Ethernet Tx Clk + * Port A, 4 (CLK4) - Ethernet Rx Clk + * Port B, 18 (RTS2) - Ethernet Tx Enable + * Port C, 8 (CD2) - Ethernet Rx Enable + * Port C, 9 (CTS2) - SCC Ethernet Collision */ /* The number of Tx and Rx buffers. These are allocated from the page @@ -149,10 +160,26 @@ static int cpm_enet_close(struct device *dev); static struct net_device_stats *cpm_enet_get_stats(struct device *dev); static void set_multicast_list(struct device *dev); -/* GET THIS FROM THE VPD!!!! +/* Get this from various configuration locations (depends on board). */ /*static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*/ +/* Right now, only the boards with an 860 use SCC1 for the Ethernet. + * All others use SCC2. We may need to make this board specific someday. + */ +#ifndef CONFIG_MPC860 +/*static ushort my_enet_addr[] = { 0x2700, 0x00ec, 0x1000 };*/ +#define CPM_CR_ENET CPM_CR_CH_SCC2 +#define PROFF_ENET PROFF_SCC2 +#define SCC_ENET 1 /* Index, not number! */ +#define CPMVEC_ENET CPMVEC_SCC2 +#else +#define CPM_CR_ENET CPM_CR_CH_SCC1 +#define PROFF_ENET PROFF_SCC1 +#define SCC_ENET 0 +#define CPMVEC_ENET CPMVEC_SCC1 +#endif + static int cpm_enet_open(struct device *dev) { @@ -178,7 +205,7 @@ cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev) /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 20) + if (tickssofar < 200) return 1; printk("%s: transmit timed out.\n", dev->name); cep->stats.tx_errors++; @@ -186,17 +213,17 @@ cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev) { int i; cbd_t *bdp; - printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n", + printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n", cep->cur_tx, cep->tx_full ? " (full)" : "", cep->cur_rx); bdp = cep->tx_bd_base; - for (i = 0 ; i < TX_RING_SIZE; i++) + for (i = 0 ; i < TX_RING_SIZE; i++, bdp++) printk("%04x %04x %08x\n", bdp->cbd_sc, bdp->cbd_datlen, bdp->cbd_bufaddr); bdp = cep->rx_bd_base; - for (i = 0 ; i < RX_RING_SIZE; i++) + for (i = 0 ; i < RX_RING_SIZE; i++, bdp++) printk("%04x %04x %08x\n", bdp->cbd_sc, bdp->cbd_datlen, @@ -263,7 +290,7 @@ cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev) /* Push the data cache so the CPM does not get stale memory * data. */ - /*flush_dcache_range(skb->data, skb->data + skb->len);*/ + flush_dcache_range(skb->data, skb->data + skb->len); /* Send it on its way. Tell CPM its ready, interrupt when done, * its the last BD of the frame, and to put the CRC on the end. @@ -300,7 +327,7 @@ static void cpm_enet_interrupt(void *dev_id) { struct device *dev = dev_id; - struct cpm_enet_private *cep; + volatile struct cpm_enet_private *cep; volatile cbd_t *bdp; ushort int_events; int must_restart; @@ -314,6 +341,7 @@ cpm_enet_interrupt(void *dev_id) /* Get the interrupt events that caused us to be here. */ int_events = cep->sccp->scc_scce; + cep->sccp->scc_scce = int_events; must_restart = 0; /* Handle receive event in its own function. @@ -329,6 +357,7 @@ cpm_enet_interrupt(void *dev_id) * I don't know if "normally" implies TXB is set when the buffer * descriptor is closed.....trial and error :-). */ +#if 0 if (int_events & SCCE_ENET_TXE) { /* Transmission errors. @@ -359,16 +388,46 @@ cpm_enet_interrupt(void *dev_id) (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) must_restart = 1; } +#endif /* Transmit OK, or non-fatal error. Update the buffer descriptors. */ if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { +#if 1 + bdp = cep->dirty_tx; + while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { + if ((bdp==cep->cur_tx) && (cep->tx_full == 0)) + break; + + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { + must_restart = 1; + cep->stats.tx_errors++; + } + cep->stats.tx_packets++; +#else bdp = cep->dirty_tx; -#ifndef final_version +#if 1 if (bdp->cbd_sc & BD_ENET_TX_READY) printk("HEY! Enet xmit interrupt and TX_READY.\n"); #endif +#endif /* Deferred means some collisions occurred during transmit, * but we eventually sent the packet OK. */ @@ -406,9 +465,9 @@ cpm_enet_interrupt(void *dev_id) } cep->dirty_tx = (cbd_t *)bdp; - } + } - if (must_restart) { + if (must_restart) { volatile cpm8xx_t *cp; /* Some transmit errors cause the transmitter to shut @@ -419,8 +478,9 @@ cpm_enet_interrupt(void *dev_id) */ cp = cpmp; cp->cp_cpcr = - mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG; + mk_cr_cmd(CPM_CR_ENET, CPM_CR_RESTART_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); + } } /* Check for receive busy, i.e. packets coming but no place to @@ -432,11 +492,6 @@ cpm_enet_interrupt(void *dev_id) printk("CPM ENET: BSY can't happen.\n"); } - /* Write the SCC event register with the events we have handled - * to clear them. Maybe we should do this sooner? - */ - cep->sccp->scc_scce = int_events; - dev->interrupt = 0; return; @@ -576,7 +631,7 @@ static void set_multicast_list(struct device *dev) int i, j; cep = (struct cpm_enet_private *)dev->priv; - /* Get pointer to SCC1 area in parameter RAM. + /* Get pointer to SCC area in parameter RAM. */ ep = (scc_enet_t *)dev->base_addr; @@ -627,7 +682,7 @@ static void set_multicast_list(struct device *dev) /* Ask CPM to run CRC and set bit in * filter mask. */ - cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; + cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_SET_GADDR) | CPM_CR_FLG; /* this delay is necessary here -- Cort */ udelay(10); while (cpmp->cp_cpcr & CPM_CR_FLG); @@ -636,13 +691,15 @@ static void set_multicast_list(struct device *dev) } } -/* Initialize the CPM Ethernet on SCC1. If EPPC-Bug loaded us, or performed +/* Initialize the CPM Ethernet on SCC. If EPPC-Bug loaded us, or performed * some other network I/O, a whole bunch of this has already been set up. * It is no big deal if we do it again, we just have to disable the * transmit and receive to make sure we don't catch the CPM with some * inconsistent control information. */ -__initfunc(int cpm_enet_init(void)) +/* until this gets cleared up -- Cort */ +int __init cpm_enet_init() { m8xx_enet_init(); } +int __init m8xx_enet_init(void) { struct device *dev; struct cpm_enet_private *cep; @@ -650,6 +707,7 @@ __initfunc(int cpm_enet_init(void)) unsigned char *eap; unsigned long mem_addr; pte_t *pte; + bd_t *bd; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile scc_t *sccp; @@ -660,6 +718,8 @@ __initfunc(int cpm_enet_init(void)) immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ + bd = (bd_t *)res; + /* Allocate some private information. */ cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL); @@ -670,13 +730,13 @@ __initfunc(int cpm_enet_init(void)) */ dev = init_etherdev(0, 0); - /* Get pointer to SCC1 area in parameter RAM. + /* Get pointer to SCC area in parameter RAM. */ - ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]); + ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_ENET]); /* And another to the SCC register area. */ - sccp = (volatile scc_t *)(&cp->cp_scc[0]); + sccp = (volatile scc_t *)(&cp->cp_scc[SCC_ENET]); cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ /* Disable receive and transmit in case EPPC-Bug started it. @@ -686,6 +746,9 @@ __initfunc(int cpm_enet_init(void)) /* Cookbook style from the MPC860 manual..... * Not all of this is necessary if EPPC-Bug has initialized * the network. + * So far we are lucky, all board configurations use the same + * pins, or at least the same I/O Port for these functions..... + * It can't last though...... */ /* Configure port A pins for Txd and Rxd. @@ -706,7 +769,7 @@ __initfunc(int cpm_enet_init(void)) immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); /* Configure Serial Interface clock routing. - * First, clear all SCC1 bits to zero, then set the ones we want. + * First, clear all SCC bits to zero, then set the ones we want. */ cp->cp_sicr &= ~SICR_ENET_MASK; cp->cp_sicr |= SICR_ENET_CLKRT; @@ -731,13 +794,13 @@ __initfunc(int cpm_enet_init(void)) cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; cep->cur_rx = cep->rx_bd_base; - /* Issue init Rx BD command for SCC1. + /* Issue init Rx BD command for SCC. * Manual says to perform an Init Rx parameters here. We have * to perform both Rx and Tx because the SCC may have been * already running. * In addition, we have to do it later because we don't yet have * all of the BD control/status set properly. - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_RX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); */ @@ -781,20 +844,17 @@ __initfunc(int cpm_enet_init(void)) ep->sen_iaddr3 = 0; ep->sen_iaddr4 = 0; - /* Set Ethernet station address. This must come from the - * Vital Product Data (VPD) EEPROM.....as soon as I get the - * I2C interface working..... + /* Set Ethernet station address. * - * Since we performed a diskless boot, the Ethernet controller + * If we performed a MBX diskless boot, the Ethernet controller * has been initialized and we copy the address out into our * own structure. */ -#ifdef notdef - ep->sen_paddrh = my_enet_addr[0]; - ep->sen_paddrm = my_enet_addr[1]; - ep->sen_paddrl = my_enet_addr[2]; -#else eap = (unsigned char *)&(ep->sen_paddrh); +#ifndef CONFIG_MBX + for (i=5; i>=0; i--) + *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; +#else for (i=5; i>=0; i--) dev->dev_addr[i] = *eap++; #endif @@ -854,7 +914,7 @@ __initfunc(int cpm_enet_init(void)) * than the manual describes because we have just now finished * the BD initialization. */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_TRX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); cep->skb_cur = cep->skb_dirty = 0; @@ -869,7 +929,7 @@ __initfunc(int cpm_enet_init(void)) /* Install our interrupt handler. */ - cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev); + cpm_install_handler(CPMVEC_ENET, cpm_enet_interrupt, dev); /* Set GSMR_H to enable all normal operating modes. * Set GSMR_L to enable Ethernet to MC68160. @@ -884,12 +944,42 @@ __initfunc(int cpm_enet_init(void)) /* Set processing mode. Use Ethernet CRC, catch broadcast, and * start frame search 22 bit times after RENA. */ - sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22); + sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_NIB22); /* It is now OK to enable the Ethernet transmitter. - */ + * Unfortunately, there are board implementation differences here. + */ +#ifdef CONFIG_MBX immap->im_ioport.iop_pcpar |= PC_ENET_TENA; immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA; +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + cp->cp_pbpar |= PB_ENET_TENA; + cp->cp_pbdir |= PB_ENET_TENA; + + /* And while we are here, set the configuration to enable ethernet. + */ + *((volatile uint *)RPX_CSR_ADDR) &= ~BCSR0_ETHLPBK; + *((volatile uint *)RPX_CSR_ADDR) |= + (BCSR0_ETHEN | BCSR0_COLTESTDIS | BCSR0_FULLDPLXDIS); +#endif + +#ifdef CONFIG_BSEIP + cp->cp_pbpar |= PB_ENET_TENA; + cp->cp_pbdir |= PB_ENET_TENA; + + /* BSE uses port B and C for PHY control. + */ + cp->cp_pbpar &= ~(PB_BSE_POWERUP | PB_BSE_FDXDIS); + cp->cp_pbdir |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); + cp->cp_pbdat |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); + + immap->im_ioport.iop_pcpar &= ~PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcdir |= PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcso &= ~PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcdat &= ~PC_BSE_LOOPBACK; +#endif dev->base_addr = (unsigned long)ep; dev->priv = cep; @@ -913,3 +1003,4 @@ __initfunc(int cpm_enet_init(void)) return 0; } + diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index 990b24b84..d865053c4 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -45,6 +45,7 @@ endif MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot +MAKEMBXBOOT = $(MAKE) -C arch/$(ARCH)/mbxboot ifdef CONFIG_8xx SUBDIRS += arch/ppc/8xx_io @@ -63,10 +64,16 @@ checks: BOOT_TARGETS = netboot znetboot zImage floppy install \ vmlinux.coff znetboot.initrd zImage.initrd vmlinux.coff.initrd +ifdef CONFIG_MBX +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKECOFFBOOT) $@ + @$(MAKEMBXBOOT) $@ +else $(BOOT_TARGETS): $(CHECKS) vmlinux @$(MAKECOFFBOOT) $@ @$(MAKEBOOT) $@ @$(MAKECHRPBOOT) $@ +endif pmac_config: rm -f .config arch/ppc/defconfig @@ -100,10 +107,10 @@ archclean: @$(MAKECOFFBOOT) clean @$(MAKEBOOT) clean @$(MAKECHRPBOOT) clean + @$(MAKEMBXBOOT) clean archmrproper: archdep: $(MAKEBOOT) fastdep $(MAKECHRPBOOT) fastdep - diff --git a/arch/ppc/apus_defconfig b/arch/ppc/apus_defconfig index c9f900cfe..5e75d496a 100644 --- a/arch/ppc/apus_defconfig +++ b/arch/ppc/apus_defconfig @@ -100,7 +100,6 @@ CONFIG_NETLINK=y # CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set # CONFIG_FIREWALL is not set -CONFIG_NET_ALIAS=y # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index da4945fd7..5a83e79ee 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -14,7 +14,7 @@ .s.o: $(AS) -o $*.o $< .c.o: - $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< + $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -c -o $*.o $< .S.s: $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< .S.o: @@ -25,17 +25,14 @@ ZSZ = 0 IOFF = 0 ISZ = 0 -ifeq ($(CONFIG_ALL_PPC),y) -# yes, we want to build prep stuff -CONFIG_PREP = y -endif - -ifeq ($(CONFIG_MBX),y) -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000 +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.prep.smp else -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000 +TFTPIMAGE=/tftpboot/zImage.prep endif +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 + GZIP_FLAGS = -v9 OBJECTS := head.o misc.o ../coffboot/zlib.o @@ -43,19 +40,13 @@ CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc -ifeq ($(CONFIG_MBX),y) -OBJECTS += mbxtty.o -CFLAGS += -DCONFIG_MBX -else -OBJECTS += vreset.o kbd.o +OBJECTS += vreset.o kbd.o of1275.o ifeq ($(CONFIG_SERIAL_CONSOLE),y) OBJECTS += ns16550.o endif -endif all: zImage -ifeq ($(CONFIG_PREP),y) zvmlinux.initrd: zvmlinux $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ @@ -66,54 +57,19 @@ zvmlinux.initrd: zvmlinux -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \ -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \ -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \ - -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp $@ - rm zvmlinux.initrd.tmp -endif -ifeq ($(CONFIG_MBX),y) -zvmlinux.initrd: zvmlinux - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp zvmlinux.initrd - $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd initrd` \ - -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \ - -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \ - -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \ - -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c + -c -o misc.o misc.c $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ --add-section=initrd=ramdisk.image.gz \ --add-section=image=../coffboot/vmlinux.gz \ zvmlinux.initrd.tmp $@ rm zvmlinux.initrd.tmp -endif -ifeq ($(CONFIG_PREP),y) zImage: zvmlinux mkprep ./mkprep -pbp zvmlinux zImage -else -ifeq ($(CONFIG_MBX),y) -zImage: zvmlinux - ln -sf zvmlinux zImage -else -zImage: -endif -endif -ifeq ($(CONFIG_PREP),y) zImage.initrd: zvmlinux.initrd mkprep ./mkprep -pbp zvmlinux.initrd zImage.initrd -endif -ifeq ($(CONFIG_MBX),y) -zImage.initrd: zvmlinux.initrd - ln -sf zvmlinux.initrd zImage.initrd -endif zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # @@ -128,7 +84,7 @@ zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux image` \ - -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` \ -c -o misc.o misc.c $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ @@ -136,34 +92,16 @@ zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz rm zvmlinux.tmp floppy: $(TOPDIR)/vmlinux zImage -ifeq ($(CONFIG_PREP),y) dd if=zImage of=/dev/fd0H1440 bs=64b -endif -ifeq ($(CONFIG_PREP),y) mkprep : mkprep.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c -endif + $(HOSTCC) -o mkprep mkprep.c -ifeq ($(CONFIG_PREP),y) znetboot : zImage - cp zImage /tftpboot/zImage.prep -else -ifeq ($(CONFIG_MBX),y) -znetboot : zImage - cp zImage /tftpboot/zImage.mbx -else -znetboot : -endif -endif + cp zImage $(TFTPIMAGE) znetboot.initrd : zImage.initrd -ifeq ($(CONFIG_PREP),y) - cp zImage.initrd /tftpboot/zImage.prep -endif -ifeq ($(CONFIG_MBX),y) - cp zImage.initrd /tftpboot/zImage.mbx -endif + cp zImage.initrd $(TFTPIMAGE) clean: rm -f vmlinux* zvmlinux* mkprep zImage* diff --git a/arch/ppc/boot/head.S b/arch/ppc/boot/head.S index 43facf7e2..552e82fa1 100644 --- a/arch/ppc/boot/head.S +++ b/arch/ppc/boot/head.S @@ -1,4 +1,3 @@ -#include <linux/config.h> #include "../kernel/ppc_defs.h" #include "../kernel/ppc_asm.tmpl" #include <asm/processor.h> @@ -7,43 +6,24 @@ .text /* - * $Id: head.S,v 1.26 1998/09/19 01:21:20 cort Exp $ + * $Id: head.S,v 1.31 1999/04/22 06:32:00 davem Exp $ * - * This code is loaded by the ROM loader at some arbitrary location. - * Move it to high memory so that it can load the kernel at 0x0000. - * - * The MBX EPPC-Bug understands ELF, so it loads us into the location - * specified in the header. This is a two step process. First, EPPC-Bug - * loads the file into the intermediate buffer memory location specified - * by the environment parameters. When it discovers this is an ELF - * binary, it relocates to the link address for us. Unfortunately, the - * header does not move with the file, so we have to find the - * intermediate load location and read the header from there. From - * information provided by Motorola (thank you), we know this intermediate - * location can be found from the NVRAM environment. - * All of these addresses must be somewhat carefully chosen to make sure - * we don't overlap the regions. I chose to load the kernel at 0, the - * compressed image loads at 0x00100000, and the MBX intermediate buffer - * was set to 0x00200000. Provided the loaded kernel image never grows - * over one megabyte (which I am going to ensure never happens :-), these - * will work fine. When we get called from EPPC-Bug, registers are: - * R1 - Stack pointer at a high memory address. - * R3 - Pointer to Board Information Block. - * R4 - Pointer to argument string. - * Interrupts masked, cache and MMU disabled. + * Boot loader philosophy: + * ROM loads us to some arbitrary location + * Move the boot code to the link address (8M) + * Call decompress_kernel() + * Relocate the initrd, zimage and residual data to 8M + * Decompress the kernel to 0 + * Jump to the kernel entry + * -- Cort */ - .globl start start: bl start_ start_: mr r11,r3 /* Save pointer to residual/board data */ - -#ifndef CONFIG_MBX - mfmsr r3 /* Turn off interrupts */ - li r4,0 - ori r4,r4,MSR_EE - andc r3,r3,r4 + mr r25,r5 /* Save OFW pointer */ + li r3,MSR_IP /* Establish default MSR value */ mtmsr r3 /* check if we need to relocate ourselves to the link addr or were we @@ -68,25 +48,6 @@ start_: mr r7,r5 b start_ldr 1010: -#if 0 -/* Copy relocation code down to location 0x0100 (where we hope it's safe!) */ - mflr r3 - addi r5,r3,start_ldr-start_ - addi r3,r3,relocate-start_ - li r4,0x0100 - mtctr r4 - subi r3,r3,4 - subi r4,r4,4 -00: lwzu r6,4(r3) - stwu r6,4(r4) - cmp 0,r3,r5 - bne 00b - mflr r21 - mfctr r22 - mtlr r21 - mtctr r22 - bctr /* Jump to code */ -#endif /* * no matter where we're loaded, move ourselves to -Ttext address */ @@ -96,13 +57,8 @@ relocate: mr r8,r3 lis r4,start@h ori r4,r4,start@l -#if 0 - lis r5,edata@h - ori r5,r5,edata@l -#else lis r5,end@h ori r5,r5,end@l -#endif addi r5,r5,3 /* Round up - just in case */ sub r5,r5,r4 /* Compute # longwords to move */ srwi r5,r5,2 @@ -120,7 +76,6 @@ relocate: mtlr r3 /* Easiest way to do an absolute jump */ blr start_ldr: -#endif /* ndef CONFIG_MBX */ /* Clear all of BSS */ lis r3,edata@h ori r3,r3,edata@l @@ -140,31 +95,11 @@ start_ldr: li r2,0x000F /* Mask pointer to 16-byte boundary */ andc r1,r1,r2 /* Run loader */ -#ifdef CONFIG_MBX - mr r3, r11 - mr r21, r11 - bl serial_init /* Init MBX serial port */ - - lis r8, 0xfa200000@h /* Disable Ethernet SCC */ - li r0, 0 - stw r0, 0x0a00(r8) - - mr r11, r21 - lis r8,start@h - ori r8,r8,start@l - li r9,end@h - ori r9,r9,end@l - sub r7,r8,r9 - srwi r7,r7,2 -#define ILAP_ADDRESS 0xfa000020 - lis r8, ILAP_ADDRESS@h - lwz r8, ILAP_ADDRESS@l(r8) - addis r8, r8, 1 /* Add 64K */ -#endif mr r3,r8 /* Load point */ mr r4,r7 /* Program length */ mr r5,r6 /* Checksum */ mr r6,r11 /* Residual data */ + mr r7,r25 /* OFW interfaces */ bl decompress_kernel /* changed to use r3 (as firmware does) for kernel @@ -193,12 +128,24 @@ start_ldr: li r9,0x0 lwz r9,0(r9) mtlr r9 -#ifndef CONFIG_MBX li r9,0 lis r10,0xdeadc0de@h ori r10,r10,0xdeadc0de@l stw r10,0(r9) -#endif +/* + * The Radstone firmware maps PCI memory at 0xc0000000 using BAT2 + * so disable BATs before setting this to avoid a clash + */ + li r8,0 + mtspr DBAT0U,r8 + mtspr DBAT1U,r8 + mtspr DBAT2U,r8 + mtspr DBAT3U,r8 + mtspr IBAT0U,r8 + mtspr IBAT1U,r8 + mtspr IBAT2U,r8 + mtspr IBAT3U,r8 + blr hang: b hang @@ -269,7 +216,6 @@ _put_MSR: _GLOBAL(flush_instruction_cache) mflr r5 bl flush_data_cache -#ifndef CONFIG_MBX mfspr r3,HID0 /* Caches are controlled by this register */ li r4,0 ori r4,r4,(HID0_ICE|HID0_ICFI) @@ -278,18 +224,12 @@ _GLOBAL(flush_instruction_cache) andc r3,r3,r4 ori r3,r3,HID0_ICE /* Enable cache */ mtspr HID0,r3 -#endif mtlr r5 blr #define NUM_CACHE_LINES 128*8 #define CACHE_LINE_SIZE 32 -#if 0 -cache_flush_buffer: - .space NUM_CACHE_LINES*CACHE_LINE_SIZE /* CAUTION! these need to match hardware */ -#else #define cache_flush_buffer 0x1000 -#endif /* * Flush data cache @@ -300,11 +240,7 @@ _GLOBAL(flush_data_cache) ori r3,r3,cache_flush_buffer@l li r4,NUM_CACHE_LINES mtctr r4 -#if 0 -00: dcbz 0,r3 /* Flush cache line with minimal BUS traffic */ -#else 00: lwz r4,0(r3) -#endif addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ bdnz 00b 10: blr diff --git a/arch/ppc/boot/kbd.c b/arch/ppc/boot/kbd.c index 9f5cd330b..49a102e9c 100644 --- a/arch/ppc/boot/kbd.c +++ b/arch/ppc/boot/kbd.c @@ -1,7 +1,6 @@ - #include <linux/keyboard.h> -#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */ +#include <../drivers/char/defkeymap.c> /* yeah I know it's bad -- Cort */ unsigned char shfts, ctls, alts, caps; @@ -119,7 +118,7 @@ enter: /* Wait for key up */ } break; } - if (brk) return (0); /* Ignore initial 'key up' codes */ + if (brk) return (-1); /* Ignore initial 'key up' codes */ goto loop; } @@ -128,6 +127,11 @@ static void kbdreset(void) unsigned char c; int i; + /* flush input queue */ + while ((inb(KBSTATP) & KBINRDY)) + { + (void)inb(KBDATAP); + } /* Send self-test */ while (inb(KBSTATP) & KBOUTRDY) ; outb(KBSTATP,0xAA); @@ -144,22 +148,63 @@ static void kbdreset(void) while (inb(KBSTATP) & KBOUTRDY) ; outb(KBDATAP,0x45); for (i = 0; i < 10000; i++) udelay(1); + + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0x20); + while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */ + if (! (inb(KBDATAP) & 0x40)) { + /* + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be + * written only when the input-buffer-full bit and + * output-buffer-full bit in the Controller Status + * register are set 0." (KBINRDY and KBOUTRDY) + */ + + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ; + outb(KBDATAP,0xF0); + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ; + outb(KBDATAP,0x01); + } + while (inb(KBSTATP) & KBOUTRDY) ; outb(KBSTATP,0xAE); } +/* We have to actually read the keyboard when CRT_tstc is called, + * since the pending data might be a key release code, and therefore + * not valid data. In this case, kbd() will return -1, even though there's + * data to be read. Of course, we might actually read a valid key press, + * in which case it gets queued into key_pending for use by CRT_getc. + */ + static int kbd_reset = 0; +static int key_pending = -1; + int CRT_getc(void) { int c; if (!kbd_reset) {kbdreset(); kbd_reset++; } + + if (key_pending != -1) { + c = key_pending; + key_pending = -1; + return c; + } else { while ((c = kbd(0)) == 0) ; - return(c); + return c; + } } int CRT_tstc(void) { if (!kbd_reset) {kbdreset(); kbd_reset++; } - return ((inb(KBSTATP) & KBINRDY) != 0); + + while (key_pending == -1 && ((inb(KBSTATP) & KBINRDY) != 0)) { + key_pending = kbd(1); + } + + return (key_pending != -1); } diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index 470e1e4a9..15d347df5 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.53 1998/12/15 17:40:15 cort Exp $ + * $Id: misc.c,v 1.64 1999/04/30 05:52:46 cort Exp $ * * Adapted for PowerPC by Gary Thomas * @@ -17,13 +17,7 @@ #include <asm/page.h> #include <asm/processor.h> #include <asm/mmu.h> -#ifdef CONFIG_MBX -#include <asm/mbx.h> -#endif -#ifdef CONFIG_FADS -#include <asm/fads.h> -#endif -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) #include "ns16550.h" struct NS16550 *com_port; #endif /* CONFIG_SERIAL_CONSOLE */ @@ -37,30 +31,18 @@ struct NS16550 *com_port; */ char *avail_ram; char *end_avail; +extern char _end[]; -/* Because of the limited amount of memory on the MBX, it presents - * loading problems. The biggest is that we load this boot program - * into a relatively low memory address, and the Linux kernel Bss often - * extends into this space when it get loaded. When the kernel starts - * and zeros the BSS space, it also writes over the information we - * save here and pass to the kernel (command line and board info). - * On the MBX we grab some known memory holes to hold this information. - */ -char cmd_preset[] = "console=tty0 console=ttyS0,9600n8"; -char cmd_buf[256]; -char *cmd_line = cmd_buf; - -#if defined(CONFIG_MBX) || defined(CONFIG_FADS) -char *root_string = "root=/dev/nfs"; -char *nfsaddrs_string = "nfsaddrs="; -char *nfsroot_string = "nfsroot="; -char *defroot_string = "/sys/mbxroot"; -int do_ipaddrs(char **cmd_cp, int echo); -void do_nfsroot(char **cmd_cp, char *dp); -int strncmp(const char * cs,const char * ct,size_t count); -char *strrchr(const char * s, int c); +#ifdef CONFIG_CMDLINE +#define CMDLINE CONFIG_CMDLINE +#else +#define CMDLINE ""; #endif +char cmd_preset[] = CMDLINE; +char cmd_buf[256]; +char *cmd_line = cmd_buf; +int keyb_present = 1; /* keyboard controller is present by default */ RESIDUAL hold_resid_buf; RESIDUAL *hold_residual = &hold_resid_buf; unsigned long initrd_start = 0, initrd_end = 0; @@ -78,6 +60,8 @@ void _bcopy(char *src, char *dst, int len); void * memcpy(void * __dest, __const void * __src, int __n); void gunzip(void *, int, unsigned char *, int *); +static int _cvt(unsigned long val, char *buf, long radix, char *digits); +unsigned char inb(int); void pause() { @@ -90,7 +74,6 @@ void exit() while(1); } -#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS) static void clear_screen() { int i, j; @@ -113,8 +96,11 @@ static void scroll() tstc(void) { -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) - return (CRT_tstc() || NS16550_tstc(com_port)); +#if defined(CONFIG_SERIAL_CONSOLE) + if (keyb_present) + return (CRT_tstc() || NS16550_tstc(com_port)); + else + NS16550_tstc(com_port); #else return (CRT_tstc() ); #endif /* CONFIG_SERIAL_CONSOLE */ @@ -123,10 +109,11 @@ tstc(void) getc(void) { while (1) { -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) if (NS16550_tstc(com_port)) return (NS16550_getc(com_port)); #endif /* CONFIG_SERIAL_CONSOLE */ - if (CRT_tstc()) return (CRT_getc()); + if (keyb_present) + if (CRT_tstc()) return (CRT_getc()); } } @@ -135,7 +122,7 @@ putc(const char c) { int x,y; -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) NS16550_putc(com_port, c); if ( c == '\n' ) NS16550_putc(com_port, '\r'); #endif /* CONFIG_SERIAL_CONSOLE */ @@ -149,6 +136,8 @@ putc(const char c) scroll(); y--; } + } else if (c == '\r') { + x = 0; } else if (c == '\b') { if (x > 0) { x--; @@ -179,7 +168,7 @@ void puts(const char *s) y = orig_y; while ( ( c = *s++ ) != '\0' ) { -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) NS16550_putc(com_port, c); if ( c == '\n' ) NS16550_putc(com_port, '\r'); #endif /* CONFIG_SERIAL_CONSOLE */ @@ -206,42 +195,11 @@ void puts(const char *s) } } + cursor(x, y); + orig_x = x; orig_y = y; } -#else -/* The MBX is just the serial port. -*/ -tstc(void) -{ - return (serial_tstc()); -} - -getc(void) -{ - while (1) { - if (serial_tstc()) return (serial_getc()); - } -} - -void -putc(const char c) -{ - serial_putchar(c); -} - -void puts(const char *s) -{ - char c; - - while ( ( c = *s++ ) != '\0' ) { - serial_putchar(c); - if ( c == '\n' ) - serial_putchar('\r'); - } -} - -#endif /* CONFIG_MBX */ void * memcpy(void * __dest, __const void * __src, int __n) @@ -354,7 +312,8 @@ void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) unsigned char sanity[0x2000]; unsigned long -decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + RESIDUAL *residual, void *OFW_interface) { int timer; extern unsigned long start; @@ -362,9 +321,13 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R unsigned long i; BATU *u; BATL *l; -#if defined(CONFIG_MBX) || defined(CONFIG_KB) - char *dp; -#endif + unsigned long TotalMemory; + unsigned long orig_MSR; + int dev_handle; + int mem_info[2]; + int res, size; + unsigned char board_type; + unsigned char base_mod; lines = 25; cols = 80; @@ -372,7 +335,6 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R orig_y = 24; -#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS) /* * IBM's have the MMU on, so we have to disable it or * things get really unhappy in the kernel when @@ -381,42 +343,79 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R */ flush_instruction_cache(); _put_HID0(_get_HID0() & ~0x0000C000); - _put_MSR(_get_MSR() & ~0x0030); - vga_init(0xC0000000); + _put_MSR((orig_MSR = _get_MSR()) & ~0x0030); -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) com_port = (struct NS16550 *)NS16550_init(0); #endif /* CONFIG_SERIAL_CONSOLE */ + vga_init(0xC0000000); if (residual) - memcpy(hold_residual,residual,sizeof(RESIDUAL)); -#else /* CONFIG_MBX */ - - /* Grab some space for the command line and board info. Since - * we no longer use the ELF header, but it was loaded, grab - * that space. - */ - cmd_line = (char *)(load_addr - 0x10000); - hold_residual = (RESIDUAL *)(cmd_line + sizeof(cmd_buf)); - /* copy board data */ - if (residual) - memcpy(hold_residual,residual,sizeof(bd_t)); -#endif /* CONFIG_MBX */ + { + /* Is this Motorola PPCBug? */ + if ((1 & residual->VitalProductData.FirmwareSupports) && + (1 == residual->VitalProductData.FirmwareSupplier)) { + board_type = inb(0x800) & 0xF0; - /* MBX/prep sometimes put the residual/board info at the end of mem - * assume 16M for now -- Cort - * To boot on standard MBX boards with 4M, we can't use initrd, - * and we have to assume less memory. -- Dan - */ - if ( INITRD_OFFSET ) - end_avail = (char *)0x01000000; - else - end_avail = (char *)0x00400000; + /* If this is genesis 2 board then check for no + * keyboard controller and more than one processor. + */ + if (board_type == 0xe0) { + base_mod = inb(0x803); + /* if a MVME2300/2400 or a Sitka then no keyboard */ + if((base_mod == 0x9) || (base_mod == 0xF9) || + (base_mod == 0xE1)) { + keyb_present = 0; /* no keyboard */ + } + } + } + memcpy(hold_residual,residual,sizeof(RESIDUAL)); + } else { + /* Assume 32M in the absence of more info... */ + TotalMemory = 0x02000000; + /* + * This is a 'best guess' check. We want to make sure + * we don't try this on a PReP box without OF + * -- Cort + */ + while (OFW_interface && ((unsigned long)OFW_interface < 0x10000000) ) + { + /* The MMU needs to be on when we call OFW */ + _put_MSR(orig_MSR); + of_init(OFW_interface); + + /* get handle to memory description */ + res = of_finddevice("/memory@0", + &dev_handle); + // puthex(res); puts("\n"); + if (res) break; + + /* get the info */ + // puts("get info = "); + res = of_getprop(dev_handle, + "reg", + mem_info, + sizeof(mem_info), + &size); + // puthex(res); puts(", info = "); puthex(mem_info[0]); + // puts(" "); puthex(mem_info[1]); puts("\n"); + if (res) break; + + TotalMemory = mem_info[1]; + break; + } + hold_residual->TotalMemory = TotalMemory; + residual = hold_residual; + /* Turn MMU back off */ + _put_MSR(orig_MSR & ~0x0030); + } - /* let residual data tell us it's higher */ - if ( (unsigned long)residual > 0x00800000 ) - end_avail = (char *)PAGE_ALIGN((unsigned long)residual); + /* assume the chunk below 8M is free */ + end_avail = (char *)0x00800000; + /* tell the user where we were loaded at and where we + * were relocated to for debugging this process + */ puts("loaded at: "); puthex(load_addr); puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); if ( (unsigned long)load_addr != (unsigned long)&start ) @@ -431,20 +430,12 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R { puts("board data at: "); puthex((unsigned long)residual); puts(" "); -#if defined(CONFIG_MBX) || defined(CONFIG_FADS) - puthex((unsigned long)((unsigned long)residual + sizeof(bd_t))); -#else puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL))); -#endif puts("\n"); puts("relocated to: "); puthex((unsigned long)hold_residual); puts(" "); -#if defined(CONFIG_MBX) || defined(CONFIG_FADS) - puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); -#else puthex((unsigned long)((unsigned long)hold_residual + sizeof(RESIDUAL))); -#endif puts("\n"); } @@ -460,33 +451,16 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R initrd_end = INITRD_SIZE + initrd_start; /* - * setup avail_ram - this is the first part of ram usable - * by the uncompress code. -- Cort + * Find a place to stick the zimage and initrd and + * relocate them if we have to. -- Cort */ - avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size); - if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram) - && (load_addr <= 0x01000000) ) - avail_ram = (char *)(load_addr+(num_words*4)); - if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram) - && (load_addr <= 0x01000000) ) - avail_ram = (char *)((unsigned long)&start+(num_words*4)); - - /* relocate zimage */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); puts("zimage at: "); puthex((unsigned long)zimage_start); puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); - /* - * don't relocate the zimage if it was loaded above 16M since - * things get weird if we try to relocate -- Cort - * We don't relocate zimage on a base MBX board because of - * insufficient memory. In this case we don't have initrd either, - * so use that as an indicator. -- Dan - */ - if (( (unsigned long)zimage_start <= 0x01000000 ) && initrd_start) + if ( (unsigned long)zimage_start <= 0x00800000 ) { - memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size), - (void *)zimage_start, zimage_size ); - zimage_start = (char *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size); - end_avail = (char *)zimage_start; + memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size ); + zimage_start = (char *)avail_ram; puts("relocated to: "); puthex((unsigned long)zimage_start); puts(" "); puthex((unsigned long)zimage_size+(unsigned long)zimage_start); @@ -498,44 +472,24 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R { puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); - /* - * Memory is really tight on the MBX (we can assume 4M) - * so put the initrd at the TOP of ram, and set end_avail - * to right after that. - * - * I should do something like this for prep, too and keep - * a variable end_of_DRAM to keep track of what we think the - * max ram is. - * -- Cort - */ -#if 0 - memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE), - (void *)initrd_start, - INITRD_SIZE ); - initrd_start = PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE); +#ifdef OMIT + avail_ram = (char *)PAGE_ALIGN( + (unsigned long)zimage_size+(unsigned long)zimage_start); + memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); + initrd_start = (unsigned long)avail_ram; initrd_end = initrd_start + INITRD_SIZE; - end_avail = (char *)initrd_start; puts("relocated to: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); -#endif +#endif } -#ifndef CONFIG_MBX - /* this is safe, just use it */ - /* I don't know why it didn't work for me on the MBX with 20 MB - * memory. I guess something was saved up there, but I can't - * figure it out......we are running on luck. -- Dan. - */ avail_ram = (char *)0x00400000; - end_avail = (char *)0x00600000; -#endif + end_avail = (char *)0x00800000; puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); puthex((unsigned long)end_avail); puts("\n"); - -#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS) - CRT_tstc(); /* Forces keyboard to be initialized */ -#endif + if (keyb_present) + CRT_tstc(); /* Forces keyboard to be initialized */ puts("\nLinux/PPC load: "); timer = 0; @@ -550,13 +504,6 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R cp--; puts("\b \b"); } -#ifdef CONFIG_MBX - } else if (ch == '?') { - if (!do_ipaddrs(&cp, 1)) { - *cp++ = ch; - putc(ch); - } -#endif } else { *cp++ = ch; putc(ch); @@ -567,32 +514,6 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R udelay(1000); /* 1 msec */ } *cp = 0; -#ifdef CONFIG_MBX - /* The MBX does not currently have any default boot strategy. - * If the command line is not filled in, we will automatically - * create the default network boot. - */ - if (cmd_line[0] == 0) { - dp = root_string; - while (*dp != 0) - *cp++ = *dp++; - *cp++ = ' '; - - dp = nfsaddrs_string; - while (*dp != 0) - *cp++ = *dp++; - dp = cp; - do_ipaddrs(&cp, 0); - *cp++ = ' '; - - /* Add the server address to the root file system path. - */ - dp = strrchr(dp, ':'); - dp++; - do_nfsroot(&cp, dp); - *cp = 0; - } -#endif puts("\n"); /* mappings on early boot can only handle 16M */ @@ -611,150 +532,6 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R return (unsigned long)hold_residual; } -#ifdef CONFIG_MBX -int -do_ipaddrs(char **cmd_cp, int echo) -{ - char *cp, *ip, ch; - unsigned char ipd; - int i, j, retval; - - /* We need to create the string: - * <my_ip>:<serv_ip> - */ - cp = *cmd_cp; - retval = 0; - - if ((cp - 9) >= cmd_line) { - if (strncmp(cp - 9, "nfsaddrs=", 9) == 0) { - ip = (char *)0xfa000060; - retval = 1; - for (j=0; j<2; j++) { - for (i=0; i<4; i++) { - ipd = *ip++; - - ch = ipd/100; - if (ch) { - ch += '0'; - if (echo) - putc(ch); - *cp++ = ch; - ipd -= 100 * (ch - '0'); - } - - ch = ipd/10; - if (ch) { - ch += '0'; - if (echo) - putc(ch); - *cp++ = ch; - ipd -= 10 * (ch - '0'); - } - - ch = ipd + '0'; - if (echo) - putc(ch); - *cp++ = ch; - - ch = '.'; - if (echo) - putc(ch); - *cp++ = ch; - } - - /* At the end of the string, remove the - * '.' and replace it with a ':'. - */ - *(cp - 1) = ':'; - if (echo) { - putc('\b'); putc(':'); - } - } - - /* At the end of the second string, remove the - * '.' from both the command line and the - * screen. - */ - --cp; - putc('\b'); putc(' '); putc('\b'); - } - } - *cmd_cp = cp; - return(retval); -} - -void -do_nfsroot(char **cmd_cp, char *dp) -{ - char *cp, *rp, *ep; - - /* The boot argument (i.e /sys/mbxroot/zImage) is stored - * at offset 0x0078 in NVRAM. We use this path name to - * construct the root file system path. - */ - cp = *cmd_cp; - - /* build command string. - */ - rp = nfsroot_string; - while (*rp != 0) - *cp++ = *rp++; - - /* Add the server address to the path. - */ - while (*dp != ' ') - *cp++ = *dp++; - *cp++ = ':'; - - rp = (char *)0xfa000078; - ep = strrchr(rp, '/'); - - if (ep != 0) { - while (rp < ep) - *cp++ = *rp++; - } - else { - rp = defroot_string; - while (*rp != 0) - *cp++ = *rp++; - } - - *cmd_cp = cp; -} - -size_t strlen(const char * s) -{ - const char *sc; - - for (sc = s; *sc != '\0'; ++sc) - /* nothing */; - return sc - s; -} - -int strncmp(const char * cs,const char * ct,size_t count) -{ - register signed char __res = 0; - - while (count) { - if ((__res = *cs - *ct++) != 0 || !*cs++) - break; - count--; - } - - return __res; -} - -char * strrchr(const char * s, int c) -{ - const char *p = s + strlen(s); - do { - if (*p == (char)c) - return (char *)p; - } while (--p >= s); - return NULL; -} -#endif - void puthex(unsigned long val) { unsigned char buf[10]; @@ -802,3 +579,243 @@ _bcopy(char *src, char *dst, int len) { while (len--) *dst++ = *src++; } + + +#define FALSE 0 +#define TRUE 1 +#include <stdarg.h> + +int +strlen(char *s) +{ + int len = 0; + while (*s++) len++; + return len; +} + +_printk(char const *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = _vprintk(putc, fmt, ap); + va_end(ap); + return (ret); +} + +#define is_digit(c) ((c >= '0') && (c <= '9')) + +int +_vprintk(putc, fmt0, ap) +int (*putc)(); +const char *fmt0; +va_list ap; +{ + char c, sign, *cp; + int left_prec, right_prec, zero_fill, length, pad, pad_on_right; + char buf[32]; + long val; + while (c = *fmt0++) + { + if (c == '%') + { + c = *fmt0++; + left_prec = right_prec = pad_on_right = 0; + if (c == '-') + { + c = *fmt0++; + pad_on_right++; + } + if (c == '0') + { + zero_fill = TRUE; + c = *fmt0++; + } else + { + zero_fill = FALSE; + } + while (is_digit(c)) + { + left_prec = (left_prec * 10) + (c - '0'); + c = *fmt0++; + } + if (c == '.') + { + c = *fmt0++; + zero_fill++; + while (is_digit(c)) + { + right_prec = (right_prec * 10) + (c - '0'); + c = *fmt0++; + } + } else + { + right_prec = left_prec; + } + sign = '\0'; + switch (c) + { + case 'd': + case 'x': + case 'X': + val = va_arg(ap, long); + switch (c) + { + case 'd': + if (val < 0) + { + sign = '-'; + val = -val; + } + length = _cvt(val, buf, 10, "0123456789"); + break; + case 'x': + length = _cvt(val, buf, 16, "0123456789abcdef"); + break; + case 'X': + length = _cvt(val, buf, 16, "0123456789ABCDEF"); + break; + } + cp = buf; + break; + case 's': + cp = va_arg(ap, char *); + length = strlen(cp); + break; + case 'c': + c = va_arg(ap, long /*char*/); + (*putc)(c); + continue; + default: + (*putc)('?'); + } + pad = left_prec - length; + if (sign != '\0') + { + pad--; + } + if (zero_fill) + { + c = '0'; + if (sign != '\0') + { + (*putc)(sign); + sign = '\0'; + } + } else + { + c = ' '; + } + if (!pad_on_right) + { + while (pad-- > 0) + { + (*putc)(c); + } + } + if (sign != '\0') + { + (*putc)(sign); + } + while (length-- > 0) + { + (*putc)(c = *cp++); + if (c == '\n') + { + (*putc)('\r'); + } + } + if (pad_on_right) + { + while (pad-- > 0) + { + (*putc)(c); + } + } + } else + { + (*putc)(c); + if (c == '\n') + { + (*putc)('\r'); + } + } + } +} + +int _cvt(unsigned long val, char *buf, long radix, char *digits) +{ + char temp[80]; + char *cp = temp; + int length = 0; + if (val == 0) + { /* Special case */ + *cp++ = '0'; + } else + while (val) + { + *cp++ = digits[val % radix]; + val /= radix; + } + while (cp != temp) + { + *buf++ = *--cp; + length++; + } + *buf = '\0'; + return (length); +} + +_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base) +{ + int i, c; + if ((unsigned int)s > (unsigned int)p) + { + s = (unsigned int)s - (unsigned int)p; + } + while (s > 0) + { + if (base) + { + _printk("%06X: ", (int)p - (int)base); + } else + { + _printk("%06X: ", p); + } + for (i = 0; i < 16; i++) + { + if (i < s) + { + _printk("%02X", p[i] & 0xFF); + } else + { + _printk(" "); + } + if ((i % 2) == 1) _printk(" "); + if ((i % 8) == 7) _printk(" "); + } + _printk(" |"); + for (i = 0; i < 16; i++) + { + if (i < s) + { + c = p[i] & 0xFF; + if ((c < 0x20) || (c >= 0x7F)) c = '.'; + } else + { + c = ' '; + } + _printk("%c", c); + } + _printk("|\n"); + s -= 16; + p += 16; + } +} + +_dump_buf(unsigned char *p, int s) +{ + _printk("\n"); + _dump_buf_with_offset(p, s, 0); +} diff --git a/arch/ppc/boot/mkprep.c b/arch/ppc/boot/mkprep.c index 7799c9acc..0a596b0ff 100644 --- a/arch/ppc/boot/mkprep.c +++ b/arch/ppc/boot/mkprep.c @@ -14,15 +14,8 @@ * Modified for x86 hosted builds by Matt Porter <porter@neta.com> */ -#ifdef linux -#include <linux/types.h> -/*#include <asm/stat.h>*/ -/*#include <asm/byteorder.h>*/ /* the byte swap funcs don't work here -- Cort */ -#else #include <unistd.h> -#endif #include <sys/stat.h> - #include <stdio.h> #include <errno.h> @@ -168,10 +161,10 @@ void write_prep_partition(int in, int out) /* set entry point and boot image size skipping over elf header */ #ifdef __i386__ *entry = 0x400/*+65536*/; - *length = info.st_size+0x400; + *length = info.st_size-elfhdr_size+0x400; #else *entry = cpu_to_le32(0x400/*+65536*/); - *length = cpu_to_le32(info.st_size+0x400); + *length = cpu_to_le32(info.st_size-elfhdr_size+0x400); #endif /* __i386__ */ /* sets magic number for msdos partition (used by linux) */ diff --git a/arch/ppc/boot/of1275.c b/arch/ppc/boot/of1275.c new file mode 100644 index 000000000..b82fa7a46 --- /dev/null +++ b/arch/ppc/boot/of1275.c @@ -0,0 +1,427 @@ +/* Open Firmware Client Interface */ + + +#include "of1275.h" + + +static int (*of_server)(void *) = (int(*)(void*))-1; + +void +of_init(void *handler) +{ + of_server = (int(*)(void*))handler; +} + + +/* 6.3.2.1 Client interface */ + + +int +of_test(const char *name, int *missing) +{ + int result; + static of_test_service s; + s.service = "test"; + s.n_args = 1; + s.n_returns = 1; + s.name = name; + result = of_server(&s); + *missing = s.missing; + return result; +} + + +/* 6.3.2.2 Device tree */ + + +int +of_peer(int phandle, int *sibling_phandle) +{ + int result; + static of_peer_service s; + s.service = "peer"; + s.n_args = 1; + s.n_returns = 1; + s.phandle = phandle; + result = of_server(&s); + *sibling_phandle = s.sibling_phandle; + return result; +} + +int +of_child(int phandle, int *child_phandle) +{ + int result; + static of_child_service s; + s.service = "child"; + s.n_args = 1; + s.n_returns = 1; + s.phandle = phandle; + result = of_server(&s); + *child_phandle = s.child_phandle; + return result; +} + +int +of_parent(int phandle, int *parent_phandle) +{ + int result; + static of_parent_service s; + s.service = "parent"; + s.n_args = 1; + s.n_returns = 1; + s.phandle = phandle; + result = of_server(&s); + *parent_phandle = s.parent_phandle; + return result; +} + +int +of_instance_to_package(int ihandle, int *phandle) +{ + int result; + static of_instance_to_package_service s; + s.service = "instance-to-package"; + s.n_args = 1; + s.n_returns = 1; + s.ihandle = ihandle; + result = of_server(&s); + *phandle = s.phandle; + return result; +} + +int +of_getproplen(int phandle, const char *name, int *proplen) +{ + int result; + static of_getproplen_service s; + s.service = "getproplen"; + s.n_args = 2; + s.n_returns = 1; + s.phandle = phandle; + s.name = name; + result = of_server(&s); + *proplen = s.proplen; + return result; +} + +int +of_getprop(int phandle, const char *name, void *buf, int buflen, int *size) +{ + int result; + static of_getprop_service s; + s.service = "getprop"; + s.n_args = 4; + s.n_returns = 1; + s.phandle = phandle; + s.name = name; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *size = s.size; + return result; +} + +int +of_nextprop(int phandle, const char *previous, void *buf, int *flag) +{ + int result; + static of_nextprop_service s; + s.service = "nextprop"; + s.n_args = 3; + s.n_returns = 1; + s.phandle = phandle; + s.previous = previous; + s.buf = buf; + result = of_server(&s); + *flag = s.flag; + return result; +} + +int +of_setprop(int phandle, const char *name, void *buf, int len, int *size) +{ + int result; + static of_setprop_service s; + s.service = "setprop"; + s.n_args = 4; + s.n_returns = 1; + s.phandle = phandle; + s.name = name; + s.buf = buf; + s.len = len; + result = of_server(&s); + *size = s.size; + return result; +} + +int +of_canon(const char *device_specifier, void *buf, int buflen, int *length) +{ + int result; + static of_canon_service s; + s.service = "canon"; + s.n_args = 3; + s.n_returns = 1; + s.device_specifier = device_specifier; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *length = s.length; + return result; +} + +int +of_finddevice(const char *device_specifier, int *phandle) +{ + int result; + static of_finddevice_service s; + s.service = "finddevice"; + s.n_args = 1; + s.n_returns = 1; + s.device_specifier = device_specifier; + result = of_server(&s); + *phandle = s.phandle; + return result; +} + +int +of_instance_to_path(int ihandle, void *buf, int buflen, int *length) +{ + int result; + static of_instance_to_path_service s; + s.service = "instance-to-path"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *length = s.length; + return result; +} + +int +of_package_to_path(int phandle, void *buf, int buflen, int *length) +{ + int result; + static of_package_to_path_service s; + s.service = "package-to-path"; + s.n_args = 3; + s.n_returns = 1; + s.phandle = phandle; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *length = s.length; + return result; +} + +/* int of_call_method(const char *method, int ihandle, ...); */ + + +/* 6.3.2.3 Device I/O */ + + +int +of_open(const char *device_specifier, int *ihandle) +{ + int result; + static of_open_service s; + s.service = "open"; + s.n_args = 1; + s.n_returns = 1; + s.device_specifier = device_specifier; + result = of_server(&s); + *ihandle = s.ihandle; + return result; +} + +int +of_close(int ihandle) +{ + int result; + static of_close_service s; + s.service = "close"; + s.n_args = 1; + s.n_returns = 0; + s.ihandle = ihandle; + result = of_server(&s); + return result; +} + +int +of_read(int ihandle, void *addr, int len, int *actual) +{ + int result; + static of_read_service s; + s.service = "read"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.addr = addr; + s.len = len; + result = of_server(&s); + *actual = s.actual; + return result; +} + +int +of_write(int ihandle, void *addr, int len, int *actual) +{ + int result; + static of_write_service s; + s.service = "write"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.addr = addr; + s.len = len; + result = of_server(&s); + *actual = s.actual; + return result; +} + +int +of_seek(int ihandle, int pos_hi, int pos_lo, int *status) +{ + int result; + static of_seek_service s; + s.service = "seek"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.pos_hi = pos_hi; + s.pos_lo = pos_lo; + result = of_server(&s); + *status = s.status; + return result; +} + + +/* 6.3.2.4 Memory */ + + +int +of_claim(void *virt, int size, int align, void **baseaddr) +{ + int result; + static of_claim_service s; + s.service = "claim"; + s.n_args = 3; + s.n_returns = 1; + s.virt = virt; + s.size = size; + s.align = align; + result = of_server(&s); + *baseaddr = s.baseaddr; + return result; +} + +int +of_release(void *virt, int size) +{ + int result; + static of_release_service s; + s.service = "release"; + s.n_args = 2; + s.n_returns = 0; + s.virt = virt; + s.size = size; + result = of_server(&s); + return result; +} + + +/* 6.3.2.5 Control transfer */ + + +int +of_boot(const char *bootspec) +{ + int result; + static of_boot_service s; + s.service = "boot"; + s.n_args = 1; + s.n_returns = 0; + s.bootspec = bootspec; + result = of_server(&s); + return result; +} + +int +of_enter(void) +{ + int result; + static of_enter_service s; + s.service = "enter"; + s.n_args = 0; + s.n_returns = 0; + result = of_server(&s); + return result; +} + +int +of_exit(void) +{ + int result; + static of_exit_service s; + s.service = "exit"; + s.n_args = 0; + s.n_returns = 0; + result = of_server(&s); + return result; +} + +/* int of_chain(void *virt, int size, void *entry, void *args, int len); */ + + +/* 6.3.2.6 User interface */ + + +/* int of_interpret(const char *arg, ...); */ + +int +of_set_callback(void *newfunc, void **oldfunc) +{ + int result; + static of_set_callback_service s; + s.service = "set-callback"; + s.n_args = 1; + s.n_returns = 1; + s.newfunc = newfunc; + result = of_server(&s); + *oldfunc = s.oldfunc; + return result; +} + +int +of_set_symbol_lookup(void *sym_to_value, void *value_to_sym) +{ + int result; + static of_set_symbol_lookup_service s; + s.service = "set-symbol-lookup"; + s.n_args = 2; + s.n_returns = 0; + s.sym_to_value = sym_to_value; + s.value_to_sym = s.value_to_sym; + result = of_server(&s); + return result; +} + + +/* 6.3.2.7 Time */ + + +int +of_milliseconds(int *ms) +{ + int result; + static of_milliseconds_service s; + s.service = "milliseconds"; + s.n_args = 0; + s.n_returns = 1; + result = of_server(&s); + *ms = s.ms; + return result; +} diff --git a/arch/ppc/boot/of1275.h b/arch/ppc/boot/of1275.h new file mode 100644 index 000000000..bc050d32b --- /dev/null +++ b/arch/ppc/boot/of1275.h @@ -0,0 +1,421 @@ +/* 6.3.2.1 Client interface */ + + +typedef struct _of_test_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *name; + /*out*/ + int missing; +} of_test_service; + +int of_test(const char *name, int *missing); + + +/* 6.3.2.2 Device tree */ + + +typedef struct _of_peer_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + /*out*/ + int sibling_phandle; +} of_peer_service; + +int of_peer(int phandle, int *sibling_phandle); + + +typedef struct _of_child_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + /*out*/ + int child_phandle; +} of_child_service; + +int of_child(int phandle, int *child_phandle); + + +typedef struct _of_parent_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + /*out*/ + int parent_phandle; +} of_parent_service; + +int of_child(int phandle, int *parent_phandle); + + +typedef struct _of_instance_to_package_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + /*out*/ + int phandle; +} of_instance_to_package_service; + +int of_instance_to_package(int ihandle, int *phandle); + + +typedef struct _of_getproplen_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *name; + /*out*/ + int proplen; +} of_getproplen_service; + +int of_getproplen(int phandle, const char *name, int *proplen); + + +typedef struct _of_getprop_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *name; + void *buf; + int buflen; + /*out*/ + int size; +} of_getprop_service; + +int of_getprop(int phandle, const char *name, void *buf, int buflen, + int *size); + + +typedef struct _of_nextprop_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *previous; + void *buf; + /*out*/ + int flag; +} of_nextprop_service; + +int of_nextprop(int phandle, const char *previous, void *buf, int *flag); + + +typedef struct _of_setprop_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *name; + void *buf; + int len; + /*out*/ + int size; +} of_setprop_service; + +int of_setprop(int phandle, const char *name, void *buf, int len, int *size); + + +typedef struct _of_canon_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *device_specifier; + void *buf; + int buflen; + /*out*/ + int length; +} of_canon_service; + +int of_canon(const char *device_specifier, void *buf, int buflen, int *length); + + +typedef struct _of_finddevice_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *device_specifier; + /*out*/ + int phandle; +} of_finddevice_service; + +int of_finddevice(const char *device_specifier, int *phandle); + + +typedef struct _of_instance_to_path_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + void *buf; + int buflen; + /*out*/ + int length; +} of_instance_to_path_service; + +int of_instance_to_path(int ihandle, void *buf, int buflen, int *length); + + +typedef struct _of_package_to_path_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + void *buf; + int buflen; + /*out*/ + int length; +} of_package_to_path_service; + +int of_package_to_path(int phandle, void *buf, int buflen, int *length); + + +typedef struct _of_call_method_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *method; + int ihandle; + /*...*/ + int args[0]; +} of_call_method_service; + +int of_call_method(const char *method, int ihandle, ...); + + +/* 6.3.2.3 Device I/O */ + + +typedef struct _of_open_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *device_specifier; + /*out*/ + int ihandle; +} of_open_service; + +int of_open(const char *device_specifier, + int *ihandle); + + +typedef struct _of_close_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + /*out*/ +} of_close_service; + +int of_close(int ihandle); + + +typedef struct _of_read_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + void *addr; + int len; + /*out*/ + int actual; +} of_read_service; + +int of_read(int ihandle, void *addr, int len, int *actual); + + +typedef struct _of_write_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + void *addr; + int len; + /*out*/ + int actual; +} of_write_service; + +int of_write(int ihandle, void *addr, int len, int *actual); + + +typedef struct _of_seek_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + int pos_hi; + int pos_lo; + /*out*/ + int status; +} of_seek_service; + +int of_seek(int ihandle, int pos_hi, int pos_lo, int *status); + + +/* 6.3.2.4 Memory */ + + +typedef struct _of_claim_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *virt; + int size; + int align; + /*out*/ + void *baseaddr; +} of_claim_service; + +int of_claim(void *virt, int size, int align, void **baseaddr); + + +typedef struct _of_release_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *virt; + int size; + int align; + /*out*/ +} of_release_service; + +int of_release(void *virt, int size); + + +/* 6.3.2.5 Control transfer */ + + +typedef struct _of_boot_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *bootspec; + /*out*/ +} of_boot_service; + +int of_boot(const char *bootspec); + + +typedef struct _of_enter_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + /*out*/ +} of_enter_service; + +int of_enter(void); + + +typedef struct _of_exit_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + /*out*/ +} of_exit_service; + +int of_exit(void); + + +typedef struct _of_chain_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *virt; + int size; + void *entry; + void *args; + int len; + /*out*/ +} of_chain_service; + +int of_chain(void *virt, int size, void *entry, void *args, int len); + + +/* 6.3.2.6 User interface */ + + +typedef struct _of_interpret_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *cmd; + int args[0]; + /*...*/ + /*out*/ + /*...*/ +} of_interpret_service; + +int of_interpret(const char *arg, ...); + + +typedef struct _of_set_callback_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *newfunc; + /*out*/ + void *oldfunc; +} of_set_callback_service; + +int of_set_callback(void *newfunc, void **oldfunc); + + +typedef struct _of_set_symbol_lookup_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *sym_to_value; + void *value_to_sym; + /*out*/ +} of_set_symbol_lookup_service; + +int of_set_symbol_lookup(void *sym_to_value, void *value_to_sym); + + +/* 6.3.2.7 Time */ + + +typedef struct _of_milliseconds_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + /*out*/ + int ms; +} of_milliseconds_service; + +int of_milliseconds(int *ms); diff --git a/arch/ppc/boot/piggyback.c b/arch/ppc/boot/piggyback.c deleted file mode 100644 index ca9fc2957..000000000 --- a/arch/ppc/boot/piggyback.c +++ /dev/null @@ -1,64 +0,0 @@ -#include <stdio.h> - -extern long ce_exec_config[]; - -main(int argc, char *argv[]) -{ - int i, cnt, pos, len; - unsigned int cksum, val; - unsigned char *lp; - unsigned char buf[8192]; - if (argc != 1) - { - fprintf(stderr, "usage: %s <in-file >out-file\n", argv[0]); - exit(1); - } - fprintf(stdout, "#\n"); - fprintf(stdout, "# Miscellaneous data structures:\n"); - fprintf(stdout, "# WARNING - this file is automatically generated!\n"); - fprintf(stdout, "#\n"); - fprintf(stdout, "\n"); - fprintf(stdout, "\t.data\n"); - fprintf(stdout, "\t.globl input_data\n"); - fprintf(stdout, "input_data:\n"); - pos = 0; - cksum = 0; - while ((len = read(0, buf, sizeof(buf))) > 0) - { - cnt = 0; - lp = (unsigned char *)buf; - len = (len + 3) & ~3; /* Round up to longwords */ - for (i = 0; i < len; i += 4) - { - if (cnt == 0) - { - fprintf(stdout, "\t.long\t"); - } - fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); - val = *(unsigned long *)lp; - cksum ^= val; - lp += 4; - if (++cnt == 4) - { - cnt = 0; - fprintf(stdout, " # %x \n", pos+i-12); - fflush(stdout); - } else - { - fprintf(stdout, ","); - } - } - if (cnt) - { - fprintf(stdout, "0\n"); - } - pos += len; - } - fprintf(stdout, "\t.globl input_len\n"); - fprintf(stdout, "input_len:\t.long\t0x%x\n", pos); - fflush(stdout); - fclose(stdout); - fprintf(stderr, "cksum = %x\n", cksum); - exit(0); -} - diff --git a/arch/ppc/chrp_defconfig b/arch/ppc/chrp_defconfig index 9141b2d90..48e305f44 100644 --- a/arch/ppc/chrp_defconfig +++ b/arch/ppc/chrp_defconfig @@ -85,7 +85,6 @@ CONFIG_PARIDE_PARPORT=y # CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -CONFIG_NET_ALIAS=y # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y diff --git a/arch/ppc/chrpboot/Makefile b/arch/ppc/chrpboot/Makefile index 946845a5d..cd0336bc9 100644 --- a/arch/ppc/chrpboot/Makefile +++ b/arch/ppc/chrpboot/Makefile @@ -28,6 +28,12 @@ ifeq ($(CONFIG_ALL_PPC),y) CONFIG_CHRP = y endif +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.chrp.smp +else +TFTPIMAGE=/tftpboot/zImage.chrp +endif + all: $(TOPDIR)/zImage # @@ -36,10 +42,10 @@ all: $(TOPDIR)/zImage # ifeq ($(CONFIG_CHRP),y) znetboot: zImage - cp zImage /tftpboot/zImage.chrp + cp zImage $(TFTPIMAGE) znetboot.initrd: zImage.initrd - cp zImage.initrd /tftpboot/zImage.chrp + cp zImage.initrd $(TFTPIMAGE) floppy: zImage mcopy zImage a:zImage diff --git a/arch/ppc/coffboot/Makefile b/arch/ppc/coffboot/Makefile index 4669d2228..9ca967785 100644 --- a/arch/ppc/coffboot/Makefile +++ b/arch/ppc/coffboot/Makefile @@ -23,6 +23,12 @@ ifeq ($(CONFIG_ALL_PPC),y) CONFIG_PMAC = y endif +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.pmac.smp +else +TFTPIMAGE=/tftpboot/zImage.pmac +endif + ifeq ($(CONFIG_PMAC),y) hack-coff: hack-coff.c $(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c @@ -33,10 +39,10 @@ floppy: zImage # umount /mnt znetboot: vmlinux.coff - cp vmlinux.coff /tftpboot/zImage.pmac + cp vmlinux.coff $(TFTPIMAGE) znetboot.initrd: vmlinux.coff.initrd - cp vmlinux.coff.initrd /tftpboot/zImage.pmac + cp vmlinux.coff.initrd $(TFTPIMAGE) coffboot: $(OBJS) ld.script $(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS) diff --git a/arch/ppc/common_defconfig b/arch/ppc/common_defconfig index f58965709..131244790 100644 --- a/arch/ppc/common_defconfig +++ b/arch/ppc/common_defconfig @@ -1,5 +1,5 @@ # -# Automatically generated by make menuconfig: don't edit +# Automatically generated make config: don't edit # # @@ -21,7 +21,7 @@ CONFIG_ALL_PPC=y # CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y -CONFIG_MODVERSIONS=y +# CONFIG_MODVERSIONS is not set CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_QUIRKS is not set @@ -32,23 +32,23 @@ CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y -# CONFIG_BINFMT_MISC is not set +CONFIG_BINFMT_MISC=m # CONFIG_BINFMT_JAVA is not set # CONFIG_PARPORT is not set -# CONFIG_VGA_CONSOLE is not set +CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_FB_COMPAT_XPMAC=y CONFIG_PMAC_PBOOK=y CONFIG_MAC_KEYBOARD=y CONFIG_MAC_FLOPPY=y CONFIG_MAC_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set CONFIG_ADBMOUSE=y -CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_PROC_DEVICETREE=y -# CONFIG_KGDB is not set -# CONFIG_XMON is not set # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y +# CONFIG_MOTOROLA_HOTSWAP is not set +# CONFIG_CMDLINE_BOOL is not set # # Plug and Play support @@ -60,18 +60,28 @@ CONFIG_BOOTX_TEXT=y # # CONFIG_BLK_DEV_FD is not set CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set +CONFIG_BLK_DEV_IDEFLOPPY=y # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set # CONFIG_BLK_DEV_IDEPCI is not set -CONFIG_BLK_DEV_SL82C105=y +# CONFIG_BLK_DEV_SL82C105 is not set +CONFIG_BLK_DEV_IDE_PMAC=y +# CONFIG_BLK_DEV_IDEDMA_PMAC is not set # CONFIG_IDE_CHIPSETS is not set -CONFIG_BLK_DEV_LOOP=m + +# +# Additional Block Devices +# +CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y @@ -84,27 +94,36 @@ CONFIG_PARIDE_PARPORT=y # # Networking options # -# CONFIG_PACKET is not set -# CONFIG_NETLINK is not set +CONFIG_PACKET=y +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set +CONFIG_IP_MULTICAST=y # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set -# CONFIG_IP_ALIAS is not set -CONFIG_SYN_COOKIES=y -# CONFIG_INET_RARP is not set -# CONFIG_IP_NOSR is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +# CONFIG_SYN_COOKIES is not set + +# +# (it is safe to leave these untouched) +# +CONFIG_INET_RARP=y CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set + +# +# +# # CONFIG_IPX is not set -# CONFIG_ATALK is not set +CONFIG_ATALK=m # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set @@ -124,13 +143,21 @@ CONFIG_SKB_LARGE=y # SCSI support # CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# CONFIG_BLK_DEV_SD=y -CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set -# CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_CONSTANTS is not set +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set # @@ -141,7 +168,10 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set -# CONFIG_SCSI_AIC7XXX is not set +CONFIG_SCSI_AIC7XXX=y +# CONFIG_OVERRIDE_CMDS is not set +CONFIG_AIC7XXX_PROC_STATS=y +CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -154,15 +184,20 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_G_NCR5380_PORT is not set +# CONFIG_SCSI_G_NCR5380_MEM is not set # CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_NCR53C7xx is not set CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_SYM53C8XX is not set CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 -CONFIG_SCSI_NCR53C8XX_SYNC=5 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set -CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set @@ -170,6 +205,7 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED=y # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -187,6 +223,7 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set +# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y @@ -199,7 +236,7 @@ CONFIG_BMAC=y # CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y -CONFIG_PCNET32=m +CONFIG_PCNET32=y # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set @@ -219,16 +256,21 @@ CONFIG_DE4X5=y # CONFIG_FDDI is not set # CONFIG_HIPPI is not set # CONFIG_DLCI is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_IPDDP is not set CONFIG_PPP=y -CONFIG_SLIP=m -# CONFIG_SLIP_COMPRESSED is not set -# CONFIG_SLIP_SMART is not set -# CONFIG_SLIP_MODE_SLIP6 is not set + +# +# CCP compressors for PPP are only built as modules. +# +# CONFIG_SLIP is not set # CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support @@ -241,7 +283,7 @@ CONFIG_SLIP=m # CONFIG_ISDN is not set # -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# Old CD-ROM drivers (not SCSI, not IDE) # # CONFIG_CD_NO_IDESCSI is not set @@ -249,27 +291,42 @@ CONFIG_SLIP=m # Console drivers # CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_PM2 is not set CONFIG_FB_OF=y CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -CONFIG_FB_ATY=y +# CONFIG_FB_ATY is not set CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set -# CONFIG_FB_MATROX is not set -CONFIG_FB_ATY=y +CONFIG_FB_MATROX=y +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G100=y +# CONFIG_FB_MATROX_MULTIHEAD is not set +# CONFIG_FB_ATY is not set # CONFIG_FB_VIRTUAL is not set -# CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_ADVANCED=y +# CONFIG_FBCON_MFB is not set +# CONFIG_FBCON_CFB2 is not set +# CONFIG_FBCON_CFB4 is not set CONFIG_FBCON_CFB8=y CONFIG_FBCON_CFB16=y CONFIG_FBCON_CFB24=y CONFIG_FBCON_CFB32=y +# CONFIG_FBCON_AFB is not set +# CONFIG_FBCON_ILBM is not set +# CONFIG_FBCON_IPLAN2P2 is not set +# CONFIG_FBCON_IPLAN2P4 is not set +# CONFIG_FBCON_IPLAN2P8 is not set +# CONFIG_FBCON_MAC is not set +# CONFIG_FBCON_VGA is not set # CONFIG_FBCON_FONTWIDTH8_ONLY is not set CONFIG_FBCON_FONTS=y -CONFIG_FONT_8x8=y +# CONFIG_FONT_8x8 is not set CONFIG_FONT_8x16=y -# CONFIG_FONT_SUN8x16 is not set +CONFIG_FONT_SUN8x16=y CONFIG_FONT_SUN12x22=y # CONFIG_FONT_6x11 is not set # CONFIG_FONT_PEARL_8x8 is not set @@ -283,7 +340,8 @@ CONFIG_VT_CONSOLE=y CONFIG_SERIAL=m # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_UNIX98_PTYS is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 CONFIG_MOUSE=y # @@ -309,30 +367,40 @@ CONFIG_PSMOUSE=y # Joystick support # # CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set # # Ftape, the floppy tape device driver # # CONFIG_FTAPE is not set +# CONFIG_FT_NORMAL_DEBUG is not set +# CONFIG_FT_FULL_DEBUG is not set +# CONFIG_FT_NO_TRACE is not set +# CONFIG_FT_NO_TRACE_AT_ALL is not set +# CONFIG_FT_STD_FDC is not set +# CONFIG_FT_MACH2 is not set +# CONFIG_FT_PROBE_FC10 is not set +# CONFIG_FT_ALT_FDC is not set # # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS_FS=y # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=m CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y @@ -344,7 +412,7 @@ CONFIG_EXT2_FS=y # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -CONFIG_NFSD=m +CONFIG_NFSD=y # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y @@ -364,7 +432,7 @@ CONFIG_NLS=y # # Native Language Support # -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set # CONFIG_NLS_CODEPAGE_850 is not set @@ -402,30 +470,11 @@ CONFIG_DMASOUND=y # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set -CONFIG_SOUND_OSS=y -# CONFIG_SOUND_PAS is not set -# CONFIG_SOUND_SB is not set -# CONFIG_SOUND_ADLIB is not set -# CONFIG_SOUND_GUS is not set -# CONFIG_SOUND_MPU401 is not set -# CONFIG_SOUND_PSS is not set -# CONFIG_SOUND_MSS is not set -# CONFIG_SOUND_SSCAPE is not set -# CONFIG_SOUND_TRIX is not set -# CONFIG_SOUND_MAD16 is not set -# CONFIG_SOUND_WAVEFRONT is not set -# CONFIG_SOUND_CS4232 is not set -# CONFIG_SOUND_OPL3SA2 is not set -# CONFIG_SOUND_MAUI is not set -# CONFIG_SOUND_SGALAXY is not set -# CONFIG_SOUND_AD1816 is not set -# CONFIG_SOUND_OPL3SA1 is not set -# CONFIG_SOUND_SOFTOSS is not set -# CONFIG_SOUND_YM3812 is not set -# CONFIG_SOUND_VMIDI is not set -# CONFIG_SOUND_UART6850 is not set +# CONFIG_SOUND_OSS is not set # -# Additional low level sound drivers +# Kernel hacking # -# CONFIG_LOWLEVEL_SOUND is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_KGDB is not set +# CONFIG_XMON is not set diff --git a/arch/ppc/config.in b/arch/ppc/config.in index b15e90afd..e10cc51bb 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -19,12 +19,10 @@ choice 'Machine Type' \ APUS CONFIG_APUS \ MBX CONFIG_MBX" PowerMac +bool 'Symmetric multi-processing support' CONFIG_SMP if [ "$CONFIG_ALL_PPC" != "y" ];then define_bool CONFIG_MACH_SPECIFIC y fi - -bool 'Symmetric multi-processing support' CONFIG_SMP - endmenu if [ "$CONFIG_MBX" = "y" ];then @@ -82,13 +80,20 @@ bool 'Power management support for PowerBook 3400/2400' CONFIG_PMAC_PBOOK bool 'Support for PowerMac keyboard' CONFIG_MAC_KEYBOARD bool 'Support for PowerMac floppy' CONFIG_MAC_FLOPPY bool 'Support for PowerMac serial ports' CONFIG_MAC_SERIAL +if [ "$CONFIG_MAC_SERIAL" = "y" ]; then + bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE +fi bool 'Support for PowerMac ADB mouse' CONFIG_ADBMOUSE -bool 'Support for PowerMac IDE devices (must also enable IDE)' CONFIG_BLK_DEV_IDE_PMAC bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE -bool 'Include kgdb kernel debugger' CONFIG_KGDB -bool 'Include xmon kernel debugger' CONFIG_XMON bool 'Support for TotalImpact TotalMP' CONFIG_TOTALMP bool 'Support for early boot text console (BootX only)' CONFIG_BOOTX_TEXT +bool 'Support for Motorola Hot Swap' CONFIG_MOTOROLA_HOTSWAP +if [ "$CONFIG_PREP" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then + bool 'PReP bootloader kernel arguments' CONFIG_CMDLINE_BOOL y + if [ "$CONFIG_CMDLINE_BOOL" = "y" ] ; then + string 'Initial kernel command string' CONFIG_CMDLINE console=ttyS0,9600 console=tty0 root=/dev/sda2 + fi +fi if [ "$CONFIG_APUS" = "y" ]; then define_bool CONFIG_FB_CONSOLE y @@ -152,7 +157,7 @@ fi endmenu mainmenu_option next_comment -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' +comment 'Old CD-ROM drivers (not SCSI, not IDE)' bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then @@ -172,8 +177,16 @@ mainmenu_option next_comment comment 'Sound' tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND + dep_tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND $CONFIG_SOUND source drivers/sound/Config.in fi endmenu + +mainmenu_option next_comment +comment 'Kernel hacking' + +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Include kgdb kernel debugger' CONFIG_KGDB +bool 'Include xmon kernel debugger' CONFIG_XMON +endmenu diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index 811d599c0..131244790 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -8,13 +8,12 @@ CONFIG_PPC=y CONFIG_6xx=y # CONFIG_8xx is not set -CONFIG_PMAC=y +# CONFIG_PMAC is not set # CONFIG_PREP is not set # CONFIG_CHRP is not set -# CONFIG_ALL_PPC is not set +CONFIG_ALL_PPC=y # CONFIG_APUS is not set # CONFIG_MBX is not set -CONFIG_MACH_SPECIFIC=y # CONFIG_SMP is not set # @@ -36,20 +35,20 @@ CONFIG_KERNEL_ELF=y CONFIG_BINFMT_MISC=m # CONFIG_BINFMT_JAVA is not set # CONFIG_PARPORT is not set -# CONFIG_VGA_CONSOLE is not set +CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_FB_COMPAT_XPMAC=y CONFIG_PMAC_PBOOK=y CONFIG_MAC_KEYBOARD=y CONFIG_MAC_FLOPPY=y CONFIG_MAC_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set CONFIG_ADBMOUSE=y -CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_PROC_DEVICETREE=y -# CONFIG_KGDB is not set -# CONFIG_XMON is not set # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y +# CONFIG_MOTOROLA_HOTSWAP is not set +# CONFIG_CMDLINE_BOOL is not set # # Plug and Play support @@ -76,15 +75,13 @@ CONFIG_BLK_DEV_IDEFLOPPY=y # CONFIG_BLK_DEV_IDEPCI is not set # CONFIG_BLK_DEV_SL82C105 is not set CONFIG_BLK_DEV_IDE_PMAC=y -CONFIG_BLK_DEV_IDEDMA_PMAC=y -CONFIG_BLK_DEV_IDEDMA=y -CONFIG_PMAC_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_IDEDMA_PMAC is not set # CONFIG_IDE_CHIPSETS is not set # # Additional Block Devices # -# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y @@ -102,7 +99,6 @@ CONFIG_NETLINK=y # CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set # CONFIG_FIREWALL is not set -CONFIG_NET_ALIAS=y # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -120,7 +116,6 @@ CONFIG_IP_ALIAS=y # (it is safe to leave these untouched) # CONFIG_INET_RARP=y -CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set @@ -156,12 +151,12 @@ CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # -# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -189,16 +184,28 @@ CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_G_NCR5380_PORT is not set +# CONFIG_SCSI_G_NCR5380_MEM is not set # CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_NCR53C7xx is not set -# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_SYM53C8XX is not set +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -229,7 +236,7 @@ CONFIG_BMAC=y # CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y -# CONFIG_PCNET32 is not set +CONFIG_PCNET32=y # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set @@ -263,6 +270,7 @@ CONFIG_PPP=y # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support @@ -275,7 +283,7 @@ CONFIG_PPP=y # CONFIG_ISDN is not set # -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# Old CD-ROM drivers (not SCSI, not IDE) # # CONFIG_CD_NO_IDESCSI is not set @@ -283,22 +291,37 @@ CONFIG_PPP=y # Console drivers # CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_PM2 is not set CONFIG_FB_OF=y CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -CONFIG_FB_ATY=y +# CONFIG_FB_ATY is not set CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set -# CONFIG_FB_MATROX is not set -CONFIG_FB_ATY=y +CONFIG_FB_MATROX=y +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G100=y +# CONFIG_FB_MATROX_MULTIHEAD is not set +# CONFIG_FB_ATY is not set # CONFIG_FB_VIRTUAL is not set -# CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_ADVANCED=y +# CONFIG_FBCON_MFB is not set +# CONFIG_FBCON_CFB2 is not set +# CONFIG_FBCON_CFB4 is not set CONFIG_FBCON_CFB8=y CONFIG_FBCON_CFB16=y CONFIG_FBCON_CFB24=y CONFIG_FBCON_CFB32=y +# CONFIG_FBCON_AFB is not set +# CONFIG_FBCON_ILBM is not set +# CONFIG_FBCON_IPLAN2P2 is not set +# CONFIG_FBCON_IPLAN2P4 is not set +# CONFIG_FBCON_IPLAN2P8 is not set +# CONFIG_FBCON_MAC is not set +# CONFIG_FBCON_VGA is not set # CONFIG_FBCON_FONTWIDTH8_ONLY is not set CONFIG_FBCON_FONTS=y # CONFIG_FONT_8x8 is not set @@ -314,15 +337,25 @@ CONFIG_FONT_SUN12x22=y # CONFIG_VT=y CONFIG_VT_CONSOLE=y -# CONFIG_SERIAL is not set +CONFIG_SERIAL=m # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 -# CONFIG_MOUSE is not set +CONFIG_MOUSE=y + +# +# Mice +# +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set -CONFIG_NVRAM=y +# CONFIG_NVRAM is not set # CONFIG_RTC is not set # @@ -334,11 +367,20 @@ CONFIG_NVRAM=y # Joystick support # # CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set # # Ftape, the floppy tape device driver # # CONFIG_FTAPE is not set +# CONFIG_FT_NORMAL_DEBUG is not set +# CONFIG_FT_FULL_DEBUG is not set +# CONFIG_FT_NO_TRACE is not set +# CONFIG_FT_NO_TRACE_AT_ALL is not set +# CONFIG_FT_STD_FDC is not set +# CONFIG_FT_MACH2 is not set +# CONFIG_FT_PROBE_FC10 is not set +# CONFIG_FT_ALT_FDC is not set # # Filesystems @@ -429,3 +471,10 @@ CONFIG_DMASOUND=y # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set # CONFIG_SOUND_OSS is not set + +# +# Kernel hacking +# +CONFIG_MAGIC_SYSRQ=y +# CONFIG_KGDB is not set +# CONFIG_XMON is not set diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index d047c2086..5c8b44a90 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -27,7 +27,7 @@ O_OBJS += totalmp.o endif ifeq ($(CONFIG_MBX),y) -O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o +O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o i8259.o ppc8xx_pic.o else ifeq ($(CONFIG_APUS),y) O_OBJS += apus_setup.o prom.o openpic.o @@ -36,7 +36,8 @@ ifneq ($(CONFIG_MBX),y) O_OBJS += prep_time.o pmac_time.o chrp_time.o \ pmac_setup.o pmac_support.o \ prep_pci.o pmac_pci.o chrp_pci.o \ - residual.o prom.o openpic.o feature.o + residual.o prom.o openpic.o feature.o \ + prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o OX_OBJS += chrp_setup.o prep_setup.o endif endif diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c index 83ee7756d..cf5fcffd3 100644 --- a/arch/ppc/kernel/align.c +++ b/arch/ppc/kernel/align.c @@ -194,13 +194,8 @@ fix_alignment(struct pt_regs *regs) return -EFAULT; /* bad address */ } -#ifdef __SMP__ - if ((flags & F) && (regs->msr & MSR_FP) ) - smp_giveup_fpu(current); -#else - if ((flags & F) && last_task_used_math == current) - giveup_fpu(); -#endif + if ((flags & F) && (regs->msr & MSR_FP)) + giveup_fpu(current); if (flags & M) return 0; /* too hard for now */ @@ -254,27 +249,16 @@ fix_alignment(struct pt_regs *regs) data.d = current->tss.fpr[reg]; break; /* these require some floating point conversions... */ - /* note that giveup_fpu enables the FPU for the kernel */ /* we'd like to use the assignment, but we have to compile * the kernel with -msoft-float so it doesn't use the * fp regs for copying 8-byte objects. */ case LD+F+S: -#ifdef __SMP__ - if (regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - giveup_fpu(); -#endif + enable_kernel_fp(); cvt_fd(&data.f, ¤t->tss.fpr[reg], ¤t->tss.fpscr); /* current->tss.fpr[reg] = data.f; */ break; case ST+F+S: -#ifdef __SMP__ - if (regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - giveup_fpu(); -#endif + enable_kernel_fp(); cvt_df(¤t->tss.fpr[reg], &data.f, ¤t->tss.fpscr); /* data.f = current->tss.fpr[reg]; */ break; diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index c3a81ad3e..55e57fc5b 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -14,12 +14,50 @@ #include <linux/sched.h> #include <linux/kd.h> #include <linux/init.h> +#include <linux/hdreg.h> + +/* Get the IDE stuff from the 68k file */ +#define ide_init_hwif_ports m68k_ide_init_hwif_ports +#define ide_default_irq m68k_ide_default_irq +#define ide_default_io_base m68k_ide_default_io_base +#define ide_check_region m68k_ide_check_region +#define ide_request_region m68k_ide_request_region +#define ide_release_region m68k_ide_release_region +#define ide_fix_driveid m68k_ide_fix_driveid +#include <asm-m68k/ide.h> +#undef ide_init_hwif_ports +#define ide_default_irq +#define ide_default_io_base +#define ide_check_region +#define ide_request_region +#define ide_release_region +#define ide_fix_driveid + #include <asm/setup.h> #include <asm/amigahw.h> #include <asm/amigappc.h> #include <asm/pgtable.h> #include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ide.h> + +#include "time.h" +#include "local_irq.h" + +unsigned long apus_get_rtc_time(void); +int apus_set_rtc_time(unsigned long nowtime); + +/* APUS defs */ +extern int parse_bootinfo(const struct bi_record *); +extern char _end[]; +#ifdef CONFIG_APUS +struct mem_info ramdisk; +unsigned long isa_io_base; +unsigned long isa_mem_base; +unsigned long pci_dram_offset; +#endif +/* END APUS defs */ unsigned long m68k_machtype; char debug_device[6] = ""; @@ -72,6 +110,8 @@ __initfunc(void apus_setup_arch(unsigned long * memory_start_p, int i; char *p, *q; + m68k_machtype = MACH_AMIGA; + /* Parse the command line for arch-specific options. * For the m68k, this is currently only "debug=xxx" to enable printing * certain kernel messages to some machine-specific device. */ @@ -409,3 +449,194 @@ void cache_clear(__u32 addr, int length) "isync \n\t" : : "r" (addr)); } + +void +apus_restart(char *cmd) +{ + cli(); + + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); + APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); + for(;;); +} + +void +apus_power_off(void) +{ + for (;;); +} + +void +apus_halt(void) +{ + apus_restart(NULL); +} + +void +apus_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int old_level, new_level; + + /* I don't think we need SMP code here - Corey */ + + old_level = ~(regs->mq) & IPLEMU_IPLMASK; + new_level = (~(regs->mq) >> 3) & IPLEMU_IPLMASK; + if (new_level != 0) + { + APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); + APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET + | (~(new_level) & IPLEMU_IPLMASK))); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); + + process_int (VEC_SPUR+new_level, regs); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); + APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET + | (~(old_level) & IPLEMU_IPLMASK))); + } + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +apus_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port, buf, ns); +} + +void +apus_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port, buf, ns); +} + +int +apus_ide_default_irq(ide_ioreg_t base) +{ + m68k_ide_default_irq(base); +} + +ide_ioreg_t +apus_ide_default_io_base(int index) +{ + m68k_ide_default_io_base(index); +} + +int +apus_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return m68k_ide_check_region(from, extent); +} + +void +apus_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + m68k_ide_request_region(from, extent, name); +} + +void +apus_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + m68k_ide_release_region(from, extent); +} + +void +apus_ide_fix_driveid(struct hd_driveid *id) +{ + m68k_ide_fix_driveid(id); +} + +__initfunc(void +apus_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) +{ + m68k_ide_init_hwif_ports(p, base, irq); +} +#endif + +__initfunc(void +apus_local_init_IRQ(void)) +{ + ppc_md.mask_irq = amiga_disable_irq; + ppc_md.unmask_irq = amiga_enable_irq; + apus_init_IRQ(); +} + +__initfunc(void +apus_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + /* Parse bootinfo. The bootinfo is located right after + the kernel bss */ + parse_bootinfo((const struct bi_record *)&_end); +#ifdef CONFIG_BLK_DEV_INITRD + /* Take care of initrd if we have one. Use data from + bootinfo to avoid the need to initialize PPC + registers when kernel is booted via a PPC reset. */ + if ( ramdisk.addr ) { + initrd_start = (unsigned long) __va(ramdisk.addr); + initrd_end = (unsigned long) + __va(ramdisk.size + ramdisk.addr); + } + /* Make sure code below is not executed. */ + r4 = 0; + r6 = 0; +#endif /* CONFIG_BLK_DEV_INITRD */ + + ISA_DMA_THRESHOLD = 0x00ffffff; + + ppc_md.setup_arch = apus_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = apus_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = apus_init_IRQ; + ppc_md.do_IRQ = apus_do_IRQ; + ppc_md.get_irq_source = NULL; + ppc_md.init = NULL; + + ppc_md.restart = apus_restart; + ppc_md.power_off = apus_power_off; + ppc_md.halt = apus_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = apus_set_rtc_time; + ppc_md.get_rtc_time = apus_get_rtc_time; + ppc_md.calibrate_decr = apus_calibrate_decr; + + /* These should not be used for the APUS yet, since it uses + the M68K keyboard now. */ + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + ppc_md.kbd_sysrq_xlate = NULL; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = apus_ide_insw; + ppc_ide_md.outsw = apus_ide_outsw; + ppc_ide_md.default_irq = apus_ide_default_irq; + ppc_ide_md.default_io_base = apus_ide_default_io_base; + ppc_ide_md.check_region = apus_ide_check_region; + ppc_ide_md.request_region = apus_ide_request_region; + ppc_ide_md.release_region = apus_ide_release_region; + ppc_ide_md.fix_driveid = apus_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = apus_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif +} diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index 98a3e182e..fa4dda10b 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -15,10 +15,14 @@ #include <asm/hydra.h> #include <asm/prom.h> #include <asm/gg2.h> +#include <asm/ide.h> +#include <asm/machdep.h> + +#include "pci.h" /* LongTrail */ #define pci_config_addr(bus, dev, offset) \ - (GG2_PCI_CONFIG_BASE | ((bus)<<16) | ((dev)<<8) | (offset)) +(GG2_PCI_CONFIG_BASE | ((bus)<<16) | ((dev)<<8) | (offset)) volatile struct Hydra *Hydra = NULL; @@ -30,144 +34,136 @@ volatile struct Hydra *Hydra = NULL; int gg2_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { - if (bus > 7) { - *val = 0xff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { - if (bus > 7) { - *val = 0xffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - *val = in_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset)); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { - if (bus > 7) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - *val = in_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset)); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { - if (bus > 7) - return PCIBIOS_DEVICE_NOT_FOUND; - out_8((unsigned char *)pci_config_addr(bus, dev_fn, offset), val); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_8((unsigned char *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { - if (bus > 7) - return PCIBIOS_DEVICE_NOT_FOUND; - out_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset), val); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { - if (bus > 7) - return PCIBIOS_DEVICE_NOT_FOUND; - out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; } -extern volatile unsigned int *pci_config_address; -extern volatile unsigned char *pci_config_data; - -#define DEV_FN_MAX (31<<3) +#define python_config_address(bus) (unsigned *)((0xfef00000+0xf8000)-(bus*0x100000)) +#define python_config_data(bus) ((0xfef00000+0xf8010)-(bus*0x100000)) +#define PYTHON_CFA(b, d, o) (0x80 | ((b<<6) << 8) | ((d) << 16) \ + | (((o) & ~3) << 24)) +unsigned int python_busnr = 1; -int raven_pcibios_read_config_byte(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned char *val) +int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - *val = in_8(pci_config_data+(offset&3)); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + *val = in_8((unsigned char *)python_config_data(bus) + (offset&3)); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_read_config_word(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned short *val) +int python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - *val = in_le16((volatile unsigned short *) - (pci_config_data+(offset&3))); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + *val = in_le16((unsigned short *)(python_config_data(bus) + (offset&3))); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_read_config_dword(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned int *val) + +int python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); - *val = in_le32((volatile unsigned int *)(pci_config_data)); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + *val = in_le32((unsigned *)python_config_data(bus)); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_write_config_byte(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned char val) +int python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - out_8(pci_config_data+(offset&3),val); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + out_8((volatile unsigned char *)python_config_data(bus) + (offset&3), val); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_write_config_word(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned short val) +int python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - out_le16((volatile unsigned short *)(pci_config_data+(offset&3)),val); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + out_le16((volatile unsigned short *)python_config_data(bus) + (offset&3), + val); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_write_config_dword(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned int val) +int python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); - out_le32((volatile unsigned int *)pci_config_data,val); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + out_le32((unsigned *)python_config_data(bus) + (offset&3), val); + return PCIBIOS_SUCCESSFUL; } /* @@ -191,7 +187,8 @@ static u_char hydra_openpic_initsenses[] __initdata = { /* all others are 1 (= default) */ }; -__initfunc(int hydra_init(void)) +int __init +hydra_init(void) { struct device_node *np; @@ -216,75 +213,95 @@ __initfunc(int hydra_init(void)) return 1; } - -extern int chrp_ide_irq; - -__initfunc(int w83c553f_init(void)) +void __init +chrp_pcibios_fixup(void) { - u_char bus, dev; -#if 0 - unsigned char t8; - unsigned short t16; -#endif - unsigned int t32; - struct pci_dev *pdev; - if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_83C553, NULL))) { - bus = pdev->bus->number; - dev = pdev->devfn + 1; - pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); - if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) { -#if 0 - printk("Enabling SL82C105 IDE on W83C553F\n"); - /* - * FIXME: this doesn't help :-( - */ - - /* I/O mapping */ - pcibios_read_config_word(bus, dev, PCI_COMMAND, &t16); - t16 |= PCI_COMMAND_IO; - pcibios_write_config_word(bus, dev, PCI_COMMAND, t16); - - /* Standard IDE registers */ - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - 0x000001f0 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, - 0x000003f4 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_2, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, - 0x00000170 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_3, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, - 0x00000374 | 1); + struct pci_dev *dev; + + /* some of IBM chrps have > 1 bus */ + if ( !strncmp("IBM", get_property(find_path_device("/"), + "name", NULL),3) ) + { + pci_scan_peer_bridge(1); + pci_scan_peer_bridge(2); + } + + /* PCI interrupts are controlled by the OpenPIC */ + for( dev=pci_devices ; dev; dev=dev->next ) + { + if ( dev->irq ) + dev->irq = openpic_to_irq( dev->irq ); + /* adjust the io_port for the NCR cards for busses other than 0 -- Cort */ + if ( (dev->bus->number > 0) && (dev->vendor == PCI_VENDOR_ID_NCR) ) + dev->base_address[0] += (dev->bus->number*0x08000000); + /* these need to be absolute addrs for OF and Matrox FB -- Cort */ + if ( dev->vendor == PCI_VENDOR_ID_MATROX ) + { + if ( dev->base_address[0] < isa_mem_base ) + dev->base_address[0] += isa_mem_base; + if ( dev->base_address[1] < isa_mem_base ) + dev->base_address[1] += isa_mem_base; + } + /* the F50 identifies the amd as a trident */ + if ( (dev->vendor == PCI_VENDOR_ID_TRIDENT) && + (dev->class == PCI_CLASS_NETWORK_ETHERNET) ) + { + dev->vendor = PCI_VENDOR_ID_AMD; + pcibios_write_config_word(dev->bus->number, dev->devfn, + PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + } + } +} - /* IDE Bus Master Control */ - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_4, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, - 0x1000 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_5, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, - 0x1010 | 1); +decl_config_access_method(grackle); +decl_config_access_method(indirect); - /* IDE Interrupt */ - pcibios_read_config_byte(bus, dev, PCI_INTERRUPT_LINE, &t8); - chrp_ide_irq = t8; -#endif - return 1; - } - } - return 0; +void __init +chrp_setup_pci_ptrs(void) +{ + struct device_node *py; + + if ( !strncmp("MOT", + get_property(find_path_device("/"), "model", NULL),3) ) + { + pci_dram_offset = 0; + isa_mem_base = 0xf7000000; + isa_io_base = 0xfe000000; + set_config_access_method(grackle); + } + else + { + if ( (py = find_compatible_devices( "pci", "IBM,python" )) ) + { + /* find out how many pythons */ + while ( (py = py->next) ) python_busnr++; + set_config_access_method(python); + /* + * We base these values on the machine type but should + * try to read them from the python controller itself. + * -- Cort + */ + if ( !strncmp("IBM,7025-F50", get_property(find_path_device("/"), "name", NULL),12) ) + { + pci_dram_offset = 0x80000000; + isa_mem_base = 0xa0000000; + isa_io_base = 0x88000000; + } else if ( !strncmp("IBM,7043-260", + get_property(find_path_device("/"), "name", NULL),12) ) + { + pci_dram_offset = 0x80000000; + isa_mem_base = 0xc0000000; + isa_io_base = 0xf8000000; + } + } + else + { + pci_dram_offset = 0; + isa_mem_base = 0xf7000000; + isa_io_base = 0xf8000000; + set_config_access_method(gg2); + } + } + + ppc_md.pcibios_fixup = chrp_pcibios_fixup; } diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 5b373c876..2c652f049 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -31,6 +31,7 @@ #include <linux/ioport.h> #include <linux/console.h> #include <linux/pci.h> +#include <linux/openpic.h> #include <asm/mmu.h> #include <asm/processor.h> @@ -40,9 +41,50 @@ #include <asm/prom.h> #include <asm/gg2.h> #include <asm/pci-bridge.h> - -extern void hydra_init(void); -extern void w83c553f_init(void); +#include <asm/dma.h> +#include <asm/machdep.h> +#include <asm/irq.h> +#include <asm/adb.h> +#include <asm/hydra.h> + +#include "time.h" +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" + +/* Fixme - need to move these into their own .c and .h file */ +extern void i8259_mask_and_ack_irq(unsigned int irq_nr); +extern void i8259_set_irq_mask(unsigned int irq_nr); +extern void i8259_mask_irq(unsigned int irq_nr); +extern void i8259_unmask_irq(unsigned int irq_nr); +extern void i8259_init(void); + +/* Fixme - remove this when it is fixed. - Corey */ +extern volatile unsigned char *chrp_int_ack_special; + +unsigned long chrp_get_rtc_time(void); +int chrp_set_rtc_time(unsigned long nowtime); +void chrp_calibrate_decr(void); +void chrp_time_init(void); + +void chrp_setup_pci_ptrs(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void mackbd_init_hw(void); +extern unsigned char mackbd_sysrq_xlate[128]; /* for the mac fs */ kdev_t boot_dev; @@ -53,7 +95,6 @@ extern int probingmem; extern unsigned long loops_per_sec; unsigned long empty_zero_page[1024]; -extern unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ @@ -62,17 +103,17 @@ extern int rd_image_start; /* starting block # of image */ #endif static const char *gg2_memtypes[4] = { - "FPM", "SDRAM", "EDO", "BEDO" + "FPM", "SDRAM", "EDO", "BEDO" }; static const char *gg2_cachesizes[4] = { - "256 KB", "512 KB", "1 MB", "Reserved" + "256 KB", "512 KB", "1 MB", "Reserved" }; static const char *gg2_cachetypes[4] = { - "Asynchronous", "Reserved", "Flow-Through Synchronous", - "Pipelined Synchronous" + "Asynchronous", "Reserved", "Flow-Through Synchronous", + "Pipelined Synchronous" }; static const char *gg2_cachemodes[4] = { - "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" + "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" }; int @@ -85,53 +126,60 @@ chrp_get_cpuinfo(char *buffer) root = find_path_device("/"); if (root) - model = get_property(root, "model", NULL); + model = get_property(root, "model", NULL); len = sprintf(buffer,"machine\t\t: CHRP %s\n", model); - /* VLSI VAS96011/12 `Golden Gate 2' */ - /* Memory banks */ - sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_CTRL)) - >>31) & 1; - for (i = 0; i < (sdramen ? 4 : 6); i++) { - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_BANK0+ - i*4)); - if (!(t & 1)) - continue; - switch ((t>>8) & 0x1f) { - case 0x1f: - model = "4 MB"; - break; - case 0x1e: - model = "8 MB"; - break; - case 0x1c: - model = "16 MB"; - break; - case 0x18: - model = "32 MB"; - break; - case 0x10: - model = "64 MB"; - break; - case 0x00: - model = "128 MB"; - break; - default: - model = "Reserved"; - break; - } - len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, - gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + /* longtrail (goldengate) stuff */ + if ( !strncmp( model, "IBM,LongTrail", 9 ) ) + { + /* VLSI VAS96011/12 `Golden Gate 2' */ + /* Memory banks */ + sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ + GG2_PCI_DRAM_CTRL)) + >>31) & 1; + for (i = 0; i < (sdramen ? 4 : 6); i++) { + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ + GG2_PCI_DRAM_BANK0+ + i*4)); + if (!(t & 1)) + continue; + switch ((t>>8) & 0x1f) { + case 0x1f: + model = "4 MB"; + break; + case 0x1e: + model = "8 MB"; + break; + case 0x1c: + model = "16 MB"; + break; + case 0x18: + model = "32 MB"; + break; + case 0x10: + model = "64 MB"; + break; + case 0x00: + model = "128 MB"; + break; + default: + model = "Reserved"; + break; + } + len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, + gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + } + /* L2 cache */ + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); + len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n", + gg2_cachesizes[(t>>7) & 3], + gg2_cachetypes[(t>>2) & 3], + gg2_cachemodes[t & 3]); } - /* L2 cache */ - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); - len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n", - gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3], - gg2_cachemodes[t & 3]); return len; } - /* +/* * Fixes for the National Semiconductor PC78308VUL SuperI/O * * Some versions of Open Firmware incorrectly initialize the IRQ settings @@ -140,57 +188,55 @@ chrp_get_cpuinfo(char *buffer) __initfunc(static inline void sio_write(u8 val, u8 index)) { - outb(index, 0x15c); - outb(val, 0x15d); + outb(index, 0x15c); + outb(val, 0x15d); } __initfunc(static inline u8 sio_read(u8 index)) { - outb(index, 0x15c); - return inb(0x15d); + outb(index, 0x15c); + return inb(0x15d); } __initfunc(static void sio_fixup_irq(const char *name, u8 device, u8 level, u8 type)) { - u8 level0, type0, active; - - /* select logical device */ - sio_write(device, 0x07); - active = sio_read(0x30); - level0 = sio_read(0x70); - type0 = sio_read(0x71); - printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, - !active ? "in" : ""); - if (level0 == level && type0 == type && active) - printk("OK\n"); - else { - printk("remapping to level %d, type %d, active\n", level, type); - sio_write(0x01, 0x30); - sio_write(level, 0x70); - sio_write(type, 0x71); - } + u8 level0, type0, active; + + /* select logical device */ + sio_write(device, 0x07); + active = sio_read(0x30); + level0 = sio_read(0x70); + type0 = sio_read(0x71); + printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, + !active ? "in" : ""); + if (level0 == level && type0 == type && active) + printk("OK\n"); + else { + printk("remapping to level %d, type %d, active\n", level, type); + sio_write(0x01, 0x30); + sio_write(level, 0x70); + sio_write(type, 0x71); + } } __initfunc(static void sio_init(void)) { - /* logical device 0 (KBC/Keyboard) */ - sio_fixup_irq("keyboard", 0, 1, 2); - /* select logical device 1 (KBC/Mouse) */ - sio_fixup_irq("mouse", 1, 12, 2); + /* logical device 0 (KBC/Keyboard) */ + sio_fixup_irq("keyboard", 0, 1, 2); + /* select logical device 1 (KBC/Mouse) */ + sio_fixup_irq("mouse", 1, 12, 2); } __initfunc(void -chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) + chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern char cmd_line[]; /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - - aux_device_present = 0xaa; #ifdef CONFIG_BLK_DEV_INITRD /* this is fine for chrp */ @@ -219,8 +265,15 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) * -- Geert */ hydra_init(); /* Mac I/O */ - w83c553f_init(); /* PCI-ISA bridge and IDE */ + /* Some IBM machines don't have the hydra -- Cort */ + if ( !OpenPIC ) + { + OpenPIC = (struct OpenPIC *)*(unsigned long *)get_property( + find_path_device("/"), "platform-open-pic", NULL); + OpenPIC = ioremap((unsigned long)OpenPIC, sizeof(struct OpenPIC)); + } + /* * Fix the Super I/O configuration */ @@ -232,27 +285,210 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) if ( !strncmp("MOT", get_property(find_path_device("/"), "model", NULL),3) ) *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + /* + * The f50 has a lot of IO space - we need to map some in that + * isn't covered by the BAT mappings in MMU_init() -- Cort + */ + if ( !strncmp("F5", get_property(find_path_device("/"), + "ibm,model-class", NULL),2) ) + { +#if 0 + /* + * This ugly hack allows us to force ioremap() to + * create a 1-to-1 mapping for us, even though + * the address is < ioremap_base. This is necessary + * since we want our PCI IO space to have contiguous + * virtual addresses and I think it's worse to have + * calls to map_page() here. + * -- Cort + */ + unsigned long hold = ioremap_base; + ioremap_base = 0; + __ioremap(0x90000000, 0x10000000, _PAGE_NO_CACHE); + ioremap_base = hold; +#endif + } } -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +void +chrp_restart(char *cmd) +{ +#if 0 + extern unsigned int rtas_entry, rtas_data, rtas_size; + printk("RTAS system-reboot returned %d\n", + call_rtas("system-reboot", 0, 1, NULL)); + printk("rtas_entry: %08lx rtas_data: %08lx rtas_size: %08lx\n", + rtas_entry,rtas_data,rtas_size); + for (;;); +#else + printk("System Halted\n"); + while(1); +#endif +} -unsigned int chrp_ide_irq = 0; -int chrp_ide_ports_known = 0; -ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; -ide_ioreg_t chrp_idedma_regbase; +void +chrp_power_off(void) +{ + /* RTAS doesn't seem to work on Longtrail. + For now, do it the same way as the PReP. */ +#if 0 + extern unsigned int rtas_entry, rtas_data, rtas_size; + printk("RTAS power-off returned %d\n", + call_rtas("power-off", 2, 1, NULL, 0, 0)); + printk("rtas_entry: %08lx rtas_data: %08lx rtas_size: %08lx\n", + rtas_entry,rtas_data,rtas_size); + for (;;); +#else + chrp_restart(NULL); +#endif +} -void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) +void +chrp_halt(void) { - ide_ioreg_t port = base; - int i = 8; + chrp_restart(NULL); +} - while (i--) - *p++ = port++; - *p++ = port; - if (irq != NULL) - *irq = chrp_ide_irq; +u_int +chrp_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +void +chrp_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int irq; + unsigned long bits = 0; + int openpic_eoi_done = 0; + +#ifdef __SMP__ + { + unsigned int loops = 1000000; + while (test_bit(0, &global_irq_lock)) { + if (smp_processor_id() == global_irq_holder) { + printk("uh oh, interrupt while we hold global irq lock!\n"); +#ifdef CONFIG_XMON + xmon(0); +#endif + break; + } + if (loops-- == 0) { + printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); +#ifdef CONFIG_XMON + xmon(0); +#endif + } + } + } +#endif /* __SMP__ */ + + irq = openpic_irq(0); + if (irq == IRQ_8259_CASCADE) + { + /* + * This magic address generates a PCI IACK cycle. + * + * This should go in the above mask/ack code soon. -- Cort + */ + if ( chrp_int_ack_special ) + irq = *chrp_int_ack_special; + else + irq = i8259_irq(0); + /* + * Acknowledge as soon as possible to allow i8259 + * interrupt nesting */ + openpic_eoi(0); + openpic_eoi_done = 1; + } + if (irq == OPENPIC_VEC_SPURIOUS) + { + /* + * Spurious interrupts should never be + * acknowledged + */ + ppc_spurious_interrupts++; + openpic_eoi_done = 1; + goto out; + } + bits = 1UL << irq; + + if (irq < 0) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + else + { + ppc_irq_dispatch_handler( regs, irq ); + } +out: + if (!openpic_eoi_done) + openpic_eoi(0); } +__initfunc(void + chrp_init_IRQ(void)) +{ + struct device_node *np; + int i; + + if ( !(np = find_devices("pci") ) ) + printk("Cannot find pci to get ack address\n"); + else + { + chrp_int_ack_special = (volatile unsigned char *) + (*(unsigned long *)get_property(np, + "8259-interrupt-acknowledge", NULL)); + } + for ( i = 16 ; i < NR_IRQS ; i++ ) + irq_desc[i].ctl = &open_pic; + /* openpic knows that it's at irq 16 offset + * so we don't need to set it in the pic structure + * -- Cort + */ + openpic_init(1); + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); +#ifdef CONFIG_XMON + request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), + xmon_irq, 0, "NMI", 0); +#endif /* CONFIG_XMON */ +#ifdef __SMP__ + request_irq(openpic_to_irq(OPENPIC_VEC_IPI), + openpic_ipi_action, 0, "IPI0", 0); +#endif /* __SMP__ */ +} + +__initfunc(void + chrp_init2(void)) +{ + adb_init(); + + /* Should this be here? - Corey */ + pmac_nvram_init(); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +unsigned int chrp_ide_irq = 0; +int chrp_ide_ports_known = 0; +ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; +ide_ioreg_t chrp_idedma_regbase; + void chrp_ide_probe(void) { struct pci_dev *pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, NULL); @@ -270,9 +506,167 @@ void chrp_ide_probe(void) { } } +void +chrp_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port+_IO_BASE, buf, ns); +} + +void +chrp_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port+_IO_BASE, buf, ns); +} + +int +chrp_ide_default_irq(ide_ioreg_t base) +{ + if (chrp_ide_ports_known == 0) + chrp_ide_probe(); + return chrp_ide_irq; +} + +ide_ioreg_t +chrp_ide_default_io_base(int index) +{ + if (chrp_ide_ports_known == 0) + chrp_ide_probe(); + return chrp_ide_regbase[index]; +} + +int +chrp_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +void +chrp_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +void +chrp_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} + +void +chrp_ide_fix_driveid(struct hd_driveid *id) +{ + ppc_generic_ide_fix_driveid(id); +} + +void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + ide_ioreg_t port = base; + int i = 8; + + while (i--) + *p++ = port++; + *p++ = port; + if (irq != NULL) + *irq = chrp_ide_irq; +} + EXPORT_SYMBOL(chrp_ide_irq); EXPORT_SYMBOL(chrp_ide_ports_known); EXPORT_SYMBOL(chrp_ide_regbase); EXPORT_SYMBOL(chrp_ide_probe); #endif + +__initfunc(void + chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + chrp_setup_pci_ptrs(); +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r3 ) + { + initrd_start = r3 + KERNELBASE; + initrd_end = r3 + r4 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */ + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = chrp_get_cpuinfo; + ppc_md.irq_cannonicalize = chrp_irq_cannonicalize; + ppc_md.init_IRQ = chrp_init_IRQ; + ppc_md.do_IRQ = chrp_do_IRQ; + + ppc_md.init = chrp_init2; + + ppc_md.restart = chrp_restart; + ppc_md.power_off = chrp_power_off; + ppc_md.halt = chrp_halt; + + ppc_md.time_init = chrp_time_init; + ppc_md.set_rtc_time = chrp_set_rtc_time; + ppc_md.get_rtc_time = chrp_get_rtc_time; + ppc_md.calibrate_decr = chrp_calibrate_decr; + +#ifdef CONFIG_VT +#ifdef CONFIG_MAC_KEYBOAD + if ( adb_hardware == ADB_NONE ) + { + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif + } + else + { + ppc_md.kbd_setkeycode = mackbd_setkeycode; + ppc_md.kbd_getkeycode = mackbd_getkeycode; + ppc_md.kbd_translate = mackbd_translate; + ppc_md.kbd_unexpected_up = mackbd_unexpected_up; + ppc_md.kbd_leds = mackbd_leds; + ppc_md.kbd_init_hw = mackbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = mackbd_sysrq_xlate; +#endif + } +#else + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif +#endif +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = chrp_ide_insw; + ppc_ide_md.outsw = chrp_ide_outsw; + ppc_ide_md.default_irq = chrp_ide_default_irq; + ppc_ide_md.default_io_base = chrp_ide_default_io_base; + ppc_ide_md.check_region = chrp_ide_check_region; + ppc_ide_md.request_region = chrp_ide_request_region; + ppc_ide_md.release_region = chrp_ide_release_region; + ppc_ide_md.fix_driveid = chrp_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = chrp_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif +} diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index 7a2ea7b26..c374c9bd1 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -154,7 +154,8 @@ unsigned long chrp_get_rtc_time(void) __initfunc(void chrp_calibrate_decr(void)) { struct device_node *cpu; - int freq, *fp, divisor; + int *fp, divisor; + unsigned long freq; if (via_calibrate_decr()) return; @@ -170,10 +171,9 @@ __initfunc(void chrp_calibrate_decr(void)) if (fp != 0) freq = *fp; } - freq *= 60; /* try to make freq/1e6 an integer */ divisor = 60; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + printk("time_init: decrementer frequency = %lu/%d\n", freq, divisor); decrementer_count = freq / HZ / divisor; count_period_num = divisor; count_period_den = freq / 1000000; diff --git a/arch/ppc/kernel/feature.c b/arch/ppc/kernel/feature.c index 48d8bcb39..25ee3424b 100644 --- a/arch/ppc/kernel/feature.c +++ b/arch/ppc/kernel/feature.c @@ -41,7 +41,6 @@ static u32 feature_bits_pbook[] = { OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0, /* FEATURE_BMac_reset */ 0, /* FEATURE_BMac_IO_enable */ - 0, /* FEATURE_Modem_PowerOn -> guess...*/ 0 /* FEATURE_Modem_Reset -> guess...*/ }; @@ -64,8 +63,7 @@ static u32 feature_bits_heathrow[] = { OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0x80000000, /* FEATURE_BMac_reset */ 0x60000000, /* FEATURE_BMac_IO_enable */ - 0x02000000, /* FEATURE_Modem_PowerOn -> guess...*/ - 0x07000000 /* FEATURE_Modem_Reset -> guess...*/ + 0x02000000 /* FEATURE_Modem_Reset -> guess...*/ }; /* definition of a feature controller object */ diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index d7b791387..b9ecc2dcc 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -1,7 +1,7 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.114 1998/12/28 10:28:45 paulus Exp $ + * $Id: head.S,v 1.130 1999/05/09 19:16:43 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -44,8 +44,13 @@ /* optimization for 603 to load the tlb directly from the linux table */ #define NO_RELOAD_HTAB 1 +#ifndef CONFIG_8xx CACHE_LINE_SIZE = 32 LG_CACHE_LINE_SIZE = 5 +#else +CACHE_LINE_SIZE = 16 +LG_CACHE_LINE_SIZE = 4 +#endif #define TOPHYS(x) (x - KERNELBASE) @@ -81,13 +86,12 @@ LG_CACHE_LINE_SIZE = 5 sync; \ isync -/* This instruction is not implemented on the PPC 603 or 601 */ #ifndef CONFIG_8xx /* This instruction is not implemented on the PPC 603 or 601 */ #define tlbia \ li r4,128; \ mtctr r4; \ - lis r4,0xC000; \ + lis r4,KERNELBASE@h; \ 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b @@ -212,6 +216,7 @@ __start: mr r29,r5 mr r28,r6 mr r27,r7 + li r24,0 /* cpu # */ #ifndef CONFIG_8xx bl prom_init .globl __secondary_start @@ -236,15 +241,33 @@ __secondary_start: mtspr IBAT1L,r10 b 5f 4: -#ifndef CONFIG_APUS - ori r11,r11,0x1fe /* set up BAT registers for 604 */ - li r8,2 /* R/W access */ -#else +#ifdef CONFIG_APUS + ori r11,r11,BL_8M<<2|0x2 /* set up an 8MB mapping */ ori r11,r11,0xfe /* set up an 8MB mapping */ lis r8,CYBERBASEp@h lwz r8,0(r8) addis r8,r8,KERNELBASE@h addi r8,r8,2 +#else + ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ + li r8,2 /* R/W access */ + /* + * allow secondary cpus to get at all of ram in early bootup + * since their init_task may be up there -- Cort + */ + oris r18,r8,0x10000000@h + oris r21,r11,(KERNELBASE+0x10000000)@h + mtspr DBAT1L,r18 /* N.B. 6xx (not 601) have valid */ + mtspr DBAT1U,r21 /* bit in upper BAT register */ + mtspr IBAT1L,r18 + mtspr IBAT1U,r21 + + oris r18,r8,0x20000000@h + oris r21,r11,(KERNELBASE+0x20000000)@h + mtspr DBAT2L,r18 /* N.B. 6xx (not 601) have valid */ + mtspr DBAT2U,r21 /* bit in upper BAT register */ + mtspr IBAT2L,r28 + mtspr IBAT2U,r21 #endif mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr DBAT0U,r11 /* bit in upper BAT register */ @@ -327,20 +350,28 @@ __secondary_start: lis r8, MI_Kp@h /* Set the protection mode */ mtspr MI_AP, r8 mtspr MD_AP, r8 -#ifdef CONFIG_MBX + +/* We will get these from a configuration file as soon as I verify + * the extraneous bits don't cause problems in the TLB. + */ +#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE) +#define BOOT_IMMR 0xfa000000 +#endif +#ifdef CONFIG_BSEIP +#define BOOT_IMMR 0xff000000 +#endif /* Map another 8 MByte at 0xfa000000 to get the processor * internal registers (among other things). */ - lis r8, 0xfa000000@h /* Create vaddr for TLB */ + lis r8, BOOT_IMMR@h /* Create vaddr for TLB */ ori r8, r8, MD_EVALID /* Mark it valid */ mtspr MD_EPN, r8 li r8, MD_PS8MEG /* Set 8M byte page */ ori r8, r8, MD_SVALID /* Make it valid */ mtspr MD_TWC, r8 - lis r8, 0xfa000000@h /* Create paddr for TLB */ + lis r8, BOOT_IMMR@h /* Create paddr for TLB */ ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ mtspr MD_RPN, r8 -#endif /* Since the cache is enabled according to the information we * just loaded into the TLB, invalidate and enable the caches here. @@ -354,9 +385,8 @@ __secondary_start: #if 0 mtspr DC_CST, r8 #else - /* I still have a bug somewhere because the Ethernet driver - * does not want to work with copyback enabled. For now, - * at least enable write through. + /* For a debug option, I left this here to easily enable + * the write through cache mode */ lis r8, DC_SFWT@h mtspr DC_CST, r8 @@ -385,7 +415,7 @@ turn_on_mmu: * this, we leave this much untouched space on the stack on exception * entry. */ -#define STACK_UNDERHEAD 64 +#define STACK_UNDERHEAD 0 /* * Exception entry code. This code runs with address translation @@ -442,7 +472,11 @@ label: \ .long int_return /* System reset */ +#ifdef CONFIG_SMP /* MVME/MTX start the secondary here */ + STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) +#else STD_EXCEPTION(0x100, Reset, UnknownException) +#endif /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) @@ -1148,6 +1182,8 @@ transfer_to_handler: mflr r23 andi. r24,r23,0x3f00 /* get vector offset */ stw r24,TRAP(r21) + li r22,RESULT + stwcx. r22,r22,r21 /* to clear the reservation */ li r22,0 stw r22,RESULT(r21) mtspr SPRG2,r22 /* r1 is now kernel sp */ @@ -1155,7 +1191,7 @@ transfer_to_handler: cmplw 0,r1,r2 cmplw 1,r1,r24 crand 1,1,4 - bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ + bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ lwz r24,0(r23) /* virtual address of handler */ lwz r23,4(r23) /* where to go when done */ mtspr SRR0,r24 @@ -1204,13 +1240,10 @@ Hash_base = 0x180000 Hash_bits = 12 /* e.g. 256kB hash table */ Hash_msk = (((1 << Hash_bits) - 1) * 64) - .globl hash_table_lock -hash_table_lock: -.long 0 - .globl hash_page hash_page: #ifdef __SMP__ + eieio lis r2,hash_table_lock@h ori r2,r2,hash_table_lock@l tophys(r2,r2,r6) @@ -1226,7 +1259,7 @@ hash_page: 12: cmpw r6,r0 bdnzf 2,10b tw 31,31,31 -11: +11: eieio #endif /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) @@ -1234,13 +1267,25 @@ hash_page: rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ lwz r5,0(r5) /* get pmd entry */ rlwinm. r5,r5,0,0,19 /* extract address of pte page */ +#ifdef __SMP__ beq- hash_page_out /* return if no mapping */ +#else + /* XXX it seems like the 601 will give a machine fault on the + rfi if its alignment is wrong (bottom 4 bits of address are + 8 or 0xc) and we have had a not-taken conditional branch + to the address following the rfi. */ + beqlr- +#endif tophys(r2,r5,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r6,0(r2) /* get linux-style pte */ ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ andc. r0,r4,r6 /* check access & ~permission */ +#ifdef __SMP__ bne- hash_page_out /* return if access not permitted */ +#else + bnelr- +#endif ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ @@ -1257,7 +1302,9 @@ hash_page: /* Construct the high word of the PPC-style PTE */ mfsrin r5,r3 /* get segment reg for segment */ rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ +#ifndef __SMP__ /* do this later for SMP */ oris r5,r5,0x8000 /* set V (valid) bit */ +#endif rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ /* Get the address of the primary PTE group in the hash table */ @@ -1274,9 +1321,6 @@ hash_page_patch_A: li r2,8 /* PTEs/group */ bne 10f /* no PTE: go look for an empty slot */ tlbie r3 /* invalidate TLB entry */ -#ifdef __SMP__ - tlbsync -#endif /* Search the primary PTEG for a PTE whose 1st word matches r5 */ mtctr r2 @@ -1345,18 +1389,43 @@ hash_page_patch_C: addi r4,r4,1 stw r4,0(r2) +#ifndef __SMP__ /* Store PTE in PTEG */ found_empty: stw r5,0(r3) found_slot: stw r6,4(r3) - SYNC + sync + +#else /* __SMP__ */ /* - * These nop's seem to be necessary to avoid getting a machine - * check on the rfi on 601 processors. + * Between the tlbie above and updating the hash table entry below, + * another CPU could read the hash table entry and put it in its TLB. + * There are 3 cases: + * 1. using an empty slot + * 2. updating an earlier entry to change permissions (i.e. enable write) + * 3. taking over the PTE for an unrelated address + * + * In each case it doesn't really matter if the other CPUs have the old + * PTE in their TLB. So we don't need to bother with another tlbie here, + * which is convenient as we've overwritten the register that had the + * address. :-) The tlbie above is mainly to make sure that this CPU comes + * and gets the new PTE from the hash table. + * + * We do however have to make sure that the PTE is never in an invalid + * state with the V bit set. */ - nop - nop +found_empty: +found_slot: + stw r5,0(r3) /* clear V (valid) bit in PTE */ + sync + tlbsync + sync + stw r6,4(r3) /* put in correct RPN, WIMG, PP bits */ + sync + oris r5,r5,0x8000 + stw r5,0(r3) /* finally set V bit in PTE */ +#endif /* __SMP__ */ /* * Update the hash table miss count. We only want misses here @@ -1380,6 +1449,7 @@ found_slot: tophys(r2,r2,r6) li r0,0 stw r0,hash_table_lock@l(r2) + eieio #endif /* Return from the exception */ @@ -1398,17 +1468,22 @@ found_slot: REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) - SYNC rfi -hash_page_out: #ifdef __SMP__ +hash_page_out: lis r2,hash_table_lock@ha tophys(r2,r2,r6) li r0,0 stw r0,hash_table_lock@l(r2) -#endif + eieio blr + + .globl hash_table_lock +hash_table_lock: + .long 0 +#endif + next_slot: .long 0 @@ -1420,27 +1495,25 @@ load_up_fpu: * On SMP we know the fpu is free, since we give it up every * switch. -- Cort */ + mfmsr r5 + ori r5,r5,MSR_FP + SYNC + mtmsr r5 /* enable use of fpu now */ + SYNC +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + */ +#ifndef __SMP__ #ifndef CONFIG_APUS lis r6,-KERNELBASE@h #else lis r6,CYBERBASEp@h lwz r6,0(r6) #endif - addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) - mfmsr r5 - ori r5,r5,MSR_FP - SYNC - mtmsr r5 /* enable use of fpu now */ -/* - * All the saving of last_task_used_math is handled - * by a switch_to() call to smp_giveup_fpu() in SMP so - * last_task_used_math is not used. - * -- Cort - */ -#ifndef __SMP__ - SYNC cmpi 0,r4,0 beq 1f add r4,r4,r6 @@ -1454,15 +1527,17 @@ load_up_fpu: li r20,MSR_FP|MSR_FE0|MSR_FE1 andc r4,r4,r20 /* disable FP for previous task */ stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: #endif /* __SMP__ */ -1: ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 /* enable use of FP after return */ + /* enable use of FP after return */ + ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 mfspr r5,SPRG3 /* current task's TSS (phys) */ lfd fr0,TSS_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) +#ifndef __SMP__ subi r4,r5,TSS sub r4,r4,r6 -#ifndef __SMP__ stw r4,last_task_used_math@l(r3) #endif /* __SMP__ */ /* restore registers and return */ @@ -1499,48 +1574,44 @@ KernelFP: .align 4 /* - * Disable FP for the task which had the FPU previously, - * and save its floating-point registers in its thread_struct. + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the floating-point registers in its thread_struct. * Enables the FPU for use in the kernel on return. */ -/* smp_giveup_fpu() takes an arg to tell it where to save the fpu - * regs since last_task_used_math can't be trusted (many many race - * conditions). -- Cort - */ - .globl smp_giveup_fpu -smp_giveup_fpu: - mr r4,r3 - b 12f .globl giveup_fpu giveup_fpu: - lis r3,last_task_used_math@ha - lwz r4,last_task_used_math@l(r3) -12: mfmsr r5 ori r5,r5,MSR_FP SYNC mtmsr r5 /* enable use of fpu now */ SYNC - cmpi 0,r4,0 + cmpi 0,r3,0 beqlr- /* if no previous owner, done */ - addi r4,r4,TSS /* want TSS of last_task_used_math */ + addi r3,r3,TSS /* want TSS of task */ + lwz r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,TSS_FPSCR-4(r3) + beq 1f + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: #ifndef __SMP__ li r5,0 - stw r5,last_task_used_math@l(r3) + lis r4,last_task_used_math@ha + stw r5,last_task_used_math@l(r4) #endif /* __SMP__ */ - SAVE_32FPRS(0, r4) - mffs fr0 - stfd fr0,TSS_FPSCR-4(r4) - lwz r5,PT_REGS(r4) - lwz r3,_MSR-STACK_FRAME_OVERHEAD(r5) - li r4,MSR_FP|MSR_FE0|MSR_FE1 - andc r3,r3,r4 /* disable FP for previous task */ - stw r3,_MSR-STACK_FRAME_OVERHEAD(r5) + blr + #else /* CONFIG_8xx */ .globl giveup_fpu giveup_fpu: -#endif /* CONFIG_8xx */ blr +#endif /* CONFIG_8xx */ /* * This code is jumped to from the startup code to copy @@ -1600,10 +1671,38 @@ copy_and_flush: . = 0x4000 #endif +#ifdef CONFIG_SMP + .globl __secondary_start_psurge +__secondary_start_psurge: + li r24,1 /* cpu # */ + b __secondary_start + + .globl __secondary_hold +__secondary_hold: + /* tell the master we're here */ + lis r5,0x4@h + ori r5,r5,0x4@l + stw r3,0(r5) + dcbf 0,r5 +100: + lis r5,0 + dcbi 0,r5 + lwz r4,0(r5) + /* wait until we're told to start */ + cmp 0,r4,r3 + bne 100b + /* our cpu # was at addr 0 - go */ + lis r5,__secondary_start@h + ori r5,r5,__secondary_start@l + tophys(r5,r5,r4) + mtlr r5 + mr r24,r3 /* cpu # */ + blr +#endif /* CONFIG_SMP */ + /* * This is where the main kernel code starts. */ - start_here: #ifndef CONFIG_8xx /* @@ -1650,9 +1749,9 @@ start_here: /* get current */ lis r2,current_set@h ori r2,r2,current_set@l - addi r2,r2,4 + slwi r24,r24,2 /* cpu # to current_set[cpu#] */ + add r2,r2,r24 lwz r2,0(r2) - b 10f 99: #endif /* __SMP__ */ @@ -1677,12 +1776,10 @@ start_here: #ifdef __SMP__ 10: #endif /* __SMP__ */ - /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 stwu r0,-STACK_FRAME_OVERHEAD(r1) - /* * Decide what sort of machine this is and initialize the MMU. */ @@ -1693,7 +1790,6 @@ start_here: mr r7,r27 bl identify_machine bl MMU_init - /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers @@ -1725,8 +1821,10 @@ start_here: 2: SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ #ifdef __SMP__ - tlbsync + tlbsync /* ... on all CPUs */ + sync #endif #ifndef CONFIG_8xx mtspr SDR1,r6 @@ -1947,8 +2045,9 @@ _GLOBAL(_switch) stw r0,GPR0(r1) lwz r0,0(r1) stw r0,GPR1(r1) - SAVE_10GPRS(2, r1) - SAVE_10GPRS(12, r1) + /* r3-r13 are caller saved -- Cort */ + SAVE_GPR(2, r1) + SAVE_8GPRS(14, r1) SAVE_10GPRS(22, r1) mflr r20 /* Return to switch caller */ mfmsr r22 @@ -1971,46 +2070,71 @@ _GLOBAL(_switch) mtspr SPRG3,r0 /* Update current TSS phys addr */ SYNC lwz r1,KSP(r4) /* Load new stack pointer */ + /* save the old current 'last' for return value */ + mr r3,r2 addi r2,r4,-TSS /* Update current */ #ifndef CONFIG_8xx /* Set up segment registers for new task */ rlwinm r5,r5,4,8,27 /* VSID = context << 4 */ addis r5,r5,0x6000 /* Set Ks, Ku bits */ - li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ mtctr r0 - li r3,0 -3: mtsrin r5,r3 + li r9,0 +3: mtsrin r5,r9 addi r5,r5,1 /* next VSID */ - addis r3,r3,0x1000 /* address of next segment */ + addis r9,r9,0x1000 /* address of next segment */ bdnz 3b #else /* On the MPC8xx, we place the physical address of the new task * page directory loaded into the MMU base register, and set the * ASID compare register with the new "context". */ - lwz r3,MM-TSS(r4) /* Get virtual address of mm */ - lwz r3,PGD(r3) /* get new->mm->pgd */ - addis r3,r3,-KERNELBASE@h /* convert to phys addr */ - mtspr M_TWB, r3 /* Update MMU base address */ + lwz r9,MM-TSS(r4) /* Get virtual address of mm */ + lwz r9,PGD(r9) /* get new->mm->pgd */ + addis r9,r9,-KERNELBASE@h /* convert to phys addr */ + mtspr M_TWB, r9 /* Update MMU base address */ mtspr M_CASID, r5 /* Update context */ tlbia #endif SYNC - -/* FALL THROUGH into int_return */ -#ifdef __SMP__ - /* call schedule_tail if this is the first time for a child process */ - lwz r5,TSS_SMP_FORK_RET(r4) - cmpi 0,r5,0 - beq+ int_return - li r3,0 - stw r3,TSS_SMP_FORK_RET(r4) - bl schedule_tail -#endif /* __SMP__ */ +2: lwz r9,_MSR(r1) /* Returning to user mode? */ + andi. r9,r9,MSR_PR + beq+ 10f /* if not, don't adjust kernel stack */ +8: addi r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD /* size of frame */ + stw r4,TSS+KSP(r2) /* save kernel stack pointer */ + tophys(r9,r1,r9) + mtspr SPRG2,r9 /* phys exception stack pointer */ +10: lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + /* r3-r13 are destroyed -- Cort */ + REST_GPR(14, r1) + REST_8GPRS(15, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi /* * Trap exit. */ +#ifdef __SMP__ + .globl ret_from_smpfork +ret_from_smpfork: + bl schedule_tail +#endif .globl ret_from_syscall ret_from_syscall: .globl int_return @@ -2025,8 +2149,8 @@ int_return: lwz r5,_MSR(r1) and. r5,r5,r4 beq 2f -3: lis r4,n_lost_interrupts@ha - lwz r4,n_lost_interrupts@l(r4) +3: lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) cmpi 0,r4,0 beq+ 1f addi r3,r1,STACK_FRAME_OVERHEAD @@ -2118,7 +2242,7 @@ _GLOBAL(fake_interrupt) _GLOBAL(set_context) rlwinm r3,r3,4,8,27 /* VSID = context << 4 */ addis r3,r3,0x6000 /* Set Ks, Ku bits */ - li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ mtctr r0 li r4,0 3: mtsrin r3,r4 @@ -2177,6 +2301,27 @@ _GLOBAL(flush_icache_range) blr /* + * Like above, but only do the D-cache. This is used by the 8xx + * to push the cache so the CPM doesn't get stale data. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* * Flush a particular page from the DATA cache * Note: this is necessary because the instruction cache does *not* * snoop from the data cache. @@ -2207,25 +2352,35 @@ _GLOBAL(flush_page_to_ram) blr /* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: dcbz 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* * Flush entries from the hash table with VSIDs in the range * given. */ #ifndef CONFIG_8xx _GLOBAL(flush_hash_segments) + lis r5,Hash@ha + lwz r5,Hash@l(r5) /* base of hash table */ #ifdef NO_RELOAD_HTAB -/* - * Bitmask of PVR numbers of 603-like chips, - * for which we don't use the hash table at all. - */ -#define PVR_603_LIKE 0x13000000 /* bits 3, 6, 7 set */ - - mfspr r0,PVR - rlwinm r0,r0,16,27,31 - lis r9,PVR_603_LIKE@h - rlwnm. r0,r9,r0,0,0 - beq+ 99f + cmpwi 0,r5,0 + bne+ 99f tlbia - isync + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr 99: #endif /* NO_RELOAD_HTAB */ @@ -2247,14 +2402,13 @@ _GLOBAL(flush_hash_segments) bne- 10b stwcx. r8,0,r9 bne- 10b + eieio #endif rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ oris r3,r3,0x8000 /* set V bit */ rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ oris r4,r4,0x8000 ori r4,r4,0x7f - lis r5,Hash@ha - lwz r5,Hash@l(r5) /* base of hash table */ lis r6,Hash_size@ha lwz r6,Hash_size@l(r6) /* size in bytes */ srwi r6,r6,3 /* # PTEs */ @@ -2270,11 +2424,11 @@ _GLOBAL(flush_hash_segments) 2: bdnz 1b /* continue with loop */ sync tlbia - isync + sync #ifdef __SMP__ tlbsync + sync lis r3,hash_table_lock@ha - li r0,0 stw r0,hash_table_lock@l(r3) mtmsr r10 SYNC @@ -2287,14 +2441,17 @@ _GLOBAL(flush_hash_segments) * flush_hash_page(unsigned context, unsigned long va) */ _GLOBAL(flush_hash_page) + lis r6,Hash@ha + lwz r6,Hash@l(r6) /* hash table base */ #ifdef NO_RELOAD_HTAB - mfspr r0,PVR - rlwinm r0,r0,16,27,31 - lis r9,PVR_603_LIKE@h - rlwnm. r0,r9,r0,0,0 - beq+ 99f + cmpwi 0,r6,0 /* hash table in use? */ + bne+ 99f tlbie r4 /* in hw tlb too */ - isync + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr 99: #endif /* NO_RELOAD_HTAB */ @@ -2311,11 +2468,12 @@ _GLOBAL(flush_hash_page) ori r9,r9,hash_table_lock@l lwz r8,PROCESSOR(r2) oris r8,r8,9 -10: lwarx r6,0,r9 - cmpi 0,r6,0 +10: lwarx r7,0,r9 + cmpi 0,r7,0 bne- 10b stwcx. r8,0,r9 bne- 10b + eieio #endif rlwinm r3,r3,11,1,20 /* put context into vsid */ rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ @@ -2328,8 +2486,6 @@ _GLOBAL(flush_hash_page) lwz r5,Hash_mask@l(r5) /* hash mask */ slwi r5,r5,6 /* << 6 */ and r7,r7,r5 - lis r6,Hash@ha - lwz r6,Hash@l(r6) /* hash table base */ add r6,r6,r7 /* address of primary PTEG */ li r8,8 mtctr r8 @@ -2350,9 +2506,10 @@ _GLOBAL(flush_hash_page) stw r0,0(r7) /* invalidate entry */ 4: sync tlbie r4 /* in hw tlb too */ - isync + sync #ifdef __SMP__ tlbsync + sync lis r3,hash_table_lock@h li r0,0 stw r0,hash_table_lock@l(r3) @@ -2418,30 +2575,27 @@ enter_rtas: rfi /* return to caller */ #endif /* CONFIG_8xx */ -#ifdef CONFIG_MBX -/* Jump into the system reset for the MBX rom. +#ifdef CONFIG_8xx +/* Jump into the system reset for the rom. * We first disable the MMU, and then jump to the ROM reset address. * - * This does not work, don't bother trying. There is no place in - * the ROM we can jump to cause a reset. We will have to program - * a watchdog of some type that we don't service to cause a processor - * reset. + * r3 is the board info structure, r4 is the location for starting. + * I use this for building a small kernel that can load other kernels, + * rather than trying to write or rely on a rom monitor that can tftp load. */ - .globl MBX_gorom -MBX_gorom: - li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) - lis r4,2f@h - addis r4,r4,-KERNELBASE@h - ori r4,r4,2f@l - mtspr SRR0,r4 - mtspr SRR1,r3 - rfi + .globl m8xx_gorom +m8xx_gorom: + li r5,MSR_KERNEL & ~(MSR_IR|MSR_DR) + lis r6,2f@h + addis r6,r6,-KERNELBASE@h + ori r6,r6,2f@l + mtspr SRR0,r6 + mtspr SRR1,r5 + rfi 2: - lis r4, 0xfe000000@h - addi r4, r4, 0xfe000000@l - mtlr r4 - blr -#endif /* CONFIG_MBX */ + mtlr r4 + blr +#endif /* CONFIG_8xx */ /* * We put a few things here that have to be page-aligned. diff --git a/arch/ppc/kernel/i8259.c b/arch/ppc/kernel/i8259.c new file mode 100644 index 000000000..4ec6d3e11 --- /dev/null +++ b/arch/ppc/kernel/i8259.c @@ -0,0 +1,130 @@ + +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/io.h> +#include "i8259.h" + +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) + +int i8259_irq(int cpu) +{ + int irq; + + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(0x0C, 0xA0); + irq = (inb(0xA0) & 7) + 8; + } + else if (irq==7) + { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid + * interrupt + */ + outb(0x0b, 0x20); + if(~inb(0x20)&0x80) + return -1; + } + return irq; +} + +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); +} + +struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + 0 +}; + +static void +no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ +} + +void __init i8259_init(void) +{ + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + outb(0xFF, 0x21); /* Mask all */ + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + outb(0xFF, 0xA1); /* Mask all */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + request_irq( i8259_pic.irq_offset + 2, no_action, SA_INTERRUPT, + "82c59 secondary cascade", NULL ); + enable_irq(i8259_pic.irq_offset + 2); /* Enable cascade interrupt */ +} diff --git a/arch/ppc/kernel/i8259.h b/arch/ppc/kernel/i8259.h new file mode 100644 index 000000000..a1d6df0a1 --- /dev/null +++ b/arch/ppc/kernel/i8259.h @@ -0,0 +1,12 @@ + +#ifndef _PPC_KERNEL_i8259_H +#define _PPC_KERNEL_i8259_H + +#include "local_irq.h" + +extern struct hw_interrupt_type i8259_pic; + +void i8259_init(void); +int i8259_irq(int); + +#endif /* _PPC_KERNEL_i8259_H */ diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index af163699b..c68d1e63e 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.57 1998/12/28 10:28:46 paulus Exp $ + * $Id: idle.c,v 1.61 1999/03/18 04:15:45 cort Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -39,6 +39,12 @@ unsigned long htab_reclaim_on = 0; unsigned long zero_paged_on = 0; unsigned long powersave_nap = 0; +unsigned long *zero_cache; /* head linked list of pre-zero'd pages */ +unsigned long zero_sz; /* # currently pre-zero'd pages */ +unsigned long zeropage_hits; /* # zero'd pages request that we've done */ +unsigned long zeropage_calls; /* # zero'd pages request that've been made */ +unsigned long zerototal; /* # pages zero'd over time */ + int idled(void *unused) { /* endless loop with no priority at all */ @@ -108,8 +114,6 @@ void inline htab_reclaim(void) /* if we don't have a htab */ if ( Hash_size == 0 ) return; - lock_dcache(1); - #if 0 /* find a random place in the htab to start each time */ start = &Hash[jiffies%(Hash_size/sizeof(PTE))]; @@ -147,7 +151,6 @@ void inline htab_reclaim(void) } out: if ( current->need_resched ) printk("need_resched: %lx\n", current->need_resched); - unlock_dcache(); #endif /* CONFIG_8xx */ } @@ -159,7 +162,7 @@ unsigned long get_zero_page_fast(void) { unsigned long page = 0; - atomic_inc((atomic_t *)&quicklists.zeropage_calls); + atomic_inc((atomic_t *)&zero_cache_calls); if ( zero_quicklist ) { /* atomically remove this page from the list */ @@ -177,10 +180,10 @@ unsigned long get_zero_page_fast(void) #endif /* __SMP__ */ /* we can update zerocount after the fact since it is not * used for anything but control of a loop which doesn't - * matter since it won't affect anything if it zero's one + * matter since it won't affect anything if it zeros one * less page -- Cort */ - atomic_inc((atomic_t *)&quicklists.zeropage_hits); + atomic_inc((atomic_t *)&zero_cache_hits); atomic_dec((atomic_t *)&zero_cache_sz); /* zero out the pointer to next in the page */ @@ -222,7 +225,6 @@ void zero_paged(void) /* * Make the page no cache so we don't blow our cache with 0's - * We should just turn off the cache instead. -- Cort */ pte = find_pte(init_task.mm, pageptr); if ( !pte ) @@ -254,8 +256,8 @@ void zero_paged(void) * So we update the list atomically without locking it. * -- Cort */ + /* turn cache on for this page */ - pte_cache(*pte); flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); /* atomically add this page to the list */ @@ -280,7 +282,7 @@ void zero_paged(void) * reads it. -- Cort */ atomic_inc((atomic_t *)&zero_cache_sz); - atomic_inc((atomic_t *)&quicklists.zerototal); + atomic_inc((atomic_t *)&zero_cache_total); } } @@ -301,11 +303,17 @@ void power_save(void) hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE); hid0 |= (powersave_nap? HID0_NAP: HID0_DOZE) | HID0_DPM; asm("mtspr 1008,%0" : : "r" (hid0)); - msr |= MSR_POW; + + /* set the POW bit in the MSR, and enable interrupts + * so we wake up sometime! */ + _nmask_and_or_msr(0, MSR_POW | MSR_EE); + + /* Disable interrupts again so restore_flags will + * work. */ + _nmask_and_or_msr(MSR_EE, 0); } restore_flags(msr); default: return; } - } diff --git a/arch/ppc/kernel/indirect_pci.c b/arch/ppc/kernel/indirect_pci.c new file mode 100644 index 000000000..641d77a52 --- /dev/null +++ b/arch/ppc/kernel/indirect_pci.c @@ -0,0 +1,121 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/system.h> + +unsigned int * pci_config_address; +unsigned char * pci_config_data; + +int indirect_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + unsigned flags; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + *val= in_8(pci_config_data + (offset&3)); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + unsigned flags; + + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + unsigned flags; + + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + *val= in_le32((unsigned *)pci_config_data); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + unsigned flags; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + out_8(pci_config_data + (offset&3), val); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + unsigned flags; + + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + out_le16((unsigned short *)(pci_config_data + (offset&3)), val); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + unsigned flags; + + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + out_le32((unsigned *)pci_config_data, val); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index f4a7c7143..461e51aa1 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,5 +1,5 @@ /* - * $Id: irq.c,v 1.91 1998/12/28 10:28:47 paulus Exp $ + * $Id: irq.c,v 1.105 1999/03/25 19:51:51 cort Exp $ * * arch/ppc/kernel/irq.c * @@ -42,6 +42,7 @@ #include <linux/malloc.h> #include <linux/openpic.h> #include <linux/pci.h> +#include <linux/delay.h> #include <asm/bitops.h> #include <asm/hydra.h> @@ -56,101 +57,46 @@ #include <asm/amigaints.h> #include <asm/amigahw.h> #include <asm/amigappc.h> -#ifdef CONFIG_8xx -#include <asm/8xx_immap.h> -#include <asm/mbx.h> -#endif +#include <asm/ptrace.h> + +#include "local_irq.h" -extern void process_int(unsigned long vec, struct pt_regs *fp); -extern void apus_init_IRQ(void); -extern void amiga_disable_irq(unsigned int irq); -extern void amiga_enable_irq(unsigned int irq); -static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } -static volatile unsigned char *chrp_int_ack_special; extern volatile unsigned long ipi_count; -static void pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base); +void enable_irq(unsigned int irq_nr); +void disable_irq(unsigned int irq_nr); + +/* Fixme - Need to figure out a way to get rid of this - Corey */ +volatile unsigned char *chrp_int_ack_special; #ifdef CONFIG_APUS /* Rename a few functions. Requires the CONFIG_APUS protection. */ #define request_irq nop_ppc_request_irq #define free_irq nop_ppc_free_irq #define get_irq_list nop_get_irq_list -#endif -#ifndef CONFIG_8xx -void (*mask_and_ack_irq)(int irq_nr); -void (*mask_irq)(unsigned int irq_nr); -void (*unmask_irq)(unsigned int irq_nr); -#else /* CONFIG_8xx */ -/* init_IRQ() happens too late for the MBX because we initialize the - * CPM early and it calls request_irq() before we have these function - * pointers initialized. - */ -#define mask_and_ack_irq(irq) mbx_mask_irq(irq) -#define mask_irq(irq) mbx_mask_irq(irq) -#define unmask_irq(irq) mbx_unmask_irq(irq) -#endif /* CONFIG_8xx */ - #define VEC_SPUR (24) -#undef SHOW_IRQ -#undef SHOW_GATWICK_IRQS -#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -#define cached_21 (((char *)(cached_irq_mask))[3]) -#define cached_A1 (((char *)(cached_irq_mask))[2]) -#define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21 - -unsigned int local_bh_count[NR_CPUS]; -unsigned int local_irq_count[NR_CPUS]; -int max_irqs; -int max_real_irqs; -static struct irqaction *irq_action[NR_IRQS]; -static int spurious_interrupts = 0; -static unsigned int cached_irq_mask[NR_MASK_WORDS]; -unsigned int lost_interrupts[NR_MASK_WORDS]; -atomic_t n_lost_interrupts; - -/* pmac */ -struct pmac_irq_hw { - unsigned int flag; - unsigned int enable; - unsigned int ack; - unsigned int level; -}; +#endif -/* XXX these addresses should be obtained from the device tree */ -volatile struct pmac_irq_hw *pmac_irq_hw[4] = { - (struct pmac_irq_hw *) 0xf3000020, - (struct pmac_irq_hw *) 0xf3000010, - (struct pmac_irq_hw *) 0xf4000020, - (struct pmac_irq_hw *) 0xf4000010, -}; +#define MAXCOUNT 10000000 -/* This is the interrupt used on the main controller for the secondary - controller. Happens on PowerBooks G3 Series (a second mac-io) - -- BenH - */ -static int second_irq = -999; +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -/* Returns the number of 0's to the left of the most significant 1 bit */ -static inline int cntlzw(int bits) -{ - int lz; +int ppc_spurious_interrupts = 0; - asm ("cntlzw %0,%1" : "=r" (lz) : "r" (bits)); - return lz; -} +unsigned int ppc_local_bh_count[NR_CPUS]; +unsigned int ppc_local_irq_count[NR_CPUS]; +struct irqaction *ppc_irq_action[NR_IRQS]; +unsigned int ppc_cached_irq_mask[NR_MASK_WORDS]; +unsigned int ppc_lost_interrupts[NR_MASK_WORDS]; +atomic_t ppc_n_lost_interrupts; -static inline void sync(void) -{ - asm volatile ("sync"); -} /* nasty hack for shared irq's since we need to do kmalloc calls but - * can't very very early in the boot when we need to do a request irq. + * can't very early in the boot when we need to do a request irq. * this needs to be removed. * -- Cort */ static char cache_bitmask = 0; -static struct irqaction malloc_cache[4]; +static struct irqaction malloc_cache[8]; extern int mem_init_done; void *irq_kmalloc(size_t size, int pri) @@ -179,168 +125,80 @@ void irq_kfree(void *ptr) kfree(ptr); } -#ifndef CONFIG_8xx -void i8259_mask_and_ack_irq(int irq_nr) -{ - /* spin_lock(&irq_controller_lock);*/ - cached_irq_mask[0] |= 1 << irq_nr; - if (irq_nr > 7) { - inb(0xA1); /* DUMMY */ - outb(cached_A1,0xA1); - outb(0x62,0x20); /* Specific EOI to cascade */ - /*outb(0x20,0xA0);*/ - outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */ - } else { - inb(0x21); /* DUMMY */ - outb(cached_21,0x21); - /*outb(0x20,0x20);*/ - outb(0x60|irq_nr,0x20); /* specific eoi */ - - } - /* spin_unlock(&irq_controller_lock);*/ -} - -void __pmac pmac_mask_and_ack_irq(int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - - if ((unsigned)irq_nr >= max_irqs) - return; - /*spin_lock(&irq_controller_lock);*/ - - clear_bit(irq_nr, cached_irq_mask); - if (test_and_clear_bit(irq_nr, lost_interrupts)) - atomic_dec(&n_lost_interrupts); - out_le32(&pmac_irq_hw[i]->ack, bit); - out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - out_le32(&pmac_irq_hw[i]->ack, bit); - /* make sure ack gets to controller before we enable interrupts */ - sync(); - - /*spin_unlock(&irq_controller_lock);*/ - /*if ( irq_controller_lock.lock ) - panic("irq controller lock still held in mask and ack\n");*/ -} +struct irqdesc irq_desc[NR_IRQS] = {{0, 0}, }; -void __openfirmware chrp_mask_and_ack_irq(int irq_nr) +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) { - /* spinlocks are done by i8259_mask_and_ack() - Cort */ - if (is_8259_irq(irq_nr)) - i8259_mask_and_ack_irq(irq_nr); -} - + struct irqaction *old, **p, *action; + unsigned long flags; -static void i8259_set_irq_mask(int irq_nr) -{ - if (irq_nr > 7) { - outb(cached_A1,0xA1); - } else { - outb(cached_21,0x21); + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + { + /* Free */ + for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + irq_kfree(action); + return 0; + } + return -ENOENT; } -} - -static void __pmac pmac_set_irq_mask(int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - - if ((unsigned)irq_nr >= max_irqs) - return; - - /* enable unmasked interrupts */ - out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - - /* - * Unfortunately, setting the bit in the enable register - * when the device interrupt is already on *doesn't* set - * the bit in the flag register or request another interrupt. - */ - if ((bit & cached_irq_mask[i]) - && (ld_le32(&pmac_irq_hw[i]->level) & bit) - && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { - if (!test_and_set_bit(irq_nr, lost_interrupts)) - atomic_inc(&n_lost_interrupts); + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + save_flags(flags); + cli(); + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->dev_id = dev_id; + action->next = NULL; + enable_irq(irq); + + p = &irq_desc[irq].action; + + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & action->flags & SA_SHIRQ)) + return -EBUSY; + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); } -} - -/* - * These have to be protected by the spinlock - * before being called. - */ -static void i8259_mask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] |= 1 << irq_nr; - i8259_set_irq_mask(irq_nr); -} - -static void i8259_unmask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] &= ~(1 << irq_nr); - i8259_set_irq_mask(irq_nr); -} - -static void __pmac pmac_mask_irq(unsigned int irq_nr) -{ - clear_bit(irq_nr, cached_irq_mask); - pmac_set_irq_mask(irq_nr); - sync(); -} - -static void __pmac pmac_unmask_irq(unsigned int irq_nr) -{ - set_bit(irq_nr, cached_irq_mask); - pmac_set_irq_mask(irq_nr); -} - -static void __openfirmware chrp_mask_irq(unsigned int irq_nr) -{ - if (is_8259_irq(irq_nr)) - i8259_mask_irq(irq_nr); - else - openpic_disable_irq(irq_to_openpic(irq_nr)); -} + *p = action; -static void __openfirmware chrp_unmask_irq(unsigned int irq_nr) -{ - if (is_8259_irq(irq_nr)) - i8259_unmask_irq(irq_nr); - else - openpic_enable_irq(irq_to_openpic(irq_nr)); -} -#else /* CONFIG_8xx */ -static void mbx_mask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] &= ~(1 << (31-irq_nr)); - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask[0]; + restore_flags(flags); + return 0; } -static void mbx_unmask_irq(unsigned int irq_nr) +void free_irq(unsigned int irq, void *dev_id) { - cached_irq_mask[0] |= (1 << (31-irq_nr)); - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask[0]; + request_irq(irq, NULL, 0, NULL, dev_id); } -#endif /* CONFIG_8xx */ void disable_irq(unsigned int irq_nr) { - /*unsigned long flags;*/ - - /* spin_lock_irqsave(&irq_controller_lock, flags);*/ mask_irq(irq_nr); - /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ synchronize_irq(); } void enable_irq(unsigned int irq_nr) { - /*unsigned long flags;*/ - - /* spin_lock_irqsave(&irq_controller_lock, flags);*/ unmask_irq(irq_nr); - /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ } int get_irq_list(char *buf) @@ -354,8 +212,8 @@ int get_irq_list(char *buf) *(char *)(buf+len++) = '\n'; for (i = 0 ; i < NR_IRQS ; i++) { - action = irq_action[i]; - if ((!action || !action->handler) && (i != second_irq)) + action = irq_desc[i].action; + if ( !action || !action->handler ) continue; len += sprintf(buf+len, "%3d: ", i); #ifdef __SMP__ @@ -365,56 +223,83 @@ int get_irq_list(char *buf) #else len += sprintf(buf+len, "%10u ", kstat_irqs(i)); #endif /* __SMP__ */ - switch( _machine ) - { - case _MACH_prep: - len += sprintf(buf+len, " 82c59 "); - break; - case _MACH_Pmac: - if (i < 64) - len += sprintf(buf+len, " PMAC-PIC "); - else - len += sprintf(buf+len, " GATWICK "); - break; - case _MACH_chrp: - if ( is_8259_irq(i) ) - len += sprintf(buf+len, " 82c59 "); - else - len += sprintf(buf+len, " OpenPIC "); - break; - case _MACH_mbx: - len += sprintf(buf+len, " MPC8xx "); - break; + if ( irq_desc[i].ctl ) + len += sprintf(buf+len, " %s ", irq_desc[i].ctl->typename ); + len += sprintf(buf+len, " %s",action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); } - - if (i != second_irq) { - len += sprintf(buf+len, " %s",action->name); - for (action=action->next; action; action = action->next) { - len += sprintf(buf+len, ", %s", action->name); - } - len += sprintf(buf+len, "\n"); - } else - len += sprintf(buf+len, " Gatwick secondary IRQ controller\n"); + len += sprintf(buf+len, "\n"); } #ifdef __SMP__ /* should this be per processor send/receive? */ - len += sprintf(buf+len, "IPI: %10lu", ipi_count); - for ( i = 0 ; i <= smp_num_cpus-1; i++ ) - len += sprintf(buf+len," "); - len += sprintf(buf+len, " interprocessor messages received\n"); + len += sprintf(buf+len, "IPI: %10lu\n", ipi_count); #endif - len += sprintf(buf+len, "BAD: %10u",spurious_interrupts); - for ( i = 0 ; i <= smp_num_cpus-1; i++ ) - len += sprintf(buf+len," "); - len += sprintf(buf+len, " spurious or short\n"); + len += sprintf(buf+len, "BAD: %10u\n", ppc_spurious_interrupts); return len; } - /* - * Global interrupt locks for SMP. Allow interrupts to come in on any - * CPU, yet make cli/sti act globally to protect critical regions.. + * Eventually, this should take an array of interrupts and an array size + * so it can dispatch multiple interrupts. */ +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) +{ + int status; + struct irqaction *action; + int cpu = smp_processor_id(); + + mask_and_ack_irq(irq); + status = 0; + action = irq_desc[irq].action; + kstat.irqs[cpu][irq]++; + if (action && action->handler) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while ( action ); + __cli(); + unmask_irq(irq); + } else { + ppc_spurious_interrupts++; + disable_irq( irq ); + } +} + +asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) +{ + int cpu = smp_processor_id(); + + hardirq_enter(cpu); + ppc_md.do_IRQ(regs, cpu, isfake); + hardirq_exit(cpu); +} + +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +void __init init_IRQ(void) +{ + static int once = 0; + + if ( once ) + return; + else + once++; + + ppc_md.init_IRQ(); +} + #ifdef __SMP__ unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile int global_irq_lock; @@ -431,9 +316,13 @@ static void show(char * str) printk("\n%s, CPU %d:\n", str, cpu); printk("irq: %d [%d %d]\n", - atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); + atomic_read(&global_irq_count), + ppc_local_irq_count[0], + ppc_local_irq_count[1]); printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); + atomic_read(&global_bh_count), + ppc_local_bh_count[0], + ppc_local_bh_count[1]); stack = (unsigned long *) &str; for (i = 40; i ; i--) { unsigned long x = *++stack; @@ -443,7 +332,6 @@ static void show(char * str) } } -#define MAXCOUNT 100000000 static inline void wait_on_bh(void) { int count = MAXCOUNT; @@ -469,7 +357,8 @@ static inline void wait_on_irq(int cpu) * already executing in one.. */ if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) + if (ppc_local_bh_count[cpu] + || !atomic_read(&global_bh_count)) break; } @@ -490,7 +379,8 @@ static inline void wait_on_irq(int cpu) continue; if (global_irq_lock) continue; - if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) + if (!ppc_local_bh_count[cpu] + && atomic_read(&global_bh_count)) continue; if (!test_and_set_bit(0,&global_irq_lock)) break; @@ -512,7 +402,6 @@ void synchronize_bh(void) wait_on_bh(); } - /* * This is called when we want to synchronize with * interrupts. We may for example tell a device to @@ -581,7 +470,7 @@ void __global_cli(void) if (flags & (1 << 15)) { int cpu = smp_processor_id(); __cli(); - if (!local_irq_count[cpu]) + if (!ppc_local_irq_count[cpu]) get_irqlock(cpu); } } @@ -590,7 +479,7 @@ void __global_sti(void) { int cpu = smp_processor_id(); - if (!local_irq_count[cpu]) + if (!ppc_local_irq_count[cpu]) release_irqlock(cpu); __sti(); } @@ -614,7 +503,7 @@ unsigned long __global_save_flags(void) retval = 2 + local_enabled; /* check for global flags if we're not in an interrupt */ - if (!local_irq_count[smp_processor_id()]) { + if (!ppc_local_irq_count[smp_processor_id()]) { if (local_enabled) retval = 1; if (global_irq_holder == (unsigned char) smp_processor_id()) @@ -623,6 +512,31 @@ unsigned long __global_save_flags(void) return retval; } +int +tb(long vals[], + int max_size) +{ + register unsigned long *orig_sp __asm__ ("r1"); + register unsigned long lr __asm__ ("r3"); + unsigned long *sp; + int i; + + asm volatile ("mflr 3"); + vals[0] = lr; + sp = (unsigned long *) *orig_sp; + sp = (unsigned long *) *sp; + for (i=1; i<max_size; i++) { + if (sp == 0) { + break; + } + + vals[i] = *(sp+1); + sp = (unsigned long *) *sp; + } + + return i; +} + void __global_restore_flags(unsigned long flags) { switch (flags) { @@ -639,559 +553,21 @@ void __global_restore_flags(unsigned long flags) __sti(); break; default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); - } -} - -#endif /* __SMP__ */ - -asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) -{ - int irq; - unsigned long bits; - struct irqaction *action; - int cpu = smp_processor_id(); - int status; - int openpic_eoi_done = 0; - - /* save the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - unsigned dcache_locked; - - dcache_locked = unlock_dcache(); - hardirq_enter(cpu); -#ifndef CONFIG_8xx -#ifdef __SMP__ - if ( cpu != 0 ) - { - if (!isfake) - { - extern void smp_message_recv(void); -#ifdef CONFIG_XMON - static int xmon_2nd; - if (xmon_2nd) - xmon(regs); -#endif - smp_message_recv(); - goto out; - } - /* could be here due to a do_fake_interrupt call but we don't - mess with the controller from the second cpu -- Cort */ - goto out; - } - - { - unsigned int loops = MAXCOUNT; - while (test_bit(0, &global_irq_lock)) { - if (smp_processor_id() == global_irq_holder) { - printk("uh oh, interrupt while we hold global irq lock!\n"); -#ifdef CONFIG_XMON - xmon(0); -#endif - break; - } - if (loops-- == 0) { - printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } - } -#endif /* __SMP__ */ - - switch ( _machine ) - { - case _MACH_Pmac: - for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - - /* Here, we handle interrupts coming from Gatwick, - * normal interrupt code will take care of acking and - * masking the irq on Gatwick itself but we ack&mask - * the Gatwick main interrupt on Heathrow now. It's - * unmasked later, after interrupt handling. -- BenH - */ - if (irq == second_irq) { - mask_and_ack_irq(second_irq); - for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - /* If not found, on exit, irq is 63 (128-1-32-32). - * We set it to -1 and revalidate second controller - */ - if (irq < max_real_irqs) { - irq = -1; - unmask_irq(second_irq); - } -#ifdef SHOW_GATWICK_IRQS - printk("Gatwick irq %d (i:%d, bits:0x%08lx\n", irq, i, bits); -#endif - } - - break; - case _MACH_chrp: - irq = openpic_irq(0); - if (irq == IRQ_8259_CASCADE) - { - /* - * This magic address generates a PCI IACK cycle. - * - * This should go in the above mask/ack code soon. -- Cort - */ - irq = *chrp_int_ack_special; - /* - * Acknowledge as soon as possible to allow i8259 - * interrupt nesting - */ - openpic_eoi(0); - openpic_eoi_done = 1; - } - else if (irq >= OPENPIC_VEC_TIMER) - { - /* - * OpenPIC interrupts >64 will be used for other purposes - * like interprocessor interrupts and hardware errors - */ - if (irq == OPENPIC_VEC_SPURIOUS) { - /* - * Spurious interrupts should never be - * acknowledged - */ - spurious_interrupts++; - openpic_eoi_done = 1; - } else { - /* - * Here we should process IPI timer - * for now the interrupt is dismissed. - */ - } - goto out; - } - break; - case _MACH_prep: - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) - { -retry_cascade: - outb(0x0C, 0xA0); - irq = inb(0xA0); - /* if no intr left */ - if ( !(irq & 128 ) ) - goto out; - irq = (irq&7) + 8; - } - bits = 1UL << irq; - break; -#ifdef CONFIG_APUS - case _MACH_apus: { - int old_level, new_level; - - old_level = ~(regs->mq) & IPLEMU_IPLMASK; - new_level = (~(regs->mq) >> 3) & IPLEMU_IPLMASK; - - if (new_level == 0) - { - goto apus_out; - } - - APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); - APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET - | (~(new_level) & IPLEMU_IPLMASK))); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); - - process_int (VEC_SPUR+new_level, regs); - - APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); - APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET - | (~(old_level) & IPLEMU_IPLMASK))); - -apus_out: - hardirq_exit(cpu); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); - goto out2; - } -#endif - } - - if (irq < 0) { - /* we get here with Gatwick but the 'bogus' isn't correct in that case -- Cort */ - if ( irq != second_irq ) - { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - spurious_interrupts++; - } - goto out; - } - -#else /* CONFIG_8xx */ - /* For MPC8xx, read the SIVEC register and shift the bits down - * to get the irq number. - */ - bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec; - irq = bits >> 26; -#endif /* CONFIG_8xx */ - mask_and_ack_irq(irq); - status = 0; - action = irq_action[irq]; - kstat.irqs[cpu][irq]++; - if (action && action->handler) { - if (!(action->flags & SA_INTERRUPT)) - __sti(); - do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } while ( action ); - __cli(); - unmask_irq(irq); - } else { -#ifndef CONFIG_8xx - if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ -#endif - spurious_interrupts++; - disable_irq( irq ); - } - - /* This was a gatwick sub-interrupt, we re-enable them on Heathrow - now */ - if (_machine == _MACH_Pmac && irq >= max_real_irqs) - unmask_irq(second_irq); - - /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ -#ifndef CONFIG_8xx - if ( is_prep && (irq > 7) ) - goto retry_cascade; - /* do_bottom_half is called if necessary from int_return in head.S */ -out: - if (_machine == _MACH_chrp && !openpic_eoi_done) - openpic_eoi(0); -#endif /* CONFIG_8xx */ - hardirq_exit(cpu); - -#ifdef CONFIG_APUS -out2: -#endif - /* restore the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - lock_dcache(dcache_locked); -} - -int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) -{ - struct irqaction *old, **p, *action; - unsigned long flags; - -#ifdef SHOW_IRQ - printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n", - irq,(int)handler,devname,(int)dev_id); -#endif /* SHOW_IRQ */ - - if (irq >= NR_IRQS) - return -EINVAL; - - /* Cannot allocate second controller IRQ */ - if (irq == second_irq) - return -EBUSY; + unsigned long trace[5]; + int count; + int i; - if (!handler) - { - /* Free */ - for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) - { - /* Found it - now free it */ - save_flags(flags); - cli(); - *p = action->next; - restore_flags(flags); - irq_kfree(action); - return 0; - } - return -ENOENT; - } - - action = (struct irqaction *) - irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); - if (!action) - return -ENOMEM; - save_flags(flags); - cli(); - - action->handler = handler; - action->flags = irqflags; - action->mask = 0; - action->name = devname; - action->dev_id = dev_id; - action->next = NULL; - enable_irq(irq); - p = irq_action + irq; - - if ((old = *p) != NULL) { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & action->flags & SA_SHIRQ)) - return -EBUSY; - /* add new interrupt at end of irq queue */ - do { - p = &old->next; - old = *p; - } while (old); - } - *p = action; - - restore_flags(flags); - return 0; -} - -void free_irq(unsigned int irq, void *dev_id) -{ - request_irq(irq, NULL, 0, NULL, dev_id); -} - -unsigned long probe_irq_on (void) -{ - return 0; -} - -int probe_irq_off (unsigned long irqs) -{ - return 0; -} - -#ifndef CONFIG_8xx -__initfunc(static void i8259_init(void)) -{ - /* init master interrupt controller */ - outb(0x11, 0x20); /* Start init sequence */ - outb(0x00, 0x21); /* Vector base */ - outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0x21); /* Select 8086 mode */ - outb(0xFF, 0x21); /* Mask all */ - - /* init slave interrupt controller */ - outb(0x11, 0xA0); /* Start init sequence */ - outb(0x08, 0xA1); /* Vector base */ - outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0xA1); /* Select 8086 mode */ - outb(0xFF, 0xA1); /* Mask all */ - outb(cached_A1, 0xA1); - outb(cached_21, 0x21); - if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0) - panic("Could not allocate cascade IRQ!"); - enable_irq(2); /* Enable cascade interrupt */ -} -#endif /* CONFIG_8xx */ - -/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External - * interrupts can be either edge or level triggered, but there is no - * reason for us to change the EPPC-bug values (it would not work if we did). - */ -__initfunc(void init_IRQ(void)) -{ - extern void xmon_irq(int, void *, struct pt_regs *); - int i; - struct device_node *irqctrler; - unsigned long addr; - struct device_node *np; - -#ifndef CONFIG_8xx - switch (_machine) - { - case _MACH_Pmac: - mask_and_ack_irq = pmac_mask_and_ack_irq; - mask_irq = pmac_mask_irq; - unmask_irq = pmac_unmask_irq; - - /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, - others have 32 */ - max_irqs = max_real_irqs = 32; - irqctrler = find_devices("mac-io"); - if (irqctrler) - { - max_real_irqs = 64; - if (irqctrler->next) - max_irqs = 128; - else - max_irqs = 64; - } - - /* get addresses of first controller */ - if (irqctrler) { - if (irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 0; i < 2; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (2 - i) * 0x10); - } - - /* get addresses of second controller */ - irqctrler = (irqctrler->next) ? irqctrler->next : NULL; - if (irqctrler && irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 2; i < 4; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (4 - i) * 0x10); - } - } - - /* disable all interrupts in all controllers */ - for (i = 0; i * 32 < max_irqs; ++i) - out_le32(&pmac_irq_hw[i]->enable, 0); - - - /* get interrupt line of secondary interrupt controller */ - if (irqctrler) { - second_irq = irqctrler->intrs[0].line; - printk(KERN_INFO "irq: secondary controller on irq %d\n", - (int)second_irq); - if (device_is_compatible(irqctrler, "gatwick")) - pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); - enable_irq(second_irq); - } - printk("System has %d possible interrupts\n", max_irqs); - if (max_irqs != max_real_irqs) - printk(KERN_DEBUG "%d interrupts on main controller\n", - max_real_irqs); - -#ifdef CONFIG_XMON - request_irq(20, xmon_irq, 0, "NMI", 0); -#endif /* CONFIG_XMON */ - break; - case _MACH_chrp: - mask_and_ack_irq = chrp_mask_and_ack_irq; - mask_irq = chrp_mask_irq; - unmask_irq = chrp_unmask_irq; - - if ( !(np = find_devices("pci") ) ) - printk("Cannot find pci to get ack address\n"); - else - { - chrp_int_ack_special = (volatile unsigned char *) - (*(unsigned long *)get_property(np, - "8259-interrupt-acknowledge", NULL)); - } - openpic_init(1); - i8259_init(); - cached_irq_mask[0] = cached_irq_mask[1] = ~0UL; -#ifdef CONFIG_XMON - request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), - xmon_irq, 0, "NMI", 0); -#endif /* CONFIG_XMON */ - break; - case _MACH_prep: - mask_and_ack_irq = i8259_mask_and_ack_irq; - mask_irq = i8259_mask_irq; - unmask_irq = i8259_unmask_irq; - cached_irq_mask[0] = ~0UL; - - i8259_init(); - /* - * According to the Carolina spec from ibm irqs 0,1,2, and 8 - * must be edge triggered. Also, the pci intrs must be level - * triggered and _only_ isa intrs can be level sensitive - * which are 3-7,9-12,14-15. 13 is special - it can be level. - * - * power on default is 0's in both regs - all edge. - * - * These edge/level control regs allow edge/level status - * to be decided on a irq basis instead of on a PIC basis. - * It's still pretty ugly. - * - Cort - */ - { - unsigned char irq_mode1 = 0, irq_mode2 = 0; - irq_mode1 = 0; /* to get rid of compiler warnings */ - /* - * On Carolina, irq 15 and 13 must be level (scsi/ide/net). - */ - if ( _prep_type == _PREP_IBM ) - irq_mode2 |= 0xa0; + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + count = tb(trace, 5); + printk("tb:"); + for(i=0; i<count; i++) { + printk(" %8.8lx", trace[i]); } - break; -#ifdef CONFIG_APUS - case _MACH_apus: - mask_irq = amiga_disable_irq; - unmask_irq = amiga_enable_irq; - apus_init_IRQ(); - break; -#endif + printk("\n"); } -#endif /* CONFIG_8xx */ -} - -/* This routine will fix some missing interrupt values in the device tree - * on the gatwick mac-io controller used by some PowerBooks - */ -static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) -{ - struct device_node *node; - static struct interrupt_info int_pool[4]; - - memset(int_pool, 0, sizeof(int_pool)); - node = gw->child; - while(node) - { - /* Fix SCC */ - if (strcasecmp(node->name, "escc") == 0) - if (node->child && node->child->n_intrs == 0) - { - node->child->n_intrs = 1; - node->child->intrs = &int_pool[0]; - int_pool[0].line = 15+irq_base; - printk(KERN_INFO "irq: fixed SCC on second controller (%d)\n", - int_pool[0].line); - } - /* Fix media-bay & left SWIM */ - if (strcasecmp(node->name, "media-bay") == 0) - { - struct device_node* ya_node; - - if (node->n_intrs == 0) - { - node->n_intrs = 1; - node->intrs = &int_pool[1]; - int_pool[1].line = 29+irq_base; - printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", - int_pool[1].line); - } - ya_node = node->child; - while(ya_node) - { - if ((strcasecmp(ya_node->name, "floppy") == 0) && - ya_node->n_intrs == 0) - { - ya_node->n_intrs = 2; - ya_node->intrs = &int_pool[2]; - int_pool[2].line = 19+irq_base; - int_pool[3].line = 1+irq_base; - printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", - int_pool[2].line, int_pool[3].line); - } - ya_node = ya_node->sibling; - } - } - node = node->sibling; } - } +#endif /* __SMP__ */ diff --git a/arch/ppc/kernel/local_irq.h b/arch/ppc/kernel/local_irq.h new file mode 100644 index 000000000..5149c291a --- /dev/null +++ b/arch/ppc/kernel/local_irq.h @@ -0,0 +1,45 @@ + +#ifndef _PPC_KERNEL_LOCAL_IRQ_H +#define _PPC_KERNEL_LOCAL_IRQ_H + +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> + +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +/* Structure describing interrupts */ +struct hw_interrupt_type { + const char * typename; + void (*startup)(unsigned int irq); + void (*shutdown)(unsigned int irq); + void (*handle)(unsigned int irq, struct pt_regs * regs); + void (*enable)(unsigned int irq); + void (*disable)(unsigned int irq); + void (*mask_and_ack)(unsigned int irq); + int irq_offset; +}; + +#define mask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->disable) irq_desc[irq].ctl->disable(irq);}) +#define unmask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->enable) irq_desc[irq].ctl->enable(irq);}) +#define mask_and_ack_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->mask_and_ack) irq_desc[irq].ctl->mask_and_ack(irq);}) + +struct irqdesc { + struct irqaction *action; + struct hw_interrupt_type *ctl; +}; + +extern struct irqdesc irq_desc[NR_IRQS]; + + +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) + +extern int ppc_spurious_interrupts; +extern int ppc_second_irq; +extern struct irqaction *ppc_irq_action[NR_IRQS]; +extern unsigned int ppc_local_bh_count[NR_CPUS]; +extern unsigned int ppc_local_irq_count[NR_CPUS]; +extern unsigned int ppc_cached_irq_mask[NR_MASK_WORDS]; +extern unsigned int ppc_lost_interrupts[NR_MASK_WORDS]; +extern atomic_t ppc_n_lost_interrupts; + +#endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c index 30b7b1184..5114c3cfb 100644 --- a/arch/ppc/kernel/mbx_pci.c +++ b/arch/ppc/kernel/mbx_pci.c @@ -252,3 +252,20 @@ int mbx_pcibios_find_class(unsigned int class_code, unsigned short index, } return PCIBIOS_DEVICE_NOT_FOUND; } + +__initfunc( +void +mbx_pcibios_fixup(void)) +{ + /* Nothing to do here? */ +} + +__initfunc( +void +mbx_setup_pci_ptrs(void)) +{ + set_config_access_method(mbx); + + ppc_md.pcibios_fixup = mbx_pcibios_fixup; +} + diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index 90647dcd9..0f1eb3eb5 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -1,5 +1,5 @@ /* - * $Id: mbx_setup.c,v 1.5 1998/12/29 18:55:07 cort Exp $ + * $Id: mbx_setup.c,v 1.9 1999/04/28 11:54:09 davem Exp $ * * linux/arch/ppc/kernel/setup.c * @@ -39,6 +39,22 @@ #include <asm/pgtable.h> #include <asm/ide.h> #include <asm/mbx.h> +#include <asm/machdep.h> + +#include "time.h" +#include "local_irq.h" + +static int mbx_set_rtc_time(unsigned long time); +unsigned long mbx_get_rtc_time(void); +void mbx_calibrate_decr(void); + +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void mackbd_init_hw(void); extern unsigned long loops_per_sec; @@ -55,35 +71,10 @@ extern char saved_command_line[256]; extern unsigned long find_available_memory(void); extern void m8xx_cpm_reset(uint); -/* this really does make things cleaner -- Cort */ -void __init powermac_init(void) -{ -} - void __init adbdev_init(void) { } -void __init mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) -{ - ide_ioreg_t port = base; - int i = 8; - - while (i--) - *p++ = port++; - *p++ = base + 0x206; - if (irq != NULL) - *irq = 0; -#ifdef ATA_FLASH - base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200); - for (i = 0; i < 8; ++i) - *p++ = base++; - *p = ++base; /* Does not matter */ - if (irq) - *irq = 13; -#endif -} - __initfunc(void mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { @@ -145,3 +136,337 @@ abort(void) #endif machine_restart(NULL); } + +/* The decrementer counts at the system (internal) clock frequency divided by + * sixteen, or external oscillator divided by four. Currently, we only + * support the MBX, which is system clock divided by sixteen. + */ +__initfunc(void mbx_calibrate_decr(void)) +{ + bd_t *binfo = (bd_t *)&res; + int freq, fp, divisor; + + if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0) + printk("WARNING: Wrong decrementer source clock.\n"); + + /* The manual says the frequency is in Hz, but it is really + * as MHz. The value 'fp' is the number of decrementer ticks + * per second. + */ + fp = (binfo->bi_intfreq * 1000000) / 16; + freq = fp*60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + +/* A place holder for time base interrupts, if they are ever enabled. +*/ +void timebase_interrupt(int irq, void * dev, struct pt_regs * regs) +{ + printk("timebase_interrupt()\n"); +} + +/* The RTC on the MPC8xx is an internal register. + * We want to protect this during power down, so we need to unlock, + * modify, and re-lock. + */ +static int +mbx_set_rtc_time(unsigned long time) +{ + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time; + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; + return(0); +} + +initfunc(unsigned long +mbx_get_rtc_time(void) +{ + /* First, unlock all of the registers we are going to modify. + * To protect them from corruption during power down, registers + * that are maintained by keep alive power are "locked". To + * modify these registers we have to write the key value to + * the key location associated with the register. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; + + + /* Disable the RTC one second and alarm interrupts. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &= + ~(RTCSC_SIE | RTCSC_ALE); + + /* Enabling the decrementer also enables the timebase interrupts + * (or from the other point of view, to get decrementer interrupts + * we have to enable the timebase). The decrementer interrupt + * is wired into the vector table, nothing to do here for that. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr = + ((mk_int_int_mask(DEC_INTERRUPT) << 8) | + (TBSCR_TBF | TBSCR_TBE)); + if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0) + panic("Could not allocate timer IRQ!"); + + /* Get time from the RTC. + */ + return ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc; +} + +void +mbx_restart(char *cmd) +{ + extern void MBX_gorom(void); + + MBX_gorom(); +} + +void +mbx_power_off(void) +{ + mbx_restart(NULL); +} + +void +mbx_halt(void) +{ + mbx_restart(NULL) +} + + +int mbx_setup_residual(char *buffer) +{ + int len = 0; + bd_t *bp; + extern RESIDUAL *res; + + bp = (bd_t *)res; + + len += sprintf(len+buffer,"clock\t\t: %dMHz\n" + "bus clock\t: %dMHz\n", + bp->bi_intfreq /*/ 1000000*/, + bp->bi_busfreq /*/ 1000000*/); + + return len; +} + +void +mbx_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int irq; + unsigned long bits = 0; + + /* For MPC8xx, read the SIVEC register and shift the bits down + * to get the irq number. */ + bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec; + irq = bits >> 26; + irq += ppc8xx_pic.irq_offset; + bits = 1UL << irq; + + if (irq < 0) { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + spurious_interrupts++; + } + else { + ppc_irq_dispatch_handler( regs, irq ); + } + +} + +static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int bits, irq; + + /* A bug in the QSpan chip causes it to give us 0xff always + * when doing a character read. So read 32 bits and shift. + * This doesn't seem to return useful values anyway, but + * read it to make sure things are acked. + * -- Cort + */ + irq = (inl(0x508) >> 24)&0xff; + if ( irq != 0xff ) printk("iack %d\n", irq); + + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + outb(0x0C, 0xA0); + irq = inb(0xA0); + irq = (irq&7) + 8; + } + bits = 1UL << irq; + irq += i8259_pic.irq_offset; + ppc_irq_dispatch_handler( regs, irq ); +} + + +/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External + * interrupts can be either edge or level triggered, but there is no + * reason for us to change the EPPC-bug values (it would not work if we did). + */ +__initfunc(void +mbx_init_IRQ(void)) +{ + int i; + + ppc8xx_pic.irq_offset = 16; + for ( i = 16 ; i < 32 ; i++ ) + irq_desc[i].ctl = &ppc8xx_pic; + unmask_irq(CPM_INTERRUPT); + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); + request_irq(ISA_BRIDGE_INT, mbx_i8259_action, 0, "8259 cascade", NULL); + enable_irq(ISA_BRIDGE_INT); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +mbx_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port+_IO_BASE), buf, ns); +} + +void +mbx_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port+_IO_BASE, buf, ns); +} + +int +mbx_ide_default_irq(ide_ioreg_t base) +{ + return 14; +} + +ide_ioreg_t +mbx_ide_default_io_base(int index) +{ + return index; +} + +int +mbx_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return 0 +} + +void +mbx_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ +} + +void +mbx_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ +} + +void +mbx_ide_fix_driveid(struct hd_driveid *id) +{ + ppc_generic_ide_fix_driveid(id); +} + +void __init mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + ide_ioreg_t port = base; + int i = 8; + + while (i--) + *p++ = port++; + *p++ = base + 0x206; + if (irq != NULL) + *irq = 0; +#ifdef ATA_FLASH + base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200); + for (i = 0; i < 8; ++i) + *p++ = base++; + *p = ++base; /* Does not matter */ + if (irq) + *irq = 13; +#endif +} +#endif + +__initfunc(void +mbx_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + + if ( r3 ) + memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + +#ifdef CONFIG_PCI + mbx_setup_pci_ptrs(); +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + /* take care of cmd line */ + if ( r6 ) + { + + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + ppc_md.setup_arch = mbx_setup_arch; + ppc_md.setup_residual = mbx_setup_residual; + ppc_md.get_cpuinfo = NULL; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = mbx_init_IRQ; + ppc_md.do_IRQ = mbx_do_IRQ; + ppc_md.init = NULL; + + ppc_md.restart = mbx_restart; + ppc_md.power_off = mbx_power_off; + ppc_md.halt = mbx_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = mbx_set_rtc_time; + ppc_md.get_rtc_time = mbx_get_rtc_time; + ppc_md.calibrate_decr = mbx_calibrate_decr; + + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = mbx_ide_insw; + ppc_ide_md.outsw = mbx_ide_outsw; + ppc_ide_md.default_irq = mbx_ide_default_irq; + ppc_ide_md.default_io_base = mbx_ide_default_io_base; + ppc_ide_md.check_region = mbx_ide_check_region; + ppc_ide_md.request_region = mbx_ide_request_region; + ppc_ide_md.release_region = mbx_ide_release_region; + ppc_ide_md.fix_driveid = mbx_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = mbx_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif +} diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index e4589b1e0..cbeb4ffce 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -36,6 +36,7 @@ * Returns (address we're running at) - (address we were linked at) * for use before the text and data are mapped to KERNELBASE. */ + _GLOBAL(reloc_offset) mflr r0 bl 1f @@ -72,8 +73,8 @@ _GLOBAL(_enable_interrupts) beqlr /* nothing to do if state == 0 */ _GLOBAL(__sti) _GLOBAL(_hard_sti) - lis r4,n_lost_interrupts@ha - lwz r4,n_lost_interrupts@l(r4) + lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) mfmsr r3 /* Get current state */ ori r3,r3,MSR_EE /* Turn on 'EE' bit */ cmpi 0,r4,0 /* lost interrupts to process first? */ @@ -93,8 +94,8 @@ do_lost_interrupts: stw r0,20(r1) stw r3,8(r1) 1: bl fake_interrupt - lis r4,n_lost_interrupts@ha - lwz r4,n_lost_interrupts@l(r4) + lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) cmpi 0,r4,0 bne- 1b lwz r3,8(r1) @@ -105,11 +106,31 @@ do_lost_interrupts: addi r1,r1,16 blr + +/* + * complement mask on the msr then "or" some values on. + * _nmask_and_or_msr(nmask, value_to_or) + */ + _GLOBAL(_nmask_and_or_msr) + mfmsr r0 /* Get current msr */ + andc r0,r0,r3 /* And off the bits set in r3 (first parm) */ + or r0,r0,r4 /* Or on the bits in r4 (second parm) */ + sync /* Some chip revs have problems here... */ + mtmsr r0 /* Update machine state */ + blr /* Done */ + + /* * Flush MMU TLB */ _GLOBAL(_tlbia) + sync tlbia + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr /* @@ -117,11 +138,17 @@ _GLOBAL(_tlbia) */ _GLOBAL(_tlbie) tlbie r3 + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr + /* * Atomic [test&set] exchange * - * void *xchg_u32(void *ptr, unsigned long val) + * unsigned long xchg_u32(void *ptr, unsigned long val) * Changes the memory location '*ptr' to be val and returns * the previous value stored there. */ @@ -133,6 +160,27 @@ _GLOBAL(xchg_u32) blr /* + * Try to acquire a spinlock. + * Only does the stwcx. if the load returned 0 - the Programming + * Environments Manual suggests not doing unnecessary stcwx.'s + * since they may inhibit forward progress by other CPUs in getting + * a lock. + */ +_GLOBAL(__spin_trylock) + mr r4,r3 + eieio /* prevent reordering of stores */ + li r5,-1 + lwarx r3,0,r4 /* fetch old value, establish reservation */ + cmpwi 0,r3,0 /* is it 0? */ + bnelr- /* return failure if not */ + stwcx. r5,0,r4 /* try to update with new value */ + bne- 1f /* if we failed */ + eieio /* prevent reordering of stores */ + blr +1: li r3,1 /* return non-zero for failure */ + blr + +/* * Atomic add/sub/inc/dec operations * * void atomic_add(int c, int *v) @@ -590,6 +638,16 @@ cvt_df: stfd 0,-4(r5) blr + .globl __clear_msr_me +__clear_msr_me: + mfmsr r0 /* Get current interrupt state */ + lis r3,0 + ori r3,r3,MSR_ME + andc r0,r0,r3 /* Clears bit in (r4) */ + sync /* Some chip revs have problems here */ + mtmsr r0 /* Update machine state */ + blr + /* * Fetch the current SR register * get_SR(int index) @@ -608,6 +666,8 @@ _GLOBAL(__kernel_thread) sc cmpi 0,r3,0 /* parent or child? */ bnelr /* return if parent */ + li r0,0 /* clear out p->tss.regs */ + stw r0,TSS+PT_REGS(r2) /* since we don't have user ctx */ mtlr r4 /* fn addr in lr */ mr r3,r5 /* load arg and call fn */ blrl @@ -836,4 +896,5 @@ sys_call_table: .long sys_sendfile .long sys_ni_syscall /* streams1 */ .long sys_ni_syscall /* streams2 */ + .long sys_vfork .space (NR_syscalls-183)*4 diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index 9b51a6cc1..b66ccffa8 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -29,7 +29,7 @@ int main(void) { - DEFINE(KERNELBASE, KERNELBASE); + /*DEFINE(KERNELBASE, KERNELBASE);*/ DEFINE(STATE, offsetof(struct task_struct, state)); DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); DEFINE(COUNTER, offsetof(struct task_struct, counter)); @@ -48,7 +48,6 @@ main(void) DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched)); DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); - DEFINE(TSS_SMP_FORK_RET, offsetof(struct thread_struct, smp_fork_ret)); /* Interrupt register frame */ DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); diff --git a/arch/ppc/kernel/open_pic.c b/arch/ppc/kernel/open_pic.c new file mode 100644 index 000000000..2ca879dd8 --- /dev/null +++ b/arch/ppc/kernel/open_pic.c @@ -0,0 +1,48 @@ +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/openpic.h> +#include <asm/irq.h> +#include "open_pic.h" +#include "i8259.h" + +#ifdef __SMP__ +void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + smp_message_recv(); +} +#endif /* __SMP__ */ + +void chrp_mask_and_ack_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_pic.mask_and_ack(irq_nr); +} + +static void chrp_mask_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_pic.disable(irq_nr); + else + openpic_disable_irq(irq_to_openpic(irq_nr)); +} + +static void chrp_unmask_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_pic.enable(irq_nr); + else + openpic_enable_irq(irq_to_openpic(irq_nr)); +} + +struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + NULL, + chrp_unmask_irq, + chrp_mask_irq, + chrp_mask_and_ack_irq, + 0 +}; diff --git a/arch/ppc/kernel/open_pic.h b/arch/ppc/kernel/open_pic.h new file mode 100644 index 000000000..77b8a46d0 --- /dev/null +++ b/arch/ppc/kernel/open_pic.h @@ -0,0 +1,11 @@ + +#ifndef _PPC_KERNEL_OPEN_PIC_H +#define _PPC_KERNEL_OPEN_PIC_H + +#include "local_irq.h" + +extern struct hw_interrupt_type open_pic; + +void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); + +#endif /* _PPC_KERNEL_OPEN_PIC_H */ diff --git a/arch/ppc/kernel/openpic.c b/arch/ppc/kernel/openpic.c index ec60ca5a6..f7893dd79 100644 --- a/arch/ppc/kernel/openpic.c +++ b/arch/ppc/kernel/openpic.c @@ -107,7 +107,7 @@ static inline void out_le32(volatile u_int *addr, u_int val) } #endif -static inline u_int openpic_read(volatile u_int *addr) +u_int openpic_read(volatile u_int *addr) { u_int val; @@ -176,8 +176,8 @@ static void openpic_safe_writefield(volatile u_int *addr, u_int mask, __initfunc(void openpic_init(int main_pic)) { u_int t, i; - u_int vendorid, devid, stepping, timerfreq; - const char *version, *vendor, *device; + u_int timerfreq; + const char *version; if (!OpenPIC) panic("No OpenPIC found"); @@ -190,6 +190,9 @@ __initfunc(void openpic_init(int main_pic)) case 2: version = "1.2"; break; + case 3: + version = "1.3"; + break; default: version = "?"; break; @@ -200,32 +203,6 @@ __initfunc(void openpic_init(int main_pic)) OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, NumProcessors, NumSources, OpenPIC); - - t = openpic_read(&OpenPIC->Global.Vendor_Identification); - vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK; - devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >> - OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT; - stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >> - OPENPIC_VENDOR_ID_STEPPING_SHIFT; - switch (vendorid) { - case OPENPIC_VENDOR_ID_APPLE: - vendor = "Apple"; - break; - default: - vendor = "Unknown"; - break; - } - switch (devid) { - case OPENPIC_DEVICE_ID_APPLE_HYDRA: - device = "Hydra"; - break; - default: - device = "Unknown"; - break; - } - printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid, - vendor, devid, device, stepping); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); printk("OpenPIC timer frequency is "); if (timerfreq) diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 1dfa3a6c8..ccef5f36a 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.43 1998/12/29 18:55:11 cort Exp $ + * $Id: pci.c,v 1.54 1999/03/18 04:16:04 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ @@ -21,94 +21,41 @@ #include <asm/irq.h> #include <asm/gg2.h> -unsigned long isa_io_base; -unsigned long isa_mem_base; -unsigned long pci_dram_offset; +#include "pci.h" -unsigned int * pci_config_address; -unsigned char * pci_config_data; - -/* - * It would be nice if we could create a include/asm/pci.h and have just - * function ptrs for all these in there, but that isn't the case. - * We have a function, pcibios_*() which calls the function ptr ptr_pcibios_*() - * which has been setup by pcibios_init(). This is all to avoid a check - * for pmac/prep every time we call one of these. It should also make the move - * to a include/asm/pcibios.h easier, we can drop the ptr_ on these functions - * and create pci.h - * -- Cort - */ -int (*ptr_pcibios_read_config_byte)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val); -int (*ptr_pcibios_read_config_word)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val); -int (*ptr_pcibios_read_config_dword)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val); -int (*ptr_pcibios_write_config_byte)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val); -int (*ptr_pcibios_write_config_word)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val); -int (*ptr_pcibios_write_config_dword)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val); - -#define decl_config_access_method(name) \ -extern int name##_pcibios_read_config_byte(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned char *val); \ -extern int name##_pcibios_read_config_word(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned short *val); \ -extern int name##_pcibios_read_config_dword(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned int *val); \ -extern int name##_pcibios_write_config_byte(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned char val); \ -extern int name##_pcibios_write_config_word(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned short val); \ -extern int name##_pcibios_write_config_dword(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned int val) - -#define set_config_access_method(name) \ - ptr_pcibios_read_config_byte = name##_pcibios_read_config_byte; \ - ptr_pcibios_read_config_word = name##_pcibios_read_config_word; \ - ptr_pcibios_read_config_dword = name##_pcibios_read_config_dword; \ - ptr_pcibios_write_config_byte = name##_pcibios_write_config_byte; \ - ptr_pcibios_write_config_word = name##_pcibios_write_config_word; \ - ptr_pcibios_write_config_dword = name##_pcibios_write_config_dword - -decl_config_access_method(pmac); -decl_config_access_method(grackle); -decl_config_access_method(gg2); -decl_config_access_method(raven); -decl_config_access_method(prep); -decl_config_access_method(mbx); +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { - return ptr_pcibios_read_config_byte(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_byte(bus,dev_fn,offset,val); } int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { - return ptr_pcibios_read_config_word(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_word(bus,dev_fn,offset,val); } int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { - return ptr_pcibios_read_config_dword(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_dword(bus,dev_fn,offset,val); } int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { - return ptr_pcibios_write_config_byte(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_byte(bus,dev_fn,offset,val); } int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { - return ptr_pcibios_write_config_word(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_word(bus,dev_fn,offset,val); } int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { - return ptr_pcibios_write_config_dword(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_dword(bus,dev_fn,offset,val); } int pcibios_present(void) @@ -116,167 +63,46 @@ int pcibios_present(void) return 1; } -__initfunc(void pcibios_init(void)) +void __init pcibios_init(void) { } -__initfunc(void - setup_pci_ptrs(void)) + +void __init pcibios_fixup(void) { -#ifndef CONFIG_MBX - PPC_DEVICE *hostbridge; - switch (_machine) { - case _MACH_prep: - hostbridge=residual_find_device(PROCESSORDEVICE, NULL, - BridgeController, PCIBridge, - -1, 0); - if (hostbridge && - hostbridge->DeviceId.Interface == PCIBridgeIndirect) { - PnP_TAG_PACKET * pkt; - set_config_access_method(raven); - pkt=PnP_find_large_vendor_packet( - res->DevicePnPHeap+hostbridge->AllocatedOffset, - 3, 0); - if(pkt) { -#define p pkt->L4_Pack.L4_Data.L4_PPCPack - pci_config_address= (unsigned *) - ld_le32((unsigned *) p.PPCData); - pci_config_data= (unsigned char *) - ld_le32((unsigned *) (p.PPCData+8)); - } else {/* default values */ - pci_config_address= (unsigned *) 0x80000cf8; - pci_config_data= (unsigned char *) 0x80000cfc; - } - } else { - set_config_access_method(prep); - } - break; - case _MACH_Pmac: - if (find_devices("pci") != 0) { - /* looks like a G3 powermac */ - set_config_access_method(grackle); - } else { - set_config_access_method(pmac); - } - break; - case _MACH_chrp: - if ( !strncmp("MOT", - get_property(find_path_device("/"), "model", NULL),3) ) - { - isa_io_base = 0xfe000000; - set_config_access_method(grackle); - } - else - { - isa_io_base = GG2_ISA_IO_BASE; - set_config_access_method(gg2); - } - break; - default: - printk("setup_pci_ptrs(): unknown machine type!\n"); - } -#else /* CONFIG_MBX */ - set_config_access_method(mbx); -#endif /* CONFIG_MBX */ -#undef set_config_access_method + ppc_md.pcibios_fixup(); } -__initfunc(void pcibios_fixup(void)) +void __init pcibios_fixup_bus(struct pci_bus *bus) { - extern unsigned long route_pci_interrupts(void); - struct pci_dev *dev; - extern struct bridge_data **bridges; - extern unsigned char *Motherboard_map; - extern unsigned char *Motherboard_routes; - unsigned char i; -#ifndef CONFIG_MBX - switch (_machine ) - { - case _MACH_prep: - route_pci_interrupts(); - for(dev=pci_devices; dev; dev=dev->next) - { - /* - * Use our old hard-coded kludge to figure out what - * irq this device uses. This is necessary on things - * without residual data. -- Cort - */ - unsigned char d = PCI_SLOT(dev->devfn); - dev->irq = Motherboard_routes[Motherboard_map[d]]; - for ( i = 0 ; i <= 5 ; i++ ) - { - if ( dev->base_address[i] > 0x10000000 ) - { - printk("Relocating PCI address %x -> %x\n", - dev->base_address[i], - (dev->base_address[i] & 0x00FFFFFF) - | 0x01000000); - dev->base_address[i] = - (dev->base_address[i] & 0x00FFFFFF) | 0x01000000; - pci_write_config_dword(dev, - PCI_BASE_ADDRESS_0+(i*0x4), - dev->base_address[i] ); - } - } -#if 0 - /* - * If we have residual data and if it knows about this - * device ask it what the irq is. - * -- Cort - */ - ppcd = residual_find_device_id( ~0L, dev->device, - -1,-1,-1, 0); -#endif - } - break; - case _MACH_chrp: - /* PCI interrupts are controlled by the OpenPIC */ - for(dev=pci_devices; dev; dev=dev->next) - if (dev->irq) - dev->irq = openpic_to_irq(dev->irq); - break; - case _MACH_Pmac: - for(dev=pci_devices; dev; dev=dev->next) - { - /* - * Open Firmware often doesn't initialize the, - * PCI_INTERRUPT_LINE config register properly, so we - * should find the device node and se if it has an - * AAPL,interrupts property. - */ - struct bridge_data *bp = bridges[dev->bus->number]; - struct device_node *node; - unsigned int *reg; - unsigned char pin; - - if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || - !pin) - continue; /* No interrupt generated -> no fixup */ - for (node = bp->node->child; node != 0; - node = node->sibling) { - reg = (unsigned int *) get_property(node, "reg", 0); - if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) - continue; - /* this is the node, see if it has interrupts */ - if (node->n_intrs > 0) - dev->irq = node->intrs[0].line; - break; - } - } - break; - } -#else /* CONFIG_MBX */ - for(dev=pci_devices; dev; dev=dev->next) - { - } -#endif /* CONFIG_MBX */ } -__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +char __init *pcibios_setup(char *str) { + return str; } -__initfunc(char *pcibios_setup(char *str)) +#ifndef CONFIG_MBX +/* Recursively searches any node that is of type PCI-PCI bridge. Without + * this, the old code would miss children of P2P bridges and hence not + * fix IRQ's for cards located behind P2P bridges. + * - Ranjit Deshpande, 01/20/99 + */ +void __init fix_intr(struct device_node *node, struct pci_dev *dev) { - return str; + unsigned int *reg, *class_code; + + for (; node != 0;node = node->sibling) { + class_code = (unsigned int *) get_property(node, "class-code", 0); + if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) + fix_intr(node->child, dev); + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + dev->irq = node->intrs[0].line; + break; + } } +#endif diff --git a/arch/ppc/kernel/pci.h b/arch/ppc/kernel/pci.h new file mode 100644 index 000000000..231f1d952 --- /dev/null +++ b/arch/ppc/kernel/pci.h @@ -0,0 +1,36 @@ + +#ifndef __PPC_KERNEL_PCI_H__ +#define __PPC_KERNEL_PCI_H__ + +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_dram_offset; + +extern unsigned int *pci_config_address; +extern unsigned char *pci_config_data; + +void fix_intr(struct device_node *node, struct pci_dev *dev); + +#define decl_config_access_method(name) \ +extern int name##_pcibios_read_config_byte(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned char *val); \ +extern int name##_pcibios_read_config_word(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned short *val); \ +extern int name##_pcibios_read_config_dword(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned int *val); \ +extern int name##_pcibios_write_config_byte(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned char val); \ +extern int name##_pcibios_write_config_word(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned short val); \ +extern int name##_pcibios_write_config_dword(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned int val) + +#define set_config_access_method(name) \ + ppc_md.pcibios_read_config_byte = name##_pcibios_read_config_byte; \ + ppc_md.pcibios_read_config_word = name##_pcibios_read_config_word; \ + ppc_md.pcibios_read_config_dword = name##_pcibios_read_config_dword; \ + ppc_md.pcibios_write_config_byte = name##_pcibios_write_config_byte; \ + ppc_md.pcibios_write_config_word = name##_pcibios_write_config_word; \ + ppc_md.pcibios_write_config_dword = name##_pcibios_write_config_dword + +#endif /* __PPC_KERNEL_PCI_H__ */ diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 7763059f7..089169e37 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -21,6 +21,9 @@ #include <asm/pgtable.h> #include <asm/prom.h> #include <asm/pci-bridge.h> +#include <asm/machdep.h> + +#include "pci.h" struct bridge_data **bridges, *bridge_list; static int max_bus; @@ -84,7 +87,12 @@ int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* Bus number once again taken into consideration. + * Change applied from 2.1.24. This makes devices located + * behind PCI-PCI bridges visible. + * -Ranjit Deshpande, 01/20/99 + */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_8(bp->cfg_data + (offset & 3)); @@ -109,7 +117,8 @@ int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); @@ -134,7 +143,8 @@ int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + offset + 1); } udelay(2); *val = in_le32((volatile unsigned int *)bp->cfg_data); @@ -156,7 +166,8 @@ int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_8(bp->cfg_data + (offset & 3), val); @@ -180,7 +191,8 @@ int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); @@ -204,7 +216,8 @@ int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_le32((volatile unsigned int *)bp->cfg_data, val); @@ -429,3 +442,47 @@ __initfunc(static void add_bridges(struct device_node *dev, unsigned long *mem_p } } +__initfunc( +void +pmac_pcibios_fixup(void)) +{ + struct pci_dev *dev; + + /* + * FIXME: This is broken: We should not assign IRQ's to IRQless + * devices (look at PCI_INTERRUPT_PIN) and we also should + * honor the existence of multi-function devices where + * different functions have different interrupt pins. [mj] + */ + for(dev=pci_devices; dev; dev=dev->next) + { + /* + * Open Firmware often doesn't initialize the, + * PCI_INTERRUPT_LINE config register properly, so we + * should find the device node and se if it has an + * AAPL,interrupts property. + */ + struct bridge_data *bp = bridges[dev->bus->number]; + unsigned char pin; + + if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || + !pin) + continue; /* No interrupt generated -> no fixup */ + fix_intr(bp->node->child, dev); + } +} + +__initfunc( +void +pmac_setup_pci_ptrs(void)) +{ + if (find_devices("pci") != 0) { + /* looks like a G3 powermac */ + set_config_access_method(grackle); + } else { + set_config_access_method(pmac); + } + + ppc_md.pcibios_fixup = pmac_pcibios_fixup; +} + diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c new file mode 100644 index 000000000..6a49f1405 --- /dev/null +++ b/arch/ppc/kernel/pmac_pic.c @@ -0,0 +1,362 @@ + +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/prom.h> +#include "pmac_pic.h" + +/* pmac */struct pmac_irq_hw { + unsigned int flag; + unsigned int enable; + unsigned int ack; + unsigned int level; +}; + +/* XXX these addresses should be obtained from the device tree */ +static volatile struct pmac_irq_hw *pmac_irq_hw[4] = { + (struct pmac_irq_hw *) 0xf3000020, + (struct pmac_irq_hw *) 0xf3000010, + (struct pmac_irq_hw *) 0xf4000020, + (struct pmac_irq_hw *) 0xf4000010, +}; + +static int max_irqs; +static int max_real_irqs; + +#define MAXCOUNT 10000000 + +#define GATWICK_IRQ_POOL_SIZE 10 +static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; + +static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((unsigned)irq_nr >= max_irqs) + return; + + clear_bit(irq_nr, ppc_cached_irq_mask); + if (test_and_clear_bit(irq_nr, ppc_lost_interrupts)) + atomic_dec(&ppc_n_lost_interrupts); + out_le32(&pmac_irq_hw[i]->ack, bit); + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + out_le32(&pmac_irq_hw[i]->ack, bit); + do { + /* make sure ack gets to controller before we enable + interrupts */ + mb(); + } while(in_le32(&pmac_irq_hw[i]->flag) & bit); +} + +static void __pmac pmac_set_irq_mask(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((unsigned)irq_nr >= max_irqs) + return; + + /* enable unmasked interrupts */ + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (ppc_cached_irq_mask[i] & bit)); + + /* + * Unfortunately, setting the bit in the enable register + * when the device interrupt is already on *doesn't* set + * the bit in the flag register or request another interrupt. + */ + if ((bit & ppc_cached_irq_mask[i]) + && (ld_le32(&pmac_irq_hw[i]->level) & bit) + && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { + if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) + atomic_inc(&ppc_n_lost_interrupts); + } +} + +static void __pmac pmac_mask_irq(unsigned int irq_nr) +{ + clear_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr); + mb(); +} + +static void __pmac pmac_unmask_irq(unsigned int irq_nr) +{ + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr); +} + +struct hw_interrupt_type pmac_pic = { + " PMAC-PIC ", + NULL, + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + 0 +}; + +struct hw_interrupt_type gatwick_pic = { + " GATWICK ", + NULL, + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + 0 +}; + +static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq, bits; + + for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | ppc_lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + /* The previous version of this code allowed for this case, we + * don't. Put this here to check for it. + * -- Cort + */ + if ( irq_desc[irq].ctl != &gatwick_pic ) + printk("gatwick irq not from gatwick pic\n"); + else + ppc_irq_dispatch_handler( regs, irq ); +} + +void +pmac_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int irq; + unsigned long bits = 0; + +#ifdef __SMP__ + /* IPI's are a hack on the powersurge -- Cort */ + if ( cpu != 0 ) + { + if (!isfake) + { +#ifdef CONFIG_XMON + static int xmon_2nd; + if (xmon_2nd) + xmon(regs); +#endif + smp_message_recv(); + goto out; + } + /* could be here due to a do_fake_interrupt call but we don't + mess with the controller from the second cpu -- Cort */ + goto out; + } + + { + unsigned int loops = MAXCOUNT; + while (test_bit(0, &global_irq_lock)) { + if (smp_processor_id() == global_irq_holder) { + printk("uh oh, interrupt while we hold global irq lock!\n"); +#ifdef CONFIG_XMON + xmon(0); +#endif + break; + } + if (loops-- == 0) { + printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); +#ifdef CONFIG_XMON + xmon(0); +#endif + } + } + } +#endif /* __SMP__ */ + + for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | ppc_lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + + if (irq < 0) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + else + { + ppc_irq_dispatch_handler( regs, irq ); + } +#ifdef CONFIG_SMP +out: +#endif /* CONFIG_SMP */ +} + +/* This routine will fix some missing interrupt values in the device tree + * on the gatwick mac-io controller used by some PowerBooks + */ +static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +{ + struct device_node *node; + int count; + + memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); + node = gw->child; + count = 0; + while(node) + { + /* Fix SCC */ + if (strcasecmp(node->name, "escc") == 0) + if (node->child) { + if (node->child->n_intrs < 3) { + node->child->intrs = &gatwick_int_pool[count]; + count += 3; + } + node->child->n_intrs = 3; + node->child->intrs[0].line = 15+irq_base; + node->child->intrs[1].line = 4+irq_base; + node->child->intrs[2].line = 5+irq_base; + printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", + node->child->intrs[0].line, + node->child->intrs[1].line, + node->child->intrs[2].line); + } + /* Fix media-bay & left SWIM */ + if (strcasecmp(node->name, "media-bay") == 0) { + struct device_node* ya_node; + + if (node->n_intrs == 0) + node->intrs = &gatwick_int_pool[count++]; + node->n_intrs = 1; + node->intrs[0].line = 29+irq_base; + printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", + node->intrs[0].line); + + ya_node = node->child; + while(ya_node) + { + if (strcasecmp(ya_node->name, "floppy") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 19+irq_base; + ya_node->intrs[1].line = 1+irq_base; + printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + if (strcasecmp(ya_node->name, "ata4") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 14+irq_base; + ya_node->intrs[1].line = 3+irq_base; + printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + ya_node = ya_node->sibling; + } + } + node = node->sibling; + } + if (count > 10) { + printk("WARNING !! Gatwick interrupt pool overflow\n"); + printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); + printk(" requested = %d\n", count); + } +} + +__initfunc(void +pmac_pic_init(void)) +{ + int i; + struct device_node *irqctrler; + unsigned long addr; + int second_irq = -999; + + + /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, + others have 32 */ + max_irqs = max_real_irqs = 32; + irqctrler = find_devices("mac-io"); + if (irqctrler) + { + max_real_irqs = 64; + if (irqctrler->next) + max_irqs = 128; + else + max_irqs = 64; + } + for ( i = 0; i < max_real_irqs ; i++ ) + irq_desc[i].ctl = &pmac_pic; + + /* get addresses of first controller */ + if (irqctrler) { + if (irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 0; i < 2; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (2 - i) * 0x10); + } + + /* get addresses of second controller */ + irqctrler = (irqctrler->next) ? irqctrler->next : NULL; + if (irqctrler && irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 2; i < 4; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (4 - i) * 0x10); + } + } + + /* disable all interrupts in all controllers */ + for (i = 0; i * 32 < max_irqs; ++i) + out_le32(&pmac_irq_hw[i]->enable, 0); + + /* get interrupt line of secondary interrupt controller */ + if (irqctrler) { + second_irq = irqctrler->intrs[0].line; + printk(KERN_INFO "irq: secondary controller on irq %d\n", + (int)second_irq); + if (device_is_compatible(irqctrler, "gatwick")) + pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); + for ( i = max_real_irqs ; i < max_irqs ; i++ ) + irq_desc[i].ctl = &gatwick_pic; + request_irq( second_irq, gatwick_action, SA_INTERRUPT, + "gatwick cascade", 0 ); + } + printk("System has %d possible interrupts\n", max_irqs); + if (max_irqs != max_real_irqs) + printk(KERN_DEBUG "%d interrupts on main controller\n", + max_real_irqs); + +#ifdef CONFIG_XMON + request_irq(20, xmon_irq, 0, "NMI - XMON", 0); +#endif /* CONFIG_XMON */ +} diff --git a/arch/ppc/kernel/pmac_pic.h b/arch/ppc/kernel/pmac_pic.h new file mode 100644 index 000000000..335a9ef69 --- /dev/null +++ b/arch/ppc/kernel/pmac_pic.h @@ -0,0 +1,15 @@ +#ifndef _PPC_KERNEL_PMAC_PIC_H +#define _PPC_KERNEL_PMAC_PIC_H + +#include "local_irq.h" + +extern struct hw_interrupt_type pmac_pic; + +void pmac_pic_init(void); +void pmac_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake); + + +#endif /* _PPC_KERNEL_PMAC_PIC_H */ + diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 499e8b6a5..9f8638021 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -44,6 +44,7 @@ #include <asm/prom.h> #include <asm/system.h> #include <asm/pgtable.h> +#include <asm/bitops.h> #include <asm/io.h> #include <asm/pci-bridge.h> #include <asm/adb.h> @@ -52,10 +53,44 @@ #include <asm/ohare.h> #include <asm/mediabay.h> #include <asm/feature.h> +#include <asm/ide.h> +#include <asm/machdep.h> + #include "time.h" +#include "local_irq.h" +#include "pmac_pic.h" + +#undef SHOW_GATWICK_IRQS + +unsigned long pmac_get_rtc_time(void); +int pmac_set_rtc_time(unsigned long nowtime); +void pmac_read_rtc_time(void); +void pmac_calibrate_decr(void); +void pmac_setup_pci_ptrs(void); + +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void mackbd_init_hw(void); +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char mackbd_sysrq_xlate[128]; +#endif /* CONFIG_MAGIC_SYSRQ */ +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); unsigned char drive_info; +int ppc_override_l2cr = 0; +int ppc_override_l2cr_value; + extern char saved_command_line[]; #define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ @@ -132,6 +167,16 @@ pmac_get_cpuinfo(char *buffer) } } + /* Checks "l2cr-value" property in the registry */ + np = find_devices("cpus"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + len += sprintf(buffer+len, "l2cr override\t: 0x%x\n", *l2cr); + } + } + return len; } @@ -210,6 +255,26 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)) *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + /* Checks "l2cr-value" property in the registry */ + if ( (_get_PVR() >> 16) == 8) { + struct device_node *np = find_devices("cpus"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + ppc_override_l2cr = 1; + ppc_override_l2cr_value = *l2cr; + _set_L2CR(0); + _set_L2CR(ppc_override_l2cr_value); + } + } + } + + if (ppc_override_l2cr) + printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n", + ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000) + ? "enabled" : "disabled"); + feature_init(); #ifdef CONFIG_KGDB @@ -258,15 +323,12 @@ int boot_target; int boot_part; kdev_t boot_dev; -void __init powermac_init(void) +__initfunc(void +pmac_init2(void)) { - if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) - return; adb_init(); pmac_nvram_init(); - if (_machine == _MACH_Pmac) { - media_bay_init(); - } + media_bay_init(); } #ifdef CONFIG_SCSI @@ -371,3 +433,174 @@ void note_bootable_part(kdev_t dev, int part) } } +void +pmac_restart(char *cmd) +{ + struct adb_request req; + + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; + + case ADB_VIAPMU: + pmu_restart(); + break; + default: + } +} + +void +pmac_power_off(void) +{ + struct adb_request req; + + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_POWERDOWN); + for (;;) + cuda_poll(); + break; + + case ADB_VIAPMU: + pmu_shutdown(); + break; + default: + } +} + +void +pmac_halt(void) +{ + pmac_power_off(); +} + + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +pmac_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port, buf, ns); +} + +void +pmac_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port, buf, ns); +} + +int +pmac_ide_default_irq(ide_ioreg_t base) +{ + return 0; +} + +ide_ioreg_t +pmac_ide_default_io_base(int index) +{ +#if defined(CONFIG_BLK_DEV_IDE_PMAC) + return pmac_ide_regbase[index]; +#else + return 0; +#endif +} + +int +pmac_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return 0; +} + +void +pmac_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ +} + +void +pmac_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ +} + +/* Convert the shorts/longs in hd_driveid from little to big endian; + * chars are endian independant, of course, but strings need to be flipped. + * (Despite what it says in drivers/block/ide.h, they come up as little + * endian...) + * + * Changes to linux/hdreg.h may require changes here. */ +void +pmac_ide_fix_driveid(struct hd_driveid *id) +{ + ppc_generic_ide_fix_driveid(id); +} + +/* This is declared in drivers/block/ide-pmac.c */ +void pmac_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq); +#endif + +__initfunc(void +pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + pmac_setup_pci_ptrs(); + + /* isa_io_base gets set in pmac_find_bridges */ + isa_mem_base = PMAC_ISA_MEM_BASE; + pci_dram_offset = PMAC_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 1; + DMA_MODE_WRITE = 2; + + ppc_md.setup_arch = pmac_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = pmac_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = pmac_pic_init; + ppc_md.do_IRQ = pmac_do_IRQ; + ppc_md.init = pmac_init2; + + ppc_md.restart = pmac_restart; + ppc_md.power_off = pmac_power_off; + ppc_md.halt = pmac_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = pmac_set_rtc_time; + ppc_md.get_rtc_time = pmac_get_rtc_time; + ppc_md.calibrate_decr = pmac_calibrate_decr; + +#if defined(CONFIG_VT) && defined(CONFIG_MAC_KEYBOARD) + ppc_md.kbd_setkeycode = mackbd_setkeycode; + ppc_md.kbd_getkeycode = mackbd_getkeycode; + ppc_md.kbd_translate = mackbd_translate; + ppc_md.kbd_unexpected_up = mackbd_unexpected_up; + ppc_md.kbd_leds = mackbd_leds; + ppc_md.kbd_init_hw = mackbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = mackbd_sysrq_xlate; +#endif +#endif + +#if defined(CONFIG_BLK_DEV_IDE_PMAC) + ppc_ide_md.insw = pmac_ide_insw; + ppc_ide_md.outsw = pmac_ide_outsw; + ppc_ide_md.default_irq = pmac_ide_default_irq; + ppc_ide_md.default_io_base = pmac_ide_default_io_base; + ppc_ide_md.check_region = pmac_ide_check_region; + ppc_ide_md.request_region = pmac_ide_request_region; + ppc_ide_md.release_region = pmac_ide_release_region; + ppc_ide_md.fix_driveid = pmac_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; + + ppc_ide_md.io_base = 0; +#endif +} + diff --git a/arch/ppc/kernel/ppc8xx_pic.c b/arch/ppc/kernel/ppc8xx_pic.c new file mode 100644 index 000000000..87bc6d0f8 --- /dev/null +++ b/arch/ppc/kernel/ppc8xx_pic.c @@ -0,0 +1,49 @@ + +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/irq.h> +#include <asm/8xx_immap.h> +#include <asm/mbx.h> +#include "ppc8xx_pic.h" + + +static void mbx_mask_irq(unsigned int irq_nr) +{ + if ( irq_nr == ISA_BRIDGE_INT ) return; + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + ppc_cached_irq_mask[0] &= ~(1 << (31-irq_nr)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = ppc_cached_irq_mask[0]; +} + +static void mbx_unmask_irq(unsigned int irq_nr) +{ + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + ppc_cached_irq_mask[0] |= (1 << (31-irq_nr)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = ppc_cached_irq_mask[0]; +} + +static void mbx_mask_and_ack(unsigned int irq_nr) +{ + /* this shouldn't be masked, we mask the 8259 if we need to -- Cort */ + if ( irq_nr != ISA_BRIDGE_INT ) + mbx_mask_irq(irq_nr); + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + /* clear the pending bits */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-irq_nr); +} + +struct hw_interrupt_type ppc8xx_pic = { + " 8xx SIU ", + NULL, + NULL, + NULL, + mbx_unmask_irq, + mbx_mask_irq, + mbx_mask_and_ack, + 0 +}; diff --git a/arch/ppc/kernel/ppc8xx_pic.h b/arch/ppc/kernel/ppc8xx_pic.h new file mode 100644 index 000000000..d6b424fec --- /dev/null +++ b/arch/ppc/kernel/ppc8xx_pic.h @@ -0,0 +1,9 @@ + +#ifndef _PPC_KERNEL_PPC8xx_H +#define _PPC_KERNEL_PPC8xx_H + +#include "local_irq.h" + +extern struct hw_interrupt_type ppc8xx_pic; + +#endif /* _PPC_KERNEL_PPC8xx_H */ diff --git a/arch/ppc/kernel/ppc_defs.h b/arch/ppc/kernel/ppc_defs.h deleted file mode 100644 index 83315a2ae..000000000 --- a/arch/ppc/kernel/ppc_defs.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * WARNING! This file is automatically generated - DO NOT EDIT! - */ -#define KERNELBASE -1073741824 -#define STATE 0 -#define NEXT_TASK 48 -#define COUNTER 24 -#define PROCESSOR 36 -#define SIGPENDING 8 -#define TSS 568 -#define MM 872 -#define TASK_STRUCT_SIZE 912 -#define KSP 0 -#define PG_TABLES 4 -#define PGD 8 -#define LAST_SYSCALL 20 -#define PT_REGS 12 -#define PF_TRACESYS 32 -#define TASK_FLAGS 4 -#define NEED_RESCHED 20 -#define TSS_FPR0 24 -#define TSS_FPSCR 284 -#define TSS_SMP_FORK_RET 288 -#define TASK_UNION_SIZE 8192 -#define STACK_FRAME_OVERHEAD 16 -#define INT_FRAME_SIZE 192 -#define GPR0 16 -#define GPR1 20 -#define GPR2 24 -#define GPR3 28 -#define GPR4 32 -#define GPR5 36 -#define GPR6 40 -#define GPR7 44 -#define GPR8 48 -#define GPR9 52 -#define GPR10 56 -#define GPR11 60 -#define GPR12 64 -#define GPR13 68 -#define GPR14 72 -#define GPR15 76 -#define GPR16 80 -#define GPR17 84 -#define GPR18 88 -#define GPR19 92 -#define GPR20 96 -#define GPR21 100 -#define GPR22 104 -#define GPR23 108 -#define GPR24 112 -#define GPR25 116 -#define GPR26 120 -#define GPR27 124 -#define GPR28 128 -#define GPR29 132 -#define GPR30 136 -#define GPR31 140 -#define _NIP 144 -#define _MSR 148 -#define _CTR 156 -#define _LINK 160 -#define _CCR 168 -#define _XER 164 -#define _DAR 180 -#define _DSISR 184 -#define ORIG_GPR3 152 -#define RESULT 188 -#define TRAP 176 diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 1b7753dd0..834bdf102 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -8,11 +8,12 @@ #include <linux/vt_kern.h> #include <linux/nvram.h> +#include <asm/page.h> #include <asm/semaphore.h> #include <asm/processor.h> #include <asm/uaccess.h> -#include <asm/ide.h> #include <asm/io.h> +#include <asm/ide.h> #include <asm/atomic.h> #include <asm/bitops.h> #include <asm/checksum.h> @@ -26,6 +27,8 @@ #include <asm/irq.h> #include <asm/feature.h> #include <asm/spinlock.h> +#include <asm/dma.h> +#include <asm/machdep.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> @@ -39,13 +42,14 @@ extern void AlignmentException(struct pt_regs *regs); extern void ProgramCheckException(struct pt_regs *regs); extern void SingleStepException(struct pt_regs *regs); extern int sys_sigreturn(struct pt_regs *regs); -extern atomic_t n_lost_interrupts; +extern atomic_t ppc_n_lost_interrupts; extern void do_lost_interrupts(unsigned long); extern int do_signal(sigset_t *, struct pt_regs *); asmlinkage long long __ashrdi3(long long, int); asmlinkage int abs(int); +EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); EXPORT_SYMBOL(transfer_to_handler); @@ -57,16 +61,21 @@ EXPORT_SYMBOL(AlignmentException); EXPORT_SYMBOL(ProgramCheckException); EXPORT_SYMBOL(SingleStepException); EXPORT_SYMBOL(sys_sigreturn); -EXPORT_SYMBOL(n_lost_interrupts); +EXPORT_SYMBOL(ppc_n_lost_interrupts); EXPORT_SYMBOL(do_lost_interrupts); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); -EXPORT_SYMBOL(local_irq_count); -EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(ppc_local_irq_count); +EXPORT_SYMBOL(ppc_local_bh_count); EXPORT_SYMBOL(isa_io_base); EXPORT_SYMBOL(isa_mem_base); EXPORT_SYMBOL(pci_dram_offset); +EXPORT_SYMBOL(ISA_DMA_THRESHOLD); +EXPORT_SYMBOL(DMA_MODE_READ); +EXPORT_SYMBOL(DMA_MODE_WRITE); +EXPORT_SYMBOL(_prep_type); +EXPORT_SYMBOL(ucSystemType); EXPORT_SYMBOL(atomic_add); EXPORT_SYMBOL(atomic_sub); @@ -155,6 +164,7 @@ EXPORT_SYMBOL(_enable_interrupts); EXPORT_SYMBOL(flush_instruction_cache); EXPORT_SYMBOL(_get_PVR); EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(enable_kernel_fp); EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(xchg_u32); #ifdef __SMP__ @@ -171,18 +181,14 @@ EXPORT_SYMBOL(_write_lock); EXPORT_SYMBOL(_write_unlock); #endif -#ifndef CONFIG_MACH_SPECIFIC EXPORT_SYMBOL(_machine); -#endif +EXPORT_SYMBOL(ppc_md); EXPORT_SYMBOL(adb_request); -EXPORT_SYMBOL(adb_autopoll); EXPORT_SYMBOL(adb_register); EXPORT_SYMBOL(cuda_request); -EXPORT_SYMBOL(cuda_send_request); EXPORT_SYMBOL(cuda_poll); EXPORT_SYMBOL(pmu_request); -EXPORT_SYMBOL(pmu_send_request); EXPORT_SYMBOL(pmu_poll); #ifdef CONFIG_PMAC_PBOOK EXPORT_SYMBOL(sleep_notifier_list); @@ -194,7 +200,6 @@ EXPORT_SYMBOL(find_compatible_devices); EXPORT_SYMBOL(find_path_device); EXPORT_SYMBOL(find_phandle); EXPORT_SYMBOL(get_property); -EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); EXPORT_SYMBOL(feature_set); @@ -209,9 +214,8 @@ EXPORT_SYMBOL(nvram_read_byte); EXPORT_SYMBOL(nvram_write_byte); #endif /* CONFIG_PMAC */ -#ifdef CONFIG_SOUND_MODULE EXPORT_SYMBOL(abs); -#endif +EXPORT_SYMBOL(device_is_compatible); /* The following are special because they're not called explicitly (the C compiler generates them). Fortunately, diff --git a/arch/ppc/kernel/prep_nvram.c b/arch/ppc/kernel/prep_nvram.c new file mode 100644 index 000000000..e69563c8a --- /dev/null +++ b/arch/ppc/kernel/prep_nvram.c @@ -0,0 +1,173 @@ +/* + * linux/arch/ppc/kernel/prep_nvram.c + * + * Copyright (C) 1998 Corey Minyard + * + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/ioport.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/machdep.h> +#include <asm/prep_nvram.h> + +/* + * Allow for a maximum of 32K of PReP NvRAM data + */ +#define MAX_PREP_NVRAM 0x8000 +static char nvramData[MAX_PREP_NVRAM]; +static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0]; + +#define PREP_NVRAM_AS0 0x74 +#define PREP_NVRAM_AS1 0x75 +#define PREP_NVRAM_DATA 0x77 + +unsigned char *rs_pcNvRAM; + +unsigned char prep_nvram_read_val(int addr) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + return inb(PREP_NVRAM_DATA); +} + +void prep_nvram_write_val(int addr, + unsigned char val) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + outb(val, PREP_NVRAM_DATA); +} + +/* + * Most Radstone boards have NvRAM memory mapped at offset 8M in ISA space + */ +unsigned char rs_nvram_read_val(int addr) +{ + return rs_pcNvRAM[addr]; +} + +void rs_nvram_write_val(int addr, + unsigned char val) +{ + rs_pcNvRAM[addr]=val; +} + +__initfunc(void init_prep_nvram(void)) +{ + unsigned char *nvp; + int i; + int nvramSize; + + /* + * I'm making the assumption that 32k will always cover the + * nvramsize. If this isn't the case please let me know and we can + * map the header, then get the size from the header, then map + * the whole size. -- Cort + */ + if ( _prep_type == _PREP_Radstone ) + rs_pcNvRAM = (unsigned char *)ioremap(_ISA_MEM_BASE+0x00800000, + 32<<10); + request_region(PREP_NVRAM_AS0, 0x8, "PReP NVRAM"); + /* + * The following could fail if the NvRAM were corrupt but + * we expect the boot firmware to have checked its checksum + * before boot + */ + nvp = (char *) &nvram->Header; + for (i=0; i<sizeof(HEADER); i++) + { + *nvp = ppc_md.nvram_read_val(i); + nvp++; + } + + /* + * The PReP NvRAM may be any size so read in the header to + * determine how much we must read in order to get the complete + * GE area + */ + nvramSize=(int)nvram->Header.GEAddress+nvram->Header.GELength; + if(nvramSize>MAX_PREP_NVRAM) + { + /* + * NvRAM is too large + */ + nvram->Header.GELength=0; + return; + } + + /* + * Read the remainder of the PReP NvRAM + */ + nvp = (char *) &nvram->GEArea[0]; + for (i=sizeof(HEADER); i<nvramSize; i++) + { + *nvp = ppc_md.nvram_read_val(i); + nvp++; + } +} + +__prep +char *prep_nvram_get_var(const char *name) +{ + char *cp; + int namelen; + + namelen = strlen(name); + cp = prep_nvram_first_var(); + while (cp != NULL) { + if ((strncmp(name, cp, namelen) == 0) + && (cp[namelen] == '=')) + { + return cp+namelen+1; + } + cp = prep_nvram_next_var(cp); + } + + return NULL; +} + +__prep +char *prep_nvram_first_var(void) +{ + if (nvram->Header.GELength == 0) { + return NULL; + } else { + return (((char *)nvram) + + ((unsigned int) nvram->Header.GEAddress)); + } +} + +__prep +char *prep_nvram_next_var(char *name) +{ + char *cp; + + + cp = name; + while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) + && (*cp != '\0')) + { + cp++; + } + + /* Skip over any null characters. */ + while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) + && (*cp == '\0')) + { + cp++; + } + + if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) { + return cp; + } else { + return NULL; + } +} + + + diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index b48f7a1fc..dd7f1eee1 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.24 1998/12/10 02:39:51 cort Exp $ + * $Id: prep_pci.c,v 1.33 1999/05/09 20:15:54 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -11,11 +11,19 @@ #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/openpic.h> #include <asm/byteorder.h> #include <asm/io.h> #include <asm/ptrace.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <asm/residual.h> #include <asm/processor.h> +#include <asm/irq.h> +#include <asm/machdep.h> + +#include "pci.h" #define MAX_DEVNR 22 @@ -27,6 +35,9 @@ unsigned char *Motherboard_map_name; /* How is the 82378 PIRQ mapping setup? */ unsigned char *Motherboard_routes; +/* Used for Motorola to store system config register */ +static unsigned long *ProcInfo; + /* Tables for known hardware */ /* Motorola PowerStackII - Utah */ @@ -34,38 +45,39 @@ static char Utah_pci_IRQ_map[23] __prepdata = { 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ - 4, /* Slot 2 - SCSI - NCR825A */ + 5, /* Slot 2 - SCSI - NCR825A */ 0, /* Slot 3 - unused */ 1, /* Slot 4 - Ethernet - DEC2114x */ 0, /* Slot 5 - unused */ - 2, /* Slot 6 - PCI Card slot #1 */ - 3, /* Slot 7 - PCI Card slot #2 */ - 4, /* Slot 8 - PCI Card slot #3 */ - 4, /* Slot 9 - PCI Bridge */ + 3, /* Slot 6 - PCI Card slot #1 */ + 4, /* Slot 7 - PCI Card slot #2 */ + 5, /* Slot 8 - PCI Card slot #3 */ + 5, /* Slot 9 - PCI Bridge */ /* added here in case we ever support PCI bridges */ /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */ 0, /* Slot 10 - unused */ 0, /* Slot 11 - unused */ - 4, /* Slot 12 - SCSI - NCR825A */ + 5, /* Slot 12 - SCSI - NCR825A */ 0, /* Slot 13 - unused */ - 2, /* Slot 14 - enet */ + 3, /* Slot 14 - enet */ 0, /* Slot 15 - unused */ - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 2, /* Slot 16 - unused */ + 3, /* Slot 17 - unused */ + 5, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ }; static char Utah_pci_IRQ_routes[] __prepdata = { 0, /* Line 0 - Unused */ 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15, /* Line 4 */ + 10, /* Line 2 */ + 11, /* Line 3 */ + 14, /* Line 4 */ + 15, /* Line 5 */ }; /* Motorola PowerStackII - Omaha */ @@ -125,9 +137,9 @@ static char Blackhawk_pci_IRQ_map[19] __prepdata = 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ - 1, /* Slot P7 */ - 2, /* Slot P6 */ - 3, /* Slot P5 */ + 1, /* Slot P7 */ + 2, /* Slot P6 */ + 3, /* Slot P5 */ }; static char Blackhawk_pci_IRQ_routes[] __prepdata = @@ -139,6 +151,122 @@ static char Blackhawk_pci_IRQ_routes[] __prepdata = 15 /* Line 4 */ }; +/* Motorola Mesquite */ +static char Mesquite_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unxued */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 3, /* Slot 16 - PMC */ + 0, /* Slot 17 - unused */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola Sitka */ +static char Sitka_pci_IRQ_map[21] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unxued */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PMC 1 */ + 12, /* Slot 17 - PMC 2 */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 4, /* Slot 20 - NT P2P bridge */ +}; + +/* Motorola MTX */ +static char MTX_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI/PMC slot 1 */ + 10, /* Slot 17 - PCI/PMC slot 2 */ + 11, /* Slot 18 - PCI slot 3 */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola MTX Plus */ +/* Secondary bus interrupt routing is not supported yet */ +static char MTXplus_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet 1 */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI slot 1P */ + 10, /* Slot 17 - PCI slot 2P */ + 11, /* Slot 18 - PCI slot 3P */ + 10, /* Slot 19 - Ethernet 2 */ + 0, /* Slot 20 - P2P Bridge */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +static char Raven_pci_IRQ_routes[] __prepdata = +{ + 0, /* This is a dummy structure */ +}; + /* Motorola MVME16xx */ static char Genesis_pci_IRQ_map[16] __prepdata = { @@ -169,8 +297,35 @@ static char Genesis_pci_IRQ_routes[] __prepdata = 15 /* Line 4 */ }; +static char Genesis2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - Ethernet */ + 0, /* Slot 11 - Universe PCI - VME Bridge */ + 3, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - SCSI */ + 0, /* Slot 15 - graphics on 3600 */ + 9, /* Slot 16 - PMC */ + 12, /* Slot 17 - pci */ + 11, /* Slot 18 - pci */ + 10, /* Slot 19 - pci */ + 0, /* Slot 20 - pci */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + /* Motorola Series-E */ -static char Comet_pci_IRQ_map[16] __prepdata = +static char Comet_pci_IRQ_map[23] __prepdata = { 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ @@ -188,6 +343,13 @@ static char Comet_pci_IRQ_map[16] __prepdata = 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, }; static char Comet_pci_IRQ_routes[] __prepdata = @@ -199,6 +361,43 @@ static char Comet_pci_IRQ_routes[] __prepdata = 15 /* Line 4 */ }; +/* Motorola Series-EX */ +static char Comet2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 1, /* Slot 4 - Ethernet - DEC2104X */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI bridge */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet - DEC2104X */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, +}; + +static char Comet2_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15, /* Line 4 */ +}; + /* * ibm 830 (and 850?). * This is actually based on the Carolina motherboard @@ -312,22 +511,40 @@ static char Nobis_pci_IRQ_routes[] __prepdata = { #define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */ #define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */ +/* + * 8259 edge/level control definitions + */ +#define ISA8259_M_ELCR 0x4d0 +#define ISA8259_S_ELCR 0x4d1 + +#define ELCRS_INT15_LVL 0x80 +#define ELCRS_INT14_LVL 0x40 +#define ELCRS_INT12_LVL 0x10 +#define ELCRS_INT11_LVL 0x08 +#define ELCRS_INT10_LVL 0x04 +#define ELCRS_INT9_LVL 0x02 +#define ELCRS_INT8_LVL 0x01 +#define ELCRM_INT7_LVL 0x80 +#define ELCRM_INT5_LVL 0x20 + +#define CFGPTR(dev) (0x80800000 | (1<<(dev>>3)) | ((dev&7)<<8) | offset) +#define DEVNO(dev) (dev>>3) + __prep int prep_pcibios_read_config_dword (unsigned char bus, unsigned char dev, unsigned char offset, unsigned int *val) { - unsigned long _val; + unsigned long _val; unsigned long *ptr; - dev >>= 3; - - if ((bus != 0) || (dev > MAX_DEVNR)) - { + + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) + { *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else + return PCIBIOS_DEVICE_NOT_FOUND; + } else { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned long *)CFGPTR(dev); _val = le32_to_cpu(*ptr); } *val = _val; @@ -339,16 +556,16 @@ int prep_pcibios_read_config_word (unsigned char bus, unsigned char dev, unsigned char offset, unsigned short *val) { - unsigned short _val; + unsigned short _val; unsigned short *ptr; - dev >>= 3; - if ((bus != 0) || (dev > MAX_DEVNR)) - { - *val = (unsigned short)0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else + + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) + { + *val = 0xFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned short *)CFGPTR(dev); _val = le16_to_cpu(*ptr); } *val = _val; @@ -360,16 +577,16 @@ int prep_pcibios_read_config_byte (unsigned char bus, unsigned char dev, unsigned char offset, unsigned char *val) { - unsigned char _val; - volatile unsigned char *ptr; - dev >>= 3; - if ((bus != 0) || (dev > MAX_DEVNR)) - { - *(unsigned long *)val = (unsigned long) 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else + unsigned char _val; + unsigned char *ptr; + + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) + { + *val = 0xFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)); + ptr = (unsigned char *)CFGPTR(dev); _val = *ptr; } *val = _val; @@ -383,14 +600,14 @@ prep_pcibios_write_config_dword (unsigned char bus, { unsigned long _val; unsigned long *ptr; - dev >>= 3; + _val = le32_to_cpu(val); - if ((bus != 0) || (dev > MAX_DEVNR)) + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) { return PCIBIOS_DEVICE_NOT_FOUND; } else { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned long *)CFGPTR(dev); *ptr = _val; } return PCIBIOS_SUCCESSFUL; @@ -403,14 +620,14 @@ prep_pcibios_write_config_word (unsigned char bus, { unsigned short _val; unsigned short *ptr; - dev >>= 3; + _val = le16_to_cpu(val); - if ((bus != 0) || (dev > MAX_DEVNR)) + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) { return PCIBIOS_DEVICE_NOT_FOUND; } else { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned short *)CFGPTR(dev); *ptr = _val; } return PCIBIOS_SUCCESSFUL; @@ -423,20 +640,151 @@ prep_pcibios_write_config_byte (unsigned char bus, { unsigned char _val; unsigned char *ptr; - dev >>= 3; + _val = val; - if ((bus != 0) || (dev > MAX_DEVNR)) + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) { return PCIBIOS_DEVICE_NOT_FOUND; } else { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset^1)); + ptr = (unsigned char *)CFGPTR(dev); *ptr = _val; } return PCIBIOS_SUCCESSFUL; } -__initfunc(unsigned long route_pci_interrupts(void)) +#define MOTOROLA_CPUTYPE_REG 0x800 +#define MOTOROLA_BASETYPE_REG 0x803 +#define MPIC_RAVEN_ID 0x48010000 +#define MPIC_HAWK_ID 0x48030000 +#define MOT_PROC2_BIT 0x800 + +static u_char mvme2600_openpic_initsenses[] __initdata = { + 1, /* MVME2600_INT_SIO */ + 0, /* MVME2600_INT_FALCN_ECC_ERR */ + 1, /* MVME2600_INT_PCI_ETHERNET */ + 1, /* MVME2600_INT_PCI_SCSI */ + 1, /* MVME2600_INT_PCI_GRAPHICS */ + 1, /* MVME2600_INT_PCI_VME0 */ + 1, /* MVME2600_INT_PCI_VME1 */ + 1, /* MVME2600_INT_PCI_VME2 */ + 1, /* MVME2600_INT_PCI_VME3 */ + 1, /* MVME2600_INT_PCI_INTA */ + 1, /* MVME2600_INT_PCI_INTB */ + 1, /* MVME2600_INT_PCI_INTC */ + 1, /* MVME2600_INT_PCI_INTD */ + 1, /* MVME2600_INT_LM_SIG0 */ + 1, /* MVME2600_INT_LM_SIG1 */ +}; + +#define MOT_RAVEN_PRESENT 0x1 +#define MOT_HAWK_PRESENT 0x2 + +int prep_keybd_present = 1; +int MotMPIC = 0; + +__initfunc(int raven_init(void)) +{ + unsigned int devid; + unsigned int pci_membase; + unsigned char base_mod; + + /* Check to see if the Raven chip exists. */ + if ( _prep_type != _PREP_Motorola) { + OpenPIC = NULL; + return 0; + } + + /* Check to see if this board is a type that might have a Raven. */ + if ((inb(MOTOROLA_CPUTYPE_REG) & 0xF0) != 0xE0) { + OpenPIC = NULL; + return 0; + } + + /* Check the first PCI device to see if it is a Raven. */ + pcibios_read_config_dword(0, 0, PCI_VENDOR_ID, &devid); + + switch (devid & 0xffff0000) { + case MPIC_RAVEN_ID: + MotMPIC = MOT_RAVEN_PRESENT; + break; + case MPIC_HAWK_ID: + MotMPIC = MOT_HAWK_PRESENT; + break; + default: + OpenPIC = NULL; + return 0; + } + + + /* Read the memory base register. */ + pcibios_read_config_dword(0, 0, PCI_BASE_ADDRESS_1, &pci_membase); + + if (pci_membase == 0) { + OpenPIC = NULL; + return 0; + } + + /* Map the Raven MPIC registers to virtual memory. */ + OpenPIC = (struct OpenPIC *)ioremap(pci_membase+0xC0000000, 0x22000); + + OpenPIC_InitSenses = mvme2600_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses); + + /* If raven is present on Motorola store the system config register + * for later use. + */ + ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + + /* This is a hack. If this is a 2300 or 2400 mot board then there is + * no keyboard controller and we have to indicate that. + */ + base_mod = inb(MOTOROLA_BASETYPE_REG); + if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || + (base_mod == 0xFA) || (base_mod == 0xE1)) + prep_keybd_present = 0; + + return 1; +} + +struct mot_info { + int cpu_type; /* 0x100 mask assumes for Raven and Hawk boards that the level/edge are set */ + /* 0x200 if this board has a Hawk chip. */ + int base_type; + int max_cpu; /* ored with 0x80 if this board should be checked for multi CPU */ + const char *name; + unsigned char *map; + unsigned char *routes; +} mot_info[] = { + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes}, + {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes}, + {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes}, + {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes}, + {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes}, + {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x000, 0x00, 0x00, "", NULL, NULL} +}; + +__initfunc(unsigned long prep_route_pci_interrupts(void)) { unsigned char *ibc_pirq = (unsigned char *)0x80800860; unsigned char *ibc_pcicon = (unsigned char *)0x80800840; @@ -445,49 +793,66 @@ __initfunc(unsigned long route_pci_interrupts(void)) if ( _prep_type == _PREP_Motorola) { unsigned short irq_mode; + unsigned char cpu_type; + unsigned char base_mod; + int entry; + int mot_entry = -1; - switch (inb(0x800) & 0xF0) - { - case 0x10: /* MVME16xx */ - Motherboard_map_name = "Genesis"; - Motherboard_map = Genesis_pci_IRQ_map; - Motherboard_routes = Genesis_pci_IRQ_routes; - break; - case 0x20: /* Series E */ - Motherboard_map_name = "Powerstack (Series E)"; - Motherboard_map = Comet_pci_IRQ_map; - Motherboard_routes = Comet_pci_IRQ_routes; - break; - case 0x50: /* PowerStackII Pro3000 */ - Motherboard_map_name = "Omaha (PowerStack II Pro3000)"; - Motherboard_map = Omaha_pci_IRQ_map; - Motherboard_routes = Omaha_pci_IRQ_routes; - break; - case 0x60: /* PowerStackII Pro4000 */ - Motherboard_map_name = "Utah (Powerstack II Pro4000)"; - Motherboard_map = Utah_pci_IRQ_map; - Motherboard_routes = Utah_pci_IRQ_routes; - break; - case 0xE0: /* MTX -- close enough?? to Genesis, so reuse it */ - Motherboard_map_name = "Motorola MTX"; - Motherboard_map = Genesis_pci_IRQ_map; - Motherboard_routes = Genesis_pci_IRQ_routes; - break; - case 0x40: /* PowerStack */ - default: /* Can't hurt, can it? */ - Motherboard_map_name = "Blackhawk (Powerstack)"; - Motherboard_map = Blackhawk_pci_IRQ_map; - Motherboard_routes = Blackhawk_pci_IRQ_routes; - break; + cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; + base_mod = inb(MOTOROLA_BASETYPE_REG); + + for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { + if (mot_info[entry].cpu_type & 0x200) { /* Check for Hawk chip */ + if (!(MotMPIC & MOT_HAWK_PRESENT)) + continue; + } else { /* Check non hawk boards */ + if ((mot_info[entry].cpu_type & 0xff) != cpu_type) + continue; + + if (mot_info[entry].base_type == 0) { + mot_entry = entry; + break; + } + + if (mot_info[entry].base_type != base_mod) + continue; + } + + if (!(mot_info[entry].max_cpu & 0x80)) { + mot_entry = entry; + break; + } + + /* processor 1 not present and max processor zero indicated */ + if ((*ProcInfo & MOT_PROC2_BIT) && !(mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* processor 1 present and max processor zero indicated */ + if (!(*ProcInfo & MOT_PROC2_BIT) && (mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } } - /* AJF adjust level/edge control according to routes */ - irq_mode = 0; - for (i = 1; i <= 4; i++) - { - irq_mode |= ( 1 << Motherboard_routes[i] ); + + if (mot_entry == -1) /* No particular cpu type found - assume Blackhawk */ + mot_entry = 3; + + Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; + Motherboard_map = mot_info[mot_entry].map; + Motherboard_routes = mot_info[mot_entry].routes; + + if (!(mot_info[entry].cpu_type & 0x100)) { + /* AJF adjust level/edge control according to routes */ + irq_mode = 0; + for (i = 1; i <= 4; i++) + { + irq_mode |= ( 1 << Motherboard_routes[i] ); + } + outb( irq_mode & 0xff, 0x4d0 ); + outb( (irq_mode >> 8) & 0xff, 0x4d1 ); } - outb( irq_mode & 0xff, 0x4d0 ); - outb( (irq_mode >> 8) & 0xff, 0x4d1 ); } else if ( _prep_type == _PREP_IBM ) { unsigned char pl_id; @@ -526,6 +891,71 @@ __initfunc(unsigned long route_pci_interrupts(void)) outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); pl_id=inb(0x04d1); /*printk("Hi mask now %#0x\n", pl_id);*/ + } else if ( _prep_type == _PREP_Radstone ) + { + unsigned char ucElcrM, ucElcrS; + + /* + * Set up edge/level + */ + switch(ucSystemType) + { + case RS_SYS_TYPE_PPC1: + { + if(ucBoardRevMaj<5) + { + ucElcrS=ELCRS_INT15_LVL; + } + else + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + } + ucElcrM=ELCRM_INT5_LVL | ELCRM_INT7_LVL; + break; + } + + case RS_SYS_TYPE_PPC1a: + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + ucElcrM=ELCRM_INT5_LVL; + break; + } + + case RS_SYS_TYPE_PPC2: + case RS_SYS_TYPE_PPC2a: + case RS_SYS_TYPE_PPC2ep: + case RS_SYS_TYPE_PPC4: + case RS_SYS_TYPE_PPC4a: + default: + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT10_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + ucElcrM=ELCRM_INT5_LVL | + ELCRM_INT7_LVL; + break; + } + } + + /* + * Write edge/level selection + */ + outb(ucElcrS, ISA8259_S_ELCR); + outb(ucElcrM, ISA8259_M_ELCR); + + /* + * Radstone boards have PCI interrupts all set up + * so leave well alone + */ + return 0; } else { printk("No known machine pci routing!\n"); @@ -542,3 +972,117 @@ __initfunc(unsigned long route_pci_interrupts(void)) return 0; } +__initfunc( +void +prep_pcibios_fixup(void)) +{ + struct pci_dev *dev; + extern unsigned char *Motherboard_map; + extern unsigned char *Motherboard_routes; + unsigned char i; + + if ( _prep_type == _PREP_Radstone ) + { + printk("Radstone boards require no PCI fixups\n"); + return; + } + + prep_route_pci_interrupts(); + + printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); + if (OpenPIC) { + /* PCI interrupts are controlled by the OpenPIC */ + for(dev=pci_devices; dev; dev=dev->next) { + if (dev->bus->number == 0) { + dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); + pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq); + } + } + return; + } + + for(dev=pci_devices; dev; dev=dev->next) + { + /* + * Use our old hard-coded kludge to figure out what + * irq this device uses. This is necessary on things + * without residual data. -- Cort + */ + unsigned char d = PCI_SLOT(dev->devfn); + dev->irq = Motherboard_routes[Motherboard_map[d]]; + + for ( i = 0 ; i <= 5 ; i++ ) + { + if ( dev->base_address[i] > 0x10000000 ) + { + printk("Relocating PCI address %lx -> %lx\n", + dev->base_address[i], + (dev->base_address[i] & 0x00FFFFFF) + | 0x01000000); + dev->base_address[i] = + (dev->base_address[i] & 0x00FFFFFF) | 0x01000000; + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0+(i*0x4), + dev->base_address[i] ); + } + } +#if 0 + /* + * If we have residual data and if it knows about this + * device ask it what the irq is. + * -- Cort + */ + ppcd = residual_find_device_id( ~0L, dev->device, + -1,-1,-1, 0); +#endif + } +} + +decl_config_access_method(indirect); + +__initfunc( +void +prep_setup_pci_ptrs(void)) +{ + PPC_DEVICE *hostbridge; + + printk("PReP architecture\n"); + if ( _prep_type == _PREP_Radstone ) + { + pci_config_address = (unsigned *)0x80000cf8; + pci_config_data = (char *)0x80000cfc; + set_config_access_method(indirect); + } + else + { + hostbridge = residual_find_device(PROCESSORDEVICE, NULL, + BridgeController, PCIBridge, -1, 0); + if (hostbridge && + hostbridge->DeviceId.Interface == PCIBridgeIndirect) { + PnP_TAG_PACKET * pkt; + set_config_access_method(indirect); + pkt = PnP_find_large_vendor_packet( + res->DevicePnPHeap+hostbridge->AllocatedOffset, + 3, 0); + if(pkt) + { +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + pci_config_address= (unsigned *)ld_le32((unsigned *) p.PPCData); + pci_config_data= (unsigned char *)ld_le32((unsigned *) (p.PPCData+8)); + } + else + { + pci_config_address= (unsigned *) 0x80000cf8; + pci_config_data= (unsigned char *) 0x80000cfc; + } + } + else + { + set_config_access_method(prep); + } + + } + + ppc_md.pcibios_fixup = prep_pcibios_fixup; +} + diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index 72752e11f..de18f465a 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -30,6 +30,9 @@ #include <linux/blk.h> #include <linux/ioport.h> #include <linux/console.h> +#include <linux/timex.h> +#include <linux/pci.h> +#include <linux/openpic.h> #include <asm/mmu.h> #include <asm/processor.h> @@ -38,12 +41,57 @@ #include <asm/pgtable.h> #include <asm/ide.h> #include <asm/cache.h> +#include <asm/dma.h> +#include <asm/machdep.h> +#include <asm/mk48t59.h> +#include <asm/prep_nvram.h> +#include <asm/raven.h> + + +#include "time.h" +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" #if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) #include <../drivers/sound/sound_config.h> #include <../drivers/sound/dev_table.h> #endif +unsigned char ucSystemType; +unsigned char ucBoardRev; +unsigned char ucBoardRevMaj, ucBoardRevMin; + +extern unsigned long mc146818_get_rtc_time(void); +extern int mc146818_set_rtc_time(unsigned long nowtime); +extern unsigned long mk48t59_get_rtc_time(void); +extern int mk48t59_set_rtc_time(unsigned long nowtime); + +extern unsigned char prep_nvram_read_val(int addr); +extern void prep_nvram_write_val(int addr, + unsigned char val); +extern unsigned char rs_nvram_read_val(int addr); +extern void rs_nvram_write_val(int addr, + unsigned char val); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; + +extern void prep_setup_pci_ptrs(void); +extern void chrp_do_IRQ(struct pt_regs *regs, int cpu, int isfake); +extern char saved_command_line[256]; + +int _prep_type; + +#define cached_21 (((char *)(ppc_cached_irq_mask))[3]) +#define cached_A1 (((char *)(ppc_cached_irq_mask))[2]) + /* for the mac fs */ kdev_t boot_dev; /* used in nasty hack for sound - see prep_setup_arch() -- Cort */ @@ -54,13 +102,15 @@ extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; extern unsigned long loops_per_sec; -extern unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ extern int rd_image_start; /* starting block # of image */ #endif +#ifdef CONFIG_VGA_CONSOLE +unsigned long vgacon_remap_base; +#endif __prep int @@ -136,6 +186,8 @@ prep_get_cpuinfo(char *buffer) break; } break; + default: + break; } @@ -163,11 +215,12 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern char cmd_line[]; unsigned char reg; + unsigned char ucMothMemType; + unsigned char ucEquipPres1; /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - aux_device_present = 0xaa; /* Set up floppy in PS/2 mode */ outb(0x09, SIO_CONFIG_RA); reg = inb(SIO_CONFIG_RD); @@ -175,20 +228,78 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) outb(reg, SIO_CONFIG_RD); outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + /* + * We need to set up the NvRAM access routines early as prep_init + * has yet to be called + */ + ppc_md.nvram_read_val = prep_nvram_read_val; + ppc_md.nvram_write_val = prep_nvram_write_val; + /* we should determine this according to what we find! -- Cort */ switch ( _prep_type ) { case _PREP_IBM: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ break; case _PREP_Motorola: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ break; + case _PREP_Radstone: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + + /* + * Determine system type + */ + ucMothMemType=inb(0x866); + ucEquipPres1=inb(0x80c); + + ucSystemType=((ucMothMemType&0x03)<<1) | + ((ucEquipPres1&0x80)>>7); + ucSystemType^=7; + + /* + * Determine board revision for use by + * rev. specific code + */ + ucBoardRev=inb(0x854); + ucBoardRevMaj=ucBoardRev>>5; + ucBoardRevMin=ucBoardRev&0x1f; + + /* + * Most Radstone boards have memory mapped NvRAM + */ + if((ucSystemType==RS_SYS_TYPE_PPC1) && (ucBoardRevMaj<5)) + { + ppc_md.nvram_read_val = prep_nvram_read_val; + ppc_md.nvram_write_val = prep_nvram_write_val; + } + else + { + ppc_md.nvram_read_val = rs_nvram_read_val; + ppc_md.nvram_write_val = rs_nvram_write_val; + } + break; } - /* Enable L2. Assume we don't need to flush -- Cort*/ - *(unsigned char *)(0x8000081c) = *(unsigned char *)(0x8000081c)|3; - + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } + printk("Boot arguments: %s\n", cmd_line); #ifdef CONFIG_SOUND_CS4232 @@ -238,12 +349,349 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); + raven_init(); + #ifdef CONFIG_VGA_CONSOLE + /* remap the VGA memory */ + vgacon_remap_base = 0xf0000000; + /*vgacon_remap_base = ioremap(0xc0000000, 0xba000);*/ conswitchp = &vga_con; #endif } -__initfunc(void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) +/* + * Determine the decrementer frequency from the residual data + * This allows for a faster boot as we do not need to calibrate the + * decrementer against another clock. This is important for embedded systems. + */ +__initfunc(void prep_res_calibrate_decr(void)) +{ + int freq, divisor; + + freq = res->VitalProductData.ProcessorBusHz; + divisor = 4; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + +/* + * Uses the on-board timer to calibrate the on-chip decrementer register + * for prep systems. On the pmac the OF tells us what the frequency is + * but on prep we have to figure it out. + * -- Cort + */ +int calibrate_done = 0; +volatile int *done_ptr = &calibrate_done; + +__initfunc(void +prep_calibrate_decr_handler(int irq, + void *dev, + struct pt_regs *regs)) +{ + unsigned long freq, divisor; + static unsigned long t1 = 0, t2 = 0; + + if ( !t1 ) + t1 = get_dec(); + else if (!t2) + { + t2 = get_dec(); + t2 = t1-t2; /* decr's in 1/HZ */ + t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ + freq = t2 * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", + freq, divisor,t2>>20); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + *done_ptr = 1; + } +} + +__initfunc(void prep_calibrate_decr(void)) +{ + unsigned long flags; + + + save_flags(flags); + +#define TIMER0_COUNT 0x40 +#define TIMER_CONTROL 0x43 + /* set timer to periodic mode */ + outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ + /* set the clock to ~100 Hz */ + outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ + outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ + + if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); + __sti(); + while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */ + restore_flags(flags); + free_irq( 0, NULL); +} + + +/* We use the NVRAM RTC to time a second to calibrate the decrementer. */ +__initfunc(void mk48t59_calibrate_decr(void)) +{ + unsigned long freq, divisor; + unsigned long t1, t2; + unsigned char save_control; + long i; + unsigned char sec; + + + /* Make sure the time is not stopped. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control & (~MK48T59_RTC_CB_STOP))); + + /* Now make sure the read bit is off so the value will change. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + save_control &= ~MK48T59_RTC_CA_READ; + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + + /* Read the seconds value to see when it changes. */ + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + t1 = get_dec(); + + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + for (i = 0 ; i < 1000000 ; i++) { /* Should take up 1 second... */ + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + + t2 = t1 - get_dec(); + + freq = t2 * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", + freq, divisor,t2>>20); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + +void +prep_restart(char *cmd) +{ + unsigned long i = 10000; + + + _disable_interrupts(); + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( i != 0 ) i++; + panic("restart failed\n"); +} + +/* + * This function will restart a board regardless of port 92 functionality + */ +void +prep_direct_restart(char *cmd) +{ + u32 jumpaddr=0xfff00100; + u32 defaultmsr=MSR_IP; + + /* + * This will ALWAYS work regardless of port 92 + * functionality + */ + _disable_interrupts(); + + __asm__ __volatile__("\n\ + mtspr 26, %1 /* SRR0 */ + mtspr 27, %0 /* SRR1 */ + rfi" + : + : "r" (defaultmsr), "r" (jumpaddr)); + /* + * Not reached + */ +} + +void +prep_halt(void) +{ + unsigned long flags; + _disable_interrupts(); + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( 1 ) ; + /* + * Not reached + */ +} + +void +prep_power_off(void) +{ + prep_halt(); +} + +int prep_setup_residual(char *buffer) +{ + int len = 0; + + + /* PREP's without residual data will give incorrect values here */ + len += sprintf(len+buffer, "clock\t\t: "); + if ( res->ResidualLength ) + len += sprintf(len+buffer, "%ldMHz\n", + (res->VitalProductData.ProcessorHz > 1024) ? + res->VitalProductData.ProcessorHz>>20 : + res->VitalProductData.ProcessorHz); + else + len += sprintf(len+buffer, "???\n"); + + return len; +} + +u_int +prep_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +void +prep_do_IRQ(struct pt_regs *regs, int cpu, int isfake) +{ + int irq; + + if ( (irq = i8259_irq(0)) < 0 ) + { + printk(KERN_DEBUG "Bogus interrupt from PC = %lx\n", + regs->nip); + ppc_spurious_interrupts++; + return; + } + ppc_irq_dispatch_handler( regs, irq ); +} + +__initfunc(void +prep_init_IRQ(void)) +{ + int i; + + if (OpenPIC != NULL) { + for ( i = 16 ; i < 36 ; i++ ) + irq_desc[i].ctl = &open_pic; + openpic_init(1); + } + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); +#ifdef __SMP__ + request_irq(openpic_to_irq(OPENPIC_VEC_SPURIOUS), openpic_ipi_action, + 0, "IPI0", 0); +#endif /* __SMP__ */ +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +prep_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + _insw((unsigned short *)((port)+_IO_BASE), buf, ns); +} + +void +prep_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + _outsw((unsigned short *)((port)+_IO_BASE), buf, ns); +} + +int +prep_ide_default_irq(ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 13; + case 0x170: return 13; + case 0x1e8: return 11; + case 0x168: return 10; + default: + return 0; + } +} + +ide_ioreg_t +prep_ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + default: + return 0; + } +} + +int +prep_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +void +prep_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +void +prep_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} + +void +prep_ide_fix_driveid(struct hd_driveid *id) +{ +} + +__initfunc(void +prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) { ide_ioreg_t port = base; int i = 8; @@ -254,6 +702,143 @@ __initfunc(void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int if (irq != NULL) *irq = 0; } +#endif + +__initfunc(void +prep_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + /* make a copy of residual data */ + if ( r3 ) + { + memcpy((void *)res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); + } + + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + /* figure out what kind of prep workstation we are */ + if ( res->ResidualLength != 0 ) + { + if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) + _prep_type = _PREP_IBM; + else if (!strncmp(res->VitalProductData.PrintableModel, + "Radstone",8)) + { + extern char *Motherboard_map_name; + + _prep_type = _PREP_Radstone; + Motherboard_map_name= + res->VitalProductData.PrintableModel; + } + else + _prep_type = _PREP_Motorola; + } + else /* assume motorola if no residual (netboot?) */ + { + _prep_type = _PREP_Motorola; + } + + prep_setup_pci_ptrs(); + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* take care of cmd line */ + if ( r6 && (((char *) r6) != '\0')) + { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + ppc_md.setup_arch = prep_setup_arch; + ppc_md.setup_residual = prep_setup_residual; + ppc_md.get_cpuinfo = prep_get_cpuinfo; + ppc_md.irq_cannonicalize = prep_irq_cannonicalize; + ppc_md.init_IRQ = prep_init_IRQ; + if ( !OpenPIC ) + ppc_md.do_IRQ = prep_do_IRQ; + else + ppc_md.do_IRQ = chrp_do_IRQ; + ppc_md.init = NULL; + + ppc_md.restart = prep_restart; + ppc_md.power_off = prep_power_off; + ppc_md.halt = prep_halt; + + ppc_md.time_init = NULL; + if (_prep_type == _PREP_Radstone) { + /* + * We require a direct restart as port 92 does not work on + * all Radstone boards + */ + ppc_md.restart = prep_direct_restart; + /* + * The RTC device used varies according to board type + */ + if(((ucSystemType==RS_SYS_TYPE_PPC1) && (ucBoardRevMaj>=5)) || + (ucSystemType==RS_SYS_TYPE_PPC1a)) + { + ppc_md.set_rtc_time = mk48t59_set_rtc_time; + ppc_md.get_rtc_time = mk48t59_get_rtc_time; + } + else + { + ppc_md.set_rtc_time = mc146818_set_rtc_time; + ppc_md.get_rtc_time = mc146818_get_rtc_time; + } + /* + * Determine the decrementer rate from the residual data + */ + ppc_md.calibrate_decr = prep_res_calibrate_decr; + } + else if (_prep_type == _PREP_IBM) { + ppc_md.set_rtc_time = mc146818_set_rtc_time; + ppc_md.get_rtc_time = mc146818_get_rtc_time; + ppc_md.calibrate_decr = prep_calibrate_decr; + } + else { + ppc_md.set_rtc_time = mk48t59_set_rtc_time; + ppc_md.get_rtc_time = mk48t59_get_rtc_time; + ppc_md.calibrate_decr = mk48t59_calibrate_decr; + } + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = prep_ide_insw; + ppc_ide_md.outsw = prep_ide_outsw; + ppc_ide_md.default_irq = prep_ide_default_irq; + ppc_ide_md.default_io_base = prep_ide_default_io_base; + ppc_ide_md.check_region = prep_ide_check_region; + ppc_ide_md.request_region = prep_ide_request_region; + ppc_ide_md.release_region = prep_ide_release_region; + ppc_ide_md.fix_driveid = prep_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = prep_ide_init_hwif_ports; +#endif + ppc_ide_md.io_base = _IO_BASE; + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif +#endif +} #ifdef CONFIG_SOUND_MODULE EXPORT_SYMBOL(ppc_cs4232_dma); diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c index f1dff8f13..5b8873d79 100644 --- a/arch/ppc/kernel/prep_time.c +++ b/arch/ppc/kernel/prep_time.c @@ -22,7 +22,9 @@ #include <asm/segment.h> #include <asm/io.h> #include <asm/processor.h> -#include <asm/nvram.h> +#include <asm/machdep.h> +#include <asm/prep_nvram.h> +#include <asm/mk48t59.h> #include "time.h" @@ -41,73 +43,29 @@ * is setup at boot time to use the correct addresses. * -- Cort */ -/* - * translate from mc146818 to m48t18 addresses - */ -unsigned int clock_transl[] __prepdata = { MOTO_RTC_SECONDS,0 /* alarm */, - MOTO_RTC_MINUTES,0 /* alarm */, - MOTO_RTC_HOURS,0 /* alarm */, /* 4,5 */ - MOTO_RTC_DAY_OF_WEEK, - MOTO_RTC_DAY_OF_MONTH, - MOTO_RTC_MONTH, - MOTO_RTC_YEAR, /* 9 */ - MOTO_RTC_CONTROLA, MOTO_RTC_CONTROLB /* 10,11 */ -}; - -__prep -int prep_cmos_clock_read(int addr) -{ - if ( _prep_type == _PREP_IBM ) - return CMOS_READ(addr); - else if ( _prep_type == _PREP_Motorola ) - { - outb(clock_transl[addr]>>8, NVRAM_AS1); - outb(clock_transl[addr], NVRAM_AS0); - return (inb(NVRAM_DATA)); - } - - printk("Unknown machine in prep_cmos_clock_read()!\n"); - return -1; -} - -__prep -void prep_cmos_clock_write(unsigned long val, int addr) -{ - if ( _prep_type == _PREP_IBM ) - { - CMOS_WRITE(val,addr); - return; - } - else if ( _prep_type == _PREP_Motorola ) - { - outb(clock_transl[addr]>>8, NVRAM_AS1); - outb(clock_transl[addr], NVRAM_AS0); - outb(val,NVRAM_DATA); - return; - } - printk("Unknown machine in prep_cmos_clock_write()!\n"); -} /* * Set the hardware clock. -- Cort */ __prep -int prep_set_rtc_time(unsigned long nowtime) +int mc146818_set_rtc_time(unsigned long nowtime) { unsigned char save_control, save_freq_select; struct rtc_time tm; to_tm(nowtime, &tm); - save_control = prep_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ - - prep_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = prep_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + /* tell the clock it's being set */ + save_control = CMOS_READ(RTC_CONTROL); - prep_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - tm.tm_year -= 1900; + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + /* stop and reset prescaler */ + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year = (tm.tm_year - 1900) % 100; if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BIN_TO_BCD(tm.tm_sec); BIN_TO_BCD(tm.tm_min); @@ -116,12 +74,12 @@ int prep_set_rtc_time(unsigned long nowtime) BIN_TO_BCD(tm.tm_mday); BIN_TO_BCD(tm.tm_year); } - prep_cmos_clock_write(tm.tm_sec,RTC_SECONDS); - prep_cmos_clock_write(tm.tm_min,RTC_MINUTES); - prep_cmos_clock_write(tm.tm_hour,RTC_HOURS); - prep_cmos_clock_write(tm.tm_mon,RTC_MONTH); - prep_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); - prep_cmos_clock_write(tm.tm_year,RTC_YEAR); + CMOS_WRITE(tm.tm_sec, RTC_SECONDS); + CMOS_WRITE(tm.tm_min, RTC_MINUTES); + CMOS_WRITE(tm.tm_hour, RTC_HOURS); + CMOS_WRITE(tm.tm_mon, RTC_MONTH); + CMOS_WRITE(tm.tm_mday, RTC_DAY_OF_MONTH); + CMOS_WRITE(tm.tm_year, RTC_YEAR); /* The following flags have to be released exactly in this order, * otherwise the DS12887 (popular MC146818A clone with integrated @@ -130,16 +88,14 @@ int prep_set_rtc_time(unsigned long nowtime) * the Dallas Semiconductor data sheets, but who believes data * sheets anyway ... -- Markus Kuhn */ - prep_cmos_clock_write(save_control, RTC_CONTROL); - prep_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) - time_state = TIME_OK; return 0; } __prep -unsigned long prep_get_rtc_time(void) +unsigned long mc146818_get_rtc_time(void) { unsigned int year, mon, day, hour, min, sec; int i; @@ -151,29 +107,123 @@ unsigned long prep_get_rtc_time(void) */ /* read RTC exactly on falling edge of update flag */ for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ - if (prep_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) break; for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ - if (!(prep_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) break; do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = prep_cmos_clock_read(RTC_SECONDS); - min = prep_cmos_clock_read(RTC_MINUTES); - hour = prep_cmos_clock_read(RTC_HOURS); - day = prep_cmos_clock_read(RTC_DAY_OF_MONTH); - mon = prep_cmos_clock_read(RTC_MONTH); - year = prep_cmos_clock_read(RTC_YEAR); - } while (sec != prep_cmos_clock_read(RTC_SECONDS)); - if (!(prep_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } if ((year += 1900) < 1970) year += 100; return mktime(year, mon, day, hour, min, sec); } + +__prep +int mk48t59_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control; + struct rtc_time tm; + + + to_tm(nowtime, &tm); + + /* tell the clock it's being written */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control | MK48T59_RTC_CA_WRITE)); + + tm.tm_year = (tm.tm_year - 1900) % 100; + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + + ppc_md.nvram_write_val(MK48T59_RTC_SECONDS, tm.tm_sec); + ppc_md.nvram_write_val(MK48T59_RTC_MINUTES, tm.tm_min); + ppc_md.nvram_write_val(MK48T59_RTC_HOURS, tm.tm_hour); + ppc_md.nvram_write_val(MK48T59_RTC_MONTH, tm.tm_mon); + ppc_md.nvram_write_val(MK48T59_RTC_DAY_OF_MONTH, tm.tm_mday); + ppc_md.nvram_write_val(MK48T59_RTC_YEAR, tm.tm_year); + + /* Turn off the write bit. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + return 0; +} + +__prep +unsigned long mk48t59_get_rtc_time(void) +{ + unsigned char save_control; + unsigned int year, mon, day, hour, min, sec; + int i; + + /* Make sure the time is not stopped. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control & (~MK48T59_RTC_CB_STOP))); + + /* Now make sure the read bit is off so the value will change. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + save_control &= ~MK48T59_RTC_CA_READ; + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + /* Read the seconds value to see when it changes. */ + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + + /* Wait until the seconds value changes, then read the value. */ + for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + + /* Set the register to read the value. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control | MK48T59_RTC_CA_READ)); + + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + min = ppc_md.nvram_read_val(MK48T59_RTC_MINUTES); + hour = ppc_md.nvram_read_val(MK48T59_RTC_HOURS); + day = ppc_md.nvram_read_val(MK48T59_RTC_DAY_OF_MONTH); + mon = ppc_md.nvram_read_val(MK48T59_RTC_MONTH); + year = ppc_md.nvram_read_val(MK48T59_RTC_YEAR); + + /* Let the time values change again. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + year = year + 1900; + if (year < 1970) { + year += 100; + } + + return mktime(year, mon, day, hour, min, sec); +} diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index ebe64a429..db87c2384 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -1,5 +1,5 @@ /* - * $Id: process.c,v 1.70 1999/01/07 16:28:59 cort Exp $ + * $Id: process.c,v 1.83 1999/05/10 04:43:43 cort Exp $ * * linux/arch/ppc/kernel/process.c * @@ -43,12 +43,18 @@ #include <asm/prom.h> int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); -void switch_to(struct task_struct *, struct task_struct *); - extern unsigned long _get_SP(void); -extern spinlock_t scheduler_lock; struct task_struct *last_task_used_math = NULL; +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM; +union task_union init_task_union = { INIT_TASK }; +/* only used to get secondary processor up */ +struct task_struct *current_set[NR_CPUS] = {&init_task, }; #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 @@ -65,32 +71,28 @@ task_top(struct task_struct *tsk) return ((unsigned long)tsk) + sizeof(struct task_struct); } -static struct vm_area_struct init_mmap = INIT_MMAP; -static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; -static struct files_struct init_files = INIT_FILES; -static struct signal_struct init_signals = INIT_SIGNALS; - -struct mm_struct init_mm = INIT_MM; -union task_union init_task_union = { INIT_TASK }; - -/* only used to get secondary processor up */ -struct task_struct *current_set[NR_CPUS] = {&init_task, }; - int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if (last_task_used_math == current) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); return 1; } +void +enable_kernel_fp(void) +{ +#ifdef __SMP__ + if (current->tss.regs && (current->tss.regs->msr & MSR_FP)) + giveup_fpu(current); + else + giveup_fpu(NULL); /* just enables FP for kernel */ +#else + giveup_fpu(last_task_used_math); +#endif /* __SMP__ */ +} + /* check to make sure the kernel stack is healthy */ int check_stack(struct task_struct *tsk) { @@ -155,7 +157,8 @@ int check_stack(struct task_struct *tsk) } void -switch_to(struct task_struct *prev, struct task_struct *new) +_switch_to(struct task_struct *prev, struct task_struct *new, + struct task_struct **last) { struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); @@ -165,10 +168,10 @@ switch_to(struct task_struct *prev, struct task_struct *new) #endif #ifdef SHOW_TASK_SWITCHES - printk("%s/%d -> %s/%d NIP %08lx cpu %d lock %x root %x/%x\n", + printk("%s/%d -> %s/%d NIP %08lx cpu %d root %x/%x\n", prev->comm,prev->pid, new->comm,new->pid,new->tss.regs->nip,new->processor, - scheduler_lock.lock,new->fs->root,prev->fs->root); + new->fs->root,prev->fs->root); #endif #ifdef __SMP__ /* avoid complexity of lazy save/restore of fpu @@ -176,18 +179,19 @@ switch_to(struct task_struct *prev, struct task_struct *new) * this task used the fpu during the last quantum. * * If it tries to use the fpu again, it'll trap and - * reload its fp regs. + * reload its fp regs. So we don't have to do a restore + * every switch, just a save. * -- Cort */ - if ( prev->tss.regs->msr & MSR_FP ) - smp_giveup_fpu(prev); + if (prev->tss.regs && (prev->tss.regs->msr & MSR_FP)) + giveup_fpu(prev); prev->last_processor = prev->processor; current_set[smp_processor_id()] = new; #endif /* __SMP__ */ new_tss = &new->tss; old_tss = ¤t->tss; - _switch(old_tss, new_tss, new->mm->context); + *last = _switch(old_tss, new_tss, new->mm->context); _enable_interrupts(s); } @@ -240,7 +244,12 @@ void instruction_dump (unsigned long *pc) printk("Instruction DUMP:"); for(i = -3; i < 6; i++) - printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>'); + { + unsigned long p; + if (__get_user( p, &pc[i] )) + break; + printk("%c%08lx%c",i?' ':'<',p,i?' ':'>'); + } printk("\n"); } @@ -268,8 +277,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { - struct pt_regs * childregs; - + struct pt_regs * childregs, *kregs; +#ifdef __SMP__ + extern void ret_from_smpfork(void); +#else + extern void ret_from_syscall(void); +#endif /* Copy registers */ childregs = ((struct pt_regs *) ((unsigned long)p + sizeof(union task_union) @@ -278,8 +291,19 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, if ((childregs->msr & MSR_PR) == 0) childregs->gpr[2] = (unsigned long) p; /* `current' in new task */ childregs->gpr[3] = 0; /* Result from fork() */ + p->tss.regs = childregs; p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; - p->tss.regs = childregs; + p->tss.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; + kregs = (struct pt_regs *)(p->tss.ksp + STACK_FRAME_OVERHEAD); +#ifdef __SMP__ + kregs->nip = (unsigned long)ret_from_smpfork; +#else + kregs->nip = (unsigned long)ret_from_syscall; +#endif + kregs->msr = MSR_KERNEL; + kregs->gpr[1] = (unsigned long)childregs - STACK_FRAME_OVERHEAD; + kregs->gpr[2] = (unsigned long)p; + if (usp >= (unsigned long) regs) { /* Stack is in kernel space - must adjust */ childregs->gpr[1] = (unsigned long)(childregs + 1); @@ -293,21 +317,14 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, * copy fpu info - assume lazy fpu switch now always * -- Cort */ -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if ( last_task_used_math == current ) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); p->tss.fpscr = current->tss.fpscr; childregs->msr &= ~MSR_FP; #ifdef __SMP__ - if ( (p->pid != 0) || !(clone_flags & CLONE_PID) ) - p->tss.smp_fork_ret = 1; p->last_processor = NO_PROC_ID; #endif /* __SMP__ */ return 0; @@ -374,11 +391,6 @@ asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, int res; lock_kernel(); res = do_fork(clone_flags, regs->gpr[1], regs); - /* - * only parent returns here, child returns to either - * syscall_ret_1() or kernel_thread() - * -- Cort - */ #ifdef __SMP__ /* When we clone the idle task we keep the same pid but * the return value of 0 for both causes problems. @@ -397,9 +409,7 @@ asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, int res; - lock_kernel(); res = do_fork(SIGCHLD, regs->gpr[1], regs); - /* only parent returns here */ #ifdef __SMP__ /* When we clone the idle task we keep the same pid but * the return value of 0 for both causes problems. @@ -408,10 +418,15 @@ asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, if ((current->pid == 0) && (current == &init_task)) res = 1; #endif /* __SMP__ */ - unlock_kernel(); return res; } +asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs); +} + asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs *regs) @@ -423,13 +438,8 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if ( last_task_used_math == current ) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); error = do_execve(filename, (char **) a1, (char **) a2, regs); putname(filename); out: diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index da9d7a04c..b2221481a 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -1,5 +1,5 @@ /* - * $Id: prom.c,v 1.46 1998/11/11 03:55:09 paulus Exp $ + * $Id: prom.c,v 1.54 1999/05/10 04:43:46 cort Exp $ * * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. @@ -16,6 +16,7 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/version.h> +#include <asm/spinlock.h> #include <asm/prom.h> #include <asm/page.h> #include <asm/processor.h> @@ -99,10 +100,12 @@ unsigned int old_rtas = 0; static struct device_node *allnodes = 0; static void clearscreen(void); +static void flushscreen(void); #ifdef CONFIG_BOOTX_TEXT static void drawchar(char c); +static void drawhex(unsigned long v); static void drawstring(const char *c); static void scrollscreen(void); @@ -167,6 +170,11 @@ boot_infos_t *boot_infos = 0; /* init it so it's in data segment not bss */ #define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) + __init static void prom_exit() @@ -256,6 +264,11 @@ __init void prom_init(int r3, int r4, prom_entry pp) { +#ifdef CONFIG_SMP + int cpu = 0, i; + phandle node; + char type[16], *path; +#endif unsigned long mem; ihandle prom_rtas; unsigned long offset = reloc_offset(); @@ -273,6 +286,9 @@ prom_init(int r3, int r4, prom_entry pp) unsigned long space; unsigned long ptr, x; char *model; +#ifdef CONFIG_BOOTX_TEXT + unsigned long flags; +#endif RELOC(boot_infos) = PTRUNRELOC(bi); @@ -283,32 +299,72 @@ prom_init(int r3, int r4, prom_entry pp) RELOC(g_loc_Y) = 0; RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; - prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE " booting...\n")); + + /* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since + there is nothing much we can do with an incompatible version, except display + a message and eventually hang the processor... + + I'll try to keep enough of boot-info compatible in the future to always allow + display of this message; + */ + if (!BOOT_INFO_IS_COMPATIBLE(bi)) + prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); + + prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); + prom_print(RELOC("\nstarted at : 0x")); + drawhex(reloc_offset() + KERNELBASE); + prom_print(RELOC("\nlinked at : 0x")); + drawhex(KERNELBASE); + prom_print(RELOC("\nframe buffer at : 0x")); + drawhex((unsigned long)bi->dispDeviceBase); + prom_print(RELOC(" (phys), 0x")); + drawhex((unsigned long)bi->logicalDisplayBase); + prom_print(RELOC(" (log)")); + prom_print(RELOC("\nMSR : 0x")); + __asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); + drawhex(flags); + prom_print(RELOC("\n\n")); #endif - - /* - * XXX If this is an iMac, turn off the USB controller. + /* Out of the #if/#endif since it flushes the clearscreen too */ + flushscreen(); + + /* New BootX enters kernel with MMU off, i/os are not allowed + here. This hack will have been done by the boostrap anyway. */ - model = (char *) early_get_property - (r4 + bi->deviceTreeOffset, 4, RELOC("model")); - if (model && strcmp(model, RELOC("iMac,1")) == 0) { - out_le32((unsigned *)0x80880008, 1); /* XXX */ + if (bi->version < 4) { + /* + * XXX If this is an iMac, turn off the USB controller. + */ + model = (char *) early_get_property + (r4 + bi->deviceTreeOffset, 4, RELOC("model")); + if (model && strcmp(model, RELOC("iMac,1")) == 0) { + out_le32((unsigned *)0x80880008, 1); /* XXX */ + } } - + space = bi->deviceTreeOffset + bi->deviceTreeSize; if (bi->ramDisk) space = bi->ramDisk + bi->ramDiskSize; RELOC(klimit) = PTRUNRELOC((char *) bi + space); - /* - * Touch each page to make sure the PTEs for them - * are in the hash table - the aim is to try to avoid - * getting DSI exceptions while copying the kernel image. + /* New BootX will have flushed all TLBs and enters kernel with + MMU switched OFF, so this should not be useful anymore. */ - for (ptr = (KERNELBASE + offset) & PAGE_MASK; - ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) - x = *(volatile unsigned long *)ptr; - + if (bi->version < 4) { + /* + * Touch each page to make sure the PTEs for them + * are in the hash table - the aim is to try to avoid + * getting DSI exceptions while copying the kernel image. + */ + for (ptr = (KERNELBASE + offset) & PAGE_MASK; + ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) + x = *(volatile unsigned long *)ptr; + } + +#ifdef CONFIG_BOOTX_TEXT + prom_print(RELOC("booting...\n")); + flushscreen(); +#endif return; } @@ -379,7 +435,7 @@ prom_init(int r3, int r4, prom_entry pp) prom_args.nret = 2; prom_args.args[0] = RELOC("instantiate-rtas"); prom_args.args[1] = prom_rtas; - prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE-offset); + prom_args.args[2] = ((void *)(RELOC(rtas_data)-KERNELBASE)); RELOC(prom)(&prom_args); if (prom_args.args[nargs] != 0) i = 0; @@ -393,6 +449,81 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC(" done\n")); } RELOC(klimit) = (char *) (mem - offset); +#ifdef CONFIG_SMP + /* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of high memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. We do that by using + * physical address 0x0. The holding pattern checks that address + * until its cpu # is there, when it is that cpu jumps to + * __secondary_start(). smp_boot_cpus() takes care of setting those + * values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * -- Cort + */ + { + extern void __secondary_hold(void); + unsigned long i; + char type[16]; + + + /* + * XXX: hack to make sure we're chrp, assume that if we're + * chrp we have a device_type property -- Cort + */ + node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if ( (int)call_prom(RELOC("getprop"), 4, 1, node, + RELOC("device_type"),type, sizeof(type)) <= 0) + return; + + /* copy the holding pattern code to someplace safe (8M) */ + memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x10000 ); + for (i = 8<<20; i < ((8<<20)+0x10000); i += 32) + { + asm volatile("dcbf 0,%0" : : "r" (i) : "memory"); + asm volatile("icbi 0,%0" : : "r" (i) : "memory"); + } + } + + /* look for cpus */ + for (node = 0; prom_next_node(&node);) + { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + path = (char *) mem; + memset(path, 0, 256); + if ((int) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + /* XXX: hack - don't start cpu 0, this cpu -- Cort */ + if ( cpu++ == 0 ) + continue; + prom_print(RELOC("starting cpu ")); + prom_print(path); + *(unsigned long *)(0x4) = 0; + asm volatile("dcbf 0,%0": : "r" (0x4) : "memory"); + call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, cpu-1); + for ( i = 0 ; (i < 10000) && + (*(ulong *)(0x4) == (ulong)0); i++ ) + ; + if (*(ulong *)(0x4) == (ulong)cpu-1 ) + prom_print(RELOC("...ok\n")); + else + prom_print(RELOC("...failed\n")); + } +#endif } /* @@ -631,6 +762,12 @@ finish_node(struct device_node *np, unsigned long mem_start, mem_start = ifunc(np, mem_start); } + /* the f50 sets the name to 'display' and 'compatible' to what we + * expect for the name -- Cort + */ + if (!strcmp(np->name, "display")) + np->name = get_property(np, "compatible", 0); + if (!strcmp(np->name, "device-tree")) ifunc = interpret_root_props; else if (np->type == 0) @@ -1007,7 +1144,7 @@ device_is_compatible(struct device_node *device, const char *compat) if (cp == NULL) return 0; while (cplen > 0) { - if (strcasecmp(cp, compat) == 0) + if (strncasecmp(cp, compat, strlen(compat)) == 0) return 1; l = strlen(cp) + 1; cp += l; @@ -1143,6 +1280,8 @@ print_properties(struct device_node *np) } #endif +spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED; + /* this can be called after setup -- Cort */ __openfirmware int @@ -1173,7 +1312,9 @@ call_rtas(const char *service, int nargs, int nret, for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); + spin_lock(&rtas_lock); enter_rtas((void *)__pa(&u)); + spin_unlock(&rtas_lock); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; @@ -1191,8 +1332,11 @@ abort() prom_exit(); } -#define CALC_BASE(y) (bi->dispDeviceBase + bi->dispDeviceRect[0] * \ - (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * (y)) +/* Calc the base address of a given point (x,y) */ +#define CALC_BASE(x,y) ((BOOT_INFO_IS_V2_COMPATIBLE(bi) ? bi->logicalDisplayBase : \ + bi->dispDeviceBase) + (bi->dispDeviceRect[0] + (x)) * \ + (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * \ + ((y) + bi->dispDeviceRect[1])) __init static void @@ -1200,7 +1344,7 @@ clearscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *base = (unsigned long *)CALC_BASE(0); + unsigned long *base = (unsigned long *)CALC_BASE(0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1214,6 +1358,33 @@ clearscreen(void) } } +__inline__ void dcbst(const void* addr) +{ + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); +} + +__init +static void +flushscreen(void) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + unsigned long *base = (unsigned long *)CALC_BASE(0,0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + unsigned long *ptr = base; + for(j=width; j>0; j-=8) { + dcbst(ptr); + ptr += 8; + } + base += (bi->dispDeviceRowBytes >> 2); + } +} + #ifdef CONFIG_BOOTX_TEXT __init @@ -1222,8 +1393,8 @@ scrollscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *src = (unsigned long *)CALC_BASE(16); - unsigned long *dst = (unsigned long *)CALC_BASE(0); + unsigned long *src = (unsigned long *)CALC_BASE(0,16); + unsigned long *dst = (unsigned long *)CALC_BASE(0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1252,20 +1423,17 @@ drawchar(char c) { unsigned long offset = reloc_offset(); - switch(c) - { - case '\r': RELOC(g_loc_X) = 0; break; - case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; + switch(c) { + case '\r': RELOC(g_loc_X) = 0; break; + case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; default: draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); - if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) - { + if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; } } - while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) - { + while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { scrollscreen(); RELOC(g_loc_Y)--; } @@ -1281,17 +1449,32 @@ drawstring(const char *c) __init static void +drawhex(unsigned long v) +{ + static char hex_table[] = "0123456789abcdef"; + unsigned long offset = reloc_offset(); + + drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]); +} + + +__init +static void draw_byte(unsigned char c, long locX, long locY) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned char *base = bi->dispDeviceBase - + (bi->dispDeviceRowBytes * ((locY * 16) + bi->dispDeviceRect[1])) - + (bi->dispDeviceDepth >> 3) * ((locX * 8) + bi->dispDeviceRect[0]); - unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; + boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + unsigned char *base = CALC_BASE(locX << 3, locY << 4); + unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; - switch(bi->dispDeviceDepth) - { + switch(bi->dispDeviceDepth) { case 32: draw_byte_32(font, (unsigned long *)base); break; diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index e2c6b13a0..b3a25fd2b 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -392,14 +392,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); } else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { -#ifdef __SMP__ - if (child->tss.regs->msr & MSR_FP ) - smp_giveup_fpu(child); -#else - /* only current can be last task to use math on SMP */ - if (last_task_used_math == child) - giveup_fpu(); -#endif + if (child->tss.regs->msr & MSR_FP) + giveup_fpu(child); tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } else @@ -433,13 +427,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; } if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { -#ifndef __SMP__ - if (last_task_used_math == child) - giveup_fpu(); -#else - if (child->tss.regs->msr & MSR_FP ) - smp_giveup_fpu(child); -#endif + if (child->tss.regs->msr & MSR_FP) + giveup_fpu(child); ((long *)child->tss.fpr)[addr - PT_FPR0] = data; ret = 0; goto out; diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 50cd70889..2d38f3adc 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.122 1998/12/31 20:51:19 cort Exp $ + * $Id: setup.c,v 1.132 1999/03/24 00:32:19 cort Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -27,40 +27,74 @@ #include <asm/smp.h> #ifdef CONFIG_MBX #include <asm/mbx.h> +#include <asm/8xx_immap.h> #endif #include <asm/bootx.h> +#include <asm/machdep.h> +#include <asm/ide.h> -/* APUS defs */ -extern unsigned long m68k_machtype; -extern int parse_bootinfo(const struct bi_record *); -extern char _end[]; -#ifdef CONFIG_APUS -extern struct mem_info ramdisk; -unsigned long isa_io_base; -unsigned long isa_mem_base; -unsigned long pci_dram_offset; -#endif -/* END APUS defs */ +extern void pmac_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void chrp_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void prep_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void mbx_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void apus_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); +extern boot_infos_t *boot_infos; extern char cmd_line[512]; char saved_command_line[256]; unsigned char aux_device_present; -#if !defined(CONFIG_MACH_SPECIFIC) +struct ide_machdep_calls ppc_ide_md; + unsigned long ISA_DMA_THRESHOLD; unsigned long DMA_MODE_READ, DMA_MODE_WRITE; -int _machine; -/* if we have openfirmware */ -unsigned long have_of; -#endif /* ! CONFIG_MACH_SPECIFIC */ + +/* Temporary hacks until machdep.h is fully done. */ +int _machine = 0; +/* do we have OF? */ +int have_of = 0; +int is_prep = 0; +int is_chrp = 0; +/* For MTX/MVME boards.. with Raven/Falcon Chipset + Real close to CHRP, but boot like PReP (via PPCbug) + There's probably a nicer way to do this.. --Troy */ +int is_powerplus = 0; + +struct machdep_calls ppc_md; + /* copy of the residual data */ +#ifndef CONFIG_MBX unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; -RESIDUAL *res = (RESIDUAL *)&__res; - -int _prep_type; +#else +unsigned char __res[sizeof(bd_t)] = {0,}; +#endif -extern boot_infos_t *boot_infos; +RESIDUAL *res = (RESIDUAL *)&__res; /* * Perhaps we can put the pmac screen_info[] here @@ -110,160 +144,28 @@ struct screen_info screen_info = { }; #endif /* CONFIG_MBX */ -/* cmd is ignored for now... */ void machine_restart(char *cmd) { -#ifndef CONFIG_MBX - struct adb_request req; - unsigned long flags; - unsigned long i = 10000; -#if 0 - int err; -#endif - - switch(_machine) - { - case _MACH_Pmac: - switch (adb_hardware) { - case ADB_VIACUDA: - cuda_request(&req, NULL, 2, CUDA_PACKET, - CUDA_RESET_SYSTEM); - for (;;) - cuda_poll(); - break; - case ADB_VIAPMU: - pmu_restart(); - break; - default: - } - break; - - case _MACH_chrp: -#if 0 /* RTAS doesn't seem to work on Longtrail. - For now, do it the same way as the PReP. */ - /*err = call_rtas("system-reboot", 0, 1, NULL); - printk("RTAS system-reboot returned %d\n", err); - for (;;);*/ - - { - extern unsigned int rtas_entry, rtas_data, rtas_size; - unsigned long status, value; - printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n", - rtas_entry,rtas_data,rtas_size); - } -#endif - case _MACH_prep: - _disable_interrupts(); - - /* set exception prefix high - to the prom */ - save_flags( flags ); - restore_flags( flags|MSR_IP ); - - /* make sure bit 0 (reset) is a 0 */ - outb( inb(0x92) & ~1L , 0x92 ); - /* signal a reset to system control port A - soft reset */ - outb( inb(0x92) | 1 , 0x92 ); - - while ( i != 0 ) i++; - panic("restart failed\n"); - break; - case _MACH_apus: - cli(); - - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); - APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); - APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); - for(;;); - break; - } -#else /* CONFIG_MBX */ - extern void MBX_gorom(void); - MBX_gorom(); -#endif /* CONFIG_MBX */ + ppc_md.restart(cmd); } - + void machine_power_off(void) { -#ifndef CONFIG_MBX - struct adb_request req; -#if 0 - int err; -#endif - - switch (_machine) { - case _MACH_Pmac: - switch (adb_hardware) { - case ADB_VIACUDA: - cuda_request(&req, NULL, 2, CUDA_PACKET, - CUDA_POWERDOWN); - for (;;) - cuda_poll(); - break; - case ADB_VIAPMU: - pmu_shutdown(); - break; - default: - } - break; - - case _MACH_chrp: -#if 0 /* RTAS doesn't seem to work on Longtrail. - For now, do it the same way as the PReP. */ - err = call_rtas("power-off", 2, 1, NULL, 0, 0); - printk("RTAS system-reboot returned %d\n", err); - for (;;); -#endif - - case _MACH_prep: - machine_restart(NULL); - case _MACH_apus: - for (;;); - } - for (;;); -#else /* CONFIG_MBX */ - machine_restart(NULL); -#endif /* CONFIG_MBX */ + ppc_md.power_off(); } - + void machine_halt(void) { - if ( _machine == _MACH_Pmac ) - { - machine_power_off(); - } - else /* prep, chrp or apus */ - machine_restart(NULL); - + ppc_md.halt(); } - + #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) { -#if !defined(CONFIG_MBX) && !defined(CONFIG_APUS) - switch (_machine) { -#if defined(CONFIG_BLK_DEV_IDE_PMAC) - case _MACH_Pmac: - pmac_ide_init_hwif_ports(p,base,irq); - break; -#endif - case _MACH_chrp: - chrp_ide_init_hwif_ports(p,base,irq); - break; - case _MACH_prep: - prep_ide_init_hwif_ports(p,base,irq); - break; + if (ppc_ide_md.ide_init_hwif != NULL) { + ppc_ide_md.ide_init_hwif(p, base, irq); } -#endif -#if defined(CONFIG_MBX) - mbx_ide_init_hwif_ports(p,base,irq); -#endif } -EXPORT_SYMBOL(ide_init_hwif_ports); #endif unsigned long cpu_temp(void) @@ -297,10 +199,6 @@ unsigned long cpu_temp(void) int get_cpuinfo(char *buffer) { - extern int pmac_get_cpuinfo(char *); - extern int chrp_get_cpuinfo(char *); - extern int prep_get_cpuinfo(char *); - extern int apus_get_cpuinfo(char *); unsigned long len = 0; unsigned long bogosum = 0; unsigned long i; @@ -364,7 +262,6 @@ int get_cpuinfo(char *buffer) break; } -#ifndef CONFIG_MBX /* * Assume here that all clock rates are the same in a * smp system. -- Cort @@ -381,33 +278,11 @@ int get_cpuinfo(char *buffer) len += sprintf(len+buffer, "clock\t\t: %dMHz\n", *fp / 1000000); } - - /* PREP's without residual data for some reason will give - incorrect values here */ - if ( is_prep ) - { - len += sprintf(len+buffer, "clock\t\t: "); - if ( res->ResidualLength ) - len += sprintf(len+buffer, "%ldMHz\n", - (res->VitalProductData.ProcessorHz > 1024) ? - res->VitalProductData.ProcessorHz>>20 : - res->VitalProductData.ProcessorHz); - else - len += sprintf(len+buffer, "???\n"); - } -#else /* CONFIG_MBX */ + + if (ppc_md.setup_residual != NULL) { - bd_t *bp; - extern RESIDUAL *res; - - bp = (bd_t *)res; - - len += sprintf(len+buffer,"clock\t\t: %dMHz\n" - "bus clock\t: %dMHz\n", - bp->bi_intfreq /*/ 1000000*/, - bp->bi_busfreq /*/ 1000000*/); + len += ppc_md.setup_residual(buffer + len); } -#endif /* CONFIG_MBX */ len += sprintf(len+buffer, "revision\t: %ld.%ld\n", (GET_PVR & 0xff00) >> 8, GET_PVR & 0xff); @@ -422,8 +297,8 @@ int get_cpuinfo(char *buffer) if ( i ) len += sprintf(buffer+len, "\n"); len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", - (bogosum+2500)/500000, - (bogosum+2500)/5000 % 100); + (bogosum+2500)/500000, + (bogosum+2500)/5000 % 100); #endif /* __SMP__ */ /* @@ -432,34 +307,21 @@ int get_cpuinfo(char *buffer) { len += sprintf(buffer+len,"zero pages\t: total %lu (%luKb) " "current: %lu (%luKb) hits: %lu/%lu (%lu%%)\n", - quicklists.zerototal, - (quicklists.zerototal*PAGE_SIZE)>>10, - quicklists.zero_sz, - (quicklists.zero_sz*PAGE_SIZE)>>10, - quicklists.zeropage_hits,quicklists.zeropage_calls, + zero_cache_total, + (zero_cache_total*PAGE_SIZE)>>10, + zero_cache_sz, + (zero_cache_sz*PAGE_SIZE)>>10, + zero_cache_hits,zero_cache_calls, /* : 1 below is so we don't div by zero */ - (quicklists.zeropage_hits*100) / - ((quicklists.zeropage_calls)?quicklists.zeropage_calls:1)); + (zero_cache_hits*100) / + ((zero_cache_calls)?zero_cache_calls:1)); } -#ifndef CONFIG_MBX - switch (_machine) + if (ppc_md.get_cpuinfo != NULL) { - case _MACH_Pmac: - len += pmac_get_cpuinfo(buffer+len); - break; - case _MACH_prep: - len += prep_get_cpuinfo(buffer+len); - break; - case _MACH_chrp: - len += chrp_get_cpuinfo(buffer+len); - break; - case _MACH_apus: - /* Not much point in printing m68k info when it is not - used. */ - break; + len += ppc_md.get_cpuinfo(buffer+len); } -#endif /* ndef CONFIG_MBX */ + return len; } @@ -471,25 +333,22 @@ unsigned long __init identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - extern void setup_pci_ptrs(void); #ifdef __SMP__ if ( first_cpu_booted ) return 0; #endif /* __SMP__ */ -#ifndef CONFIG_MBX #ifndef CONFIG_MACH_SPECIFIC /* boot loader will tell us if we're APUS */ if ( r3 == 0x61707573 ) { _machine = _MACH_apus; - have_of = 0; r3 = 0; } /* prep boot loader tells us if we're prep or not */ else if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) ) { _machine = _MACH_prep; - have_of = 0; + is_prep = 1; } else { char *model; @@ -500,19 +359,49 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, /* ask the OF info if we're a chrp or pmac */ model = get_property(find_path_device("/"), "device_type", NULL); if ( model && !strncmp("chrp",model,4) ) + { _machine = _MACH_chrp; + is_chrp = 1; + } else { model = get_property(find_path_device("/"), "model", NULL); if ( model && !strncmp(model, "IBM", 3)) + { _machine = _MACH_chrp; + is_chrp = 1; + } else + { _machine = _MACH_Pmac; + is_prep = 1; + } } } -#endif /* CONFIG_MACH_SPECIFIC */ +#else /* CONFIG_MACH_SPECIFIC */ + +#ifdef CONFIG_PREP + _machine = _MACH_prep; + is_prep = 1; +#elif defined(CONFIG_CHRP) + _machine = _MACH_chrp; + is_chrp = 1; + have_of = 1; +#elif defined(CONFIG_PMAC) + _machine = _MACH_Pmac; + have_of = 1; +#elif defined(CONFIG_MBX) + _machine = _MACH_mbx; +#elif defined(CONFIG_FADS) + _machine = _MACH_fads; +#elif defined(CONFIG_APUS) + _machine = _MACH_apus; +#else +#error "Machine not defined correctly" +#endif /* CONFIG_APUS */ +#endif /* CONFIG_MACH_SPECIFIC */ if ( have_of ) { @@ -571,131 +460,31 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, cmd_line[sizeof(cmd_line) - 1] = 0; } - switch (_machine) { case _MACH_Pmac: - setup_pci_ptrs(); - /* isa_io_base gets set in pmac_find_bridges */ - isa_mem_base = PMAC_ISA_MEM_BASE; - pci_dram_offset = PMAC_PCI_DRAM_OFFSET; -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = ~0L; - DMA_MODE_READ = 1; - DMA_MODE_WRITE = 2; -#endif /* ! CONFIG_MACH_SPECIFIC */ + pmac_init(r3, r4, r5, r6, r7); break; case _MACH_prep: - /* make a copy of residual data */ - if ( r3 ) - memcpy((void *)res,(void *)(r3+KERNELBASE), - sizeof(RESIDUAL)); - setup_pci_ptrs(); - isa_io_base = PREP_ISA_IO_BASE; - isa_mem_base = PREP_ISA_MEM_BASE; - pci_dram_offset = PREP_PCI_DRAM_OFFSET; -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = 0x00ffffff; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; -#endif /* ! CONFIG_MACH_SPECIFIC */ - /* figure out what kind of prep workstation we are */ - if ( res->ResidualLength != 0 ) - { - if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) - _prep_type = _PREP_IBM; - else - _prep_type = _PREP_Motorola; - } - else /* assume motorola if no residual (netboot?) */ - _prep_type = _PREP_Motorola; -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - /* take care of cmd line */ - if ( r6 ) - { - *(char *)(r7+KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6+KERNELBASE)); - } + prep_init(r3, r4, r5, r6, r7); break; case _MACH_chrp: - setup_pci_ptrs(); -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r3 ) - { - initrd_start = r3 + KERNELBASE; - initrd_end = r3 + r4 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - /* isa_io_base set by setup_pci_ptrs() */ - isa_mem_base = CHRP_ISA_MEM_BASE; - pci_dram_offset = CHRP_PCI_DRAM_OFFSET; -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = ~0L; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; -#endif /* ! CONFIG_MACH_SPECIFIC */ + chrp_init(r3, r4, r5, r6, r7); break; -#ifdef CONFIG_APUS +#ifdef CONFIG_APUS case _MACH_apus: - /* Parse bootinfo. The bootinfo is located right after - the kernel bss */ - parse_bootinfo((const struct bi_record *)&_end); -#ifdef CONFIG_BLK_DEV_INITRD - /* Take care of initrd if we have one. Use data from - bootinfo to avoid the need to initialize PPC - registers when kernel is booted via a PPC reset. */ - if ( ramdisk.addr ) { - initrd_start = (unsigned long) __va(ramdisk.addr); - initrd_end = (unsigned long) - __va(ramdisk.size + ramdisk.addr); - } - /* Make sure code below is not executed. */ - r4 = 0; - r6 = 0; -#endif /* CONFIG_BLK_DEV_INITRD */ -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = 0x00ffffff; -#endif /* ! CONFIG_MACH_SPECIFIC */ + apus_init(r3, r4, r5, r6, r7); break; #endif +#ifdef CONFIG_MBX + case _MACH_mbx: + mbx_init(r3, r4, r5, r6, r7); + break; +#endif default: printk("Unknown machine type in identify_machine!\n"); } -#else /* CONFIG_MBX */ - - if ( r3 ) - memcpy( (void *)res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); - -#ifdef CONFIG_PCI - setup_pci_ptrs(); -#endif - -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - /* take care of cmd line */ - if ( r6 ) - { - - *(char *)(r7+KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6+KERNELBASE)); - } -#endif /* CONFIG_MBX */ - /* Check for nobats option (used in mapin_ram). */ if (strstr(cmd_line, "nobats")) { extern int __map_without_bats; @@ -717,14 +506,17 @@ void ppc_setup_l2cr(char *str, int *ints) } } +__initfunc(void + ppc_init(void)) +{ + if (ppc_md.init != NULL) { + ppc_md.init(); + } +} + __initfunc(void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p)) + unsigned long * memory_start_p, unsigned long * memory_end_p)) { - extern void pmac_setup_arch(unsigned long *, unsigned long *); - extern void chrp_setup_arch(unsigned long *, unsigned long *); - extern void prep_setup_arch(unsigned long *, unsigned long *); - extern void mbx_setup_arch(unsigned long *, unsigned long *); - extern void apus_setup_arch(unsigned long *, unsigned long *); extern int panic_timeout; extern char _etext[], _edata[]; extern char *klimit; @@ -737,7 +529,7 @@ __initfunc(void setup_arch(char **cmdline_p, if (strstr(cmd_line, "xmon")) xmon(0); #endif /* CONFIG_XMON */ - + /* reboot on panic */ panic_timeout = 180; @@ -753,27 +545,113 @@ __initfunc(void setup_arch(char **cmdline_p, *memory_start_p = find_available_memory(); *memory_end_p = (unsigned long) end_of_DRAM; -#ifdef CONFIG_MBX - mbx_setup_arch(memory_start_p,memory_end_p); -#else /* CONFIG_MBX */ - switch (_machine) { - case _MACH_Pmac: - pmac_setup_arch(memory_start_p, memory_end_p); - break; - case _MACH_prep: - prep_setup_arch(memory_start_p, memory_end_p); - break; - case _MACH_chrp: - chrp_setup_arch(memory_start_p, memory_end_p); - break; -#ifdef CONFIG_APUS - case _MACH_apus: - m68k_machtype = MACH_AMIGA; - apus_setup_arch(memory_start_p,memory_end_p); - break; -#endif - default: - printk("Unknown machine %d in setup_arch()\n", _machine); - } -#endif /* CONFIG_MBX */ + ppc_md.setup_arch(memory_start_p, memory_end_p); +} + +void ppc_generic_ide_fix_driveid(struct hd_driveid *id) +{ + int i; + unsigned short *stringcast; + + + id->config = __le16_to_cpu(id->config); + id->cyls = __le16_to_cpu(id->cyls); + id->reserved2 = __le16_to_cpu(id->reserved2); + id->heads = __le16_to_cpu(id->heads); + id->track_bytes = __le16_to_cpu(id->track_bytes); + id->sector_bytes = __le16_to_cpu(id->sector_bytes); + id->sectors = __le16_to_cpu(id->sectors); + id->vendor0 = __le16_to_cpu(id->vendor0); + id->vendor1 = __le16_to_cpu(id->vendor1); + id->vendor2 = __le16_to_cpu(id->vendor2); + stringcast = (unsigned short *)&id->serial_no[0]; + for (i=0; i<(20/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->buf_type = __le16_to_cpu(id->buf_type); + id->buf_size = __le16_to_cpu(id->buf_size); + id->ecc_bytes = __le16_to_cpu(id->ecc_bytes); + stringcast = (unsigned short *)&id->fw_rev[0]; + for (i=0; i<(8/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + stringcast = (unsigned short *)&id->model[0]; + for (i=0; i<(40/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->dword_io = __le16_to_cpu(id->dword_io); + id->reserved50 = __le16_to_cpu(id->reserved50); + id->field_valid = __le16_to_cpu(id->field_valid); + id->cur_cyls = __le16_to_cpu(id->cur_cyls); + id->cur_heads = __le16_to_cpu(id->cur_heads); + id->cur_sectors = __le16_to_cpu(id->cur_sectors); + id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0); + id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->dma_1word = __le16_to_cpu(id->dma_1word); + id->dma_mword = __le16_to_cpu(id->dma_mword); + id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes); + id->eide_dma_min = __le16_to_cpu(id->eide_dma_min); + id->eide_dma_time = __le16_to_cpu(id->eide_dma_time); + id->eide_pio = __le16_to_cpu(id->eide_pio); + id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy); + id->word69 = __le16_to_cpu(id->word69); + id->word70 = __le16_to_cpu(id->word70); + id->word71 = __le16_to_cpu(id->word71); + id->word72 = __le16_to_cpu(id->word72); + id->word73 = __le16_to_cpu(id->word73); + id->word74 = __le16_to_cpu(id->word74); + id->word75 = __le16_to_cpu(id->word75); + id->word76 = __le16_to_cpu(id->word76); + id->word77 = __le16_to_cpu(id->word77); + id->word78 = __le16_to_cpu(id->word78); + id->word79 = __le16_to_cpu(id->word79); + id->word80 = __le16_to_cpu(id->word80); + id->word81 = __le16_to_cpu(id->word81); + id->command_sets = __le16_to_cpu(id->command_sets); + id->word83 = __le16_to_cpu(id->word83); + id->word84 = __le16_to_cpu(id->word84); + id->word85 = __le16_to_cpu(id->word85); + id->word86 = __le16_to_cpu(id->word86); + id->word87 = __le16_to_cpu(id->word87); + id->dma_ultra = __le16_to_cpu(id->dma_ultra); + id->word89 = __le16_to_cpu(id->word89); + id->word90 = __le16_to_cpu(id->word90); + id->word91 = __le16_to_cpu(id->word91); + id->word92 = __le16_to_cpu(id->word92); + id->word93 = __le16_to_cpu(id->word93); + id->word94 = __le16_to_cpu(id->word94); + id->word95 = __le16_to_cpu(id->word95); + id->word96 = __le16_to_cpu(id->word96); + id->word97 = __le16_to_cpu(id->word97); + id->word98 = __le16_to_cpu(id->word98); + id->word99 = __le16_to_cpu(id->word99); + id->word100 = __le16_to_cpu(id->word100); + id->word101 = __le16_to_cpu(id->word101); + id->word102 = __le16_to_cpu(id->word102); + id->word103 = __le16_to_cpu(id->word103); + id->word104 = __le16_to_cpu(id->word104); + id->word105 = __le16_to_cpu(id->word105); + id->word106 = __le16_to_cpu(id->word106); + id->word107 = __le16_to_cpu(id->word107); + id->word108 = __le16_to_cpu(id->word108); + id->word109 = __le16_to_cpu(id->word109); + id->word110 = __le16_to_cpu(id->word110); + id->word111 = __le16_to_cpu(id->word111); + id->word112 = __le16_to_cpu(id->word112); + id->word113 = __le16_to_cpu(id->word113); + id->word114 = __le16_to_cpu(id->word114); + id->word115 = __le16_to_cpu(id->word115); + id->word116 = __le16_to_cpu(id->word116); + id->word117 = __le16_to_cpu(id->word117); + id->word118 = __le16_to_cpu(id->word118); + id->word119 = __le16_to_cpu(id->word119); + id->word120 = __le16_to_cpu(id->word120); + id->word121 = __le16_to_cpu(id->word121); + id->word122 = __le16_to_cpu(id->word122); + id->word123 = __le16_to_cpu(id->word123); + id->word124 = __le16_to_cpu(id->word124); + id->word125 = __le16_to_cpu(id->word125); + id->word126 = __le16_to_cpu(id->word126); + id->word127 = __le16_to_cpu(id->word127); + id->security = __le16_to_cpu(id->security); + for (i=0; i<127; i++) + id->reserved[i] = __le16_to_cpu(id->reserved[i]); } diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 3ffb7981e..17c0f55d1 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -1,7 +1,7 @@ /* * linux/arch/ppc/kernel/signal.c * - * $Id: signal.c,v 1.21 1998/10/22 19:37:49 paulus Exp $ + * $Id: signal.c,v 1.24 1999/04/03 11:25:16 paulus Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -218,13 +218,8 @@ int sys_sigreturn(struct pt_regs *regs) if (sc == (struct sigcontext_struct *)(sigctx.regs)) { /* Last stacked signal - restore registers */ sr = (struct sigregs *) sigctx.regs; -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if (last_task_used_math == current) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP ) + giveup_fpu(current); if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) goto badframe; @@ -271,13 +266,8 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame, if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto badframe; -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if (last_task_used_math == current) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __copy_to_user(&frame->fp_regs, current->tss.fpr, ELF_NFPREG * sizeof(double)) @@ -374,7 +364,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) if (!oldset) oldset = ¤t->blocked; - newsp = frame = regs->gpr[1] - sizeof(struct sigregs); + newsp = frame = 0; for (;;) { unsigned long signr; @@ -471,6 +461,13 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) } } + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + newsp = (current->sas_ss_sp + current->sas_ss_size); + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs); + /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); } diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index ba505e133..c38fc3108 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,10 +1,13 @@ /* - * $Id: smp.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ + * $Id: smp.c,v 1.49 1999/03/18 04:16:31 cort Exp $ * * Smp support for ppc. * * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great * deal of code from the sparc and intel versions. + * + * Support for PReP (Motorola MTX/MVME) SMP by Troy Benjegerdes + * (troy@microux.com, hozer@drgw.net) */ #include <linux/kernel.h> @@ -18,6 +21,7 @@ #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> #include <linux/init.h> +#include <linux/openpic.h> #include <asm/ptrace.h> #include <asm/atomic.h> @@ -29,9 +33,10 @@ #include <asm/softirq.h> #include <asm/init.h> #include <asm/io.h> +#include <asm/prom.h> #include "time.h" - +int first_cpu_booted = 0; int smp_threads_ready = 0; volatile int smp_commenced = 0; int smp_num_cpus = 1; @@ -42,7 +47,6 @@ volatile unsigned long ipi_count; spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; -int first_cpu_booted = 0; cycles_t cacheflush_time; /* all cpu mappings are 1-1 -- Cort */ @@ -51,6 +55,10 @@ volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; int start_secondary(void *); extern int cpu_idle(void *unused); +u_int openpic_read(volatile u_int *addr); + +/* register for interrupting the secondary processor on the powersurge */ +#define PSURGE_INTR ((volatile unsigned *)0xf80000c0) void smp_local_timer_interrupt(struct pt_regs * regs) { @@ -99,29 +107,33 @@ void smp_local_timer_interrupt(struct pt_regs * regs) /* * Dirty hack to get smp message passing working. - * Right now it only works for stop cpu's but will be setup - * later for more general message passing. * * As it is now, if we're sending two message at the same time - * we have race conditions. I avoided doing locks here since - * all that works right now is the stop cpu message. + * we have race conditions. The PowerSurge doesn't easily + * allow us to send IPI messages so we put the messages in + * smp_message[]. * + * This is because don't have several IPI's on the PowerSurge even though + * we do on the chrp. It would be nice to use the actual IPI's on the chrp + * rather than this but having two methods of doing IPI isn't a good idea + * right now. * -- Cort */ int smp_message[NR_CPUS]; void smp_message_recv(void) { int msg = smp_message[smp_processor_id()]; - - /* clear interrupt */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); + + if ( _machine == _MACH_Pmac ) + { + /* clear interrupt */ + out_be32(PSURGE_INTR, ~0); + } /* make sure msg is for us */ if ( msg == -1 ) return; ipi_count++; - /*printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);*/ switch( msg ) { @@ -158,12 +170,17 @@ void smp_send_stop(void) spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; void smp_message_pass(int target, int msg, unsigned long data, int wait) { - if ( _machine != _MACH_Pmac ) + int i; + if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) return; -printk("SMP %d: sending smp message %x\n", current->processor, msg); -if (smp_processor_id() ) printk("pass from cpu 1\n"); + spin_lock(&mesg_pass_lock); -#define OTHER (~smp_processor_id() & 1) + + /* + * We assume here that the msg is not -1. If it is, + * the recipient won't know the message was destined + * for it. -- Cort + */ switch( target ) { @@ -171,105 +188,180 @@ if (smp_processor_id() ) printk("pass from cpu 1\n"); smp_message[smp_processor_id()] = msg; /* fall through */ case MSG_ALL_BUT_SELF: - smp_message[OTHER] = msg; + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + smp_message[i] = msg; break; default: smp_message[target] = msg; break; } - /* interrupt secondary processor */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0L; - eieio(); - /* interrupt primary */ - /**(volatile unsigned long *)(0xf3019000);*/ - spin_unlock(&mesg_pass_lock); + + if ( _machine == _MACH_Pmac ) + { + /* interrupt secondary processor */ + out_be32(PSURGE_INTR, ~0); + out_be32(PSURGE_INTR, 0); + /* + * Assume for now that the secondary doesn't send + * IPI's -- Cort + */ + /* interrupt primary */ + /**(volatile unsigned long *)(0xf3019000);*/ + } + + if ( _machine == _MACH_chrp ) + { + /* + * There has to be some way of doing this better - + * perhaps a sent-to-all or send-to-all-but-self + * in the openpic. This gets us going for now, though. + * -- Cort + */ + switch ( target ) + { + case MSG_ALL: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + openpic_cause_IPI(i, 0, 0xffffffff ); + break; + case MSG_ALL_BUT_SELF: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + openpic_cause_IPI(i, 0, + 0xffffffff & ~(1 << smp_processor_id())); + break; + default: + openpic_cause_IPI(target, 0, 1U << target); + break; + } + } + + spin_unlock(&mesg_pass_lock); } void __init smp_boot_cpus(void) { extern struct task_struct *current_set[NR_CPUS]; - extern void __secondary_start(void); + extern void __secondary_start_psurge(void); int i; struct task_struct *p; unsigned long a; printk("Entering SMP Mode...\n"); - + /* let other processors know to not do certain initialization */ first_cpu_booted = 1; - /*dcbf(&first_cpu_booted);*/ + + /* + * assume for now that the first cpu booted is + * cpu 0, the master -- Cort + */ + cpu_callin_map[0] = 1; + cpu_callin_map[1] = 0; + smp_store_cpu_info(0); + active_kernel_processor = 0; + current->processor = 0; for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; prof_multiplier[i] = 1; } - cpu_callin_map[0] = 1; - smp_store_cpu_info(0); - active_kernel_processor = 0; - current->processor = 0; - /* * XXX very rough, assumes 20 bus cycles to read a cache line, * timebase increments every 4 bus cycles, 32kB L1 data cache. */ cacheflush_time = 5 * 1024; - if ( _machine != _MACH_Pmac ) + if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) { printk("SMP not supported on this machine.\n"); return; } - /* create a process for second processor */ - kernel_thread(start_secondary, NULL, CLONE_PID); - p = task[1]; - if ( !p ) - panic("No idle task for secondary processor\n"); - p->processor = 1; - p->has_cpu = 1; - current_set[1] = p; - - /* need to flush here since secondary bat's aren't setup */ - /* XXX ??? */ - for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) - asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); - asm volatile("sync"); - - /*dcbf((void *)¤t_set[1]);*/ - /* setup entry point of secondary processor */ - *(volatile unsigned long *)(0xf2800000) = - (unsigned long)__secondary_start-KERNELBASE; - eieio(); - /* interrupt secondary to begin executing code */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0L; - eieio(); + switch ( _machine ) + { + case _MACH_Pmac: + /* assume powersurge board - 2 processors -- Cort */ + smp_num_cpus = 2; + break; + case _MACH_chrp: + smp_num_cpus = ((openpic_read(&OpenPIC->Global.Feature_Reporting0) + & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1; + /* get our processor # - we may not be cpu 0 */ + printk("SMP %d processors, boot CPU is %d (should be 0)\n", + smp_num_cpus, + 10/*openpic_read(&OpenPIC->Processor[0]._Who_Am_I)*/); + break; + } + /* - * wait to see if the secondary made a callin (is actually up). - * udelay() isn't accurate here since we haven't yet called - * calibrate_delay() so use this value that I found through - * experimentation. -- Cort + * only check for cpus we know exist. We keep the callin map + * with cpus at the bottom -- Cort */ - for ( i = 1000; i && !cpu_callin_map[1] ; i-- ) - udelay(100); + for ( i = 1 ; i < smp_num_cpus; i++ ) + { + int c; + + /* create a process for the processor */ + kernel_thread(start_secondary, NULL, CLONE_PID); + p = task[i]; + if ( !p ) + panic("No idle task for secondary processor\n"); + p->processor = i; + p->has_cpu = 1; + current_set[i] = p; + + /* need to flush here since secondary bats aren't setup */ + for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) + asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); + asm volatile("sync"); + + /* wake up cpus */ + switch ( _machine ) + { + case _MACH_Pmac: + /* setup entry point of secondary processor */ + *(volatile unsigned long *)(0xf2800000) = + (unsigned long)__secondary_start_psurge-KERNELBASE; + eieio(); + /* interrupt secondary to begin executing code */ + out_be32(PSURGE_INTR, ~0); + out_be32(PSURGE_INTR, 0); + break; + case _MACH_chrp: + *(unsigned long *)KERNELBASE = i; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); + break; + } + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for ( c = 1000; c && !cpu_callin_map[i] ; c-- ) + udelay(100); + + if ( cpu_callin_map[i] ) + { + printk("Processor %d found.\n", i); + /* this sync's the decr's -- Cort */ + if ( _machine == _MACH_Pmac ) + set_dec(decrementer_count); + } else { + printk("Processor %d is stuck.\n", i); + } + } - if(cpu_callin_map[1]) { - printk("Processor %d found.\n", smp_num_cpus); - smp_num_cpus++; -#if 1 /* this sync's the decr's, but we don't want this now -- Cort */ - set_dec(decrementer_count); -#endif - } else { - printk("Processor %d is stuck. \n", smp_num_cpus); + if ( _machine == _MACH_Pmac ) + { + /* reset the entry point so if we get another intr we won't + * try to startup again */ + *(volatile unsigned long *)(0xf2800000) = 0x100; + /* send interrupt to other processors to start decr's on all cpus */ + smp_message_pass(1,0xf0f0, 0, 0); } - /* reset the entry point so if we get another intr we won't - * try to startup again */ - *(volatile unsigned long *)(0xf2800000) = 0x100; - /* send interrupt to other processors to start decr's on all cpus */ - smp_message_pass(1,0xf0f0, 0, 0); } void __init smp_commence(void) @@ -308,7 +400,6 @@ void __init smp_callin(void) while(!smp_commenced) barrier(); __sti(); - printk("SMP %d: smp_callin done\n", current->processor); } void __init smp_setup(char *str, int *ints) diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c index ae44266f7..b90760d7e 100644 --- a/arch/ppc/kernel/softemu8xx.c +++ b/arch/ppc/kernel/softemu8xx.c @@ -38,6 +38,7 @@ #define LFDU 51 #define STFD 54 #define STFDU 55 +#define FMR 63 /* * We return 0 on success, 1 on unimplemented instruction, and EFAULT @@ -49,6 +50,7 @@ Soft_emulate_8xx(struct pt_regs *regs) uint inst, instword; uint flreg, idxreg, disp; uint retval; + signed short sdisp; uint *ea, *ip; retval = 0; @@ -66,6 +68,11 @@ Soft_emulate_8xx(struct pt_regs *regs) switch ( inst ) { case LFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); if (copy_from_user(ip, ea, sizeof(double))) retval = EFAULT; break; @@ -77,6 +84,11 @@ Soft_emulate_8xx(struct pt_regs *regs) regs->gpr[idxreg] = (uint)ea; break; case STFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); if (copy_to_user(ea, ip, sizeof(double))) retval = EFAULT; break; @@ -87,6 +99,11 @@ Soft_emulate_8xx(struct pt_regs *regs) else regs->gpr[idxreg] = (uint)ea; break; + case FMR: + /* assume this is a fp move -- Cort */ + memcpy( ip, ¤t->tss.fpr[(instword>>11)&0x1f], + sizeof(double) ); + break; default: retval = 1; printk("Bad emulation %s/%d\n" @@ -98,7 +115,7 @@ Soft_emulate_8xx(struct pt_regs *regs) (instword>>16)&0x1f, (instword>>11)&0x1f, (instword>>6)&0x1f, - (instword>>1)&0x1f, + (instword>>1)&0x3ff, instword&1); { int pa; diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index b97456226..4d96a6f0a 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -205,12 +205,15 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + file = fget(fd); + if (!file) goto out; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); ret = do_mmap(file, addr, len, prot, flags, offset); + if (file) + fput(file); out: unlock_kernel(); return ret; diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index 32f60a236..6f0eb798b 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ + * $Id: time.c,v 1.47 1999/03/18 05:11:11 cort Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -18,8 +18,8 @@ * Since it is not possible to get a nice 100 Hz clock out of this, without * creating a software PLL, I have set HZ to 128. -- Dan * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills */ #include <linux/config.h> @@ -41,18 +41,14 @@ #include <asm/processor.h> #include <asm/nvram.h> #include <asm/cache.h> -#ifdef CONFIG_MBX -#include <asm/mbx.h> -#endif +/* Fixme - Why is this here? - Corey */ #ifdef CONFIG_8xx #include <asm/8xx_immap.h> #endif +#include <asm/machdep.h> #include "time.h" -/* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */ -int (*set_rtc_time)(unsigned long); - void smp_local_timer_interrupt(struct pt_regs *); /* keep track of when we need to update the rtc */ @@ -76,9 +72,6 @@ void timer_interrupt(struct pt_regs * regs) { int dval, d; unsigned long cpu = smp_processor_id(); - /* save the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - unsigned dcache_locked = unlock_dcache(); hardirq_enter(cpu); #ifdef __SMP__ @@ -119,16 +112,21 @@ void timer_interrupt(struct pt_regs * regs) */ if ( xtime.tv_sec > last_rtc_update + 660 ) { - if (set_rtc_time(xtime.tv_sec) == 0) + if (ppc_md.set_rtc_time(xtime.tv_sec) == 0) { last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } + else { + /* do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 60; + } } } } #ifdef __SMP__ smp_local_timer_interrupt(regs); #endif + + /* Fixme - make this more generic - Corey */ #ifdef CONFIG_APUS { extern void apus_heartbeat (void); @@ -136,33 +134,8 @@ void timer_interrupt(struct pt_regs * regs) } #endif hardirq_exit(cpu); - /* restore the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - lock_dcache(dcache_locked); } -#ifdef CONFIG_MBX -/* A place holder for time base interrupts, if they are ever enabled. -*/ -void timebase_interrupt(int irq, void * dev, struct pt_regs * regs) -{ - printk("timebase_interrupt()\n"); -} - -/* The RTC on the MPC8xx is an internal register. - * We want to protect this during power down, so we need to unlock, - * modify, and re-lock. - */ -static int -mbx_set_rtc_time(unsigned long time) -{ - ((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; - ((immap_t *)IMAP_ADDR)->im_sit.sit_rtc = time; - ((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; - return(0); -} -#endif /* CONFIG_MBX */ - /* * This version of gettimeofday has microsecond resolution. */ @@ -198,9 +171,9 @@ void do_settimeofday(struct timeval *tv) xtime.tv_sec = tv->tv_sec; xtime.tv_usec = tv->tv_usec - frac_tick; set_dec(frac_tick * count_period_den / count_period_num); - time_adjust = 0; /* stop active adjtime() */ + time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ + time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; restore_flags(flags); @@ -209,81 +182,23 @@ void do_settimeofday(struct timeval *tv) __initfunc(void time_init(void)) { -#ifndef CONFIG_MBX + if (ppc_md.time_init != NULL) + { + ppc_md.time_init(); + } + if ((_get_PVR() >> 16) == 1) { /* 601 processor: dec counts down by 128 every 128ns */ decrementer_count = DECREMENTER_COUNT_601; count_period_num = COUNT_PERIOD_NUM_601; count_period_den = COUNT_PERIOD_DEN_601; + } else if (!smp_processor_id()) { + ppc_md.calibrate_decr(); } - switch (_machine) { - case _MACH_Pmac: - xtime.tv_sec = pmac_get_rtc_time(); - if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) ) - pmac_calibrate_decr(); - if ( !smp_processor_id() ) - set_rtc_time = pmac_set_rtc_time; - break; - case _MACH_chrp: - chrp_time_init(); - xtime.tv_sec = chrp_get_rtc_time(); - if ((_get_PVR() >> 16) != 1) - chrp_calibrate_decr(); - set_rtc_time = chrp_set_rtc_time; - break; - case _MACH_prep: - xtime.tv_sec = prep_get_rtc_time(); - prep_calibrate_decr(); - set_rtc_time = prep_set_rtc_time; - break; -#ifdef CONFIG_APUS - case _MACH_apus: - { - xtime.tv_sec = apus_get_rtc_time(); - apus_calibrate_decr(); - set_rtc_time = apus_set_rtc_time; - break; - } -#endif - } - xtime.tv_usec = 0; -#else /* CONFIG_MBX */ - mbx_calibrate_decr(); - set_rtc_time = mbx_set_rtc_time; - - /* First, unlock all of the registers we are going to modify. - * To protect them from corruption during power down, registers - * that are maintained by keep alive power are "locked". To - * modify these registers we have to write the key value to - * the key location associated with the register. - */ - ((immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; - ((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; - + xtime.tv_sec = ppc_md.get_rtc_time(); + xtime.tv_usec = 0; - /* Disable the RTC one second and alarm interrupts. - */ - ((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc &= - ~(RTCSC_SIE | RTCSC_ALE); - - /* Enabling the decrementer also enables the timebase interrupts - * (or from the other point of view, to get decrementer interrupts - * we have to enable the timebase). The decrementer interrupt - * is wired into the vector table, nothing to do here for that. - */ - ((immap_t *)IMAP_ADDR)->im_sit.sit_tbscr = - ((mk_int_int_mask(DEC_INTERRUPT) << 8) | - (TBSCR_TBF | TBSCR_TBE)); - if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0) - panic("Could not allocate timer IRQ!"); - - /* Get time from the RTC. - */ - xtime.tv_sec = ((immap_t *)IMAP_ADDR)->im_sit.sit_rtc; - xtime.tv_usec = 0; - -#endif /* CONFIG_MBX */ set_dec(decrementer_count); /* mark the rtc/on-chip timer as in sync * so we don't update right away @@ -291,109 +206,6 @@ __initfunc(void time_init(void)) last_rtc_update = xtime.tv_sec; } -#ifndef CONFIG_MBX -/* - * Uses the on-board timer to calibrate the on-chip decrementer register - * for prep systems. On the pmac the OF tells us what the frequency is - * but on prep we have to figure it out. - * -- Cort - */ -int calibrate_done = 0; -volatile int *done_ptr = &calibrate_done; -__initfunc(void prep_calibrate_decr(void)) -{ - unsigned long flags; - - /* the Powerstack II's have trouble with the timer so - * we use a default value -- Cort - */ - if ( (_prep_type == _PREP_Motorola) && - ((inb(0x800) & 0xF0) & 0x40) ) - { - unsigned long freq, divisor; - static unsigned long t2 = 0; - - t2 = 998700000/60; - freq = t2 * 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", - freq, divisor,t2>>20); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; - return; - } - - - save_flags(flags); - -#define TIMER0_COUNT 0x40 -#define TIMER_CONTROL 0x43 - /* set timer to periodic mode */ - outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ - /* set the clock to ~100 Hz */ - outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ - outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ - - if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) - panic("Could not allocate timer IRQ!"); - __sti(); - while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */ - restore_flags(flags); - free_irq( 0, NULL); -} - -__initfunc(void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)) -{ - unsigned long freq, divisor; - static unsigned long t1 = 0, t2 = 0; - - if ( !t1 ) - t1 = get_dec(); - else if (!t2) - { - t2 = get_dec(); - t2 = t1-t2; /* decr's in 1/HZ */ - t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ - freq = t2 * 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", - freq, divisor,t2>>20); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; - *done_ptr = 1; - } -} - -#else /* CONFIG_MBX */ - -/* The decrementer counts at the system (internal) clock frequency divided by - * sixteen, or external oscillator divided by four. Currently, we only - * support the MBX, which is system clock divided by sixteen. - */ -__initfunc(void mbx_calibrate_decr(void)) -{ - bd_t *binfo = (bd_t *)res; - int freq, fp, divisor; - - if ((((immap_t *)IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0) - printk("WARNING: Wrong decrementer source clock.\n"); - - /* The manual says the frequency is in Hz, but it is really - * as MHz. The value 'fp' is the number of decrementer ticks - * per second. - */ - fp = (binfo->bi_intfreq * 1000000) / 16; - freq = fp*60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; -} -#endif /* CONFIG_MBX */ - /* 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. @@ -439,6 +251,49 @@ static int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + void to_tm(int tim, struct rtc_time * tm) { register int i; @@ -467,6 +322,11 @@ void to_tm(int tim, struct rtc_time * tm) /* Days are what is left over (+1) from all that. */ tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); } diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h index 64a07250c..a1c912308 100644 --- a/arch/ppc/kernel/time.h +++ b/arch/ppc/kernel/time.h @@ -1,5 +1,5 @@ /* - * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $ + * $Id: time.h,v 1.11 1999/03/18 04:16:34 cort Exp $ * Common time prototypes and such for all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -9,32 +9,15 @@ #include <linux/mc146818rtc.h> /* time.c */ -void prep_calibrate_decr_handler(int, void *,struct pt_regs *); -void prep_calibrate_decr(void); -void pmac_calibrate_decr(void); -extern void apus_calibrate_decr(void); extern unsigned decrementer_count; extern unsigned count_period_num; extern unsigned count_period_den; -extern unsigned long mktime(unsigned int, unsigned int,unsigned int, +extern unsigned long mktime(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); extern void to_tm(int tim, struct rtc_time * tm); extern unsigned long last_rtc_update; -/* pmac/prep/chrp_time.c */ -unsigned long prep_get_rtc_time(void); -unsigned long pmac_get_rtc_time(void); -unsigned long chrp_get_rtc_time(void); -unsigned long apus_get_rtc_time(void); -int prep_set_rtc_time(unsigned long nowtime); -int pmac_set_rtc_time(unsigned long nowtime); -int chrp_set_rtc_time(unsigned long nowtime); -int apus_set_rtc_time(unsigned long nowtime); -void pmac_read_rtc_time(void); -void chrp_calibrate_decr(void); -void chrp_time_init(void); int via_calibrate_decr(void); -void mbx_calibrate_decr(void); /* Accessor functions for the decrementer register. */ static __inline__ unsigned int get_dec(void) diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index fa5184af2..87cc2bd68 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -191,13 +191,8 @@ AlignmentException(struct pt_regs *regs) { int fixed; -#ifdef __SMP__ - if (regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if (last_task_used_math == current) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); fixed = fix_alignment(regs); if (fixed == 1) { regs->nip += 4; /* skip over emulated instruction */ diff --git a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c index 2d2a2d8c0..699c13fad 100644 --- a/arch/ppc/lib/locks.c +++ b/arch/ppc/lib/locks.c @@ -1,5 +1,5 @@ /* - * $Id: locks.c,v 1.21 1998/12/28 10:28:53 paulus Exp $ + * $Id: locks.c,v 1.23 1999/02/12 07:06:32 cort Exp $ * * Locks for smp ppc * @@ -26,24 +26,18 @@ void _spin_lock(spinlock_t *lock) #ifdef DEBUG_LOCKS unsigned int stuck = INIT_STUCK; #endif /* DEBUG_LOCKS */ - /* try expensive atomic load/store to get lock */ - while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) { - /* try cheap load until it's free */ - while(lock->lock) { + while (__spin_trylock(&lock->lock)) { #ifdef DEBUG_LOCKS - if(!--stuck) - { - printk("_spin_lock(%p) CPU#%d NIP %p" - " holder: cpu %ld pc %08lX\n", - lock, cpu, __builtin_return_address(0), - lock->owner_cpu,lock->owner_pc); - stuck = INIT_STUCK; - /* steal the lock */ - /*xchg_u32((void *)&lock->lock,0);*/ - } -#endif /* DEBUG_LOCKS */ - barrier(); + if(!--stuck) { + printk("_spin_lock(%p) CPU#%d NIP %p" + " holder: cpu %ld pc %08lX\n", + lock, cpu, __builtin_return_address(0), + lock->owner_cpu,lock->owner_pc); + stuck = INIT_STUCK; + /* steal the lock */ + /*xchg_u32((void *)&lock->lock,0);*/ } +#endif /* DEBUG_LOCKS */ } lock->owner_pc = (unsigned long)__builtin_return_address(0); lock->owner_cpu = cpu; @@ -51,15 +45,11 @@ void _spin_lock(spinlock_t *lock) int spin_trylock(spinlock_t *lock) { - unsigned long result; - - result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff); - if ( !result ) - { - lock->owner_cpu = smp_processor_id(); - lock->owner_pc = (unsigned long)__builtin_return_address(0); - } - return (result == 0); + if (__spin_trylock(&lock->lock)) + return 0; + lock->owner_cpu = smp_processor_id(); + lock->owner_pc = (unsigned long)__builtin_return_address(0); + return 1; } @@ -76,11 +66,11 @@ void _spin_unlock(spinlock_t *lp) lp->owner_pc,lp->lock); #endif /* DEBUG_LOCKS */ lp->owner_pc = lp->owner_cpu = 0; - eieio(); /* actually I believe eieio only orders */ - lp->lock = 0; /* non-cacheable accesses (on 604 at least) */ - eieio(); /* - paulus. */ + wmb(); + lp->lock = 0; + wmb(); } - + /* * Just like x86, implement read-write locks as a 32-bit counter * with the high bit (sign) being the "write" bit. @@ -95,6 +85,7 @@ void _read_lock(rwlock_t *rw) again: /* get our read lock in there */ + wmb(); atomic_inc((atomic_t *) &(rw)->lock); if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */ { @@ -114,6 +105,7 @@ again: /* try to get the read lock again */ goto again; } + wmb(); } void _read_unlock(rwlock_t *rw) @@ -124,7 +116,9 @@ void _read_unlock(rwlock_t *rw) current->comm,current->pid,current->tss.regs->nip, rw->lock); #endif /* DEBUG_LOCKS */ + wmb(); atomic_dec((atomic_t *) &(rw)->lock); + wmb(); } void _write_lock(rwlock_t *rw) @@ -134,7 +128,8 @@ void _write_lock(rwlock_t *rw) int cpu = smp_processor_id(); #endif /* DEBUG_LOCKS */ -again: +again: + wmb(); if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */ { while ( (rw)->lock & (1<<31) ) /* wait for write lock */ @@ -170,6 +165,7 @@ again: } goto again; } + wmb(); } void _write_unlock(rwlock_t *rw) @@ -180,7 +176,9 @@ void _write_unlock(rwlock_t *rw) current->comm,current->pid,current->tss.regs->nip, rw->lock); #endif /* DEBUG_LOCKS */ + wmb(); clear_bit(31,&(rw)->lock); + wmb(); } void __lock_kernel(struct task_struct *task) @@ -196,24 +194,18 @@ void __lock_kernel(struct task_struct *task) } #endif /* DEBUG_LOCKS */ - if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 ) + if (atomic_inc_return((atomic_t *) &task->lock_depth) != 1) return; /* mine! */ - while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) ) - { - /* try cheap load until it's free */ - while(klock_info.kernel_flag) { + while (__spin_trylock(&klock_info.kernel_flag)) { #ifdef DEBUG_LOCKS - if(!--stuck) - { - printk("_lock_kernel() CPU#%d NIP %p\n", - smp_processor_id(), - __builtin_return_address(0)); - stuck = INIT_STUCK; - } -#endif /* DEBUG_LOCKS */ - barrier(); + if(!--stuck) { + printk("_lock_kernel() CPU#%d NIP %p\n", + smp_processor_id(), + __builtin_return_address(0)); + stuck = INIT_STUCK; } +#endif /* DEBUG_LOCKS */ } klock_info.akp = smp_processor_id(); @@ -223,7 +215,7 @@ void __lock_kernel(struct task_struct *task) void __unlock_kernel(struct task_struct *task) { #ifdef DEBUG_LOCKS - if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) ) + if ((task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD)) { printk("__unlock_kernel(): %s/%d (nip %08lX) " "lock depth %x flags %lx\n", @@ -234,10 +226,13 @@ void __unlock_kernel(struct task_struct *task) return; } #endif /* DEBUG_LOCKS */ - if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) ) + if (atomic_dec_and_test((atomic_t *) &task->lock_depth)) { + wmb(); klock_info.akp = NO_PROC_ID; + wmb(); klock_info.kernel_flag = KLOCK_CLEAR; + wmb(); } } @@ -249,5 +244,5 @@ void reacquire_kernel_lock(struct task_struct *task, int cpu,int depth) __lock_kernel(task); task->lock_depth = depth; __sti(); - } + } } diff --git a/arch/ppc/lib/strcase.c b/arch/ppc/lib/strcase.c index a9e40bce9..36b521091 100644 --- a/arch/ppc/lib/strcase.c +++ b/arch/ppc/lib/strcase.c @@ -10,3 +10,14 @@ int strcasecmp(const char *s1, const char *s2) } while (c1 == c2 && c1 != 0); return c1 - c2; } + +int strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while ((--n > 0) && c1 == c2 && c1 != 0); + return c1 - c2; +} diff --git a/arch/ppc/lib/string.S b/arch/ppc/lib/string.S index 2ff171ee3..be5504940 100644 --- a/arch/ppc/lib/string.S +++ b/arch/ppc/lib/string.S @@ -8,7 +8,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include <linux/config.h> #include "../kernel/ppc_asm.tmpl" #include <asm/processor.h> #include <asm/errno.h> @@ -321,8 +320,6 @@ __copy_tofrom_user: .long 75b,76b .text -#undef CLEAR_USE_DCBZ 1 -#undef CLEAR_NO_CACHE 1 .globl __clear_user __clear_user: addi r6,r3,-4 @@ -333,15 +330,6 @@ __clear_user: /* clear a single word */ 11: stwu r5,4(r6) beqlr -#if defined(CLEAR_NO_CACHE) && defined (CONFIG_6xx) - /* - * no reason to turn off the cache for a single word - * or a few bytes -- Cort - */ - mfspr r7,HID0 - ori r8,r7,HID0_DLOCK - mtspr HID0,r8 -#endif /* CLEAR_NO_CACHE */ /* clear word sized chunks */ andi. r0,r6,3 add r4,r0,r4 @@ -353,10 +341,6 @@ __clear_user: 1: stwu r5,4(r6) bdnz 1b 6: andi. r4,r4,3 -#if defined(CLEAR_NO_CACHE) && defined (CONFIG_6xx) - /* restore the original state of HID0 in case cache was off -- Cort */ - mtspr HID0,r7 -#endif /* CLEAR_NO_CACHE */ /* clear byte sized chunks */ 7: cmpwi 0,r4,0 beqlr @@ -371,9 +355,6 @@ __clear_user: .align 2 .long 11b,99b .long 1b,99b -#ifdef CLEAR_USE_DCBZ - /*.long 66b,99b*/ -#endif .long 8b,99b .text diff --git a/arch/ppc/mbx_defconfig b/arch/ppc/mbx_defconfig index 54035b866..ddb420383 100644 --- a/arch/ppc/mbx_defconfig +++ b/arch/ppc/mbx_defconfig @@ -14,8 +14,8 @@ CONFIG_8xx=y # CONFIG_ALL_PPC is not set # CONFIG_APUS is not set CONFIG_MBX=y -CONFIG_SMP=n CONFIG_MACH_SPECIFIC=y +# CONFIG_SMP is not set CONFIG_SERIAL_CONSOLE=y # @@ -58,8 +58,26 @@ CONFIG_KERNEL_ELF=y # Block devices # # CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_IDE is not set -# CONFIG_BLK_DEV_HD_ONLY is not set +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_VIA82C586 is not set +# CONFIG_BLK_DEV_CMD646 is not set +CONFIG_BLK_DEV_SL82C105=y +# CONFIG_IDE_CHIPSETS is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set @@ -75,7 +93,6 @@ CONFIG_PARIDE_PARPORT=y # CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -91,7 +108,7 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_SYN_COOKIES is not set # CONFIG_INET_RARP is not set CONFIG_IP_NOSR=y -# CONFIG_SKB_LARGE is not set +CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set @@ -103,7 +120,11 @@ CONFIG_IP_NOSR=y # CONFIG_WAN_ROUTER is not set # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set -# CONFIG_CPU_IS_SLOW is not set +CONFIG_CPU_IS_SLOW=y + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -127,6 +148,7 @@ CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set @@ -139,6 +161,8 @@ CONFIG_NET_ETHERNET=y # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support @@ -162,7 +186,8 @@ CONFIG_NET_ETHERNET=y # # Character devices # -# CONFIG_VT is not set +CONFIG_VT=y +# CONFIG_VT_CONSOLE is not set CONFIG_SERIAL=y CONFIG_SERIAL_CONSOLE=y # CONFIG_SERIAL_EXTENDED is not set @@ -171,9 +196,17 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set # CONFIG_RTC is not set + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -# CONFIG_NVRAM is not set + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -185,35 +218,46 @@ CONFIG_SERIAL_CONSOLE=y # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -# CONFIG_EXT2_FS is not set -# CONFIG_ISO9660_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_AUTOFS_FS is not set -# CONFIG_UFS_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MAC_PARTITION is not set # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set # CONFIG_NLS is not set # diff --git a/arch/ppc/mbxboot/Makefile b/arch/ppc/mbxboot/Makefile new file mode 100644 index 000000000..235a79e7a --- /dev/null +++ b/arch/ppc/mbxboot/Makefile @@ -0,0 +1,101 @@ +# +# arch/ppc/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Adapted for PowerPC by Gary Thomas +# modified by Cort (cort@cs.nmt.edu) +# +.c.s: + $(CC) $(CFLAGS) -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< +.S.s: + $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + +ZOFF = 0 +ZSZ = 0 +IOFF = 0 +ISZ = 0 + +TFTPIMAGE=/tftpboot/zImage.mbx +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000 +GZIP_FLAGS = -v9 + +OBJECTS := head.o misc.o ../coffboot/zlib.o mbxtty.o +CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include -DCONFIG_MBX +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJCOPY_ARGS = -O elf32-powerpc + +all: zImage + +zvmlinux.initrd: zvmlinux + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp zvmlinux.initrd + $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd initrd` \ + -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \ + -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \ + -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp $@ + rm zvmlinux.initrd.tmp + +zImage: zvmlinux + ln -sf zvmlinux zImage + +zImage.initrd: zvmlinux.initrd + ln -sf zvmlinux.initrd zImage.initrd + +zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz +# +# build the boot loader image and then compute the offset into it +# for the kernel image +# + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.tmp $@ +# +# then with the offset rebuild the bootloader so we know where the kernel is +# + $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ + -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux image` \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ + -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.tmp $@ + rm zvmlinux.tmp + +znetboot : zImage + cp zImage $(TFTPIMAGE) + +znetboot.initrd : zImage.initrd + cp zImage.initrd $(TFTPIMAGE) + +clean: + rm -f vmlinux* zvmlinux* zImage* + +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) -M *.S *.c > .depend + +# just here to match coffboot/Makefile +vmlinux.coff: + +vmlinux.coff.initrd: diff --git a/arch/ppc/mbxboot/head.S b/arch/ppc/mbxboot/head.S new file mode 100644 index 000000000..a7b3f4ffc --- /dev/null +++ b/arch/ppc/mbxboot/head.S @@ -0,0 +1,200 @@ +#include "../kernel/ppc_defs.h" +#include "../kernel/ppc_asm.tmpl" +#include <asm/processor.h> +#include <asm/cache.h> + + .text + +/* + * $Id: head.S,v 1.4 1999/04/22 06:32:09 davem Exp $ + * + * This code is loaded by the ROM loader at some arbitrary location. + * Move it to high memory so that it can load the kernel at 0x0000. + * + * The MBX EPPC-Bug understands ELF, so it loads us into the location + * specified in the header. This is a two step process. First, EPPC-Bug + * loads the file into the intermediate buffer memory location specified + * by the environment parameters. When it discovers this is an ELF + * binary, it relocates to the link address for us. Unfortunately, the + * header does not move with the file, so we have to find the + * intermediate load location and read the header from there. From + * information provided by Motorola (thank you), we know this intermediate + * location can be found from the NVRAM environment. + * All of these addresses must be somewhat carefully chosen to make sure + * we don't overlap the regions. I chose to load the kernel at 0, the + * compressed image loads at 0x00100000, and the MBX intermediate buffer + * was set to 0x00200000. Provided the loaded kernel image never grows + * over one megabyte (which I am going to ensure never happens :-), these + * will work fine. When we get called from EPPC-Bug, registers are: + * R1 - Stack pointer at a high memory address. + * R3 - Pointer to Board Information Block. + * R4 - Pointer to argument string. + * Interrupts masked, cache and MMU disabled. + */ + + .globl start +start: + bl start_ +start_: + mr r11,r3 /* Save pointer to residual/board data */ +/* Clear all of BSS */ + lis r3,edata@h + ori r3,r3,edata@l + lis r4,end@h + ori r4,r4,end@l + subi r3,r3,4 + subi r4,r4,4 + li r0,0 +50: stwu r0,4(r3) + cmp 0,r3,r4 + bne 50b +90: mr r9,r1 /* Save old stack pointer (in case it matters) */ + lis r1,.stack@h + ori r1,r1,.stack@l + addi r1,r1,4096*2 + subi r1,r1,256 + li r2,0x000F /* Mask pointer to 16-byte boundary */ + andc r1,r1,r2 +/* Run loader */ + mr r3, r11 + mr r21, r11 + bl serial_init /* Init MBX serial port */ + + lis r8, 0xfa200000@h /* Disable Ethernet SCC */ + li r0, 0 + stw r0, 0x0a00(r8) + + mr r11, r21 + lis r8,start@h + ori r8,r8,start@l + lis r9,end@h + ori r9,r9,end@l + sub r7,r8,r9 + srwi r7,r7,2 +#define ILAP_ADDRESS 0xfa000020 + lis r8, ILAP_ADDRESS@h + lwz r8, ILAP_ADDRESS@l(r8) + addis r8, r8, 1 /* Add 64K */ + mr r3,r8 /* Load point */ + mr r4,r7 /* Program length */ + mr r5,r6 /* Checksum */ + mr r6,r11 /* Residual data */ + bl decompress_kernel + + /* changed to use r3 (as firmware does) for kernel + as ptr to residual -- Cort*/ + lis r6,cmd_line@h + ori r6,r6,cmd_line@l + lwz r6, 0(r6) + subi r7,r6,1 +00: lbzu r2,1(r7) + cmpi 0,r2,0 + bne 00b + + /* r4,r5 have initrd_start, size */ + lis r2,initrd_start@h + ori r2,r2,initrd_start@l + lwz r4,0(r2) + lis r2,initrd_end@h + ori r2,r2,initrd_end@l + lwz r5,0(r2) + + /* tell kernel we're prep */ + /* + * get start address of kernel code which is stored as a coff + * entry. see boot/head.S -- Cort + */ + li r9,0x0 + lwz r9,0(r9) + mtlr r9 + blr +hang: + b hang + +/* + * Delay for a number of microseconds + * -- Use the BUS timer (assumes 66MHz) + */ + .globl udelay +udelay: + mfspr r4,PVR + srwi r4,r4,16 + cmpi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + addi r4,r4,59 + li r5,60 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmp 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmp 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmp 0,r6,r9 + blt 2b +3: blr + +.globl _get_HID0 +_get_HID0: + mfspr r3,HID0 + blr + +.globl _put_HID0 +_put_HID0: + mtspr HID0,r3 + blr + +.globl _get_MSR +_get_MSR: + mfmsr r3 + blr + +.globl _put_MSR +_put_MSR: + mtmsr r3 + blr + +/* + * Flush instruction cache + * *** I'm really paranoid here! + */ +_GLOBAL(flush_instruction_cache) + mflr r5 + bl flush_data_cache + mtlr r5 + blr + +#define NUM_CACHE_LINES 128*8 +#define CACHE_LINE_SIZE 32 +#define cache_flush_buffer 0x1000 + +/* + * Flush data cache + * *** I'm really paranoid here! + */ +_GLOBAL(flush_data_cache) + lis r3,cache_flush_buffer@h + ori r3,r3,cache_flush_buffer@l + li r4,NUM_CACHE_LINES + mtctr r4 +00: lwz r4,0(r3) + addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ + bdnz 00b +10: blr + .comm .stack,4096*2,4 diff --git a/arch/ppc/boot/mbxtty.c b/arch/ppc/mbxboot/mbxtty.c index e5566dc32..e5566dc32 100644 --- a/arch/ppc/boot/mbxtty.c +++ b/arch/ppc/mbxboot/mbxtty.c diff --git a/arch/ppc/mbxboot/misc.c b/arch/ppc/mbxboot/misc.c new file mode 100644 index 000000000..15bff41dd --- /dev/null +++ b/arch/ppc/mbxboot/misc.c @@ -0,0 +1,637 @@ +/* + * misc.c + * + * $Id: misc.c,v 1.1 1999/02/17 05:00:06 cort Exp $ + * + * Adapted for PowerPC by Gary Thomas + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) + * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort + */ + +#include <linux/types.h> +#include "../coffboot/zlib.h" +#include "asm/residual.h" +#include <linux/elf.h> +#include <linux/config.h> +#include <asm/page.h> +#include <asm/processor.h> +#include <asm/mmu.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif +#ifdef CONFIG_FADS +#include <asm/fads.h> +#endif + +/* + * Please send me load/board info and such data for hardware not + * listed here so I can keep track since things are getting tricky + * with the different load addrs with different firmware. This will + * help to avoid breaking the load/boot process. + * -- Cort + */ +char *avail_ram; +char *end_avail; + +/* Because of the limited amount of memory on the MBX, it presents + * loading problems. The biggest is that we load this boot program + * into a relatively low memory address, and the Linux kernel Bss often + * extends into this space when it get loaded. When the kernel starts + * and zeros the BSS space, it also writes over the information we + * save here and pass to the kernel (command line and board info). + * On the MBX we grab some known memory holes to hold this information. + */ +#if defined(CONFIG_SERIAL_CONSOLE) +char cmd_preset[] = "console=ttyS0,9600n8"; +#else +char cmd_preset[] = ""; +#endif +char cmd_buf[256]; +char *cmd_line = cmd_buf; + +char *root_string = "root=/dev/nfs"; +char *nfsaddrs_string = "nfsaddrs="; +char *nfsroot_string = "nfsroot="; +char *defroot_string = "/sys/mbxroot"; +int do_ipaddrs(char **cmd_cp, int echo); +void do_nfsroot(char **cmd_cp, char *dp); +int strncmp(const char * cs,const char * ct,size_t count); +char *strrchr(const char * s, int c); + +RESIDUAL hold_resid_buf; +RESIDUAL *hold_residual = &hold_resid_buf; +unsigned long initrd_start = 0, initrd_end = 0; +char *zimage_start; +int zimage_size; + +char *vidmem = (char *)0xC00B8000; +int lines, cols; +int orig_x, orig_y; + +void puts(const char *); +void putc(const char c); +void puthex(unsigned long val); +void _bcopy(char *src, char *dst, int len); +void * memcpy(void * __dest, __const void * __src, + int __n); +void gunzip(void *, int, unsigned char *, int *); + +void pause() +{ + puts("pause\n"); +} + +void exit() +{ + puts("exit\n"); + while(1); +} + +/* The MBX is just the serial port. +*/ +tstc(void) +{ + return (serial_tstc()); +} + +getc(void) +{ + while (1) { + if (serial_tstc()) return (serial_getc()); + } +} + +void +putc(const char c) +{ + serial_putchar(c); +} + +void puts(const char *s) +{ + char c; + + while ( ( c = *s++ ) != '\0' ) { + serial_putchar(c); + if ( c == '\n' ) + serial_putchar('\r'); + } +} + + +void * memcpy(void * __dest, __const void * __src, + int __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++) d[i] = s[i]; +} + +int memcmp(__const void * __dest, __const void * __src, + int __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++, d++, s++) + { + if (*d != *s) + { + return (*s - *d); + } + } + return (0); +} + +void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p = avail_ram; + + size *= items; + size = (size + 7) & -8; + avail_ram += size; + if (avail_ram > end_avail) { + puts("oops... out of memory\n"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + puts("bad gzipped data\n"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + puts("gunzip: ran out of data in header\n"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + puts("inflateInit2 returned %d\n"); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + puts("inflate returned %d\n"); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} + +unsigned char sanity[0x2000]; + +unsigned long +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) +{ + int timer; + extern unsigned long start; + char *cp, ch; + unsigned long i, motorola_id = 0; + char needs_reloc = 0; + BATU *u; + BATL *l; + char *dp; + + lines = 25; + cols = 80; + orig_x = 0; + orig_y = 24; + + /* Grab some space for the command line and board info. Since + * we no longer use the ELF header, but it was loaded, grab + * that space. + */ + cmd_line = (char *)(load_addr - 0x10000); + hold_residual = (RESIDUAL *)(cmd_line + sizeof(cmd_buf)); + /* copy board data */ + if (residual) + memcpy(hold_residual,residual,sizeof(bd_t)); + + /* MBX/prep sometimes put the residual/board info at the end of mem + * assume 16M for now -- Cort + * To boot on standard MBX boards with 4M, we can't use initrd, + * and we have to assume less memory. -- Dan + */ + if ( INITRD_OFFSET ) + end_avail = (char *)0x01000000; + else + end_avail = (char *)0x00400000; + + /* let residual data tell us it's higher */ + if ( (unsigned long)residual > 0x00800000 ) + end_avail = (char *)PAGE_ALIGN((unsigned long)residual); + + puts("loaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) + { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + if ( residual ) + { + puts("board data at: "); puthex((unsigned long)residual); + puts(" "); + puthex((unsigned long)((unsigned long)residual + sizeof(bd_t))); + puts("\n"); + puts("relocated to: "); + puthex((unsigned long)hold_residual); + puts(" "); + puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); + puts("\n"); + } + + /* we have to subtract 0x10000 here to correct for objdump including the + size of the elf header which we strip -- Cort */ + zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); + zimage_size = ZIMAGE_SIZE; + + if ( INITRD_OFFSET ) + initrd_start = load_addr - 0x10000 + INITRD_OFFSET; + else + initrd_start = 0; + initrd_end = INITRD_SIZE + initrd_start; + + /* + * setup avail_ram - this is the first part of ram usable + * by the uncompress code. -- Cort + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size); + if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram) + && (load_addr <= 0x01000000) ) + avail_ram = (char *)(load_addr+(num_words*4)); + if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram) + && (load_addr <= 0x01000000) ) + avail_ram = (char *)((unsigned long)&start+(num_words*4)); + + /* relocate zimage */ + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); + /* + * don't relocate the zimage if it was loaded above 16M since + * things get weird if we try to relocate -- Cort + * We don't relocate zimage on a base MBX board because of + * insufficient memory. In this case we don't have initrd either, + * so use that as an indicator. -- Dan + */ + + /* Determine if we have a Motorola board */ + needs_reloc = 0; + if ( (( (unsigned long)zimage_start <= 0x01000000 ) && initrd_start) + || needs_reloc) + { + memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size), + (void *)zimage_start, zimage_size ); + zimage_start = (char *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size); + end_avail = (char *)zimage_start; + puts("relocated to: "); puthex((unsigned long)zimage_start); + puts(" "); + puthex((unsigned long)zimage_size+(unsigned long)zimage_start); + puts("\n"); + } + + /* relocate initrd */ + if ( initrd_start ) + { + puts("initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + /* + * Memory is really tight on the MBX (we can assume 4M) + * so put the initrd at the TOP of ram, and set end_avail + * to right after that. + * + * I should do something like this for prep, too and keep + * a variable end_of_DRAM to keep track of what we think the + * max ram is. + * -- Cort + */ + if (needs_reloc) + { + memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+ + (unsigned long)end_avail-INITRD_SIZE), + (void *)initrd_start, + INITRD_SIZE ); + initrd_start = PAGE_ALIGN(-PAGE_SIZE+ + (unsigned long)end_avail-INITRD_SIZE); + initrd_end = initrd_start + INITRD_SIZE; + end_avail = (char *)initrd_start; + puts("relocated to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } + } + + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + + puts("\nLinux/PPC load: "); + timer = 0; + cp = cmd_line; + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); + while ( *cp ) putc(*cp++); + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else if (ch == '?') { + if (!do_ipaddrs(&cp, 1)) { + *cp++ = ch; + putc(ch); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + /* The MBX does not currently have any default boot strategy. + * If the command line is not filled in, we will automatically + * create the default network boot. + */ + if (cmd_line[0] == 0) { + dp = root_string; + while (*dp != 0) + *cp++ = *dp++; + *cp++ = ' '; + + dp = nfsaddrs_string; + while (*dp != 0) + *cp++ = *dp++; + dp = cp; + do_ipaddrs(&cp, 0); + *cp++ = ' '; + + /* Add the server address to the root file system path. + */ + dp = strrchr(dp, ':'); + dp++; + do_nfsroot(&cp, dp); + *cp = 0; + } + puts("\n"); + + /* mappings on early boot can only handle 16M */ + if ( (int)(cmd_line[0]) > (16<<20)) + puts("cmd_line located > 16M\n"); + if ( (int)hold_residual > (16<<20)) + puts("hold_residual located > 16M\n"); + if ( initrd_start > (16<<20)) + puts("initrd_start located > 16M\n"); + + puts("Uncompressing Linux..."); + + gunzip(0, 0x400000, zimage_start, &zimage_size); + puts("done.\n"); + puts("Now booting the kernel\n"); + return (unsigned long)hold_residual; +} + +int +do_ipaddrs(char **cmd_cp, int echo) +{ + char *cp, *ip, ch; + unsigned char ipd; + int i, j, retval; + + /* We need to create the string: + * <my_ip>:<serv_ip> + */ + cp = *cmd_cp; + retval = 0; + + if ((cp - 9) >= cmd_line) { + if (strncmp(cp - 9, "nfsaddrs=", 9) == 0) { + ip = (char *)0xfa000060; + retval = 1; + for (j=0; j<2; j++) { + for (i=0; i<4; i++) { + ipd = *ip++; + + ch = ipd/100; + if (ch) { + ch += '0'; + if (echo) + putc(ch); + *cp++ = ch; + ipd -= 100 * (ch - '0'); + } + + ch = ipd/10; + if (ch) { + ch += '0'; + if (echo) + putc(ch); + *cp++ = ch; + ipd -= 10 * (ch - '0'); + } + + ch = ipd + '0'; + if (echo) + putc(ch); + *cp++ = ch; + + ch = '.'; + if (echo) + putc(ch); + *cp++ = ch; + } + + /* At the end of the string, remove the + * '.' and replace it with a ':'. + */ + *(cp - 1) = ':'; + if (echo) { + putc('\b'); putc(':'); + } + } + + /* At the end of the second string, remove the + * '.' from both the command line and the + * screen. + */ + --cp; + putc('\b'); putc(' '); putc('\b'); + } + } + *cmd_cp = cp; + return(retval); +} + +void +do_nfsroot(char **cmd_cp, char *dp) +{ + char *cp, *rp, *ep; + + /* The boot argument (i.e /sys/mbxroot/zImage) is stored + * at offset 0x0078 in NVRAM. We use this path name to + * construct the root file system path. + */ + cp = *cmd_cp; + + /* build command string. + */ + rp = nfsroot_string; + while (*rp != 0) + *cp++ = *rp++; + + /* Add the server address to the path. + */ + while (*dp != ' ') + *cp++ = *dp++; + *cp++ = ':'; + + rp = (char *)0xfa000078; + ep = strrchr(rp, '/'); + + if (ep != 0) { + while (rp < ep) + *cp++ = *rp++; + } + else { + rp = defroot_string; + while (*rp != 0) + *cp++ = *rp++; + } + + *cmd_cp = cp; +} + +size_t strlen(const char * s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +int strncmp(const char * cs,const char * ct,size_t count) +{ + register signed char __res = 0; + + while (count) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + + return __res; +} + +char * strrchr(const char * s, int c) +{ + const char *p = s + strlen(s); + do { + if (*p == (char)c) + return (char *)p; + } while (--p >= s); + return NULL; +} + +void puthex(unsigned long val) +{ + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + puts(buf); +} + +/* + * PCI/ISA I/O support + */ + +volatile unsigned char *ISA_io = (unsigned char *)0x80000000; +volatile unsigned char *ISA_mem = (unsigned char *)0xC0000000; + +void +outb(int port, char val) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + ISA_io[port] = val; +} + +unsigned char +inb(int port) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + return (ISA_io[port]); +} + +unsigned long +local_to_PCI(unsigned long addr) +{ + return ((addr & 0x7FFFFFFF) | 0x80000000); +} + +void +_bcopy(char *src, char *dst, int len) +{ + while (len--) *dst++ = *src++; +} diff --git a/arch/ppc/mbxboot/offset b/arch/ppc/mbxboot/offset new file mode 100644 index 000000000..52a1b5546 --- /dev/null +++ b/arch/ppc/mbxboot/offset @@ -0,0 +1,4 @@ +#!/bin/bash + +OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux| awk '{print $6}'` +echo "0x"$OFFSET diff --git a/arch/ppc/mbxboot/size b/arch/ppc/mbxboot/size new file mode 100644 index 000000000..6c48f8d14 --- /dev/null +++ b/arch/ppc/mbxboot/size @@ -0,0 +1,4 @@ +#!/bin/bash + +OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'` +echo "0x"$OFFSET diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index 42e2918b1..92299e825 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -140,7 +140,8 @@ good_area: if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } - handle_mm_fault(current, vma, address, error_code & 0x02000000); + if (!handle_mm_fault(current, vma, address, error_code & 0x02000000)) + goto bad_area; up(&mm->mmap_sem); /* * keep track of tlb+htab misses that are good addrs but diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index b80d4c21c..eac750568 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -1,5 +1,5 @@ /* - * $Id: init.c,v 1.139 1998/12/29 19:53:49 cort Exp $ + * $Id: init.c,v 1.164 1999/05/05 17:33:55 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -34,6 +34,7 @@ #include <linux/vmalloc.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/openpic.h> #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> /* for initrd_* */ #endif @@ -70,10 +71,12 @@ struct device_node *memory_node; unsigned long ioremap_base; unsigned long ioremap_bot; unsigned long avail_start; -struct pgtable_cache_struct quicklists; extern int num_memory; extern struct mem_info memory[NUM_MEMINFO]; extern boot_infos_t *boot_infos; +#ifndef __SMP__ +struct pgtable_cache_struct quicklists; +#endif void MMU_init(void); static void *MMU_get_page(void); @@ -116,12 +119,19 @@ extern struct task_struct *current_set[NR_CPUS]; PTE *Hash, *Hash_end; unsigned long Hash_size, Hash_mask; #ifndef CONFIG_8xx +#ifdef CONFIG_PPC64 +unsigned long long _SDR1; +#else unsigned long _SDR1; +#endif static void hash_init(void); union ubat { /* BAT register values to be loaded */ BAT bat; - P601_BAT bat_601; +#ifdef CONFIG_PPC64 + u64 word[2]; +#else u32 word[2]; +#endif } BATS[4][2]; /* 4 pairs of IBAT, DBAT */ struct batrange { /* stores address ranges mapped by BATs */ @@ -129,6 +139,8 @@ struct batrange { /* stores address ranges mapped by BATs */ unsigned long limit; unsigned long phys; } bat_addrs[4]; +unsigned long inline v_mapped_by_bats(unsigned long); +unsigned long inline p_mapped_by_bats(unsigned long); #endif /* CONFIG_8xx */ /* @@ -329,15 +341,40 @@ __ioremap(unsigned long addr, unsigned long size, unsigned long flags) * virt == phys; for addresses below this we use * space going down from ioremap_base (ioremap_bot * records where we're up to). - * - * We should also look out for a frame buffer and - * map it with a free BAT register, if there is one. */ p = addr & PAGE_MASK; size = PAGE_ALIGN(addr + size) - p; + + /* + * Don't allow anybody to remap normal RAM that we're using. + * mem_init() sets high_memory so only do the check after that. + */ + if ( mem_init_done && (p < virt_to_phys(high_memory)) ) + { + printk("__ioremap(): phys addr %0lx is RAM lr %p\n", p, + __builtin_return_address(0)); + return NULL; + } + if (size == 0) return NULL; +#ifndef CONFIG_8xx + /* + * Is it already mapped? Perhaps overlapped by a previous + * BAT mapping. If the whole area is mapped then we're done, + * otherwise remap it since we want to keep the virt addrs for + * each request contiguous. + * + * We make the assumption here that if the bottom and top + * of the range we want are mapped then it's mapped to the + * same virt address (and this is contiguous). + * -- Cort + */ + if ( (v = p_mapped_by_bats(addr)) /*&& p_mapped_by_bats(addr+(size-1))*/ ) + goto out; +#endif /* CONFIG_8xx */ + if (mem_init_done) { struct vm_struct *area; area = get_vm_area(size); @@ -355,10 +392,17 @@ __ioremap(unsigned long addr, unsigned long size, unsigned long flags) flags |= pgprot_val(PAGE_KERNEL); if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) flags |= _PAGE_GUARDED; + +#ifndef CONFIG_8xx + /* + * Is it a candidate for a BAT mapping? + */ +#endif /* CONFIG_8xx */ + for (i = 0; i < size; i += PAGE_SIZE) map_page(&init_task, v+i, p+i, flags); - - return (void *) (v + (addr & ~PAGE_MASK)); +out: + return (void *) (v + (p & ~PAGE_MASK)); } void iounmap(void *addr) @@ -409,9 +453,7 @@ map_page(struct task_struct *tsk, unsigned long va, { pmd_t *pd; pte_t *pg; -#ifndef CONFIG_8xx - int b; -#endif + if (tsk->mm->pgd == NULL) { /* Allocate upper level page map */ tsk->mm->pgd = (pgd_t *) MMU_get_page(); @@ -419,20 +461,8 @@ map_page(struct task_struct *tsk, unsigned long va, /* Use upper 10 bits of VA to index the first level map */ pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT)); if (pmd_none(*pd)) { -#ifndef CONFIG_8xx - /* - * Need to allocate second-level table, but first - * check whether this address is already mapped by - * the BATs; if so, don't bother allocating the page. - */ - for (b = 0; b < 4; ++b) { - if (va >= bat_addrs[b].start - && va <= bat_addrs[b].limit) { - /* XXX should check the phys address matches */ - return; - } - } -#endif /* CONFIG_8xx */ + if ( v_mapped_by_bats(va) ) + return; pg = (pte_t *) MMU_get_page(); pmd_val(*pd) = (unsigned long) pg; } @@ -677,6 +707,33 @@ static void get_mem_prop(char *, struct mem_pieces *); static void sort_mem_pieces(struct mem_pieces *); static void coalesce_mem_pieces(struct mem_pieces *); +/* + * Return 1 if this VA is mapped by BATs + */ +unsigned long inline v_mapped_by_bats(unsigned long va) +{ + int b; + for (b = 0; b < 4; ++b) + if (va >= bat_addrs[b].start + && va < bat_addrs[b].limit) + return 1; + return 0; +} + +/* + * Return VA for a given PA or 0 if not mapped + */ +unsigned long inline p_mapped_by_bats(unsigned long pa) +{ + int b; + for (b = 0; b < 4; ++b) + if (pa >= bat_addrs[b].phys + && pa < (bat_addrs[b].limit-bat_addrs[b].start) + +bat_addrs[b].phys) + return bat_addrs[b].start+(pa-bat_addrs[b].phys); + return 0; +} + __initfunc(static void sort_mem_pieces(struct mem_pieces *mp)) { unsigned long a, s; @@ -833,7 +890,7 @@ __initfunc(static void mapin_ram(void)) setbat(2, KERNELBASE, mem_base, bl, RAM_PAGE); done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1; - if (done < tot) { + if ((done < tot) && !bat_addrs[3].limit) { /* use BAT3 to cover a bit more */ tot -= done; for (bl = 128<<10; bl < max_size; bl <<= 1) @@ -883,7 +940,8 @@ __initfunc(static void mapin_ram(void)) } } -__initfunc(static void *MMU_get_page(void)) +/* This can get called from ioremap, so don't make it an initfunc, OK? */ +static void *MMU_get_page(void) { void *p; @@ -926,14 +984,16 @@ __initfunc(void free_initmem(void)) break; case _MACH_prep: FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); - FREESEC(__openfirmware_begin,__openfirmware_end,num_openfirmware_pages); break; case _MACH_mbx: FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); - FREESEC(__openfirmware_begin,__openfirmware_end,num_openfirmware_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); break; } + + if ( !have_of ) + FREESEC( __openfirmware_begin, __openfirmware_end, + num_openfirmware_pages ); printk ("Freeing unused kernel memory: %ldk init", (num_freed_pages * PAGE_SIZE) >> 10); @@ -955,7 +1015,6 @@ __initfunc(void free_initmem(void)) */ __initfunc(void MMU_init(void)) { - #ifdef __SMP__ if ( first_cpu_booted ) return; #endif /* __SMP__ */ @@ -985,10 +1044,13 @@ __initfunc(void MMU_init(void)) switch (_machine) { case _MACH_prep: setbat(0, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); - setbat(1, 0xd0000000, 0xc0000000, 0x10000000, IO_PAGE); + setbat(1, 0xf0000000, 0xc0000000, 0x08000000, IO_PAGE); + ioremap_base = 0xf0000000; break; case _MACH_chrp: setbat(0, 0xf8000000, 0xf8000000, 0x08000000, IO_PAGE); + setbat(1, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); + setbat(3, 0x90000000, 0x90000000, 0x10000000, IO_PAGE); break; case _MACH_Pmac: { @@ -1029,6 +1091,7 @@ __initfunc(void MMU_init(void)) ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE); /* ide needs to be able to get at PCI space -- Cort */ ioremap(0x80000000, 0x4000); + ioremap(0x81000000, 0x4000); #endif /* CONFIG_8xx */ } @@ -1236,6 +1299,9 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) unsigned long a, total; unsigned long kstart, ksize; int i; + + /* max amount of RAM we allow -- Cort */ +#define RAM_LIMIT (768<<20) memory_node = find_devices("memory"); if (memory_node == NULL) { @@ -1260,7 +1326,18 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) a = phys_mem.regions[0].address; if (a != 0) panic("RAM doesn't start at physical address 0"); + /* + * XXX: + * Make sure ram mappings don't stomp on IO space + * This is a temporary hack to keep this from happening + * until we move the KERNELBASE and can allocate RAM up + * to our nearest IO area. + * -- Cort + */ + if ( phys_mem.regions[0].size >= RAM_LIMIT ) + phys_mem.regions[0].size = RAM_LIMIT; total = phys_mem.regions[0].size; + if (phys_mem.n_regions > 1) { printk("RAM starting at 0x%x is not contiguous\n", phys_mem.regions[1].address); @@ -1277,8 +1354,15 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) } prom_mem = phys_mem; for (i = 0; i < phys_avail.n_regions; ++i) + { + if ( phys_avail.regions[i].address >= RAM_LIMIT ) + continue; + if ( (phys_avail.regions[i].address+phys_avail.regions[i].size) + >= RAM_LIMIT ) + phys_avail.regions[i].size = RAM_LIMIT - phys_avail.regions[i].address; remove_mem_piece(&prom_mem, phys_avail.regions[i].address, phys_avail.regions[i].size, 1); + } /* * phys_avail records memory we can use now. @@ -1292,7 +1376,7 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) remove_mem_piece(&prom_mem, kstart, ksize, 0); remove_mem_piece(&phys_avail, 0, 0x4000, 0); remove_mem_piece(&prom_mem, 0, 0x4000, 0); - +#undef RAM_LIMIT return __va(total); } @@ -1421,10 +1505,18 @@ __initfunc(static void hash_init(void)) * up to a maximum of 2MB. */ ramsize = (ulong)end_of_DRAM - KERNELBASE; +#ifdef CONFIG_PPC64 + Hash_mask = 0; + for (h = 256<<10; h < ramsize / 256 && h < 4<<20; h *= 2, Hash_mask++) + ; + Hash_size = h; + Hash_mask << 10; /* so setting _SDR1 works the same -- Cort */ +#else for (h = 64<<10; h < ramsize / 256 && h < 2<<20; h *= 2) ; Hash_size = h; Hash_mask = (h >> 6) - 1; +#endif #ifdef NO_RELOAD_HTAB /* shrink the htab since we don't use it on 603's -- Cort */ @@ -1502,4 +1594,3 @@ __initfunc(static void hash_init(void)) } } #endif /* ndef CONFIG_8xx */ - diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig index 811d599c0..274f6c679 100644 --- a/arch/ppc/pmac_defconfig +++ b/arch/ppc/pmac_defconfig @@ -102,7 +102,6 @@ CONFIG_NETLINK=y # CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set # CONFIG_FIREWALL is not set -CONFIG_NET_ALIAS=y # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -263,6 +262,7 @@ CONFIG_PPP=y # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig index 14b4ea4a0..01c314cb3 100644 --- a/arch/ppc/prep_defconfig +++ b/arch/ppc/prep_defconfig @@ -84,7 +84,6 @@ CONFIG_PARIDE_PARPORT=y # CONFIG_PACKET is not set # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback.c index 21a32932a..b75edb418 100644 --- a/arch/sparc/boot/piggyback.c +++ b/arch/sparc/boot/piggyback.c @@ -1,4 +1,4 @@ -/* $Id: piggyback.c,v 1.1 1997/07/18 06:26:14 ralf Exp $ +/* $Id: piggyback.c,v 1.2 1998/12/15 12:24:43 jj Exp $ Simple utility to make a single-image install kernel with initial ramdisk for Sparc tftpbooting without need to set up nfs. @@ -46,16 +46,21 @@ int main(int argc,char **argv) struct stat s; int image, tail; + start = end = 0; if (stat (argv[3], &s) < 0) die (argv[3]); map = fopen (argv[2], "r"); if (!map) die(argv[2]); while (fgets (buffer, 1024, map)) { - if (!strcmp (buffer + 11, "start\n")) + if (!strcmp (buffer + 8, " T start\n") || !strcmp (buffer + 16, " T start\n")) start = strtoul (buffer, NULL, 16); - else if (!strcmp (buffer + 11, "end\n")) + else if (!strcmp (buffer + 8, " A end\n") || !strcmp (buffer + 16, " A end\n")) end = strtoul (buffer, NULL, 16); } fclose (map); + if (!start || !end) { + fprintf (stderr, "Could not determine start and end from System.map\n"); + exit(1); + } if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]); if (read(image,buffer,512) != 512) die(argv[1]); if (!memcmp (buffer, "\177ELF", 4)) { diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 6220b2085..1fc89b977 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.63 1998/09/21 05:05:56 jj Exp $ +# $Id: config.in,v 1.68 1999/03/14 03:12:42 anton Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -25,7 +25,7 @@ define_bool CONFIG_VT y define_bool CONFIG_VT_CONSOLE y bool 'Support for AP1000 multicomputer' CONFIG_AP1000 -bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Symmetric multi-processing support (does not work on sun4/sun4c)' CONFIG_SMP if [ "$CONFIG_AP1000" = "y" ]; then define_bool CONFIG_NO_KEYBOARD y @@ -165,6 +165,9 @@ if [ "$CONFIG_NET" = "y" ]; then fi tristate 'Sun LANCE support' CONFIG_SUNLANCE tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Sun BigMAC 10/100baseT support' CONFIG_SUNBMAC + fi tristate 'Sun QuadEthernet support' CONFIG_SUNQE tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS # bool 'FDDI driver support' CONFIG_FDDI @@ -174,6 +177,15 @@ if [ "$CONFIG_NET" = "y" ]; then endmenu fi +# This one must be before the filesystem configs. -DaveM +mainmenu_option next_comment +comment 'Unix98 PTY support' +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi +endmenu + source fs/Config.in mainmenu_option next_comment diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 8e6d9e2f5..102ba924f 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -101,7 +101,6 @@ CONFIG_BLK_DEV_NBD=m CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -118,7 +117,6 @@ CONFIG_INET=y # (it is safe to leave these untouched) # CONFIG_INET_RARP=m -CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m # CONFIG_IPV6_EUI64 is not set @@ -139,6 +137,10 @@ CONFIG_ATALK=m # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -181,11 +183,13 @@ CONFIG_FC4=m # FC4 drivers # CONFIG_FC4_SOC=m +CONFIG_FC4_SOCAL=m # # FC4 targets # CONFIG_SCSI_PLUTO=m +CONFIG_SCSI_FCAL=m # # Network device support @@ -203,28 +207,51 @@ CONFIG_SLIP_SMART=y # CONFIG_SLIP_MODE_SLIP6 is not set CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=m +CONFIG_SUNBMAC=m CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m # +# Unix98 PTY support +# +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# # Filesystems # # CONFIG_QUOTA is not set -CONFIG_MINIX_FS=m -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=m -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=m +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +# CONFIG_HFS_FS is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +CONFIG_HPFS_FS=m CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +CONFIG_UFS_FS_WRITE=y + +# +# Network File Systems +# +CONFIG_CODA_FS=m CONFIG_NFS_FS=y CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -CONFIG_CODA_FS=m CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m @@ -234,21 +261,18 @@ CONFIG_NCP_FS=m # CONFIG_NCPFS_NFS_NS is not set # CONFIG_NCPFS_OS2_NS is not set # CONFIG_NCPFS_MOUNT_SUBDIR is not set -CONFIG_HPFS_FS=m -# CONFIG_NTFS_FS is not set -CONFIG_SYSV_FS=m -CONFIG_AFFS_FS=m -# CONFIG_HFS_FS is not set -CONFIG_ROMFS_FS=m -CONFIG_AUTOFS_FS=m -CONFIG_AMIGA_PARTITION=y -CONFIG_UFS_FS=m +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# CONFIG_BSD_DISKLABEL=y +# CONFIG_MAC_PARTITION is not set CONFIG_SMD_DISKLABEL=y CONFIG_SOLARIS_X86_PARTITION=y -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_AMIGA_PARTITION=y CONFIG_NLS=y # @@ -279,6 +303,7 @@ CONFIG_NLS=y # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index d5f65dda3..f4d5e3f67 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.48 1998/09/21 05:04:46 jj Exp $ +# $Id: Makefile,v 1.49 1999/01/02 16:45:37 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c index 69f3070e5..11f1587dd 100644 --- a/arch/sparc/kernel/devices.c +++ b/arch/sparc/kernel/devices.c @@ -29,24 +29,25 @@ device_scan(unsigned long mem_start)) prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str)); + prom_printf("Booting Linux...\n"); if(strcmp(node_str, "cpu") == 0) { linux_num_cpus++; } else { int scan; scan = prom_getchild(prom_root_node); - prom_printf("root child is %08lx\n", (unsigned long) scan); + /* One can look it up in PROM instead */ + /* prom_printf("root child is %08lx\n", (unsigned long) scan); */ while((scan = prom_getsibling(scan)) != 0) { prom_getstring(scan, "device_type", node_str, sizeof(node_str)); if(strcmp(node_str, "cpu") == 0) { linux_cpus[linux_num_cpus].prom_node = scan; prom_getproperty(scan, "mid", (char *) &thismid, sizeof(thismid)); linux_cpus[linux_num_cpus].mid = thismid; - prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", - linux_num_cpus, (unsigned long) scan, - thismid); + /* prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", linux_num_cpus, (unsigned long) scan, thismid); */ + printk("Found CPU %d <node=%08lx,mid=%d>\n", linux_num_cpus, (unsigned long) scan, thismid); linux_num_cpus++; } - }; + } if(linux_num_cpus == 0) { if (sparc_cpu_model == sun4d) { scan = prom_getchild(prom_root_node); @@ -59,9 +60,10 @@ device_scan(unsigned long mem_start)) prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid)); linux_cpus[linux_num_cpus].prom_node = node; linux_cpus[linux_num_cpus].mid = thismid; - prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", - linux_num_cpus, (unsigned long) node, - thismid); + /* prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", + linux_num_cpus, (unsigned long) node, thismid); */ + printk("Found CPU %d <node=%08lx,mid=%d>\n", + linux_num_cpus, (unsigned long) node, thismid); linux_num_cpus++; } } diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index d628c0c8d..8eeac72b0 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1,10 +1,10 @@ -/* $Id: entry.S,v 1.153 1998/11/11 15:12:33 jj Exp $ +/* $Id: entry.S,v 1.159 1999/05/08 03:00:03 davem Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) - * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) */ @@ -874,7 +874,7 @@ C_LABEL(vac_hwflush_patch2_on): sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG ! We want error in %l5, vaddr in %l6 sun4c_fault: #ifdef CONFIG_SUN4 - sethi C_LABEL(sun4c_memerr_reg), %l4 + sethi %hi(C_LABEL(sun4c_memerr_reg)), %l4 ld [%l4+%lo(C_LABEL(sun4c_memerr_reg))], %l4 ! memerr ctrl reg addr ld [%l4], %l6 ! memerr ctrl reg ld [%l4 + 4], %l5 ! memerr vaddr reg @@ -956,7 +956,7 @@ sun4c_fault: sll %l6, 2, %l6 ld [%l4 + %l6], %l4 #ifdef CONFIG_SUN4 - sethi PAGE_MASK, %l6 + sethi %hi(PAGE_MASK), %l6 andcc %l4, %l6, %g0 #else andcc %l4, PAGE_MASK, %g0 @@ -1117,7 +1117,7 @@ C_LABEL(num_context_patch2): #ifndef CONFIG_SUN4 and %l4, PAGE_MASK, %l4 #else - sethi PAGE_MASK, %l6 + sethi %hi(PAGE_MASK), %l6 and %l4, %l6, %l4 #endif @@ -1380,11 +1380,13 @@ C_LABEL(sys_rt_sigreturn): /* Now that we have a real sys_clone, sys_fork() is * implemented in terms of it. Our _real_ implementation - * of SunOS vfork() will use sys_clone() instead. + * of SunOS vfork() will use sys_vfork(). + * + * XXX These three should be consolidated into mostly shared + * XXX code just like on sparc64... -DaveM */ .align 4 - .globl C_LABEL(sys_fork), C_LABEL(sys_vfork), flush_patch_two -C_LABEL(sys_vfork): + .globl C_LABEL(sys_fork), flush_patch_two C_LABEL(sys_fork): mov %o7, %l5 flush_patch_two: @@ -1422,6 +1424,23 @@ flush_patch_three: call C_LABEL(do_fork) mov %l5, %o7 + /* Whee, real vfork! */ + .globl C_LABEL(sys_vfork), flush_patch_four +C_LABEL(sys_vfork): +flush_patch_four: + FLUSH_ALL_KERNEL_WINDOWS; + rd %psr, %g4 + WRITE_PAUSE + rd %wim, %g5 + WRITE_PAUSE + std %g4, [%curptr + AOFF_task_tss + AOFF_thread_fork_kpsr] + sethi %hi(0x4000 | 0x0100 | SIGCHLD), %o0 + mov %fp, %o1 + or %o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0 + sethi %hi(C_LABEL(do_fork)), %l1 + jmpl %l1 + %lo(C_LABEL(do_fork)), %g0 + add %sp, REGWIN_SZ, %o2 + .align 4 linux_sparc_ni_syscall: sethi %hi(C_LABEL(sys_ni_syscall)), %l7 @@ -1454,11 +1473,10 @@ C_LABEL(ret_from_syscall): #ifdef __SMP__ .globl C_LABEL(ret_from_smpfork) C_LABEL(ret_from_smpfork): - /* Nowadays all we need to do is drop the scheduler lock. */ - sethi %hi(C_LABEL(scheduler_lock)), %o4 - stb %g0, [%o4 + %lo(C_LABEL(scheduler_lock))] wr %l0, PSR_ET, %psr WRITE_PAUSE + call schedule_tail + mov %g3, %o0 b C_LABEL(ret_sys_call) ld [%sp + REGWIN_SZ + PT_I0], %o0 #endif diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 9c396c928..0020770e0 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.92 1998/06/10 07:21:55 davem Exp $ +/* $Id: head.S,v 1.95 1999/04/13 07:40:34 anton Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -408,13 +408,11 @@ C_LABEL(trapbase_cpu3): /* This was the only reasonable way I could think of to properly align * these page-table data structures. */ - .globl C_LABEL(bootup_user_stack) .globl C_LABEL(pg0), C_LABEL(pg1), C_LABEL(pg2), C_LABEL(pg3) .globl C_LABEL(empty_bad_page) .globl C_LABEL(empty_bad_page_table) .globl C_LABEL(empty_zero_page) .globl C_LABEL(swapper_pg_dir) -C_LABEL(bootup_user_stack): .skip 0x2000 C_LABEL(swapper_pg_dir): .skip PAGE_SIZE C_LABEL(pg0): .skip PAGE_SIZE C_LABEL(pg1): .skip PAGE_SIZE @@ -427,8 +425,8 @@ C_LABEL(empty_zero_page): .skip PAGE_SIZE .global C_LABEL(root_flags) .global C_LABEL(ram_flags) .global C_LABEL(root_dev) - .global C_LABEL(ramdisk_image) - .global C_LABEL(ramdisk_size) + .global C_LABEL(sparc_ramdisk_image) + .global C_LABEL(sparc_ramdisk_size) /* This stuff has to be in sync with SILO and other potential boot loaders * Fields should be kept upward compatible and whenever any change is made, @@ -443,9 +441,9 @@ C_LABEL(root_dev): .half 0 C_LABEL(ram_flags): .half 0 -C_LABEL(ramdisk_image): +C_LABEL(sparc_ramdisk_image): .word 0 -C_LABEL(ramdisk_size): +C_LABEL(sparc_ramdisk_size): .word 0 .word C_LABEL(reboot_command) @@ -1005,8 +1003,8 @@ sun4c_continue_boot: WRITE_PAUSE /* I want a kernel stack NOW! */ - set C_LABEL(bootup_user_stack), %g1 - set (0x2000 - REGWIN_SZ), %g2 + set C_LABEL(init_task_union), %g1 + set (TASK_UNION_SIZE - REGWIN_SZ), %g2 add %g1, %g2, %sp mov 0, %fp /* And for good luck */ @@ -1110,6 +1108,9 @@ sun4c_continue_boot: set flush_patch_three, %g5 st %g4, [%g5 + 0x18] st %g4, [%g5 + 0x1c] + set flush_patch_four, %g5 + st %g4, [%g5 + 0x18] + st %g4, [%g5 + 0x1c] set flush_patch_exception, %g5 st %g4, [%g5 + 0x18] st %g4, [%g5 + 0x1c] diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index 4c8f78c8a..26f3194bd 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.91 1998/10/14 07:04:17 jj Exp $ +/* $Id: irq.c,v 1.93 1999/04/21 06:15:45 anton Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device @@ -8,7 +8,7 @@ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1995 Pete A. Zaitcev (zaitcev@metabyte.com) * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk) - * Copyright (C) 1998 Anton Blanchard (anton@progsoc.uts.edu.au) + * Copyright (C) 1998-99 Anton Blanchard (anton@progsoc.uts.edu.au) */ #include <linux/config.h> @@ -24,6 +24,8 @@ #include <linux/init.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/delay.h> +#include <linux/tasks.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -192,12 +194,18 @@ void free_irq(unsigned int irq, void *dev_id) restore_flags(flags); } -/* Per-processor IRQ and bh locking depth, both SMP and non-SMP code use this. */ +#ifndef __SMP__ +unsigned int local_bh_count; +unsigned int local_irq_count; + +#else +/* SMP interrupt locking on Sparc. */ + unsigned int local_bh_count[NR_CPUS]; unsigned int local_irq_count[NR_CPUS]; -#ifdef __SMP__ -/* SMP interrupt locking on Sparc. */ +atomic_t global_bh_lock = ATOMIC_INIT(0); +spinlock_t global_bh_count = SPIN_LOCK_UNLOCKED; /* Who has global_irq_lock. */ unsigned char global_irq_holder = NO_PROC_ID; @@ -208,58 +216,71 @@ spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; /* Global IRQ locking depth. */ atomic_t global_irq_count = ATOMIC_INIT(0); -atomic_t global_bh_count = ATOMIC_INIT(0); -atomic_t global_bh_lock = ATOMIC_INIT(0); - /* This protects BH software state (masks, things like that). */ spinlock_t sparc_bh_lock = SPIN_LOCK_UNLOCKED; -#ifdef DEBUG_IRQLOCK +void smp_show_backtrace_all_cpus(void); +void show_backtrace(void); -#undef INIT_STUCK -#define INIT_STUCK 100000000 +#define MAXCOUNT 100000000 +#define VERBOSE_DEBUG_IRQLOCK -#undef STUCK -#define STUCK \ -if (!--stuck) {printk("wait_on_bh CPU#%d stuck at %08lx\n", cpu, where); stuck = INIT_STUCK; } - -static inline void wait_on_bh(int cpu, unsigned long where) +static void show(char * str) { - int stuck = INIT_STUCK; - do { - STUCK; - /* nothing .. wait for the other bh's to go away */ - } while (atomic_read(&global_bh_count) != 0); -} + int i; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [ ", atomic_read(&global_irq_count)); -static unsigned long previous_irqholder; + for (i = 0; i < NR_CPUS; i++) { + printk("%d ", local_irq_count[i]); + } + printk("]\n"); -#undef INIT_STUCK -#define INIT_STUCK 100000000 + printk("bh: %d [ ", (spin_is_locked(&global_bh_count) ? 1 : 0)); -#undef STUCK -#define STUCK \ -if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, atomic_read(&global_irq_count)); stuck = INIT_STUCK; } + for (i = 0; i < NR_CPUS; i++) { + printk("%d ", local_bh_count[cpu]); + } + printk("]\n"); + +#ifdef VERBOSE_DEBUG_IRQLOCK + smp_show_backtrace_all_cpus(); +#else + show_backtrace(); +#endif +} + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if(!--count) { + show("wait_on_bh"); + count = 0; + } + barrier(); + } while(spin_is_locked(&global_bh_count)); +} /* * We have to allow irqs to arrive between __sti and __cli */ -#define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") +#define SYNC_OTHER_CORES(x) udelay(x+1) -static inline void wait_on_irq(int cpu, unsigned long where) +static inline void wait_on_irq(int cpu) { - int stuck = INIT_STUCK; - int local_count = local_irq_count[cpu]; + int count = MAXCOUNT; for (;;) { - /* * Wait until all interrupts are gone. Wait * for bottom half handlers unless we're * already executing in one.. */ if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) + if (local_bh_count[cpu] || !spin_is_locked(&global_bh_count)) break; } @@ -267,17 +288,18 @@ static inline void wait_on_irq(int cpu, unsigned long where) spin_unlock(&global_irq_lock); for (;;) { - STUCK; - + if (!--count) { + show("wait_on_irq"); + count = ~0; + } __sti(); SYNC_OTHER_CORES(cpu); __cli(); - if (atomic_read(&global_irq_count)) continue; - if (*((unsigned char *)&global_irq_lock)) + if (spin_is_locked (&global_irq_lock)) continue; - if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) + if (!local_bh_count[cpu] && spin_is_locked(&global_bh_count)) continue; if (spin_trylock(&global_irq_lock)) break; @@ -295,14 +317,8 @@ static inline void wait_on_irq(int cpu, unsigned long where) */ void synchronize_bh(void) { - unsigned long where; - - __asm__("mov %%i7, %0" : "=r" (where)); - - if (atomic_read(&global_bh_count) && !in_interrupt()) { - int cpu = smp_processor_id(); - wait_on_bh(cpu, where); - } + if (spin_is_locked (&global_bh_count) && !in_interrupt()) + wait_on_bh(); } /* @@ -321,16 +337,9 @@ void synchronize_irq(void) } } -#undef INIT_STUCK -#define INIT_STUCK 10000000 - -#undef STUCK -#define STUCK \ -if (!--stuck) {printk("get_irqlock stuck at %08lx, waiting for %08lx\n", where, previous_irqholder); stuck = INIT_STUCK;} - -static inline void get_irqlock(int cpu, unsigned long where) +static inline void get_irqlock(int cpu) { - int stuck = INIT_STUCK; + int count = MAXCOUNT; if (!spin_trylock(&global_irq_lock)) { /* do we already hold the lock? */ @@ -338,23 +347,25 @@ static inline void get_irqlock(int cpu, unsigned long where) return; /* Uhhuh.. Somebody else got it. Wait.. */ do { - do { - STUCK; + while (spin_is_locked(&global_irq_lock)) { + if (!--count) { + show("get_irqlock"); + count = ~0; + } barrier(); - } while (*((volatile unsigned char *)&global_irq_lock)); + } } while (!spin_trylock(&global_irq_lock)); } /* * We also to make sure that nobody else is running * in an interrupt context. */ - wait_on_irq(cpu, where); + wait_on_irq(cpu); /* * Ok, finally.. */ global_irq_holder = cpu; - previous_irqholder = where; } /* @@ -372,9 +383,6 @@ static inline void get_irqlock(int cpu, unsigned long where) void __global_cli(void) { unsigned int flags; - unsigned long where; - - __asm__("mov %%i7, %0" : "=r" (where)); __save_flags(flags); @@ -382,7 +390,7 @@ void __global_cli(void) int cpu = smp_processor_id(); __cli(); if (!local_irq_count[cpu]) - get_irqlock(cpu, where); + get_irqlock(cpu); } } @@ -442,65 +450,14 @@ void __global_restore_flags(unsigned long flags) __sti(); break; default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); + { + unsigned long pc; + __asm__ __volatile__("mov %%i7, %0" : "=r" (pc)); + printk("global_restore_flags: Bogon flags(%08lx) caller %08lx\n", flags, pc); } -} - -#undef INIT_STUCK -#define INIT_STUCK 200000000 - -#undef STUCK -#define STUCK \ -if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;} - -#define VERBOSE_IRQLOCK_DEBUGGING - -void irq_enter(int cpu, int irq, void *_opaque) -{ -#ifdef VERBOSE_IRQLOCK_DEBUGGING - extern void smp_show_backtrace_all_cpus(void); -#endif - int stuck = INIT_STUCK; - - hardirq_enter(cpu); - barrier(); - while (*((volatile unsigned char *)&global_irq_lock)) { - if ((unsigned char) cpu == global_irq_holder) { - struct pt_regs *regs = _opaque; - int sbh_cnt = atomic_read(&global_bh_count); - int globl_locked = *((unsigned char *)&global_irq_lock); - int globl_icount = atomic_read(&global_irq_count); - int local_count = local_irq_count[cpu]; - unsigned long pc = regs->pc; - - /* It is very important that we load the state variables - * before we do the first call to printk() as printk() - * could end up changing them... - */ - - printk("CPU[%d]: BAD! Local IRQ's enabled, global disabled " - "interrupt at PC[%08lx]\n", cpu, pc); - printk("CPU[%d]: bhcnt[%d] glocked[%d] gicnt[%d] licnt[%d]\n", - cpu, sbh_cnt, globl_locked, globl_icount, local_count); -#ifdef VERBOSE_IRQLOCK_DEBUGGING - printk("Performing backtrace on all cpus, write this down!\n"); - smp_show_backtrace_all_cpus(); -#endif - break; - } - STUCK; - barrier(); } } -void irq_exit(int cpu, int irq) -{ - hardirq_exit(cpu); - release_irqlock(cpu); -} - -#endif /* DEBUG_IRQLOCK */ #endif /* __SMP__ */ void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs) @@ -542,7 +499,7 @@ void handler_irq(int irq, struct pt_regs * regs) smp4m_irq_rotate(cpu); #endif #endif - irq_enter(cpu, irq, regs); + irq_enter(cpu, irq); action = *(irq + irq_action); kstat.irqs[cpu][irq]++; do { @@ -563,7 +520,7 @@ void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs) int cpu = smp_processor_id(); disable_pil_irq(irq); - irq_enter(cpu, irq, regs); + irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; floppy_interrupt(irq, dev_id, regs); irq_exit(cpu, irq); diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index aae13c515..a2beedbf1 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -1,4 +1,4 @@ -/* $Id: pcic.c,v 1.3 1998/10/07 11:34:56 jj Exp $ +/* $Id: pcic.c,v 1.5 1999/03/16 00:15:20 davem Exp $ * pcic.c: Sparc/PCI controller support * * Copyright (C) 1998 V. Roganov and G. Raiko @@ -498,9 +498,10 @@ static void pci_do_settimeofday(struct timeval *tv) tv->tv_sec--; } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = 0x70000000; - time_esterror = 0x70000000; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 3aeee6f6b..301747c22 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.126 1998/09/21 05:05:18 jj Exp $ +/* $Id: process.c,v 1.137 1999/05/08 03:00:10 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -62,7 +62,9 @@ asmlinkage int sys_idle(void) /* endless idle loop with no priority at all */ current->priority = 0; - current->counter = 0; + current->counter = -100; + init_idle(); + for (;;) { if (ARCH_SUN4C_SUN4) { static int count = HZ; @@ -108,13 +110,17 @@ out: /* This is being executed in task 0 'user space'. */ int cpu_idle(void *unused) { + /* endless idle loop with no priority at all */ current->priority = 0; + current->counter = -100; + init_idle(); + while(1) { - check_pgt_cache(); - run_task_queue(&tq_scheduler); - /* endless idle loop with no priority at all */ - current->counter = 0; - schedule(); + if(current->need_resched) { + schedule(); + check_pgt_cache(); + } + barrier(); /* or else gcc optimizes... */ } } @@ -203,8 +209,10 @@ void __show_backtrace(unsigned long fp) int cpu = smp_processor_id(); spin_lock_irqsave(&sparc_backtrace_lock, flags); - rw = (struct reg_window *) fp; - while(rw) { + + rw = (struct reg_window *)fp; + while(rw && (((unsigned long) rw) >= PAGE_OFFSET) && + !(((unsigned long) rw) & 0x7)) { printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] " "FP[%08lx] CALLER[%08lx]\n", cpu, rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], @@ -216,28 +224,21 @@ void __show_backtrace(unsigned long fp) spin_unlock_irqrestore(&sparc_backtrace_lock, flags); } +#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t") +#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t") +#define __GET_FP(fp) __asm__ __volatile__("mov %%i6, %0" : "=r" (fp)) + void show_backtrace(void) { unsigned long fp; - __asm__ __volatile__( - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "save %%sp, -64, %%sp\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "mov %%i6, %0" : "=r" (fp)); + __SAVE; __SAVE; __SAVE; __SAVE; + __SAVE; __SAVE; __SAVE; __SAVE; + __RESTORE; __RESTORE; __RESTORE; __RESTORE; + __RESTORE; __RESTORE; __RESTORE; __RESTORE; + + __GET_FP(fp); + __show_backtrace(fp); } @@ -379,8 +380,21 @@ void flush_thread(void) current->tss.current_ds = USER_DS; if (current->tss.flags & SPARC_FLAG_KTHREAD) { current->tss.flags &= ~SPARC_FLAG_KTHREAD; - switch_to_context(current); + + /* We must fixup kregs as well. */ + current->tss.kregs = (struct pt_regs *) + (((unsigned long)current) + + (TASK_UNION_SIZE - TRACEREG_SZ)); } + + /* Exec'ing out of a vfork() shared address space is + * tricky on sparc32. exec_mmap will not set the mmu + * context because it sets the new current->mm after + * calling init_new_context and activate_context is + * a nop on sparc32, so we gotta catch it here. And + * clone()'s had the same problem. -DaveM + */ + switch_to_context(current); } static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src) @@ -440,10 +454,17 @@ clone_stackframe(struct sparc_stackf *dst, struct sparc_stackf *src) size = ((unsigned long)src->fp) - ((unsigned long)src); sp = (struct sparc_stackf *)(((unsigned long)dst) - size); + /* do_fork() grabs the parent semaphore, we must release it + * temporarily so we can build the child clone stack frame + * without deadlocking. + */ + up(¤t->mm->mmap_sem); if (copy_to_user(sp, src, size)) - return 0; - if (put_user(dst, &sp->fp)) - return 0; + sp = (struct sparc_stackf *) 0; + else if (put_user(dst, &sp->fp)) + sp = (struct sparc_stackf *) 0; + down(¤t->mm->mmap_sem); + return sp; } @@ -505,14 +526,24 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, p->tss.kpsr = current->tss.fork_kpsr; #endif p->tss.kwim = current->tss.fork_kwim; - p->tss.kregs = childregs; if(regs->psr & PSR_PS) { - childregs->u_regs[UREG_FP] = p->tss.ksp; + extern struct pt_regs fake_swapper_regs; + + p->tss.kregs = &fake_swapper_regs; + new_stack = (struct reg_window *) + ((((unsigned long)p) + + (TASK_UNION_SIZE)) - + (REGWIN_SZ)); + childregs->u_regs[UREG_FP] = (unsigned long) new_stack; p->tss.flags |= SPARC_FLAG_KTHREAD; p->tss.current_ds = KERNEL_DS; + memcpy((void *)new_stack, + (void *)regs->u_regs[UREG_FP], + sizeof(struct reg_window)); childregs->u_regs[UREG_G6] = (unsigned long) p; } else { + p->tss.kregs = childregs; childregs->u_regs[UREG_FP] = sp; p->tss.flags &= ~SPARC_FLAG_KTHREAD; p->tss.current_ds = USER_DS; @@ -658,3 +689,37 @@ out: unlock_kernel(); return error; } + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + + __asm__ __volatile("mov %4, %%g2\n\t" /* Set aside fn ptr... */ + "mov %5, %%g3\n\t" /* and arg. */ + "mov %1, %%g1\n\t" + "mov %2, %%o0\n\t" /* Clone flags. */ + "mov 0, %%o1\n\t" /* usp arg == 0 */ + "t 0x10\n\t" /* Linux/Sparc clone(). */ + "cmp %%o1, 0\n\t" + "be 1f\n\t" /* The parent, just return. */ + " nop\n\t" /* Delay slot. */ + "jmpl %%g2, %%o7\n\t" /* Call the function. */ + " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */ + "mov %3, %%g1\n\t" + "t 0x10\n\t" /* Linux/Sparc exit(). */ + /* Notreached by child. */ + "1: mov %%o0, %0\n\t" : + "=r" (retval) : + "i" (__NR_clone), "r" (flags | CLONE_VM), + "i" (__NR_exit), "r" (fn), "r" (arg) : + "g1", "g2", "g3", "o0", "o1", "memory", "cc"); + return retval; +} diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index ed8fe6a25..7f6ec54f9 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -528,6 +528,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs) if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH)) || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) { + unsigned long flags; + if(child == current) { /* Try this under SunOS/Solaris, bwa haha * You'll never be able to kill the process. ;-) @@ -539,8 +541,9 @@ asmlinkage void do_ptrace(struct pt_regs *regs) (current->uid != child->euid) || (current->uid != child->uid) || (current->gid != child->egid) || - (current->gid != child->gid)) && - !capable(CAP_SYS_PTRACE)) { + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); goto out; } @@ -550,14 +553,13 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } child->flags |= PF_PTRACED; + write_lock_irqsave(&tasklist_lock, flags); if(child->p_pptr != current) { - unsigned long flags; - write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); send_sig(SIGSTOP, child, 1); pt_succ_return(regs, 0); goto out; diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 84190cf5a..d29c1cb66 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.103 1998/09/21 05:05:23 jj Exp $ +/* $Id: setup.c,v 1.105 1999/04/13 14:17:08 jj Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -271,8 +271,8 @@ extern unsigned long sun_serial_setup(unsigned long); extern unsigned short root_flags; extern unsigned short root_dev; extern unsigned short ram_flags; -extern unsigned ramdisk_image; -extern unsigned ramdisk_size; +extern unsigned sparc_ramdisk_image; +extern unsigned sparc_ramdisk_size; #define RAMDISK_IMAGE_START_MASK 0x07FF #define RAMDISK_PROMPT_FLAG 0x8000 #define RAMDISK_LOAD_FLAG 0x4000 @@ -285,7 +285,7 @@ enum sparc_cpu sparc_cpu_model; struct tt_entry *sparc_ttable; -static struct pt_regs fake_swapper_regs = { 0, 0, 0, 0, { 0, } }; +struct pt_regs fake_swapper_regs = { 0, 0, 0, 0, { 0, } }; static void prom_cons_write(struct console *con, const char *str, unsigned count) { @@ -375,7 +375,7 @@ __initfunc(void setup_arch(char **cmdline_p, sun4c_probe_vac(); load_mmu(); total = prom_probe_memory(); - *memory_start_p = (((unsigned long) &end)); + *memory_start_p = PAGE_ALIGN(((unsigned long) &end)); if(!packed) { for(i=0; sp_banks[i].num_bytes != 0; i++) { @@ -404,10 +404,10 @@ __initfunc(void setup_arch(char **cmdline_p, rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0); #endif #ifdef CONFIG_BLK_DEV_INITRD - if (ramdisk_image) { - initrd_start = ramdisk_image; + if (sparc_ramdisk_image) { + initrd_start = sparc_ramdisk_image; if (initrd_start < KERNBASE) initrd_start += KERNBASE; - initrd_end = initrd_start + ramdisk_size; + initrd_end = initrd_start + sparc_ramdisk_size; if (initrd_end > *memory_end_p) { printk(KERN_CRIT "initrd extends beyond end of memory " "(0x%08lx > 0x%08lx)\ndisabling initrd\n", @@ -417,6 +417,14 @@ __initfunc(void setup_arch(char **cmdline_p, if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) { initrd_below_start_ok = 1; *memory_start_p = PAGE_ALIGN (initrd_end); + } else if (initrd_start && sparc_ramdisk_image < KERNBASE) { + switch (sparc_cpu_model) { + case sun4m: + case sun4d: + initrd_start -= KERNBASE; + initrd_end -= KERNBASE; + break; + } } } #endif diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index 287ed6cdc..e6023f43b 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.90 1998/10/18 03:31:05 davem Exp $ +/* $Id: signal.c,v 1.91 1999/01/26 11:00:44 jj Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -38,6 +38,8 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, /* This turned off for production... */ /* #define DEBUG_SIGNALS 1 */ +/* #define DEBUG_SIGNALS_TRACE 1 */ +/* #define DEBUG_SIGNALS_MAPS 1 */ /* Signal frames: the original one (compatible with SunOS): * @@ -1004,6 +1006,59 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, } } +#ifdef DEBUG_SIGNALS_MAPS + +#define MAPS_LINE_FORMAT "%08lx-%08lx %s %08lx %s %lu " + +static inline void read_maps (void) +{ + struct vm_area_struct * map, * next; + char * buffer; + ssize_t i; + + buffer = (char*)__get_free_page(GFP_KERNEL); + if (!buffer) + return; + + for (map = current->mm->mmap ; map ; map = next ) { + /* produce the next line */ + char *line; + char str[5], *cp = str; + int flags; + kdev_t dev; + unsigned long ino; + + /* + * Get the next vma now (but it won't be used if we sleep). + */ + next = map->vm_next; + flags = map->vm_flags; + + *cp++ = flags & VM_READ ? 'r' : '-'; + *cp++ = flags & VM_WRITE ? 'w' : '-'; + *cp++ = flags & VM_EXEC ? 'x' : '-'; + *cp++ = flags & VM_MAYSHARE ? 's' : 'p'; + *cp++ = 0; + + dev = 0; + ino = 0; + if (map->vm_file != NULL) { + dev = map->vm_file->f_dentry->d_inode->i_dev; + ino = map->vm_file->f_dentry->d_inode->i_ino; + line = d_path(map->vm_file->f_dentry, buffer, PAGE_SIZE); + } + printk(MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_offset, + kdevname(dev), ino); + if (map->vm_file != NULL) + printk("%s\n", line); + else + printk("\n"); + } + free_page((unsigned long)buffer); + return; +} +#endif + /* Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. @@ -1115,8 +1170,25 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, } #ifdef DEBUG_SIGNALS /* Very useful to debug dynamic linker problems */ - printk ("Sig ILL going...\n"); + printk ("Sig %ld going for %s[%d]...\n", signr, current->comm, current->pid); show_regs (regs); +#ifdef DEBUG_SIGNALS_TRACE + { + struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP]; + unsigned int ins[8]; + + while(rw && + !(((unsigned long) rw) & 0x3)) { + copy_from_user(ins, &rw->ins[0], sizeof(ins)); + printk("Caller[%08x](%08x,%08x,%08x,%08x,%08x,%08x)\n", ins[7], ins[0], ins[1], ins[2], ins[3], ins[4], ins[5]); + rw = (struct reg_window *)(unsigned long)ins[6]; + } + } +#endif +#ifdef DEBUG_SIGNALS_MAPS + printk("Maps:\n"); + read_maps(); +#endif #endif /* fall through */ default: diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c index f77d823aa..fb55c1ffa 100644 --- a/arch/sparc/kernel/smp.c +++ b/arch/sparc/kernel/smp.c @@ -52,6 +52,7 @@ unsigned char boot_cpu_id4 = 0; /* boot_cpu_id << 2 */ int smp_activated = 0; volatile int cpu_number_map[NR_CPUS]; volatile int __cpu_logical_map[NR_CPUS]; +cycles_t cacheflush_time = 0; /* XXX */ /* The only guaranteed locking primitive available on all Sparc * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 43f963217..b043d647d 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.73 1998/11/06 13:49:54 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.77 1999/03/21 06:37:43 davem Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -66,6 +66,7 @@ extern char saved_command_line[]; extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); +extern int __lshrdi3(int, int); extern void dump_thread(struct pt_regs *, struct user *); @@ -91,6 +92,7 @@ __attribute__((section("__ksymtab"))) = \ /* used by various drivers */ EXPORT_SYMBOL(sparc_cpu_model); EXPORT_SYMBOL_PRIVATE(_spinlock_waitfor); +EXPORT_SYMBOL(kernel_thread); #ifdef SPIN_LOCK_DEBUG EXPORT_SYMBOL(_do_spin_lock); EXPORT_SYMBOL(_do_spin_unlock); @@ -118,6 +120,7 @@ EXPORT_SYMBOL_PRIVATE(_global_cli); #endif EXPORT_SYMBOL(page_offset); +EXPORT_SYMBOL(sparc_valid_addr_bitmap); #ifndef CONFIG_SUN4 EXPORT_SYMBOL(stack_top); @@ -211,6 +214,7 @@ EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_apply_obio_ranges); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); +EXPORT_SYMBOL(prom_getbool); EXPORT_SYMBOL(prom_getstring); EXPORT_SYMBOL(prom_apply_sbus_ranges); EXPORT_SYMBOL(prom_getint); @@ -268,6 +272,7 @@ EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); EXPORT_SYMBOL_DOT(rem); EXPORT_SYMBOL_DOT(urem); diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 93474714a..1d0de8d23 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -1,4 +1,4 @@ -/* $Id: sun4d_irq.c,v 1.17 1998/10/18 03:31:03 davem Exp $ +/* $Id: sun4d_irq.c,v 1.18 1999/04/20 13:22:30 anton Exp $ * arch/sparc/kernel/sun4d_irq.c: * SS1000/SC2000 interrupt handling. * @@ -193,7 +193,7 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs) cc_set_iclr(1 << irq); - irq_enter(cpu, irq, regs); + irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; if (!sbusl) { action = *(irq + irq_action); diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index af0aaf58d..edd736b41 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -190,6 +190,7 @@ __initfunc(void smp4d_boot_cpus(void)) current->processor = boot_cpu_id; smp_store_cpu_info(boot_cpu_id); smp_setup_percpu_timer(); + init_idle(); local_flush_cache_all(); if(linux_num_cpus == 1) return; /* Not an MP box. */ @@ -211,6 +212,7 @@ __initfunc(void smp4d_boot_cpus(void)) p = task[++cpucount]; p->processor = i; + p->has_cpu = 1; /* we schedule the first task manually */ current_set[i] = p; for (no = 0; no < linux_num_cpus; no++) diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index bd6fc8e20..4d7177dac 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -253,7 +253,7 @@ __initfunc(static void sun4m_init_timers(void (*counter_fn)(int, void *, struct /* Map the per-cpu Counter registers. */ sun4m_timers = sparc_alloc_io(cnt_regs[0].phys_addr, 0, - PAGE_SIZE*NCPUS, "counters_percpu", + PAGE_SIZE*SUN4M_NCPUS, "counters_percpu", cnt_regs[0].which_io, 0x0); /* Map the system Counter register. */ @@ -334,7 +334,7 @@ __initfunc(void sun4m_init_IRQ(void)) /* Map the interrupt registers for all possible cpus. */ sun4m_interrupts = sparc_alloc_io(int_regs[0].phys_addr, 0, - PAGE_SIZE*NCPUS, "interrupts_percpu", + PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu", int_regs[0].which_io, 0x0); /* Map the system interrupt control registers. */ diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 183ea7323..c9acf609c 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -161,6 +161,7 @@ __initfunc(void smp4m_boot_cpus(void)) smp_store_cpu_info(boot_cpu_id); set_irq_udt(mid_xlate[boot_cpu_id]); smp_setup_percpu_timer(); + init_idle(); local_flush_cache_all(); if(linux_num_cpus == 1) return; /* Not an MP box. */ @@ -180,6 +181,7 @@ __initfunc(void smp4m_boot_cpus(void)) p = task[++cpucount]; p->processor = i; + p->has_cpu = 1; /* we schedule the first task manually */ current_set[i] = p; /* See trampoline.S for details... */ @@ -448,6 +450,7 @@ void smp4m_percpu_timer_interrupt(struct pt_regs *regs) if(!--prof_counter[cpu]) { int user = user_mode(regs); + irq_enter(cpu, 0); if(current->pid) { update_one_process(current, 1, user, !user, cpu); @@ -456,7 +459,6 @@ void smp4m_percpu_timer_interrupt(struct pt_regs *regs) current->need_resched = 1; } - spin_lock(&ticker_lock); if(user) { if(current->priority < DEF_PRIORITY) { kstat.cpu_nice++; @@ -469,9 +471,9 @@ void smp4m_percpu_timer_interrupt(struct pt_regs *regs) kstat.cpu_system++; kstat.per_cpu_system[cpu]++; } - spin_unlock(&ticker_lock); } prof_counter[cpu] = prof_multiplier[cpu]; + irq_exit(cpu, 0); } } diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index e5ea2e9b3..ed3918c10 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.49 1998/10/11 06:57:53 davem Exp $ +/* $Id: sys_sparc.c,v 1.52 1999/05/08 08:09:48 anton Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -25,6 +25,8 @@ #include <asm/uaccess.h> #include <asm/ipc.h> +/* #define DEBUG_UNIMP_SYSCALL */ + /* XXX Make this per-binary type, this way we can detect the type of * XXX a binary. Every Sparc executable calls this very early on. */ @@ -189,6 +191,7 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, goto out; } retval = -ENOMEM; + len = PAGE_ALIGN(len); if(!(flags & MAP_FIXED) && !addr) { addr = get_unmapped_area(addr, len); if(!addr) @@ -202,6 +205,7 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, if(ARCH_SUN4C_SUN4) { if(((addr >= 0x20000000) && (addr < 0xe0000000))) { + /* VM hole */ retval = current->mm->brk; goto out_putf; } @@ -223,9 +227,14 @@ out: asmlinkage unsigned long c_sys_nis_syscall (struct pt_regs *regs) { + static int count = 0; + + if (count++ > 5) return -ENOSYS; lock_kernel(); - printk ("Unimplemented SPARC system call %d\n",(int)regs->u_regs[1]); + printk ("%s[%d]: Unimplemented SPARC system call %d\n", current->comm, current->pid, (int)regs->u_regs[1]); +#ifdef DEBUG_UNIMP_SYSCALL show_regs (regs); +#endif unlock_kernel(); return -ENOSYS; } diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S index 5508f850a..4d0dfff5f 100644 --- a/arch/sparc/kernel/systbls.S +++ b/arch/sparc/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.80 1998/09/21 05:04:59 jj Exp $ +/* $Id: systbls.S,v 1.83 1999/04/07 17:14:06 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -25,7 +25,7 @@ sys_call_table: /*30*/ .long sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice /*35*/ .long sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_sendfile /*40*/ .long sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall -/*45*/ .long sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid +/*45*/ .long sys_umount, sys_setgid, sys_getgid, sys_signal, sys_geteuid /*50*/ .long sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys_ioctl /*55*/ .long sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve /*60*/ .long sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize @@ -47,7 +47,7 @@ sys_call_table: /*140*/ .long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getrlimit /*145*/ .long sys_setrlimit, sys_nis_syscall, sys_prctl, sys_pciconfig_read, sys_pciconfig_write /*150*/ .long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall -/*155*/ .long sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount +/*155*/ .long sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_oldumount /*160*/ .long sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_nis_syscall /*165*/ .long sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall /*170*/ .long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents @@ -129,7 +129,7 @@ sunos_sys_table: /*150*/ .long sys_getsockname, sunos_nosys, sunos_nosys .long sys_poll, sunos_nosys, sunos_nosys .long sunos_getdirentries, sys_statfs, sys_fstatfs - .long sys_umount, sunos_nosys, sunos_nosys + .long sys_oldumount, sunos_nosys, sunos_nosys .long sys_getdomainname, sys_setdomainname .long sunos_nosys, sys_quotactl, sunos_nosys .long sunos_mount, sys_ustat, sunos_semsys diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 9e5afcfcf..633ef2adb 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.39 1998/09/29 09:46:15 davem Exp $ +/* $Id: time.c,v 1.43 1999/03/15 22:13:31 davem Exp $ * linux/arch/sparc/kernel/time.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -39,6 +39,8 @@ #include <asm/sun4paddr.h> #include <asm/page.h> +extern rwlock_t xtime_lock; + enum sparc_clock_type sp_clock_typ; struct mostek48t02 *mstk48t02_regs = 0; struct mostek48t08 *mstk48t08_regs = 0; @@ -80,7 +82,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) #ifdef CONFIG_SUN4 if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { + (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { int temp; intersil_read_intr(intersil_clock, temp); /* re-enable the irq */ @@ -89,6 +91,8 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) #endif clear_clock_irq(); + write_lock(&xtime_lock); + do_timer(regs); /* Determine when to update the Mostek clock. */ @@ -101,6 +105,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) else last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ } + write_unlock(&xtime_lock); } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. @@ -436,6 +441,9 @@ extern __inline__ unsigned long do_gettimeoffset(void) return offset + count; } +/* This need not obtain the xtime_lock as it is coded in + * an implicitly SMP safe way already. + */ void do_gettimeofday(struct timeval *tv) { #if CONFIG_AP1000 @@ -485,12 +493,13 @@ void do_gettimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv) { + write_lock_irq(&xtime_lock); bus_do_settimeofday(tv); + write_unlock_irq(&xtime_lock); } static void sbus_do_settimeofday(struct timeval *tv) { - cli(); #if !CONFIG_AP1000 tv->tv_usec -= do_gettimeoffset(); if(tv->tv_usec < 0) { @@ -501,10 +510,8 @@ static void sbus_do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); } /* @@ -544,7 +551,7 @@ static int set_rtc_mmss(unsigned long nowtime) } else { printk(KERN_WARNING "set_rtc_mmss: can't update from %d to %d\n", - cmos_minutes, real_minutes); + mostek_minutes, real_minutes); return -1; } diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c index 86d632409..b9afa02f7 100644 --- a/arch/sparc/kernel/traps.c +++ b/arch/sparc/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.57 1998/09/17 11:04:51 jj Exp $ +/* $Id: traps.c,v 1.59 1999/03/06 12:07:31 anton Exp $ * arch/sparc/kernel/traps.c * * Copyright 1995 David S. Miller (davem@caip.rutgers.edu) @@ -82,8 +82,13 @@ void instruction_dump (unsigned long *pc) printk("\n"); } +#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t") +#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t") + void die_if_kernel(char *str, struct pt_regs *regs) { + int count = 0; + /* Amuse the user. */ printk( " \\|/ ____ \\|/\n" @@ -93,6 +98,27 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("%s(%d): %s\n", current->comm, current->pid, str); show_regs(regs); + + __SAVE; __SAVE; __SAVE; __SAVE; + __SAVE; __SAVE; __SAVE; __SAVE; + __RESTORE; __RESTORE; __RESTORE; __RESTORE; + __RESTORE; __RESTORE; __RESTORE; __RESTORE; + + { + struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP]; + + /* Stop the back trace when we hit userland or we + * find some badly aligned kernel stack. Set an upper + * bound in case our stack is trashed and we loop. + */ + while(rw && + count++ < 30 && + (((unsigned long) rw) >= PAGE_OFFSET) && + !(((unsigned long) rw) & 0x7)) { + printk("Caller[%08lx]\n", rw->ins[7]); + rw = (struct reg_window *)rw->ins[6]; + } + } printk("Instruction DUMP:"); instruction_dump ((unsigned long *) regs->pc); if(regs->psr & PSR_PS) diff --git a/arch/sparc/kernel/unaligned.c b/arch/sparc/kernel/unaligned.c index 1fb434b49..a683efb3e 100644 --- a/arch/sparc/kernel/unaligned.c +++ b/arch/sparc/kernel/unaligned.c @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.17 1997/04/11 00:42:08 davem Exp $ +/* $Id: unaligned.c,v 1.18 1999/04/03 11:36:17 anton Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -332,7 +332,6 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn) enum direction dir = decode_direction(insn); int size = decode_access_size(insn); - lock_kernel(); if(!ok_for_kernel(insn) || dir == both) { printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n", regs->pc); @@ -380,7 +379,6 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn) } advance(regs); } - unlock_kernel(); } static inline int ok_for_user(struct pt_regs *regs, unsigned int insn, diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 0670ff273..d5b475480 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -1,11 +1,11 @@ -# $Id: Makefile,v 1.26 1998/07/26 03:02:43 davem Exp $ +# $Id: Makefile,v 1.28 1999/03/21 06:37:44 davem Exp $ # Makefile for Sparc library files.. # OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \ strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \ strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \ - copy_user.o locks.o atomic.o bitops.o debuglocks.o + copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o ifdef CONFIG_SMP OBJS += irqlock.o @@ -89,6 +89,9 @@ urem.o: urem.S ashrdi3.o: ashrdi3.S $(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S +lshrdi3.o: lshrdi3.S + $(CC) -D__ASSEMBLY__ -c -o lshrdi3.o lshrdi3.S + dep: include $(TOPDIR)/Rules.make diff --git a/arch/sparc/lib/atomic.S b/arch/sparc/lib/atomic.S index 4e4aa0646..c57e61574 100644 --- a/arch/sparc/lib/atomic.S +++ b/arch/sparc/lib/atomic.S @@ -44,6 +44,7 @@ ___xchg32_sun4md: .globl ___atomic_add ___atomic_add: rd %psr, %g3 ! Keep the code small, old way was stupid + nop; nop; nop; ! Let the bits set or %g3, PSR_PIL, %g7 ! Disable interrupts wr %g7, 0x0, %psr ! Set %psr nop; nop; nop; ! Let the bits set @@ -51,12 +52,16 @@ ___atomic_add: 1: ldstub [%g1 + 3], %g7 ! Spin on the byte lock for SMP. orcc %g7, 0x0, %g0 ! Did we get it? bne 1b ! Nope... -#endif ld [%g1], %g7 ! Load locked atomic_t sra %g7, 8, %g7 ! Get signed 24-bit integer add %g7, %g2, %g2 ! Add in argument sll %g2, 8, %g7 ! Transpose back to atomic_t st %g7, [%g1] ! Clever: This releases the lock as well. +#else + ld [%g1], %g7 ! Load locked atomic_t + add %g7, %g2, %g2 ! Add in argument + st %g2, [%g1] ! Store it back +#endif wr %g3, 0x0, %psr ! Restore original PSR_PIL nop; nop; nop; ! Let the bits set jmpl %o7, %g0 ! NOTE: not + 8, see callers in atomic.h @@ -65,6 +70,7 @@ ___atomic_add: .globl ___atomic_sub ___atomic_sub: rd %psr, %g3 ! Keep the code small, old way was stupid + nop; nop; nop; ! Let the bits set or %g3, PSR_PIL, %g7 ! Disable interrupts wr %g7, 0x0, %psr ! Set %psr nop; nop; nop; ! Let the bits set @@ -72,12 +78,16 @@ ___atomic_sub: 1: ldstub [%g1 + 3], %g7 ! Spin on the byte lock for SMP. orcc %g7, 0x0, %g0 ! Did we get it? bne 1b ! Nope... -#endif ld [%g1], %g7 ! Load locked atomic_t sra %g7, 8, %g7 ! Get signed 24-bit integer sub %g7, %g2, %g2 ! Subtract argument sll %g2, 8, %g7 ! Transpose back to atomic_t st %g7, [%g1] ! Clever: This releases the lock as well +#else + ld [%g1], %g7 ! Load locked atomic_t + sub %g7, %g2, %g2 ! Subtract argument + st %g2, [%g1] ! Store it back +#endif wr %g3, 0x0, %psr ! Restore original PSR_PIL nop; nop; nop; ! Let the bits set jmpl %o7, %g0 ! NOTE: not + 8, see callers in atomic.h diff --git a/arch/sparc/lib/bitops.S b/arch/sparc/lib/bitops.S index 253c358fe..ea9ed5a6c 100644 --- a/arch/sparc/lib/bitops.S +++ b/arch/sparc/lib/bitops.S @@ -20,12 +20,10 @@ .globl ___set_bit ___set_bit: rd %psr, %g3 - andcc %g3, PSR_PIL, %g0 - bne 1f - nop - wr %g3, PSR_PIL, %psr + nop; nop; nop; + or %g3, PSR_PIL, %g5 + wr %g5, 0x0, %psr nop; nop; nop -1: #ifdef __SMP__ set C_LABEL(bitops_spinlock), %g5 2: ldstub [%g5], %g7 ! Spin on the byte lock for SMP. @@ -38,17 +36,12 @@ ___set_bit: #ifdef __SMP__ st %g5, [%g1] set C_LABEL(bitops_spinlock), %g5 - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g0, [%g5] + stb %g0, [%g5] #else - andcc %g3, PSR_PIL, %g0 - bne 1f - st %g5, [%g1] + st %g5, [%g1] #endif wr %g3, 0x0, %psr nop; nop; nop -1: jmpl %o7, %g0 mov %g4, %o7 @@ -56,12 +49,10 @@ ___set_bit: .globl ___clear_bit ___clear_bit: rd %psr, %g3 - andcc %g3, PSR_PIL, %g0 - bne 1f - nop - wr %g3, PSR_PIL, %psr nop; nop; nop -1: + or %g3, PSR_PIL, %g5 + wr %g5, 0x0, %psr + nop; nop; nop #ifdef __SMP__ set C_LABEL(bitops_spinlock), %g5 2: ldstub [%g5], %g7 ! Spin on the byte lock for SMP. @@ -74,17 +65,12 @@ ___clear_bit: #ifdef __SMP__ st %g5, [%g1] set C_LABEL(bitops_spinlock), %g5 - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g0, [%g5] + stb %g0, [%g5] #else - andcc %g3, PSR_PIL, %g0 - bne 1f - st %g5, [%g1] + st %g5, [%g1] #endif wr %g3, 0x0, %psr nop; nop; nop -1: jmpl %o7, %g0 mov %g4, %o7 @@ -92,12 +78,10 @@ ___clear_bit: .globl ___change_bit ___change_bit: rd %psr, %g3 - andcc %g3, PSR_PIL, %g0 - bne 1f - nop - wr %g3, PSR_PIL, %psr nop; nop; nop -1: + or %g3, PSR_PIL, %g5 + wr %g5, 0x0, %psr + nop; nop; nop #ifdef __SMP__ set C_LABEL(bitops_spinlock), %g5 2: ldstub [%g5], %g7 ! Spin on the byte lock for SMP. @@ -110,17 +94,12 @@ ___change_bit: #ifdef __SMP__ st %g5, [%g1] set C_LABEL(bitops_spinlock), %g5 - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g0, [%g5] + stb %g0, [%g5] #else - andcc %g3, PSR_PIL, %g0 - bne 1f - st %g5, [%g1] + st %g5, [%g1] #endif wr %g3, 0x0, %psr nop; nop; nop -1: jmpl %o7, %g0 mov %g4, %o7 @@ -128,12 +107,10 @@ ___change_bit: .globl ___set_le_bit ___set_le_bit: rd %psr, %g3 - andcc %g3, PSR_PIL, %g0 - bne 1f - nop - wr %g3, PSR_PIL, %psr nop; nop; nop -1: + or %g3, PSR_PIL, %g5 + wr %g5, 0x0, %psr + nop; nop; nop #ifdef __SMP__ set C_LABEL(bitops_spinlock), %g5 2: ldstub [%g5], %g7 ! Spin on the byte lock for SMP. @@ -146,29 +123,22 @@ ___set_le_bit: #ifdef __SMP__ stb %g5, [%g1] set C_LABEL(bitops_spinlock), %g5 - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g0, [%g5] + stb %g0, [%g5] #else - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g5, [%g1] + stb %g5, [%g1] #endif wr %g3, 0x0, %psr nop; nop; nop -1: jmpl %o7, %g0 mov %g4, %o7 .globl ___clear_le_bit ___clear_le_bit: rd %psr, %g3 - andcc %g3, PSR_PIL, %g0 - bne 1f - nop - wr %g3, PSR_PIL, %psr nop; nop; nop -1: + or %g3, PSR_PIL, %g5 + wr %g5, 0x0, %psr + nop; nop; nop #ifdef __SMP__ set C_LABEL(bitops_spinlock), %g5 2: ldstub [%g5], %g7 ! Spin on the byte lock for SMP. @@ -181,16 +151,11 @@ ___clear_le_bit: #ifdef __SMP__ stb %g5, [%g1] set C_LABEL(bitops_spinlock), %g5 - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g0, [%g5] + stb %g0, [%g5] #else - andcc %g3, PSR_PIL, %g0 - bne 1f - stb %g5, [%g1] + stb %g5, [%g1] #endif wr %g3, 0x0, %psr nop; nop; nop -1: jmpl %o7, %g0 mov %g4, %o7 diff --git a/arch/sparc/lib/debuglocks.c b/arch/sparc/lib/debuglocks.c index 8f0941ebf..62313e4ae 100644 --- a/arch/sparc/lib/debuglocks.c +++ b/arch/sparc/lib/debuglocks.c @@ -1,12 +1,13 @@ -/* $Id: debuglocks.c,v 1.5 1998/10/14 09:19:04 jj Exp $ +/* $Id: debuglocks.c,v 1.7 1999/04/21 02:26:58 anton Exp $ * debuglocks.c: Debugging versions of SMP locking primitives. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 Anton Blanchard (anton@progsoc.uts.edu.au) + * Copyright (C) 1998-99 Anton Blanchard (anton@progsoc.uts.edu.au) */ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/tasks.h> /* For NR_CPUS */ #include <asm/psr.h> #include <asm/system.h> #include <asm/spinlock.h> @@ -28,29 +29,33 @@ static inline void show(char *str, spinlock_t *lock, unsigned long caller) { int cpu = smp_processor_id(); + extern spinlock_t console_lock; - printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, - lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); + if (lock != &console_lock) + printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, + lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); } static inline void show_read(char *str, rwlock_t *lock, unsigned long caller) { int cpu = smp_processor_id(); - printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, + printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n", str, lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); } static inline void show_write(char *str, rwlock_t *lock, unsigned long caller) { int cpu = smp_processor_id(); + int i; + + printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)", str, + lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); + + for(i = 0; i < NR_CPUS; i++) + printk(" reader[i]=%08lx", lock->reader_pc[i]); - printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx) reader[0]=%08lx reader[1]=%08lx reader[2]=%08lx reader[3]=%08lx\n", - str, lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3, - lock->reader_pc[0], - lock->reader_pc[1], - lock->reader_pc[2], - lock->reader_pc[3]); + printk("\n"); } #undef INIT_STUCK @@ -103,9 +108,6 @@ void _do_spin_unlock(spinlock_t *lock) lock->lock = 0; } -#undef INIT_STUCK -#define INIT_STUCK 100000000 - void _do_read_lock(rwlock_t *rw, char *str) { unsigned long caller; @@ -133,9 +135,6 @@ wlock_again: rw->lock++; } -#undef INIT_STUCK -#define INIT_STUCK 100000000 - void _do_read_unlock(rwlock_t *rw, char *str) { unsigned long caller; @@ -163,9 +162,6 @@ wlock_again: rw->lock -= 0x1ff; } -#undef INIT_STUCK -#define INIT_STUCK 100000000 - void _do_write_lock(rwlock_t *rw, char *str) { unsigned long caller; diff --git a/arch/sparc/lib/irqlock.S b/arch/sparc/lib/irqlock.S index 23194e723..4c41e9825 100644 --- a/arch/sparc/lib/irqlock.S +++ b/arch/sparc/lib/irqlock.S @@ -1,4 +1,4 @@ -/* $Id: irqlock.S,v 1.4 1997/05/01 02:26:54 davem Exp $ +/* $Id: irqlock.S,v 1.5 1999/04/20 13:22:37 anton Exp $ * irqlock.S: High performance IRQ global locking and interrupt entry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -10,69 +10,6 @@ .text .align 4 - /* This is incredibly insane... */ - .globl ___irq_enter -___irq_enter: - sethi %hi(local_irq_count), %g2 - sll %g1, 2, %g1 - or %g2, %lo(local_irq_count), %g2 - ld [%g2 + %g1], %g3 - sethi %hi(global_irq_count), %g5 - add %g3, 1, %g3 - or %g5, %lo(global_irq_count), %g5 - st %g3, [%g2 + %g1] -1: - ldstub [%g5 + 3], %g2 - orcc %g2, 0x0, %g0 - bne 1b - ld [%g5], %g3 - sra %g3, 8, %g3 - add %g3, 1, %g3 - sll %g3, 8, %g3 - st %g3, [%g5] - sethi %hi(global_irq_lock), %g1 - ldub [%g1 + %lo(global_irq_lock)], %g2 -1: - orcc %g2, 0x0, %g0 - bne,a 1b - ldub [%g1 + %lo(global_irq_lock)], %g2 -___irq_enter_out: - jmpl %o7, %g0 - mov %g4, %o7 - - .globl ___irq_exit -___irq_exit: - rd %psr, %g3 - sethi %hi(global_irq_count), %g1 - or %g3, PSR_PIL, %g3 - or %g1, %lo(global_irq_count), %g1 - wr %g3, 0x0, %psr - sethi %hi(local_irq_count), %g2 - sll %g7, 2, %g7 - or %g2, %lo(local_irq_count), %g2 - ld [%g2 + %g7], %g3 -1: - ldstub [%g1 + 3], %g5 - orcc %g5, 0x0, %g0 - bne 1b - ld [%g1], %g5 - sra %g5, 8, %g5 - sub %g5, 1, %g5 - sll %g5, 8, %g5 - st %g5, [%g1] - sub %g3, 1, %g3 - sethi %hi(global_irq_holder), %g1 - st %g3, [%g2 + %g7] - srl %g7, 2, %g7 - ldub [%g1 + %lo(global_irq_holder)], %g5 - cmp %g5, %g7 - bne ___irq_enter_out - mov NO_PROC_ID, %g2 - stb %g2, [%g1 + %lo(global_irq_holder)] - sethi %hi(global_irq_lock), %g5 - b ___irq_enter_out - stb %g0, [%g5 + %lo(global_irq_lock)] - /* Weird calling conventions... %g7=flags, %g4=%prev_o7 * Very clever for the __global_sti case, the inline which * gets us here clears %g7 and it just works. diff --git a/arch/sparc/lib/lshrdi3.S b/arch/sparc/lib/lshrdi3.S new file mode 100644 index 000000000..f5300a1c8 --- /dev/null +++ b/arch/sparc/lib/lshrdi3.S @@ -0,0 +1,29 @@ +/* $Id: lshrdi3.S,v 1.1 1999/03/21 06:37:45 davem Exp $ */ + +#include <asm/cprefix.h> + + .globl C_LABEL(__lshrdi3) +C_LABEL(__lshrdi3): + cmp %o2, 0 + be 3f + mov 0x20, %g2 + + sub %g2, %o2, %g2 + cmp %g2, 0 + bg 1f + srl %o0, %o2, %o4 + + clr %o4 + neg %g2 + b 2f + srl %o0, %g2, %o5 +1: + sll %o0, %g2, %g3 + srl %o1, %o2, %g2 + or %g2, %g3, %o5 +2: + mov %o4, %o0 + mov %o5, %o1 +3: + retl + nop diff --git a/arch/sparc/math-emu/fabss.c b/arch/sparc/math-emu/fabss.c index accfd4f59..5429cc733 100644 --- a/arch/sparc/math-emu/fabss.c +++ b/arch/sparc/math-emu/fabss.c @@ -2,5 +2,5 @@ int FABSS(unsigned long *rd, unsigned long *rs2) { /* Clear the sign bit (high bit of word 0) */ rd[0] = rs2[0] & 0x7fffffffUL; - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fcmpd.c b/arch/sparc/math-emu/fcmpd.c index 3a9926575..8adb30d88 100644 --- a/arch/sparc/math-emu/fcmpd.c +++ b/arch/sparc/math-emu/fcmpd.c @@ -14,5 +14,5 @@ int FCMPD(void *rd, void *rs2, void *rs1) ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fcmped.c b/arch/sparc/math-emu/fcmped.c index a8c188042..2033b1dc8 100644 --- a/arch/sparc/math-emu/fcmped.c +++ b/arch/sparc/math-emu/fcmped.c @@ -14,5 +14,5 @@ int FCMPED(void *rd, void *rs2, void *rs1) ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fcmpeq.c b/arch/sparc/math-emu/fcmpeq.c index c109c51ce..de99bf343 100644 --- a/arch/sparc/math-emu/fcmpeq.c +++ b/arch/sparc/math-emu/fcmpeq.c @@ -14,5 +14,5 @@ int FCMPEQ(void *rd, void *rs2, void *rs1) fsr = *(unsigned long *)rd; fsr &= ~0xc00; fsr |= (ret << 10); *(unsigned long *)rd = fsr; - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fcmpes.c b/arch/sparc/math-emu/fcmpes.c index e20884cfd..a078a1243 100644 --- a/arch/sparc/math-emu/fcmpes.c +++ b/arch/sparc/math-emu/fcmpes.c @@ -14,5 +14,5 @@ int FCMPES(void *rd, void *rs2, void *rs1) ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fcmpq.c b/arch/sparc/math-emu/fcmpq.c index 549f02cae..f3d1b1233 100644 --- a/arch/sparc/math-emu/fcmpq.c +++ b/arch/sparc/math-emu/fcmpq.c @@ -14,5 +14,5 @@ int FCMPQ(void *rd, void *rs2, void *rs1) fsr = *(unsigned long *)rd; fsr &= ~0xc00; fsr |= (ret << 10); *(unsigned long *)rd = fsr; - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fcmps.c b/arch/sparc/math-emu/fcmps.c index 1b53312ae..7e273320f 100644 --- a/arch/sparc/math-emu/fcmps.c +++ b/arch/sparc/math-emu/fcmps.c @@ -14,5 +14,5 @@ int FCMPS(void *rd, void *rs2, void *rs1) ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fdmulq.c b/arch/sparc/math-emu/fdmulq.c index 1d5bc5053..dd9c7953c 100644 --- a/arch/sparc/math-emu/fdmulq.c +++ b/arch/sparc/math-emu/fdmulq.c @@ -11,6 +11,5 @@ int FDMULQ(void *rd, void *rs2, void *rs1) __FP_UNPACK_D(IN, rs2); FP_CONV(Q,D,4,2,B,IN); FP_MUL_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc/math-emu/fdtoq.c b/arch/sparc/math-emu/fdtoq.c index 84ebcf4a2..7b7746821 100644 --- a/arch/sparc/math-emu/fdtoq.c +++ b/arch/sparc/math-emu/fdtoq.c @@ -8,6 +8,5 @@ int FDTOQ(void *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_CONV(Q,D,4,2,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc/math-emu/fdtos.c b/arch/sparc/math-emu/fdtos.c index 83b8a14ed..612434c40 100644 --- a/arch/sparc/math-emu/fdtos.c +++ b/arch/sparc/math-emu/fdtos.c @@ -8,6 +8,5 @@ int FDTOS(void *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_CONV(S,D,1,2,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc/math-emu/fnegs.c b/arch/sparc/math-emu/fnegs.c index 1c662f201..26a90d778 100644 --- a/arch/sparc/math-emu/fnegs.c +++ b/arch/sparc/math-emu/fnegs.c @@ -2,5 +2,5 @@ int FNEGS(unsigned long *rd, unsigned long *rs2) { /* just change the sign bit */ rd[0] = rs2[0] ^ 0x80000000UL; - return 1; + return 0; } diff --git a/arch/sparc/math-emu/fqtod.c b/arch/sparc/math-emu/fqtod.c index dc5b6f9aa..62a437e31 100644 --- a/arch/sparc/math-emu/fqtod.c +++ b/arch/sparc/math-emu/fqtod.c @@ -8,6 +8,5 @@ int FQTOD(void *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_CONV(D,Q,2,4,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc/math-emu/fqtos.c b/arch/sparc/math-emu/fqtos.c index 608f57be0..2520affbf 100644 --- a/arch/sparc/math-emu/fqtos.c +++ b/arch/sparc/math-emu/fqtos.c @@ -8,6 +8,5 @@ int FQTOS(void *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_CONV(S,Q,1,4,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc/math-emu/fsmuld.c b/arch/sparc/math-emu/fsmuld.c index dead5a042..b7b992818 100644 --- a/arch/sparc/math-emu/fsmuld.c +++ b/arch/sparc/math-emu/fsmuld.c @@ -11,6 +11,5 @@ int FSMULD(void *rd, void *rs2, void *rs1) __FP_UNPACK_S(IN, rs2); FP_CONV(D,S,2,1,B,IN); FP_MUL_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc/math-emu/fstod.c b/arch/sparc/math-emu/fstod.c index cb34329c9..ea73660d8 100644 --- a/arch/sparc/math-emu/fstod.c +++ b/arch/sparc/math-emu/fstod.c @@ -8,6 +8,5 @@ int FSTOD(void *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_CONV(D,S,2,1,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc/math-emu/fstoq.c b/arch/sparc/math-emu/fstoq.c index 081c4d4d0..7d201310c 100644 --- a/arch/sparc/math-emu/fstoq.c +++ b/arch/sparc/math-emu/fstoq.c @@ -8,6 +8,5 @@ int FSTOQ(void *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_CONV(Q,S,4,1,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc/math-emu/math.c b/arch/sparc/math-emu/math.c index df5c879c5..68ccb932a 100644 --- a/arch/sparc/math-emu/math.c +++ b/arch/sparc/math-emu/math.c @@ -124,6 +124,7 @@ #include <linux/mm.h> #include <asm/uaccess.h> +#include "soft-fp.h" #define FLOATFUNC(x) extern int x(void *,void *,void *) @@ -189,6 +190,13 @@ FLOATFUNC(FNEGS); /* v6 */ FLOATFUNC(FITOS); /* v6 */ FLOATFUNC(FITOD); /* v6 */ +#define FSR_TEM_SHIFT 23UL +#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) +#define FSR_AEXC_SHIFT 5UL +#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT) +#define FSR_CEXC_SHIFT 0UL +#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT) + static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs); /* Unlike the Sparc64 version (which has a struct fpustate), we @@ -254,12 +262,85 @@ int do_mathemu(struct pt_regs *regs, struct task_struct *fpt) break; } /* Now empty the queue and clear the queue_not_empty flag */ - fpt->tss.fsr &= ~0x3000; + if(retcode) + fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK); + else + fpt->tss.fsr &= ~0x3000; fpt->tss.fpqdepth = 0; return retcode; } +/* All routines returning an exception to raise should detect + * such exceptions _before_ rounding to be consistant with + * the behavior of the hardware in the implemented cases + * (and thus with the recommendations in the V9 architecture + * manual). + * + * We return 0 if a SIGFPE should be sent, 1 otherwise. + */ +static int record_exception(unsigned long *pfsr, int eflag) +{ + unsigned long fsr = *pfsr; + int would_trap; + + /* Determine if this exception would have generated a trap. */ + would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL; + + /* If trapping, we only want to signal one bit. */ + if(would_trap != 0) { + eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); + if((eflag & (eflag - 1)) != 0) { + if(eflag & EFLAG_INVALID) + eflag = EFLAG_INVALID; + else if(eflag & EFLAG_DIVZERO) + eflag = EFLAG_DIVZERO; + else if(eflag & EFLAG_INEXACT) + eflag = EFLAG_INEXACT; + } + } + + /* Set CEXC, here are the rules: + * + * 1) In general all FPU ops will set one and only one + * bit in the CEXC field, this is always the case + * when the IEEE exception trap is enabled in TEM. + * + * 2) As a special case, if an overflow or underflow + * is being signalled, AND the trap is not enabled + * in TEM, then the inexact field shall also be set. + */ + fsr &= ~(FSR_CEXC_MASK); + if(would_trap || + (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) { + fsr |= ((long)eflag << FSR_CEXC_SHIFT); + } else { + fsr |= (((long)eflag << FSR_CEXC_SHIFT) | + (EFLAG_INEXACT << FSR_CEXC_SHIFT)); + } + + /* Set the AEXC field, rules are: + * + * 1) If a trap would not be generated, the + * CEXC just generated is OR'd into the + * existing value of AEXC. + * + * 2) When a trap is generated, AEXC is cleared. + */ + if(would_trap == 0) + fsr |= ((long)eflag << FSR_AEXC_SHIFT); + else + fsr &= ~(FSR_AEXC_MASK); + + /* If trapping, indicate fault trap type IEEE. */ + if(would_trap != 0) + fsr |= (1UL << 14); + + *pfsr = fsr; + + return (would_trap ? 0 : 1); +} + static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs) { /* Emulate the given insn, updating fsr and fregs appropriately. */ @@ -270,7 +351,7 @@ static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs) * (this field not used on sparc32 code, as we can't * extract trap type info for ops on the FP queue) */ - int freg; + int freg, eflag; int (*func)(void *,void *,void *) = NULL; void *rs1 = NULL, *rs2 = NULL, *rd = NULL; @@ -411,6 +492,8 @@ static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs) #ifdef DEBUG_MATHEMU printk("executing insn...\n"); #endif - func(rd, rs2, rs1); /* do the Right Thing */ - return 1; /* success! */ + eflag = func(rd, rs2, rs1); /* do the Right Thing */ + if(eflag == 0) + return 1; /* success! */ + return record_exception(fsr, eflag); } diff --git a/arch/sparc/math-emu/sfp-machine.h b/arch/sparc/math-emu/sfp-machine.h index eafad4273..67a74580c 100644 --- a/arch/sparc/math-emu/sfp-machine.h +++ b/arch/sparc/math-emu/sfp-machine.h @@ -115,16 +115,6 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_RAW_2(fs, X, val) \ do { \ union _FP_UNION_##fs *_flo = \ @@ -136,17 +126,6 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_RAW_4(fs, X, val) \ do { \ union _FP_UNION_##fs *_flo = \ @@ -160,55 +139,103 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_4(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f[0]; \ - _flo->bits.frac1 = X##_f[1]; \ - _flo->bits.frac2 = X##_f[2]; \ - _flo->bits.frac3 = X##_f[3]; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_S(X,val) \ do { \ __FP_UNPACK_RAW_1(S,X,val); \ _FP_UNPACK_CANONICAL(S,1,X); \ } while (0) -#define __FP_PACK_S(val,X) \ - do { \ - _FP_PACK_CANONICAL(S,1,X); \ - __FP_PACK_RAW_1(S,val,X); \ - } while (0) - #define __FP_UNPACK_D(X,val) \ do { \ __FP_UNPACK_RAW_2(D,X,val); \ _FP_UNPACK_CANONICAL(D,2,X); \ } while (0) -#define __FP_PACK_D(val,X) \ - do { \ - _FP_PACK_CANONICAL(D,2,X); \ - __FP_PACK_RAW_2(D,val,X); \ - } while (0) - #define __FP_UNPACK_Q(X,val) \ do { \ __FP_UNPACK_RAW_4(Q,X,val); \ _FP_UNPACK_CANONICAL(Q,4,X); \ } while (0) -#define __FP_PACK_Q(val,X) \ - do { \ - _FP_PACK_CANONICAL(Q,4,X); \ - __FP_PACK_RAW_4(Q,val,X); \ +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ } while (0) +#define __FP_PACK_RAW_4(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f[0]; \ + _flo->bits.frac1 = X##_f[1]; \ + _flo->bits.frac2 = X##_f[2]; \ + _flo->bits.frac3 = X##_f[3]; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#include <linux/kernel.h> +#include <linux/sched.h> + +/* We only actually write to the destination register + * if exceptions signalled (if any) will not trap. + */ +#ifdef __SMP__ +#define __FPU_TEM \ + (((current->tss.fsr)>>23)&0x1f) +#else +extern struct task_struct *last_task_used_math; +#define __FPU_TEM \ + (((last_task_used_math->tss.fsr)>>23)&0x1f) +#endif +#define __FPU_TRAP_P(bits) \ + ((__FPU_TEM & (bits)) != 0) + +#define __FP_PACK_S(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(S,val,X); \ + __exc; \ +}) + +#define __FP_PACK_D(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(D,2,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_2(D,val,X); \ + __exc; \ +}) + +#define __FP_PACK_Q(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(Q,4,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_4(Q,val,X); \ + __exc; \ +}) + +/* Obtain the current rounding mode. */ +#ifdef __SMP__ +#define FP_ROUNDMODE ((current->tss.fsr >> 30) & 0x3) +#else +#define FP_ROUNDMODE ((last_task_used_math->tss.fsr >> 30) & 0x3) +#endif + /* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */ #include <linux/types.h> @@ -361,3 +388,9 @@ #define __BYTE_ORDER __LITTLE_ENDIAN #endif +/* Exception flags. */ +#define EFLAG_INVALID (1 << 4) +#define EFLAG_OVERFLOW (1 << 3) +#define EFLAG_UNDERFLOW (1 << 2) +#define EFLAG_DIVZERO (1 << 1) +#define EFLAG_INEXACT (1 << 0) diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index ecb1943c3..76dbf643a 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.32 1998/08/16 16:02:25 ecd Exp $ +# $Id: Makefile,v 1.33 1999/01/02 16:45:47 davem Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c index 3c8ffbfae..a6110b886 100644 --- a/arch/sparc/mm/fault.c +++ b/arch/sparc/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.96 1998/11/08 11:13:56 davem Exp $ +/* $Id: fault.c,v 1.101 1999/01/04 06:24:52 jj Exp $ * fault.c: Page fault handlers for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -13,11 +13,13 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/tasks.h> +#include <linux/kernel.h> #include <linux/smp.h> #include <linux/signal.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/interrupt.h> #include <asm/system.h> #include <asm/segment.h> @@ -149,9 +151,7 @@ static void unhandled_fault(unsigned long address, struct task_struct *tsk, (unsigned long) tsk->mm->context); printk(KERN_ALERT "tsk->mm->pgd = %08lx\n", (unsigned long) tsk->mm->pgd); - lock_kernel(); die_if_kernel("Oops", regs); - unlock_kernel(); } asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, @@ -202,6 +202,13 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, if(text_fault) address = regs->pc; + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto do_kernel_fault; + down(&mm->mmap_sem); /* The kernel referencing a bad kernel pointer can lock up * a sun4c machine completely, so we must attempt recovery. diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 391a4dedb..4652e4fe6 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.60 1998/09/13 04:30:31 davem Exp $ +/* $Id: init.c,v 1.65 1999/04/09 16:28:03 davem Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -37,6 +37,8 @@ extern void show_net_buffers(void); +unsigned long *sparc_valid_addr_bitmap; + struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; unsigned long sparc_unmapped_base; @@ -215,16 +217,20 @@ __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long e unsigned long limit = base + sp_banks[tmp2].num_bytes; if((phys_addr >= base) && (phys_addr < limit) && - ((phys_addr + PAGE_SIZE) < limit)) + ((phys_addr + PAGE_SIZE) < limit)) { mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved); + set_bit(MAP_NR(addr) >> 8, sparc_valid_addr_bitmap); + } } } } else { if((sparc_cpu_model == sun4m) || (sparc_cpu_model == sun4d)) { srmmu_frob_mem_map(start_mem); } else { - for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE) + for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE) { mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved); + set_bit(MAP_NR(addr) >> 8, sparc_valid_addr_bitmap); + } } } } @@ -234,6 +240,7 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) int codepages = 0; int datapages = 0; int initpages = 0; + int i; unsigned long addr; struct page *page, *end; @@ -243,6 +250,12 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) end_mem &= PAGE_MASK; max_mapnr = MAP_NR(end_mem); high_memory = (void *) end_mem; + + sparc_valid_addr_bitmap = (unsigned long *)start_mem; + i = max_mapnr >> (8 + 5); + i += 1; + memset(sparc_valid_addr_bitmap, 0, i << 2); + start_mem += i << 2; start_mem = PAGE_ALIGN(start_mem); num_physpages = 0; @@ -255,6 +268,7 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) else #endif mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved); + set_bit(MAP_NR(addr) >> 8, sparc_valid_addr_bitmap); addr += PAGE_SIZE; } @@ -266,6 +280,9 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) if (PageSkip(page)) { unsigned long low, high; + /* See srmmu_frob_mem_map() for why this is done. -DaveM */ + page++; + low = PAGE_ALIGN((unsigned long)(page+1)); if (page->next_hash < page) high = ((unsigned long)end) & PAGE_MASK; @@ -313,11 +330,18 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) initpages << (PAGE_SHIFT-10), (unsigned long)PAGE_OFFSET, end_mem); - freepages.min = nr_free_pages >> 7; - if(freepages.min < 16) - freepages.min = 16; - freepages.low = freepages.min + (freepages.min >> 1); - freepages.high = freepages.min + freepages.min; + /* NOTE NOTE NOTE NOTE + * Please keep track of things and make sure this + * always matches the code in mm/page_alloc.c -DaveM + */ + i = nr_free_pages >> 7; + if (i < 48) + i = 48; + if (i > 256) + i = 256; + freepages.min = i; + freepages.low = i << 1; + freepages.high = freepages.low + i; } void free_initmem (void) diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index c7c6bdd5f..a0f92ea79 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -1,4 +1,4 @@ -/* $Id: iommu.c,v 1.9 1998/04/15 14:58:37 jj Exp $ +/* $Id: iommu.c,v 1.10 1999/05/07 17:03:34 jj Exp $ * iommu.c: IOMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -51,8 +51,7 @@ iommu_init(int iommund, struct linux_sbus *sbus)) unsigned long tmp; struct iommu_struct *iommu; struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - int i, j, k, l, m; - struct iommu_alloc { unsigned long addr; int next; } *ia; + int i; iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC); prom_getproperty(iommund, "reg", (void *) iommu_promregs, @@ -97,62 +96,18 @@ iommu_init(int iommund, struct linux_sbus *sbus)) ptsize = (ptsize >> PAGE_SHIFT) * sizeof(iopte_t); /* Stupid alignment constraints give me a headache. - We want to get very large aligned memory area, larger than - maximum what get_free_pages gives us (128K): we need - 256K or 512K or 1M or 2M aligned to its size. */ - ia = (struct iommu_alloc *) kmalloc (sizeof(struct iommu_alloc) * 128, GFP_ATOMIC); - for (i = 0; i < 128; i++) { - ia[i].addr = 0; - ia[i].next = -1; - } - k = 0; - for (i = 0; i < 128; i++) { - ia[i].addr = __get_free_pages(GFP_DMA, 5); - if (ia[i].addr <= ia[k].addr) { - if (i) { - ia[i].next = k; - k = i; - } - } else { - for (m = k, l = ia[k].next; l != -1; m = l, l = ia[l].next) - if (ia[i].addr <= ia[l].addr) { - ia[i].next = l; - ia[m].next = i; - } - if (l == -1) - ia[m].next = i; - } - for (m = -1, j = 0, l = k; l != -1; l = ia[l].next) { - if (!(ia[l].addr & (ptsize - 1))) { - tmp = ia[l].addr; - m = l; - j = 128 * 1024; - } else if (m != -1) { - if (ia[l].addr != tmp + j) - m = -1; - else { - j += 128 * 1024; - if (j == ptsize) { - break; - } - } - } - } - if (l != -1) + We need 256K or 512K or 1M or 2M area aligned to + its size and current gfp will fortunately give + it to us. */ + for (i = 6; i < 9; i++) + if ((1 << (i + PAGE_SHIFT)) == ptsize) break; - } - if (i == 128) { + tmp = __get_free_pages(GFP_DMA, i); + if (!tmp) { prom_printf("Could not allocate iopte of size 0x%08x\n", ptsize); prom_halt(); } - for (l = m, j = 0; j < ptsize; j += 128 * 1024, l = ia[l].next) - ia[l].addr = 0; - for (l = k; l != -1; l = ia[l].next) - if (ia[l].addr) - free_pages(ia[l].addr, 5); - kfree (ia); iommu->lowest = iommu->page_table = (iopte_t *)tmp; - /* Initialize new table. */ flush_cache_all(); diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c index f82599f42..b87b2bdeb 100644 --- a/arch/sparc/mm/nosrmmu.c +++ b/arch/sparc/mm/nosrmmu.c @@ -1,4 +1,4 @@ -/* $Id: nosrmmu.c,v 1.1 1998/03/09 14:04:15 jj Exp $ +/* $Id: nosrmmu.c,v 1.2 1999/03/30 10:17:39 jj Exp $ * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, * so that it does not need srmmu and avoid ifdefs. * @@ -48,3 +48,13 @@ __initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *mem_e { return 0; } + +__u32 iounit_map_dma_init(struct linux_sbus *sbus, int size) +{ + return 0; +} + +__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct linux_sbus *sbus) +{ + return 0; +} diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index d94fd4083..5b63aa11a 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.175 1998/08/28 18:57:31 zaitcev Exp $ +/* $Id: srmmu.c,v 1.187 1999/04/28 17:00:45 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -12,7 +12,9 @@ #include <linux/mm.h> #include <linux/malloc.h> #include <linux/vmalloc.h> +#include <linux/pagemap.h> #include <linux/init.h> +#include <linux/blk.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -216,24 +218,36 @@ __initfunc(void srmmu_frob_mem_map(unsigned long start_mem)) mem_map[MAP_NR(pg1)].flags &= ~(1<<PG_reserved); mem_map[MAP_NR(pg2)].flags &= ~(1<<PG_reserved); mem_map[MAP_NR(pg3)].flags &= ~(1<<PG_reserved); - + start_mem = PAGE_ALIGN(start_mem); for(i = 0; srmmu_map[i].size; i++) { bank_start = srmmu_map[i].vbase; - if (i && bank_start - bank_end > 2 * PAGE_SIZE) { + /* Making a one or two pages PG_skip holes + * is not necessary. We add one more because + * we must set the PG_skip flag on the first + * two mem_map[] entries for the hole. Go and + * see the mm/filemap.c:shrink_mmap() loop for + * details. -DaveM + */ + if (i && bank_start - bank_end > 3 * PAGE_SIZE) { mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip); mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(bank_start); + mem_map[MAP_NR(bank_end)+1UL].flags |= (1<<PG_skip); + mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map + MAP_NR(bank_start); PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(bank_start)); if (bank_end > KERNBASE && bank_start < KERNBASE) { mem_map[0].flags |= (1<<PG_skip); mem_map[0].next_hash = mem_map + MAP_NR(bank_start); + mem_map[1].flags |= (1<<PG_skip); + mem_map[1].next_hash = mem_map + MAP_NR(bank_start); PGSKIP_DEBUG(0, MAP_NR(bank_start)); } } bank_end = bank_start + srmmu_map[i].size; while(bank_start < bank_end) { + set_bit(MAP_NR(bank_start) >> 8, sparc_valid_addr_bitmap); if((bank_start >= KERNBASE) && (bank_start < start_mem)) { bank_start += PAGE_SIZE; @@ -250,14 +264,19 @@ __initfunc(void srmmu_frob_mem_map(unsigned long start_mem)) if (bank_end < KERNBASE) { mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip); mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(KERNBASE); + mem_map[MAP_NR(bank_end)+1UL].flags |= (1<<PG_skip); + mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map + MAP_NR(KERNBASE); PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(KERNBASE)); } else if (MAP_NR(bank_end) < max_mapnr) { mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip); + mem_map[MAP_NR(bank_end)+1UL].flags |= (1<<PG_skip); if (mem_map[0].flags & (1 << PG_skip)) { mem_map[MAP_NR(bank_end)].next_hash = mem_map[0].next_hash; + mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map[0].next_hash; PGSKIP_DEBUG(MAP_NR(bank_end), mem_map[0].next_hash - mem_map); } else { mem_map[MAP_NR(bank_end)].next_hash = mem_map; + mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map; PGSKIP_DEBUG(MAP_NR(bank_end), 0); } } @@ -447,7 +466,8 @@ static inline pte_t *srmmu_s_pte_offset(pmd_t * dir, unsigned long address) /* This must update the context table entry for this process. */ static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { - if(tsk->mm->context != NO_CONTEXT) { + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); flush_tlb_mm(tsk->mm); @@ -800,9 +820,7 @@ static void srmmu_switch_to_context(struct task_struct *tsk) { if(tsk->mm->context == NO_CONTEXT) { alloc_context(tsk->mm); - flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd); - flush_tlb_mm(tsk->mm); } srmmu_set_context(tsk->mm->context); } @@ -1273,6 +1291,12 @@ extern void viking_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end); extern void viking_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); +extern void sun4dsmp_flush_tlb_all(void); +extern void sun4dsmp_flush_tlb_mm(struct mm_struct *mm); +extern void sun4dsmp_flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end); +extern void sun4dsmp_flush_tlb_page(struct vm_area_struct *vma, + unsigned long page); /* hypersparc.S */ extern void hypersparc_flush_cache_all(void); @@ -1311,7 +1335,8 @@ static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) if(pgdp != swapper_pg_dir) hypersparc_flush_page_to_ram(page); - if(tsk->mm->context != NO_CONTEXT) { + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); flush_tlb_mm(tsk->mm); @@ -1320,11 +1345,13 @@ static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) static void viking_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { - viking_flush_page((unsigned long)pgdp); - if(tsk->mm->context != NO_CONTEXT) { - flush_cache_mm(current->mm); + if(pgdp != swapper_pg_dir) + flush_chunk((unsigned long)pgdp); + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { + flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); - flush_tlb_mm(current->mm); + flush_tlb_mm(tsk->mm); } } @@ -1334,6 +1361,9 @@ static void cypress_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) unsigned long page = ((unsigned long) pgdp) & PAGE_MASK; unsigned long line; + if(pgdp == swapper_pg_dir) + goto skip_flush; + a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; page &= PAGE_MASK; line = (page + PAGE_SIZE) - 0x100; @@ -1354,11 +1384,12 @@ static void cypress_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f), "r" (g)); } while(line != page); - - if(tsk->mm->context != NO_CONTEXT) { - flush_cache_mm(current->mm); +skip_flush: + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { + flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); - flush_tlb_mm(current->mm); + flush_tlb_mm(tsk->mm); } } @@ -1386,9 +1417,10 @@ static void hypersparc_init_new_context(struct mm_struct *mm) srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) mm->pgd) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); - hyper_flush_whole_icache(); - if(mm == current->mm) + if(mm == current->mm) { + hyper_flush_whole_icache(); srmmu_set_context(mm->context); + } } static unsigned long mempool; @@ -1917,12 +1949,13 @@ __initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned lon /* Find the number of contexts on the srmmu. */ cpunode = prom_getchild(prom_root_node); num_contexts = 0; - while((cpunode = prom_getsibling(cpunode)) != 0) { + while(cpunode != 0) { prom_getstring(cpunode, "device_type", node_str, sizeof(node_str)); if(!strcmp(node_str, "cpu")) { num_contexts = prom_getintdefault(cpunode, "mmu-nctx", 0x8); break; } + cpunode = prom_getsibling(cpunode); } } @@ -1969,6 +2002,18 @@ __initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned lon start_mem = sparc_context_init(start_mem, num_contexts); start_mem = free_area_init(start_mem, end_mem); + +#ifdef CONFIG_BLK_DEV_INITRD + /* If initial ramdisk was specified with physical address, + translate it here, as the p2v translation in srmmu + is not straightforward. */ + if (initrd_start && initrd_start < KERNBASE) { + initrd_start = srmmu_p2v(initrd_start); + initrd_end = srmmu_p2v(initrd_end); + if (initrd_end <= initrd_start) + initrd_start = 0; + } +#endif return PAGE_ALIGN(start_mem); } @@ -1998,6 +2043,11 @@ static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long ad static void srmmu_destroy_context(struct mm_struct *mm) { if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) { + /* XXX This could be drastically improved. + * XXX We are only called from __exit_mm and it just did + * XXX cache/tlb mm flush and right after this will (re-) + * XXX SET_PAGE_DIR to swapper_pg_dir. -DaveM + */ flush_cache_mm(mm); ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir); flush_tlb_mm(mm); @@ -2028,8 +2078,11 @@ static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma, offset = (address & PAGE_MASK) - vma->vm_start; vmaring = inode->i_mmap; do { - vaddr = vmaring->vm_start + offset; + /* Do not mistake ourselves as another mapping. */ + if(vmaring == vma) + continue; + vaddr = vmaring->vm_start + offset; if ((vaddr ^ address) & vac_badbits) { alias_found++; start = vmaring->vm_start; @@ -2042,7 +2095,7 @@ static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma, if(!ptep) goto next; if((pte_val(*ptep) & SRMMU_ET_MASK) == SRMMU_VALID) { -#if 1 +#if 0 printk("Fixing USER/USER alias [%ld:%08lx]\n", vmaring->vm_mm->context, start); #endif @@ -2057,11 +2110,12 @@ static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma, } } while ((vmaring = vmaring->vm_next_share) != NULL); - if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { + if(alias_found && ((pte_val(pte) & SRMMU_CACHE) != 0)) { pgdp = srmmu_pgd_offset(vma->vm_mm, address); - ptep = srmmu_pte_offset((pmd_t *) pgdp, address); + pmdp = srmmu_pmd_offset(pgdp, address); + ptep = srmmu_pte_offset(pmdp, address); flush_cache_page(vma, address); - *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); + set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE))); flush_tlb_page(vma, address); } done: @@ -2652,15 +2706,8 @@ __initfunc(static void init_viking(void)) /* Ahhh, the viking. SRMMU VLSI abortion number two... */ if(mreg & VIKING_MMODE) { - unsigned long bpreg; - srmmu_name = "TI Viking"; viking_mxcc_present = 0; - - bpreg = viking_get_bpreg(); - bpreg &= ~(VIKING_ACTION_MIX); - viking_set_bpreg(bpreg); - msi_set_sync(); BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_viking, BTFIXUPCALL_NORM); @@ -2691,16 +2738,25 @@ __initfunc(static void init_viking(void)) BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP); } - /* flush_cache_* are nops */ - BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM); +#ifdef __SMP__ + if (sparc_cpu_model == sun4d) { + BTFIXUPSET_CALL(flush_tlb_all, sun4dsmp_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, sun4dsmp_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, sun4dsmp_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, sun4dsmp_flush_tlb_range, BTFIXUPCALL_NORM); + } else +#endif + { + BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM); + } BTFIXUPSET_CALL(flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP); @@ -3027,10 +3083,12 @@ __initfunc(void ld_mmu_srmmu(void)) BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM); + if (sparc_cpu_model != sun4d) { + BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM); + } BTFIXUPSET_CALL(flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM); diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index fa8105d57..d6387d473 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.171 1998/09/21 05:05:41 jj Exp $ +/* $Id: sun4c.c,v 1.173 1999/01/17 02:20:37 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -2688,6 +2688,10 @@ static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long addr unsigned long vaddr = vmaring->vm_start + offset; unsigned long start; + /* Do not mistake ourselves as another mapping. */ + if(vmaring == vma) + continue; + if (S4CVAC_BADALIAS(vaddr, address)) { alias_found++; start = vmaring->vm_start; @@ -2699,8 +2703,8 @@ static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long addr if(pte_val(*ptep) & _SUN4C_PAGE_PRESENT) { flush_cache_page(vmaring, start); - pte_val(*ptep) = (pte_val(*ptep) | - _SUN4C_PAGE_NOCACHE); + *ptep = __pte(pte_val(*ptep) | + _SUN4C_PAGE_NOCACHE); flush_tlb_page(vmaring, start); } next: @@ -2712,7 +2716,7 @@ static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long addr if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { pgdp = sun4c_pgd_offset(vma->vm_mm, address); ptep = sun4c_pte_offset((pmd_t *) pgdp, address); - pte_val(*ptep) = (pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); + *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); pte = pte_val(*ptep); } } diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S index c65f72007..2ab87121f 100644 --- a/arch/sparc/mm/viking.S +++ b/arch/sparc/mm/viking.S @@ -1,8 +1,9 @@ -/* $Id: viking.S,v 1.11 1998/02/20 18:07:50 jj Exp $ +/* $Id: viking.S,v 1.13 1999/03/24 11:42:32 davem Exp $ * viking.S: High speed Viking cache/mmu operations * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 1999 Pavel Semerad (semerad@ss1000.ms.mff.cuni.cz) */ #include <asm/ptrace.h> @@ -15,16 +16,12 @@ #include <asm/cprefix.h> #include <asm/btfixup.h> -#define WINDOW_FLUSH(tmp1, tmp2) \ - mov 0, tmp1; \ -98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ - orcc %g0, tmp2, %g0; \ - add tmp1, 1, tmp1; \ - bne 98b; \ - save %sp, -64, %sp; \ -99: subcc tmp1, 1, tmp1; \ - bne 99b; \ - restore %g0, %g0, %g0; +#ifdef __SMP__ + .data + .align 4 +sun4dsmp_flush_tlb_spin: + .word 0 +#endif .text .align 4 @@ -70,7 +67,7 @@ viking_flush_chunk: clr %o1 ! set counter, 0 - 127 sethi %hi(KERNBASE + PAGE_SIZE - 0x80000000), %o3 sethi %hi(0x80000000), %o4 - sethi %hi(VIKING_PTAG_VALID | VIKING_PTAG_DIRTY), %o5 + sethi %hi(VIKING_PTAG_VALID), %o5 sethi %hi(2*PAGE_SIZE), %o0 sethi %hi(PAGE_SIZE), %g7 clr %o2 ! block counter, 0 - 3 @@ -83,15 +80,12 @@ viking_flush_chunk: or %g5, %g4, %g5 ldda [%g5] ASI_M_DATAC_TAG, %g2 cmp %g3, %g1 ! ptag == ppage? - bne,a 7f - inc %o2 - - and %g2, %o5, %g3 ! ptag VALID and DIRTY? - cmp %g3, %o5 - bne,a 7f + bne 7f inc %o2 - add %g4, %o3, %g2 ! (KERNBASE + PAGE_SIZE) | (set << 5) + andcc %g2, %o5, %g0 ! ptag VALID? + be 7f + add %g4, %o3, %g2 ! (KERNBASE + PAGE_SIZE) | (set << 5) ld [%g2], %g3 ld [%g2 + %g7], %g3 add %g2, %o0, %g2 @@ -102,18 +96,15 @@ viking_flush_chunk: ld [%g2 + %g7], %g3 add %g2, %o0, %g2 ld [%g2], %g3 - ld [%g2 + %g7], %g3 - b 8f - inc %o1 + ld [%g2 + %g7], %g3 7: cmp %o2, 3 ble 6b sll %o2, 26, %g5 ! block << 26 - inc %o1 -8: +8: inc %o1 cmp %o1, 0x7f ble 5b clr %o2 @@ -151,10 +142,33 @@ viking_mxcc_flush_chunk: retl nop -viking_flush_cache_all: +#define WINDOW_FLUSH(tmp1, tmp2) \ + mov 0, tmp1; \ +98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ + orcc %g0, tmp2, %g0; \ + add tmp1, 1, tmp1; \ + bne 98b; \ + save %sp, -64, %sp; \ +99: subcc tmp1, 1, tmp1; \ + bne 99b; \ + restore %g0, %g0, %g0; + +viking_flush_cache_page: +#ifndef __SMP__ + ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */ +#endif viking_flush_cache_mm: viking_flush_cache_range: -viking_flush_cache_page: +#ifndef __SMP__ + ld [%o0 + AOFF_mm_context], %g1 + cmp %g1, -1 + bne viking_flush_cache_all + nop + b,a viking_flush_cache_out +#endif +viking_flush_cache_all: + WINDOW_FLUSH(%g4, %g5) +viking_flush_cache_out: retl nop @@ -176,8 +190,10 @@ viking_flush_tlb_mm: sta %g0, [%g2] ASI_M_FLUSH_PROBE retl sta %g5, [%g1] ASI_M_MMUREGS +#ifndef __SMP__ 1: retl nop +#endif viking_flush_tlb_range: mov SRMMU_CTX_REG, %g1 @@ -198,8 +214,10 @@ viking_flush_tlb_range: sta %g0, [%o1] ASI_M_FLUSH_PROBE retl sta %g5, [%g1] ASI_M_MMUREGS +#ifndef __SMP__ 2: retl nop +#endif viking_flush_tlb_page: ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */ @@ -215,11 +233,96 @@ viking_flush_tlb_page: sta %g0, [%o1] ASI_M_FLUSH_PROBE retl sta %g5, [%g1] ASI_M_MMUREGS +#ifndef __SMP__ 1: retl nop +#endif viking_flush_page_to_ram: viking_flush_page_for_dma: viking_flush_sig_insns: retl nop + +#ifdef __SMP__ + .globl sun4dsmp_flush_tlb_all, sun4dsmp_flush_tlb_mm + .globl sun4dsmp_flush_tlb_range, sun4dsmp_flush_tlb_page +sun4dsmp_flush_tlb_all: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 2f + mov 0x400, %g1 + sta %g0, [%g1] ASI_M_FLUSH_PROBE + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +2: tst %g5 + bne,a 2b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + +sun4dsmp_flush_tlb_mm: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 2f + mov SRMMU_CTX_REG, %g1 + ld [%o0 + AOFF_mm_context], %o1 + lda [%g1] ASI_M_MMUREGS, %g5 + mov 0x300, %g2 + sta %o1, [%g1] ASI_M_MMUREGS + sta %g0, [%g2] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +2: tst %g5 + bne,a 2b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + +sun4dsmp_flush_tlb_range: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 3f + mov SRMMU_CTX_REG, %g1 + ld [%o0 + AOFF_mm_context], %o3 + lda [%g1] ASI_M_MMUREGS, %g5 + sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4 + sta %o3, [%g1] ASI_M_MMUREGS + and %o1, %o4, %o1 + add %o1, 0x200, %o1 + sta %g0, [%o1] ASI_M_FLUSH_PROBE +2: sub %o1, %o4, %o1 + cmp %o1, %o2 + blu,a 2b + sta %g0, [%o1] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +3: tst %g5 + bne,a 3b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + +sun4dsmp_flush_tlb_page: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 2f + mov SRMMU_CTX_REG, %g1 + ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */ + ld [%o0 + AOFF_mm_context], %o3 + lda [%g1] ASI_M_MMUREGS, %g5 + and %o1, PAGE_MASK, %o1 + sta %o3, [%g1] ASI_M_MMUREGS + sta %g0, [%o1] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +2: tst %g5 + bne,a 2b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + nop +#endif diff --git a/arch/sparc/vmlinux.lds b/arch/sparc/vmlinux.lds index 13e4d7202..1edbd8cfa 100644 --- a/arch/sparc/vmlinux.lds +++ b/arch/sparc/vmlinux.lds @@ -31,6 +31,10 @@ SECTIONS __start___ksymtab = .; __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + . = ALIGN(4096); __init_begin = .; .text.init : { *(.text.init) } diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index 9df70fc36..b76cd05b9 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.33 1998/10/19 07:04:02 jj Exp $ +# $Id: Makefile,v 1.35 1999/01/02 16:45:50 davem Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -46,6 +46,7 @@ ifneq ($(IS_EGCS),y) else CFLAGS := $(CFLAGS) -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow \ -ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare + AFLAGS += -m64 -mcpu=ultrasparc endif # Uncomment this to get spinlock/rwlock debugging on SMP. diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index fa3539525..58ac24500 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,8 +1,8 @@ -# $Id: config.in,v 1.58 1998/11/16 04:47:30 davem Exp $ +# $Id: config.in,v 1.66 1999/03/29 05:08:42 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # -mainmenu_name "Linux/SPARC Kernel Configuration" +mainmenu_name "Linux/UltraSPARC Kernel Configuration" mainmenu_option next_comment comment 'Code maturity level options' @@ -24,7 +24,6 @@ comment 'General setup' define_bool CONFIG_VT y define_bool CONFIG_VT_CONSOLE y -bool 'Support for AP1000 multicomputer' CONFIG_AP1000 bool 'Symmetric multi-processing support' CONFIG_SMP mainmenu_option next_comment @@ -34,28 +33,20 @@ bool 'Support Frame buffer devices' CONFIG_FB source drivers/video/Config.in endmenu -if [ "$CONFIG_AP1000" = "y" ]; then - define_bool CONFIG_NO_KEYBOARD y - define_bool CONFIG_APFDDI y - define_bool CONFIG_APBLOCK y - define_bool CONFIG_APBIF y - tristate 'OPIU DDV Driver' CONFIG_DDV -else - # Global things across all Sun machines. - define_bool CONFIG_SBUS y - define_bool CONFIG_SBUSCHAR y - define_bool CONFIG_SUN_MOUSE y - define_bool CONFIG_SERIAL y - define_bool CONFIG_SUN_SERIAL y - define_bool CONFIG_SERIAL_CONSOLE y - define_bool CONFIG_SUN_KEYBOARD y - define_bool CONFIG_SUN_CONSOLE y - define_bool CONFIG_SUN_AUXIO y - define_bool CONFIG_SUN_IO y - bool 'PCI support' CONFIG_PCI - source drivers/sbus/char/Config.in - source drivers/sbus/audio/Config.in -fi +# Global things across all Sun machines. +define_bool CONFIG_SBUS y +define_bool CONFIG_SBUSCHAR y +define_bool CONFIG_SUN_MOUSE y +define_bool CONFIG_SERIAL y +define_bool CONFIG_SUN_SERIAL y +define_bool CONFIG_SERIAL_CONSOLE y +define_bool CONFIG_SUN_KEYBOARD y +define_bool CONFIG_SUN_CONSOLE y +define_bool CONFIG_SUN_AUXIO y +define_bool CONFIG_SUN_IO y +bool 'PCI support' CONFIG_PCI +source drivers/sbus/char/Config.in +source drivers/sbus/audio/Config.in tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS if [ "$CONFIG_PCI" = "y" ]; then @@ -194,6 +185,7 @@ if [ "$CONFIG_SCSI" != "n" ]; then bool ' assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT fi fi + dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI fi endmenu @@ -221,14 +213,14 @@ if [ "$CONFIG_NET" = "y" ]; then fi bool 'Sun LANCE support' CONFIG_SUNLANCE tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Sun BigMAC 10/100baseT support' CONFIG_SUNBMAC + fi tristate 'Sun QuadEthernet support' CONFIG_SUNQE tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS if [ "$CONFIG_PCI" = "y" ]; then tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 -# Turned off until updated 3c59x.c driver -# gets approved by Linus... --DAVEM -# -# tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX + tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX fi # bool 'FDDI driver support' CONFIG_FDDI # if [ "$CONFIG_FDDI" = "y" ]; then @@ -237,6 +229,15 @@ if [ "$CONFIG_NET" = "y" ]; then endmenu fi +# This one must be before the filesystem configs. -DaveM +mainmenu_option next_comment +comment 'Unix 98 PTY support' +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi +endmenu + source fs/Config.in mainmenu_option next_comment diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 59b3b4618..8a132b01d 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -19,7 +19,6 @@ CONFIG_KMOD=y # CONFIG_VT=y CONFIG_VT_CONSOLE=y -# CONFIG_AP1000 is not set # CONFIG_SMP is not set # @@ -28,7 +27,9 @@ CONFIG_VT_CONSOLE=y CONFIG_PROM_CONSOLE=y CONFIG_FB=y CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_PM2 is not set # CONFIG_FB_MATROX is not set +CONFIG_FB_ATY=y CONFIG_FB_SBUS=y CONFIG_FB_CREATOR=y CONFIG_FB_CGSIX=y @@ -71,9 +72,9 @@ CONFIG_OBP_FLASH=m # # Linux/SPARC audio subsystem (EXPERIMENTAL) # -# CONFIG_SPARCAUDIO is not set +CONFIG_SPARCAUDIO=y # CONFIG_SPARCAUDIO_AMD7930 is not set -# CONFIG_SPARCAUDIO_CS4231 is not set +CONFIG_SPARCAUDIO_CS4231=y # CONFIG_SPARCAUDIO_DBRI is not set # CONFIG_SPARCAUDIO_DUMMY is not set CONFIG_SUN_OPENPROMFS=m @@ -128,7 +129,6 @@ CONFIG_BLK_DEV_CMD646=y CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -145,7 +145,6 @@ CONFIG_INET=y # (it is safe to leave these untouched) # CONFIG_INET_RARP=m -CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m # CONFIG_IPV6_EUI64 is not set @@ -166,6 +165,10 @@ CONFIG_ATALK=m # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -205,6 +208,7 @@ CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 CONFIG_SCSI_NCR53C8XX_SYNC=10 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +CONFIG_SCSI_QLOGIC_ISP=y # # Fibre Channel support @@ -215,11 +219,13 @@ CONFIG_FC4=m # FC4 drivers # CONFIG_FC4_SOC=m +CONFIG_FC4_SOCAL=m # # FC4 targets # CONFIG_SCSI_PLUTO=m +CONFIG_SCSI_FCAL=m # # Network device support @@ -237,29 +243,53 @@ CONFIG_SLIP_SMART=y # CONFIG_SLIP_MODE_SLIP6 is not set CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=y +CONFIG_SUNBMAC=m CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m CONFIG_DE4X5=m +CONFIG_VORTEX=m + +# +# Unix 98 PTY support +# +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 # # Filesystems # # CONFIG_QUOTA is not set -CONFIG_MINIX_FS=m -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=m -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=m +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +# CONFIG_HFS_FS is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +CONFIG_HPFS_FS=m CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +CONFIG_UFS_FS_WRITE=y + +# +# Network File Systems +# +CONFIG_CODA_FS=m CONFIG_NFS_FS=y CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -CONFIG_CODA_FS=m CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m @@ -269,21 +299,18 @@ CONFIG_NCP_FS=m # CONFIG_NCPFS_NFS_NS is not set # CONFIG_NCPFS_OS2_NS is not set # CONFIG_NCPFS_MOUNT_SUBDIR is not set -CONFIG_HPFS_FS=m -# CONFIG_NTFS_FS is not set -CONFIG_SYSV_FS=m -CONFIG_AFFS_FS=m -# CONFIG_HFS_FS is not set -CONFIG_ROMFS_FS=m -CONFIG_AUTOFS_FS=m -CONFIG_AMIGA_PARTITION=y -CONFIG_UFS_FS=m +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# CONFIG_BSD_DISKLABEL=y +# CONFIG_MAC_PARTITION is not set CONFIG_SMD_DISKLABEL=y CONFIG_SOLARIS_X86_PARTITION=y -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_AMIGA_PARTITION=y CONFIG_NLS=y # @@ -314,6 +341,7 @@ CONFIG_NLS=y # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index fbeb83126..6934dda6e 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.41 1998/10/11 06:58:14 davem Exp $ +# $Id: Makefile,v 1.43 1999/01/02 16:45:53 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -20,7 +20,7 @@ O_OBJS := process.o setup.o cpu.o idprom.o \ traps.o devices.o auxio.o ioport.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ unaligned.o sys_sunos32.o sunos_ioctl32.o \ - central.o psycho.o + central.o psycho.o starfire.o OX_OBJS := sparc64_ksyms.o ifdef CONFIG_PCI diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index dc898e300..450451ac0 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -9,7 +9,6 @@ #include <linux/module.h> -#include <linux/fs.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -18,9 +17,10 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/string.h> +#include <linux/fs.h> +#include <linux/file.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> @@ -58,14 +58,13 @@ static void set_brk(unsigned long start, unsigned long end) * macros to write out all the necessary info. */ #define DUMP_WRITE(addr,nr) \ -while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) \ - goto close_coredump +while (file->f_op->write(file,(char *)(addr),(nr),&file->f_pos) != (nr)) goto close_coredump #define DUMP_SEEK(offset) \ -if (file.f_op->llseek) { \ - if (file.f_op->llseek(&file,(offset),0) != (offset)) \ +if (file->f_op->llseek) { \ + if (file->f_op->llseek(file,(offset),0) != (offset)) \ goto close_coredump; \ -} else file.f_pos = (offset) +} else file->f_pos = (offset) /* * Routine writes a core dump image in the current directory. @@ -82,7 +81,7 @@ do_aout32_core_dump(long signr, struct pt_regs * regs) { struct dentry * dentry = NULL; struct inode * inode = NULL; - struct file file; + struct file * file; mm_segment_t fs; int has_dumped = 0; char corefile[6+sizeof(current->comm)]; @@ -106,29 +105,16 @@ do_aout32_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(dentry)) { - dentry = NULL; + file = filp_open(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); + if (IS_ERR(file)) goto end_coredump; - } + dentry = file->f_dentry; inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) - goto end_coredump; + goto close_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) - goto end_coredump; - if (get_write_access(inode)) - goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_dentry = dentry; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto done_coredump; - if (!file.f_op->write) + goto close_coredump; + if (!file->f_op->write) goto close_coredump; has_dumped = 1; current->flags |= PF_DUMPCORE; @@ -175,13 +161,9 @@ do_aout32_core_dump(long signr, struct pt_regs * regs) set_fs(KERNEL_DS); DUMP_WRITE(current,sizeof(*current)); close_coredump: - if (file.f_op->release) - file.f_op->release(inode,&file); -done_coredump: - put_write_access(inode); + filp_close(file, NULL); end_coredump: set_fs(fs); - dput(dentry); return has_dumped; } @@ -269,7 +251,6 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm, return -ENOEXEC; } - current->personality = PER_LINUX; fd_offset = N_TXTOFF(ex); /* Check initial limits. This avoids letting people circumvent @@ -288,6 +269,8 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm, return retval; /* OK, This is the point of no return */ + current->personality = PER_LINUX; + current->mm->end_code = ex.a_text + (current->mm->start_code = N_TXTADDR(ex)); current->mm->end_data = ex.a_data + @@ -297,8 +280,7 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm, current->mm->rss = 0; current->mm->mmap = NULL; - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; if (N_MAGIC(ex) == NMAGIC) { /* Fuck me plenty... */ @@ -404,48 +386,44 @@ static inline int do_load_aout32_library(int fd) { struct file * file; - struct exec ex; - struct dentry * dentry; struct inode * inode; - unsigned int len; - unsigned int bss; - unsigned int start_addr; + unsigned long bss, start_addr, len; unsigned long error; + int retval; + loff_t offset = 0; + struct exec ex; - file = fcheck(fd); - - if (!file || !file->f_op) - return -EACCES; - - dentry = file->f_dentry; - inode = dentry->d_inode; - - /* Seek into the file */ - if (file->f_op->llseek) { - if ((error = file->f_op->llseek(file, 0, 0)) != 0) - return -ENOEXEC; - } else - file->f_pos = 0; + retval = -EACCES; + file = fget(fd); + if (!file) + goto out; + if (!file->f_op) + goto out_putf; + inode = file->f_dentry->d_inode; + retval = -ENOEXEC; + /* N.B. Save current fs? */ set_fs(KERNEL_DS); - error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos); + error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset); set_fs(USER_DS); if (error != sizeof(ex)) - return -ENOEXEC; + goto out_putf; /* We come in here for the regular a.out style of shared libraries */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { - return -ENOEXEC; + goto out_putf; } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); - return -ENOEXEC; + goto out_putf; } - if (N_FLAGS(ex)) return -ENOEXEC; + if (N_FLAGS(ex)) + goto out_putf; /* For QMAGIC, the starting address is 0x20 into the page. We mask this off to get the starting address for the page */ @@ -457,18 +435,26 @@ do_load_aout32_library(int fd) PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, N_TXTOFF(ex)); + retval = error; if (error != start_addr) - return error; + goto out_putf; + len = PAGE_ALIGN(ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss; if (bss > len) { - error = do_mmap(NULL, start_addr + len, bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_FIXED, 0); + error = do_mmap(NULL, start_addr + len, bss - len, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_FIXED, 0); + retval = error; if (error != start_addr + len) - return error; + goto out_putf; } - return 0; + retval = 0; + +out_putf: + fput(file); +out: + return retval; } static int diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index 1d1cb25e1..198841f89 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -1,4 +1,4 @@ -/* $Id: central.c,v 1.6 1998/05/14 13:35:45 jj Exp $ +/* $Id: central.c,v 1.11 1998/12/14 12:18:16 davem Exp $ * central.c: Central FHC driver for Sunfire/Starfire/Wildfire. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -7,11 +7,17 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/delay.h> #include <asm/page.h> #include <asm/fhc.h> struct linux_central *central_bus = NULL; +struct linux_fhc *fhc_list = NULL; + +#define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child) static inline unsigned long long_align(unsigned long addr) { @@ -22,6 +28,156 @@ static inline unsigned long long_align(unsigned long addr) extern void prom_central_ranges_init(int cnode, struct linux_central *central); extern void prom_fhc_ranges_init(int fnode, struct linux_fhc *fhc); +static unsigned long probe_other_fhcs(unsigned long memory_start) +{ + struct linux_prom64_registers fpregs[6]; + char namebuf[128]; + int node; + + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(node, "fhc"); + if (node == 0) { + prom_printf("FHC: Cannot find any toplevel firehose controllers.\n"); + prom_halt(); + } + while(node) { + struct linux_fhc *fhc; + int board; + u32 tmp; + + fhc = (struct linux_fhc *)memory_start; + memory_start += sizeof(struct linux_fhc); + memory_start = long_align(memory_start); + + /* Link it into the FHC chain. */ + fhc->next = fhc_list; + fhc_list = fhc; + + /* Toplevel FHCs have no parent. */ + fhc->parent = NULL; + + fhc->prom_node = node; + prom_getstring(node, "name", namebuf, sizeof(namebuf)); + strcpy(fhc->prom_name, namebuf); + prom_fhc_ranges_init(node, fhc); + + /* Non-central FHC's have 64-bit OBP format registers. */ + if(prom_getproperty(node, "reg", + (char *)&fpregs[0], sizeof(fpregs)) == -1) { + prom_printf("FHC: Fatal error, cannot get fhc regs.\n"); + prom_halt(); + } + + /* Only central FHC needs special ranges applied. */ + fhc->fhc_regs.pregs = (struct fhc_internal_regs *) + __va(fpregs[0].phys_addr); + fhc->fhc_regs.ireg = (struct fhc_ign_reg *) + __va(fpregs[1].phys_addr); + fhc->fhc_regs.ffregs = (struct fhc_fanfail_regs *) + __va(fpregs[2].phys_addr); + fhc->fhc_regs.sregs = (struct fhc_system_regs *) + __va(fpregs[3].phys_addr); + fhc->fhc_regs.uregs = (struct fhc_uart_regs *) + __va(fpregs[4].phys_addr); + fhc->fhc_regs.tregs = (struct fhc_tod_regs *) + __va(fpregs[5].phys_addr); + + board = prom_getintdefault(node, "board#", -1); + fhc->board = board; + + tmp = fhc->fhc_regs.pregs->fhc_jtag_ctrl; + if((tmp & FHC_JTAG_CTRL_MENAB) != 0) + fhc->jtag_master = 1; + else + fhc->jtag_master = 0; + + tmp = fhc->fhc_regs.pregs->fhc_id; + printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n", + board, + (tmp & FHC_ID_VERS) >> 28, + (tmp & FHC_ID_PARTID) >> 12, + (tmp & FHC_ID_MANUF) >> 1, + (fhc->jtag_master ? "(JTAG Master)" : "")); + + /* This bit must be set in all non-central FHC's in + * the system. When it is clear, this identifies + * the central board. + */ + fhc->fhc_regs.pregs->fhc_control |= FHC_CONTROL_IXIST; + + /* Look for the next FHC. */ + node = prom_getsibling(node); + if(node == 0) + break; + node = prom_searchsiblings(node, "fhc"); + if(node == 0) + break; + } + + return memory_start; +} + +static void probe_clock_board(struct linux_central *central, + struct linux_fhc *fhc, + int cnode, int fnode) +{ + struct linux_prom_registers cregs[3]; + int clknode, nslots, tmp, nregs; + + clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board"); + if(clknode == 0 || clknode == -1) { + prom_printf("Critical error, central lacks clock-board.\n"); + prom_halt(); + } + nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs)); + if (nregs == -1) { + prom_printf("CENTRAL: Fatal error, cannot map clock-board regs.\n"); + prom_halt(); + } + nregs /= sizeof(struct linux_prom_registers); + prom_apply_fhc_ranges(fhc, &cregs[0], nregs); + prom_apply_central_ranges(central, &cregs[0], nregs); + central->cfreg = (volatile u8 *) + __va((((unsigned long)cregs[0].which_io) << 32) | + (((unsigned long)cregs[0].phys_addr)+0x02)); + central->clkregs = (struct clock_board_regs *) + __va((((unsigned long)cregs[1].which_io) << 32) | + (((unsigned long)cregs[1].phys_addr))); + if(nregs == 2) + central->clkver = NULL; + else + central->clkver = (volatile u8 *) + __va((((unsigned long)cregs[2].which_io) << 32) | + (((unsigned long)cregs[2].phys_addr))); + + tmp = central->clkregs->stat1; + tmp &= 0xc0; + switch(tmp) { + case 0x40: + nslots = 16; + break; + case 0xc0: + nslots = 8; + break; + case 0x80: + if(central->clkver != NULL && + *(central->clkver) != 0) { + if((*(central->clkver) & 0x80) != 0) + nslots = 4; + else + nslots = 5; + break; + } + default: + nslots = 4; + break; + }; + central->slots = nslots; + printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n", + central->slots, *(central->cfreg), + (central->clkver ? *(central->clkver) : 0x00)); +} + unsigned long central_probe(unsigned long memory_start) { struct linux_prom_registers fpregs[6]; @@ -30,9 +186,12 @@ unsigned long central_probe(unsigned long memory_start) int cnode, fnode, err; cnode = prom_finddevice("/central"); - if(cnode == 0 || cnode == -1) + if(cnode == 0 || cnode == -1) { + extern void starfire_check(void); + + starfire_check(); return memory_start; - printk("CENTRAL: found central PROM node %08x.\n", cnode); + } /* Ok we got one, grab some memory for software state. */ memory_start = long_align(memory_start); @@ -54,7 +213,9 @@ unsigned long central_probe(unsigned long memory_start) prom_central_ranges_init(cnode, central_bus); /* And then central's FHC. */ - fhc->next = NULL; + fhc->next = fhc_list; + fhc_list = fhc; + fhc->parent = central_bus; fnode = prom_searchsiblings(prom_getchild(cnode), "fhc"); if(fnode == 0 || fnode == -1) { @@ -67,9 +228,9 @@ unsigned long central_probe(unsigned long memory_start) prom_fhc_ranges_init(fnode, fhc); - /* Finally, map in FHC register set. */ + /* Now, 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_printf("CENTRAL: Fatal error, cannot get fhc regs.\n"); prom_halt(); } prom_apply_central_ranges(central_bus, &fpregs[0], 6); @@ -93,11 +254,144 @@ unsigned long central_probe(unsigned long memory_start) __va((((unsigned long)fpregs[5].which_io)<<32) | (((unsigned long)fpregs[5].phys_addr))); + /* Obtain board number from board status register, Central's + * FHC lacks "board#" property. + */ + err = fhc->fhc_regs.pregs->fhc_bsr; + fhc->board = (((err >> 16) & 0x01) | + ((err >> 12) & 0x0e)); + + fhc->jtag_master = 0; + + /* Attach the clock board registers for CENTRAL. */ + probe_clock_board(central_bus, fhc, cnode, fnode); + err = fhc->fhc_regs.pregs->fhc_id; - printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n", + printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n", + fhc->board, ((err & FHC_ID_VERS) >> 28), ((err & FHC_ID_PARTID) >> 12), ((err & FHC_ID_MANUF) >> 1)); - return memory_start; + return probe_other_fhcs(memory_start); +} + +static __inline__ void fhc_ledblink(struct linux_fhc *fhc, int on) +{ + volatile u32 *ctrl = (volatile u32 *) + &fhc->fhc_regs.pregs->fhc_control; + u32 tmp; + + tmp = *ctrl; + + /* NOTE: reverse logic on this bit */ + if (on) + tmp &= ~(FHC_CONTROL_RLED); + else + tmp |= FHC_CONTROL_RLED; + tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); + + *ctrl = tmp; + tmp = *ctrl; +} + +static __inline__ void central_ledblink(struct linux_central *central, int on) +{ + volatile u8 *ctrl = (volatile u8 *) ¢ral->clkregs->control; + int tmp; + + tmp = *ctrl; + + /* NOTE: reverse logic on this bit */ + if(on) + tmp &= ~(CLOCK_CTRL_RLED); + else + tmp |= CLOCK_CTRL_RLED; + + *ctrl = tmp; + tmp = *ctrl; +} + +static struct timer_list sftimer; +static int led_state; + +static void sunfire_timer(unsigned long __ignored) +{ + struct linux_fhc *fhc; + + central_ledblink(central_bus, led_state); + for(fhc = fhc_list; fhc != NULL; fhc = fhc->next) + if(! IS_CENTRAL_FHC(fhc)) + fhc_ledblink(fhc, led_state); + led_state = ! led_state; + sftimer.expires = jiffies + (HZ >> 1); + add_timer(&sftimer); +} + +/* After PCI/SBUS busses have been probed, this is called to perform + * final initialization of all FireHose Controllers in the system. + */ +void firetruck_init(void) +{ + struct linux_central *central = central_bus; + struct linux_fhc *fhc; + + /* No central bus, nothing to do. */ + if (central == NULL) + return; + + for(fhc = fhc_list; fhc != NULL; fhc = fhc->next) { + volatile u32 *ctrl = (volatile u32 *) + &fhc->fhc_regs.pregs->fhc_control; + u32 tmp; + + /* Clear all of the interrupt mapping registers + * just in case OBP left them in a foul state. + */ +#define ZAP(REG1, REG2) \ +do { volatile u32 *__iclr = (volatile u32 *)(&(REG1)); \ + volatile u32 *__imap = (volatile u32 *)(&(REG2)); \ + *(__iclr) = 0; \ + (void) *(__iclr); \ + *(__imap) &= ~(0x80000000); \ + (void) *(__imap); \ +} while(0) + + ZAP(fhc->fhc_regs.ffregs->fhc_ff_iclr, + fhc->fhc_regs.ffregs->fhc_ff_imap); + ZAP(fhc->fhc_regs.sregs->fhc_sys_iclr, + fhc->fhc_regs.sregs->fhc_sys_imap); + ZAP(fhc->fhc_regs.uregs->fhc_uart_iclr, + fhc->fhc_regs.uregs->fhc_uart_imap); + ZAP(fhc->fhc_regs.tregs->fhc_tod_iclr, + fhc->fhc_regs.tregs->fhc_tod_imap); + +#undef ZAP + + /* Setup FHC control register. */ + tmp = *ctrl; + + /* All non-central boards have this bit set. */ + if(! IS_CENTRAL_FHC(fhc)) + tmp |= FHC_CONTROL_IXIST; + + /* For all FHCs, clear the firmware synchronization + * line and both low power mode enables. + */ + tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); + *ctrl = tmp; + tmp = *ctrl; /* Ensure completion */ + } + + /* OBP leaves it on, turn it off so clock board timer LED + * is in sync with FHC ones. + */ + central->clkregs->control &= ~(CLOCK_CTRL_RLED); + + led_state = 0; + init_timer(&sftimer); + sftimer.data = 0; + sftimer.function = &sunfire_timer; + sftimer.expires = jiffies + (HZ >> 1); + add_timer(&sftimer); } diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 86efc4bb7..86518e50e 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -49,11 +49,11 @@ struct cpu_iu_info linux_sparc_chips[] = { #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) #ifdef __SMP__ -char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" }; -char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" }; +char *sparc_cpu_type[64] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" }; +char *sparc_fpu_type[64] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" }; #else -char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", }; -char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", }; +char *sparc_cpu_type[64] = { "cpu-oops", }; +char *sparc_fpu_type[64] = { "fpu-oops", }; #endif unsigned int fsr_storage; @@ -65,11 +65,11 @@ __initfunc(void cpu_probe(void)) long ver, fpu_vers; long fprs; - cpuid = smp_processor_id(); + cpuid = hard_smp_processor_id(); fprs = fprs_read (); fprs_write (FPRS_FEF); - __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" : "=r" (ver) : "r" (&fpu_vers)); + __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" : "=&r" (ver) : "r" (&fpu_vers)); fprs_write (fprs); manuf = ((ver >> 48)&0xffff); @@ -88,7 +88,7 @@ __initfunc(void cpu_probe(void)) if(i==NSPARCCHIPS) { printk("DEBUG: manuf = 0x%x impl = 0x%x\n", manuf, impl); - sparc_cpu_type[cpuid] = "Unknow CPU"; + sparc_cpu_type[cpuid] = "Unknown CPU"; } for(i = 0; i<NSPARCFPU; i++) { diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 93ea8ca4f..0aef0b019 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -13,8 +13,8 @@ #include <asm/system.h> #include <asm/smp.h> -struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } }; -unsigned prom_cpu_nodes[NR_CPUS]; +struct prom_cpuinfo linux_cpus[64] __initdata = { { 0 } }; +unsigned prom_cpu_nodes[64]; int linux_num_cpus = 0; extern void cpu_probe(void); @@ -25,11 +25,12 @@ device_scan(unsigned long mem_start)) { char node_str[128]; int nd, prom_node_cpu, thismid; - int cpu_nds[NR_CPUS]; /* One node for each cpu */ + int cpu_nds[64]; /* One node for each cpu */ int cpu_ctr = 0; prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str)); + prom_printf("Booting Linux...\n"); if(strcmp(node_str, "cpu") == 0) { cpu_nds[0] = prom_root_node; linux_cpus[0].prom_node = prom_root_node; @@ -38,7 +39,7 @@ device_scan(unsigned long mem_start)) } else { int scan; scan = prom_getchild(prom_root_node); - prom_printf("root child is %08x\n", (unsigned) scan); + /* prom_printf("root child is %08x\n", (unsigned) scan); */ nd = 0; while((scan = prom_getsibling(scan)) != 0) { prom_getstring(scan, "device_type", node_str, sizeof(node_str)); @@ -49,11 +50,11 @@ device_scan(unsigned long mem_start)) (char *) &thismid, sizeof(thismid)); linux_cpus[cpu_ctr].mid = thismid; #ifdef __SMP__ - prom_printf("Found CPU %d (node=%08x,mid=%d)\n", - cpu_ctr, (unsigned) scan, - thismid); - printk("Found CPU %d (node=%08x,mid=%d)\n", - cpu_ctr, (unsigned) scan, thismid); + /* Don't pollute PROM screen with these messages. If the kernel is screwed enough + that console does not start up, then we don't care how many CPUs have been found, + if it starts up, the user can use console=prom to see it. */ + /* prom_printf("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid); */ + printk("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid); #endif cpu_ctr++; } @@ -72,6 +73,9 @@ device_scan(unsigned long mem_start)) prom_cpu_nodes[0] = prom_node_cpu; + mem_start = central_probe(mem_start); + cpu_probe(); - return central_probe(mem_start); + + return mem_start; } diff --git a/arch/sparc64/kernel/dtlb_backend.S b/arch/sparc64/kernel/dtlb_backend.S index 6207101fd..9fe613a51 100644 --- a/arch/sparc64/kernel/dtlb_backend.S +++ b/arch/sparc64/kernel/dtlb_backend.S @@ -1,4 +1,4 @@ -/* $Id: dtlb_backend.S,v 1.6 1998/09/24 03:21:32 davem Exp $ +/* $Id: dtlb_backend.S,v 1.7 1998/12/16 04:33:28 davem Exp $ * dtlb_backend.S: Back end to DTLB miss replacement strategy. * This is included directly into the trap table. * @@ -37,28 +37,30 @@ be,pn %xcc, sparc64_vpte_nucleus ! Is it from Nucleus? and %g1, 0xffe, %g1 ! Mask PMD offset bits brnz,pt %g5, sparc64_vpte_continue ! Yep, go like smoke - nop ! Pipe bubble... + add %g1, %g1, %g1 ! Position PMD offset some more srlx %g6, (PGD_SHIFT - 2), %g5 ! Position PGD offset and %g5, 0xffc, %g5 ! Mask PGD offset /* TLB1 ** ICACHE line 3: Quick VPTE miss */ lduwa [%g7 + %g5] ASI_PHYS_USE_EC, %g5! Load PGD - brz,pn %g5, 2f ! Valid? + brz,pn %g5, vpte_noent ! Valid? +sparc64_kpte_continue: + sllx %g5, 11, %g5 ! Shift into place sparc64_vpte_continue: - add %g1, %g1, %g1 ! Position PMD offset once again lduwa [%g5 + %g1] ASI_PHYS_USE_EC, %g5! Load PMD - brz,pn %g5, 2f ! Valid? + sllx %g5, 11, %g5 ! Shift into place + brz,pn %g5, vpte_noent ! Valid? sllx %g2, 62, %g1 ! Put _PAGE_VALID into %g1 or %g5, VPTE_BITS, %g5 ! Prepare VPTE data - or %g5, %g1, %g5 ! ... /* TLB1 ** ICACHE line 4: Quick VPTE miss */ + or %g5, %g1, %g5 ! ... mov TLB_SFSR, %g1 ! Restore %g1 value stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Load VPTE into TLB - membar #Sync ! Synchronize ASI stores stxa %g4, [%g1 + %g1] ASI_DMMU ! Restore previous TAG_ACCESS retry ! Load PTE once again -2: mov TLB_SFSR, %g1 ! Restore %g1 value +vpte_noent: + mov TLB_SFSR, %g1 ! Restore %g1 value stxa %g4, [%g1 + %g1] ASI_DMMU ! Restore previous TAG_ACCESS done ! Slick trick diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S index c4ce36502..067a1d051 100644 --- a/arch/sparc64/kernel/dtlb_prot.S +++ b/arch/sparc64/kernel/dtlb_prot.S @@ -1,4 +1,4 @@ -/* $Id: dtlb_prot.S,v 1.17 1998/05/25 16:59:11 davem Exp $ +/* $Id: dtlb_prot.S,v 1.18 1999/03/02 15:42:14 jj Exp $ * dtlb_prot.S: DTLB protection trap strategy. * This is included directly into the trap table. * @@ -45,13 +45,13 @@ mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5 - sethi %hi(1f), %g7 ! Nope, normal fault + ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault /* PROT ** ICACHE line 4: More real fault processing */ - ba,pt %xcc, etrap ! Save state -1: or %g7, %lo(1b), %g7 ! ... - ba,pt %xcc, sparc64_realfault_continue! Now call the fault handler - mov 1, %o2 ! Indicate this was a write + mov 1, %g4 ! Indicate this was a write + nop + nop + nop nop nop nop diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index e60a5dc62..e64e87299 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.33 1998/09/21 05:06:03 jj Exp $ +/* $Id: ebus.c,v 1.36 1999/05/04 03:21:42 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -35,9 +35,6 @@ extern void prom_ebus_intmap_init(struct linux_ebus *); #ifdef CONFIG_SUN_OPENPROMIO extern int openprom_init(void); #endif -#ifdef CONFIG_SPARCAUDIO -extern int sparcaudio_init(void); -#endif #ifdef CONFIG_SUN_AUXIO extern void auxio_probe(void); #endif @@ -263,6 +260,31 @@ __initfunc(void ebus_init(void)) ebus->next = 0; while (ebusnd) { + /* SUNW,pci-qfe uses four empty ebuses on it. + I think we should not consider them here, + as they have half of the properties this + code expects and once we do PCI hot-plug, + we'd have to tweak with the ebus_chain + in the runtime after initialization. -jj */ + if (!prom_getchild (ebusnd)) { + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_EBUS, pdev); + if (!pdev) { + if (ebus == ebus_chain) { + ebus_chain = NULL; + printk("ebus: No EBus's found.\n"); +#ifdef PROM_DEBUG + dprintf("ebus: No EBus's found.\n"); +#endif + return; + } + break; + } + + cookie = pdev->sysdata; + ebusnd = cookie->prom_node; + continue; + } printk("ebus%d:", num_ebus); #ifdef PROM_DEBUG dprintf("ebus%d:", num_ebus); @@ -280,6 +302,12 @@ __initfunc(void ebus_init(void)) pci_command |= PCI_COMMAND_MASTER; pci_write_config_word(pdev, PCI_COMMAND, pci_command); + /* Set reasonable cache line size and latency timer values. */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); + + /* NOTE: Cache line size is in 32-bit word units. */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10); + len = prom_getproperty(ebusnd, "reg", (void *)regs, sizeof(regs)); if (len == 0 || len == -1) { @@ -368,9 +396,6 @@ __initfunc(void ebus_init(void)) #ifdef CONFIG_SUN_OPENPROMIO openprom_init(); #endif -#ifdef CONFIG_SPARCAUDIO - sparcaudio_init(); -#endif #ifdef CONFIG_SUN_BPP bpp_init(); #endif diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 7c0d80eb4..4134dcc3a 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1,10 +1,10 @@ -/* $Id: entry.S,v 1.91 1998/10/07 01:27:08 davem Exp $ +/* $Id: entry.S,v 1.103 1999/05/08 03:00:21 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) - * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,98,99 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -29,8 +29,17 @@ .text .align 32 + .globl sparc64_vpte_patchme1 + .globl sparc64_vpte_patchme2 +sparc64_vpte_nucleus: +sparc64_vpte_patchme1: + sethi %hi(0), %g5 ! This has to be patched +sparc64_vpte_patchme2: + or %g5, %lo(0), %g5 ! This is patched too + ba,pt %xcc, sparc64_kpte_continue ! Part of dtlb_backend + add %g1, %g1, %g1 ! Finish PMD offset adjustment + /* This is trivial with the new code... */ - .align 32 .globl do_fpdis do_fpdis: ldub [%g6 + AOFF_task_tss + AOFF_thread_fpsaved], %g5 ! Load Group @@ -155,6 +164,39 @@ fpdis_exit2: wr %g0, FPRS_FEF, %fprs ! clean DU/DL bits retry + .globl do_fptrap + .align 32 +do_fptrap: + ldub [%g6 + AOFF_task_tss + AOFF_thread_fpsaved], %g3 + stx %fsr, [%g6 + AOFF_task_tss + AOFF_thread_xfsr] + rd %fprs, %g1 + or %g3, %g1, %g3 + stb %g3, [%g6 + AOFF_task_tss + AOFF_thread_fpsaved] + rd %gsr, %g3 + stb %g3, [%g6 + AOFF_task_tss + AOFF_thread_gsr] + mov SECONDARY_CONTEXT, %g3 + add %g6, AOFF_task_fpregs, %g2 + ldxa [%g3] ASI_DMMU, %g5 + stxa %g0, [%g3] ASI_DMMU + flush %g6 + membar #StoreStore | #LoadStore + andcc %g1, FPRS_DL, %g0 + be,pn %icc, 4f + mov 0x40, %g3 + stda %f0, [%g2] ASI_BLK_S + stda %f16, [%g2 + %g3] ASI_BLK_S + andcc %g1, FPRS_DU, %g0 + be,pn %icc, 5f +4: add %g2, 128, %g2 + stda %f32, [%g2] ASI_BLK_S + stda %f48, [%g2 + %g3] ASI_BLK_S +5: mov SECONDARY_CONTEXT, %g1 + membar #Sync + stxa %g5, [%g1] ASI_DMMU + flush %g6 + ba,pt %xcc, etrap + wr %g0, 0, %fprs + /* The registers for cross calls will be: * * DATA 0: [low 32-bits] Address of function to call, jmp to this @@ -164,69 +206,57 @@ fpdis_exit2: * * With this method we can do most of the cross-call tlb/cache * flushing very quickly. + * + * Current CPU's IRQ worklist table is locked into %g1, + * don't touch. */ - .data - .align 8 - .globl ivec_spurious_cookie -ivec_spurious_cookie: .xword 0 - .text - .align 32 - .globl do_ivec + .align 32 + .globl do_ivec do_ivec: - ldxa [%g0] ASI_INTR_RECEIVE, %g5 - andcc %g5, 0x20, %g0 - be,pn %xcc, do_ivec_return - mov 0x40, %g2 - - /* Load up Interrupt Vector Data 0 register. */ + wr %g0, ASI_UDB_INTR_R, %asi + ldxa [%g0 + 0x40] %asi, %g3 sethi %hi(KERNBASE), %g4 - ldxa [%g2] ASI_UDB_INTR_R, %g3 cmp %g3, %g4 bgeu,pn %xcc, do_ivec_xcall - nop - and %g3, 0x7ff, %g3 - sllx %g3, 3, %g3 - ldx [%g1 + %g3], %g2 - brz,pn %g2, do_ivec_spurious - sethi %hi(0x80000000), %g5 + srlx %g3, 32, %g5 + stxa %g0, [%g0] ASI_INTR_RECEIVE + membar #Sync - or %g2, %g5, %g2 - stx %g2, [%g1 + %g3] + sethi %hi(ivector_table), %g2 + sllx %g3, 5, %g3 + or %g2, %lo(ivector_table), %g2 + add %g2, %g3, %g3 + ldx [%g3 + 0x08], %g2 /* irq_info */ + ldub [%g3 + 0x04], %g4 /* pil */ + brz,pn %g2, do_ivec_spurious + mov 1, %g2 - /* No branches, worse case we don't know about this interrupt - * yet, so we would just write a zero into the softint register - * which is completely harmless. - */ + sllx %g2, %g4, %g2 + sllx %g4, 2, %g4 + lduw [%g1 + %g4], %g5 /* g5 = irq_work(cpu, pil) */ + stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */ + stw %g3, [%g1 + %g4] /* irq_work(cpu, pil) = bucket */ wr %g2, 0x0, %set_softint -do_ivec_return: - stxa %g0, [%g0] ASI_INTR_RECEIVE - membar #Sync retry do_ivec_xcall: - srlx %g3, 32, %g5 - add %g2, 0x10, %g2 + ldxa [%g0 + 0x50] %asi, %g6 + srl %g3, 0, %g3 - ldxa [%g2] ASI_UDB_INTR_R, %g6 - add %g2, 0x10, %g2 - ldxa [%g2] ASI_UDB_INTR_R, %g7 + ldxa [%g0 + 0x60] %asi, %g7 stxa %g0, [%g0] ASI_INTR_RECEIVE membar #Sync jmpl %g3, %g0 nop - do_ivec_spurious: - srl %g3, 3, %g3 - sethi %hi(ivec_spurious_cookie), %g2 - stx %g3, [%g2 + %lo(ivec_spurious_cookie)] - stxa %g0, [%g0] ASI_INTR_RECEIVE - membar #Sync + stw %g3, [%g1 + 0x00] /* irq_work(cpu, 0) = bucket */ rdpr %pstate, %g5 + wrpr %g5, PSTATE_IG | PSTATE_AG, %pstate sethi %hi(109f), %g7 ba,pt %xcc, etrap 109: or %g7, %lo(109b), %g7 - call report_spurious_ivec + call catch_disabled_ivec add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap clr %l6 @@ -337,7 +367,7 @@ floppy_fifo_emptied: or %g1, %lo(irq_action), %g1 ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq] ldx [%g3 + 0x10], %g4 ! action->mask == ino_bucket ptr - ldx [%g4 + 0x18], %g4 ! bucket->iclr + ldx [%g4 + 0x10], %g4 ! bucket->iclr stw %g0, [%g4] ! SYSIO_ICLR_IDLE membar #Sync ! probably not needed... retry @@ -588,12 +618,20 @@ sunos_getgid: /* SunOS's execv() call only specifies the argv argument, the * environment settings are the same as the calling processes. */ - .globl sunos_execv + .globl sunos_execv, sys_execve, sys32_execve +sys_execve: + sethi %hi(sparc_execve), %g1 + ba,pt %xcc, execve_merge + or %g1, %lo(sparc_execve), %g1 sunos_execv: - sethi %hi(sparc32_execve), %g1 - stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] - jmpl %g1 + %lo(sparc32_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] +sys32_execve: + sethi %hi(sparc32_execve), %g1 + or %g1, %lo(sparc32_execve), %g1 +execve_merge: + flushw + jmpl %g1, %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall .globl sys_sigsuspend, sys_rt_sigsuspend, sys32_rt_sigsuspend @@ -612,14 +650,6 @@ sys_nis_syscall:sethi %hi(c_sys_nis_syscall), %g1 jmpl %g1 + %lo(c_sys_nis_syscall), %g0 nop -sys_execve: sethi %hi(sparc_execve), %g1 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - jmpl %g1 + %lo(sparc_execve), %g0 - nop -sys32_execve: sethi %hi(sparc32_execve), %g1 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - jmpl %g1 + %lo(sparc32_execve), %g0 - nop sys_memory_ordering: sethi %hi(sparc_memory_ordering), %g1 add %sp, STACK_BIAS + REGWIN_SZ, %o1 @@ -719,27 +749,31 @@ sys_ptrace: add %sp, STACK_BIAS + REGWIN_SZ, %o0 .globl sys_fork, sys_vfork, sys_clone, sparc_exit .globl ret_from_syscall .align 32 -sys_fork: -sys_vfork: mov SIGCHLD, %o0 - clr %o1 +sys_vfork: /* Under Linux, vfork and fork are just special cases of clone. */ + sethi %hi(0x4000 | 0x0100 | SIGCHLD), %o0 + or %o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0 + ba,pt %xcc, sys_clone +sys_fork: clr %o1 + mov SIGCHLD, %o0 sys_clone: flushw - mov %o7, %l5 - add %sp, STACK_BIAS + REGWIN_SZ, %o2 movrz %o1, %fp, %o1 - call do_fork - mov %l5, %o7 + nop + ba,pt %xcc, do_fork + add %sp, STACK_BIAS + REGWIN_SZ, %o2 ret_from_syscall: /* Clear SPARC_FLAG_NEWCHILD, switch_to leaves tss.flags in * %o7 for us. Check performance counter stuff too. */ - andn %o7, 0x100, %o7 - sth %o7, [%g6 + AOFF_task_tss + AOFF_thread_flags] #ifdef __SMP__ - sethi %hi(scheduler_lock), %o4 - membar #StoreStore | #LoadStore - stb %g0, [%o4 + %lo(scheduler_lock)] + andn %o7, 0x100, %l0 + mov %g5, %o0 /* 'prev' */ + call schedule_tail + sth %l0, [%g6 + AOFF_task_tss + AOFF_thread_flags] +#else + andn %o7, 0x100, %l0 + sth %l0, [%g6 + AOFF_task_tss + AOFF_thread_flags] #endif - andcc %o7, 0x200, %g0 + andcc %l0, 0x200, %g0 be,pt %icc, 1f nop ldx [%g6 + AOFF_task_tss + AOFF_thread_pcr_reg], %o7 diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 8c92688f1..21c1872a8 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.54 1998/10/06 20:48:30 ecd Exp $ +/* $Id: head.S,v 1.60 1999/04/12 08:08:21 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -7,6 +7,7 @@ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) */ +#include <linux/config.h> #include <linux/version.h> #include <linux/errno.h> #include <asm/asm_offsets.h> @@ -46,7 +47,7 @@ bootup_user_stack: * HdrS version should be incremented. */ .global root_flags, ram_flags, root_dev - .global ramdisk_image, ramdisk_size + .global sparc_ramdisk_image, sparc_ramdisk_size .globl silo_args .ascii "HdrS" @@ -58,9 +59,9 @@ root_dev: .half 0 ram_flags: .half 0 -ramdisk_image: +sparc_ramdisk_image: .word 0 -ramdisk_size: +sparc_ramdisk_size: .word 0 .xword reboot_command .xword bootstr_len @@ -330,7 +331,7 @@ sun4u_init: /* IMPORTANT NOTE: Whenever making changes here, check * trampoline.S as well. -jj */ .globl setup_tba -setup_tba: +setup_tba: /* i0 = is_starfire */ save %sp, -160, %sp rdpr %tba, %g7 @@ -376,9 +377,34 @@ setup_tba: /* Setup Interrupt globals */ wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate - sethi %hi(ivector_to_mask), %g5 - or %g5, %lo(ivector_to_mask), %g1 /* IVECTOR table */ - mov 0x40, %g2 /* INTR data 0 register */ +#ifndef __SMP__ + sethi %hi(__up_workvec), %g5 + or %g5, %lo(__up_workvec), %g1 +#else + /* By definition of where we are, this is boot_cpu. */ + sethi %hi(cpu_data), %g5 + or %g5, %lo(cpu_data), %g5 + + brz,pt %i0, not_starfire + sethi %hi(0x1fff4000), %g1 + or %g1, %lo(0x1fff4000), %g1 + sllx %g1, 12, %g1 + or %g1, 0xd0, %g1 + lduwa [%g1] ASI_PHYS_BYPASS_EC_E, %g1 + b,pt %xcc, set_worklist + nop + +not_starfire: + ldxa [%g0] ASI_UPA_CONFIG, %g1 + srlx %g1, 17, %g1 + and %g1, 0x1f, %g1 + + /* In theory this is: &(cpu_data[boot_cpu_id].irq_worklists[0]) */ +set_worklist: + sllx %g1, 7, %g1 + add %g5, %g1, %g5 + add %g5, 64, %g1 +#endif /* Kill PROM timer */ wr %g0, 0, %tick_cmpr @@ -408,6 +434,13 @@ bootup_user_stack_end: empty_bad_page: .skip 0x2000 +#ifdef CONFIG_SBUS +/* This is just a hack to fool make depend config.h discovering + strategy: As the .S files below need config.h, but + make depend does not find it for them, we include config.h + in head.S */ +#endif + ! 0x0000000000408000 #include "ttable.S" diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 42f3de3b8..84d4de363 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.55 1998/11/17 07:43:17 davem Exp $ +/* $Id: ioctl32.c,v 1.62 1999/05/01 09:17:44 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -26,6 +26,7 @@ #include <linux/fs.h> #include <linux/file.h> #include <linux/fd.h> +#include <linux/ppp_defs.h> #include <linux/if_ppp.h> #include <linux/mtio.h> #include <linux/cdrom.h> @@ -35,6 +36,7 @@ #include <linux/vt_kern.h> #include <linux/fb.h> #include <linux/ext2_fs.h> +#include <linux/videodev.h> #include <scsi/scsi.h> /* Ugly hack. */ @@ -52,6 +54,7 @@ #include <asm/openpromio.h> #include <asm/envctrl.h> #include <asm/audioio.h> +#include <asm/ethtool.h> #include <linux/soundcard.h> @@ -116,6 +119,247 @@ static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) return sys_ioctl(fd, cmd, arg); } +struct video_tuner32 { + s32 tuner; + u8 name[32]; + u32 rangelow, rangehigh; + u32 flags; + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(get_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __get_user(kp->name[i], &up->name[i]); + __get_user(kp->rangelow, &up->rangelow); + __get_user(kp->rangehigh, &up->rangehigh); + __get_user(kp->flags, &up->flags); + __get_user(kp->mode, &up->mode); + __get_user(kp->signal, &up->signal); + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(put_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __put_user(kp->name[i], &up->name[i]); + __put_user(kp->rangelow, &up->rangelow); + __put_user(kp->rangehigh, &up->rangehigh); + __put_user(kp->flags, &up->flags); + __put_user(kp->mode, &up->mode); + __put_user(kp->signal, &up->signal); + return 0; +} + +struct video_buffer32 { + /* void * */ u32 base; + s32 height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp; + + if(get_user(tmp, &up->base)) + return -EFAULT; + kp->base = (void *) ((unsigned long)tmp); + __get_user(kp->height, &up->height); + __get_user(kp->width, &up->width); + __get_user(kp->depth, &up->depth); + __get_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->height, &up->height); + __put_user(kp->width, &up->width); + __put_user(kp->depth, &up->depth); + __put_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; + /* struct video_clip32 * */ u32 next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + /* struct video_clip32 * */ u32 clips; + s32 clipcount; +}; + +static void free_kvideo_clips(struct video_window *kp) +{ + struct video_clip *cp; + + cp = kp->clips; + if(cp != NULL) + kfree(cp); +} + +static int get_video_window32(struct video_window *kp, struct video_window32 *up) +{ + struct video_clip32 *ucp; + struct video_clip *kcp; + int nclips, err, i; + u32 tmp; + + if(get_user(kp->x, &up->x)) + return -EFAULT; + __get_user(kp->y, &up->y); + __get_user(kp->width, &up->width); + __get_user(kp->height, &up->height); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->flags, &up->flags); + __get_user(kp->clipcount, &up->clipcount); + __get_user(tmp, &up->clips); + ucp = (struct video_clip32 *)A(tmp); + kp->clips = NULL; + + nclips = kp->clipcount; + if(nclips == 0) + return 0; + + if(ucp == 0) + return -EINVAL; + + /* Peculiar interface... */ + if(nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + kcp = kmalloc(nclips * sizeof(struct video_clip), GFP_KERNEL); + err = -ENOMEM; + if(kcp == NULL) + goto cleanup_and_err; + + kp->clips = kcp; + for(i = 0; i < nclips; i++) { + __get_user(kcp[i].x, &ucp[i].x); + __get_user(kcp[i].y, &ucp[i].y); + __get_user(kcp[i].width, &ucp[i].width); + __get_user(kcp[i].height, &ucp[i].height); + kcp[nclips].next = NULL; + } + + return 0; + +cleanup_and_err: + free_kvideo_clips(kp); + return err; +} + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 *up) +{ + if(put_user(kp->x, &up->x)) + return -EFAULT; + __put_user(kp->y, &up->y); + __put_user(kp->width, &up->width); + __put_user(kp->height, &up->height); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->flags, &up->flags); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +static int do_video_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + unsigned long vx; + } karg; + mm_segment_t old_fs = get_fs(); + void *up = (void *)arg; + int err = 0; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + break; + + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + break; + + case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 *)up); + break; + }; + if(err) + goto out; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if(cmd == VIDIOCSWIN) + free_kvideo_clips(&karg.vw); + + if(err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 *)up); + break; + }; + } +out: + return err; +} + struct timeval32 { int tv_sec; int tv_usec; @@ -253,11 +497,23 @@ static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long ar case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: + case SIOCETHTOOL: if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) return -EFAULT; ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); if (!ifr.ifr_data) return -EAGAIN; + if(cmd == SIOCETHTOOL) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if(copy_from_user(ifr.ifr_data, + (char *)A(data), + sizeof(struct ethtool_cmd))) { + free_page((unsigned long)ifr.ifr_data); + return -EFAULT; + } + } break; default: if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) @@ -280,17 +536,21 @@ static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long ar case SIOCGIFBRDADDR: case SIOCGIFDSTADDR: case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) return -EFAULT; break; case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: + case SIOCETHTOOL: { u32 data; int len; __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if(cmd == SIOCETHTOOL) + len = sizeof(struct ethtool_cmd); if(cmd == SIOCGPPPVER) len = strlen(PPP_VERSION) + 1; else if(cmd == SIOCGPPPCSTATS) @@ -298,7 +558,9 @@ static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long ar else len = sizeof(struct ppp_stats); - if (copy_to_user((char *)A(data), ifr.ifr_data, len)) + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + free_page((unsigned long)ifr.ifr_data); + if(len) return -EFAULT; break; } @@ -558,8 +820,7 @@ static int fb_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) cmap.transp = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); if (!cmap.transp) goto out; - } else - cmap.transp = NULL; + } if (cmd == FBIOGETCMAP) break; @@ -1458,6 +1719,9 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: + case SIOCGIFTXQLEN: + case SIOCSIFTXQLEN: + case SIOCETHTOOL: error = dev_ifsioc(fd, cmd, arg); goto out; @@ -1579,11 +1843,22 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) error = do_ext2_ioctl(fd, cmd, arg); goto out; + case VIDIOCGTUNER32: + case VIDIOCSTUNER32: + case VIDIOCGWIN32: + case VIDIOCSWIN32: + case VIDIOCGFBUF32: + case VIDIOCSFBUF32: + case VIDIOCGFREQ32: + case VIDIOCSFREQ32: + error = do_video_ioctl(fd, cmd, arg); + goto out; + /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... */ - /* Bit T */ + /* Big T */ case TCGETA: case TCSETA: case TCSETAW: @@ -1618,6 +1893,8 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) case TIOCSPGRP: case TIOCGPGRP: case TIOCSCTTY: + case TIOCGPTN: + case TIOCSPTLCK: /* Big F */ case FBIOGTYPE: @@ -1770,6 +2047,33 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) case VUIDSFORMAT: case VUIDGFORMAT: + /* Little v, the video4linux ioctls */ + case VIDIOCGCAP: + case VIDIOCGCHAN: + case VIDIOCSCHAN: + case VIDIOCGPICT: + case VIDIOCSPICT: + case VIDIOCCAPTURE: + case VIDIOCKEY: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + case VIDIOCSYNC: + case VIDIOCMCAPTURE: + case VIDIOCGMBUF: + case VIDIOCGUNIT: + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + + /* BTTV specific... */ + case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]): + case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]): + case _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int): + case _IOW('v' , BASE_VIDIOCPRIVATE+3, char [16]): /* struct bttv_pll_info */ + case _IOR('v' , BASE_VIDIOCPRIVATE+4, int): + case _IOR('v' , BASE_VIDIOCPRIVATE+5, int): + case _IOR('v' , BASE_VIDIOCPRIVATE+6, int): + case _IOR('v' , BASE_VIDIOCPRIVATE+7, int): + /* Little p (/dev/rtc, /dev/envctrl, etc.) */ case RTCGET: case RTCSET: diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index d8707261f..6eccb883a 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.66 1998/10/21 15:02:25 ecd Exp $ +/* $Id: irq.c,v 1.76 1999/04/02 14:54:30 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -41,20 +41,30 @@ #define SA_DMA_SYNC 0x200 #ifdef __SMP__ -void distribute_irqs(void); -static int irqs_have_been_distributed = 0; +static void distribute_irqs(void); #endif -/* UPA nodes send interrupt packet to UltraSparc with first data reg value - * low 5 bits holding the IRQ identifier being delivered. We must translate - * this into a non-vector IRQ so we can set the softint on this cpu. To - * make things even more swift we store the complete mask here. +/* UPA nodes send interrupt packet to UltraSparc with first data reg + * value low 5 (7 on Starfire) bits holding the IRQ identifier being + * delivered. We must translate this into a non-vector IRQ so we can + * set the softint on this cpu. + * + * To make processing these packets efficient and race free we use + * an array of irq buckets below. The interrupt vector handler in + * entry.S feeds incoming packets into per-cpu pil-indexed lists. + * The IVEC handler does not need to act atomically, the PIL dispatch + * code uses CAS to get an atomic snapshot of the list and clear it + * at the same time. */ -#define NUM_HARD_IVECS 2048 -#define NUM_IVECS (NUM_HARD_IVECS + 64) /* For SMP IRQ distribution alg. */ +struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (64))); -unsigned long ivector_to_mask[NUM_IVECS]; +#ifndef __SMP__ +unsigned int __up_workvec[16] __attribute__ ((aligned (64))); +#define irq_work(__cpu, __pil) &(__up_workvec[(__pil)]) +#else +#define irq_work(__cpu, __pil) &(cpu_data[(__cpu)].irq_worklists[(__pil)]) +#endif /* This is based upon code in the 32-bit Sparc kernel written mostly by * David Redman (djhr@tadpole.co.uk). @@ -63,30 +73,21 @@ unsigned long ivector_to_mask[NUM_IVECS]; static struct irqaction static_irqaction[MAX_STATIC_ALLOC]; static int static_irq_count = 0; -/* XXX Must be exported so that fast IRQ handlers can get at it... -DaveM */ +/* This is exported so that fast IRQ handlers can get at it... -DaveM */ struct irqaction *irq_action[NR_IRQS+1] = { NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL }; -#define IBF_DMA_SYNC 0x01 -#define IBF_PCI 0x02 -#define IBF_ACTIVE 0x04 +/* Only 8-bits are available, be careful. -DaveM */ +#define IBF_DMA_SYNC 0x01 /* DMA synchronization behind PCI bridge needed. */ +#define IBF_PCI 0x02 /* Indicates PSYCHO/SCHIZO PCI interrupt. */ +#define IBF_ACTIVE 0x04 /* This interrupt is active and has a handler. */ +#define IBF_MULTI 0x08 /* On PCI, indicates shared bucket. */ -#define __imap(bucket) ((bucket)->iclr + (bucket)->imap_off) #define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq)) #define __irq(bucket) ((unsigned int)(unsigned long)(bucket)) -static struct ino_bucket *bucket_base, *buckets, *endbuckets; - -__initfunc(unsigned long irq_init(unsigned long start_mem, unsigned long end_mem)) -{ - start_mem = (start_mem + 15) & ~15; - bucket_base = buckets = (struct ino_bucket *)start_mem; - endbuckets = buckets + 2048; - return (unsigned long)endbuckets; -} - int get_irq_list(char *buf) { int i, len = 0; @@ -104,7 +105,7 @@ int get_irq_list(char *buf) #else for (j = 0; j < smp_num_cpus; j++) len += sprintf(buf + len, "%10u ", - kstat.irqs[cpu_logical_map(j)][i]); + kstat.irqs[cpu_logical_map(j)][i]); #endif len += sprintf(buf + len, "%c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', @@ -224,8 +225,7 @@ unsigned char psycho_ino_to_pil[] = { 1, /* Power Management */ }; -/* INO number to IMAP register offset for PSYCHO external IRQ's. - */ +/* INO number to IMAP register offset for PSYCHO external IRQ's. */ #define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x))) #define psycho_imap_offset(ino) \ @@ -241,16 +241,27 @@ unsigned char psycho_ino_to_pil[] = { /* Now these are always passed a true fully specified sun4u INO. */ void enable_irq(unsigned int irq) { + extern int this_is_starfire; struct ino_bucket *bucket = __bucket(irq); - unsigned long tid; unsigned int *imap; + unsigned long tid; - imap = __imap(bucket); - if (!imap) return; + imap = bucket->imap; + if (!imap) + return; - /* We send it to our UPA MID, for SMP this will be different. */ - __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (tid) : "i" (ASI_UPA_CONFIG)); - tid = ((tid & UPA_CONFIG_MID) << 9); + if(this_is_starfire == 0) { + /* We set it to our UPA MID. */ + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=r" (tid) + : "i" (ASI_UPA_CONFIG)); + tid = ((tid & UPA_CONFIG_MID) << 9); + } else { + extern unsigned int starfire_translate(unsigned int *imap, + unsigned int upaid); + + tid = (starfire_translate(imap, current->processor) << 26); + } /* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product * of this SYSIO's preconfigured IGN in the SYSIO Control @@ -269,35 +280,83 @@ void disable_irq(unsigned int irq) struct ino_bucket *bucket = __bucket(irq); unsigned int *imap; - imap = __imap(bucket); - if (!imap) return; - - /* NOTE: We do not want to futz with the IRQ clear registers - * and move the state to IDLE, the SCSI code does call - * disable_irq() to assure atomicity in the queue cmd - * SCSI adapter driver code. Thus we'd lose interrupts. - */ - *imap &= ~(SYSIO_IMAP_VALID); + imap = bucket->imap; + if (imap != NULL) { + /* NOTE: We do not want to futz with the IRQ clear registers + * and move the state to IDLE, the SCSI code does call + * disable_irq() to assure atomicity in the queue cmd + * SCSI adapter driver code. Thus we'd lose interrupts. + */ + *imap &= ~(SYSIO_IMAP_VALID); + } } +/* The timer is the one "weird" interrupt which is generated by + * the CPU %tick register and not by some normal vectored interrupt + * source. To handle this special case, we use this dummy INO bucket. + */ +static struct ino_bucket pil0_dummy_bucket = { + 0, /* irq_chain */ + 0, /* pil */ + 0, /* pending */ + 0, /* flags */ + 0, /* __unused */ + NULL, /* irq_info */ + NULL, /* iclr */ + NULL, /* imap */ +}; + unsigned int build_irq(int pil, int inofixup, unsigned int *iclr, unsigned int *imap) { - if (buckets == endbuckets) - panic("Out of IRQ buckets. Should not happen.\n"); - buckets->pil = pil; - if (pil && (!iclr || !imap)) { - prom_printf("Invalid build_irq %d %d %016lx %016lx\n", pil, inofixup, iclr, imap); + struct ino_bucket *bucket; + int ino; + + if(pil == 0) { + if(iclr != NULL || imap != NULL) { + prom_printf("Invalid dummy bucket for PIL0 (%p:%p)\n", + iclr, imap); + prom_halt(); + } + return __irq(&pil0_dummy_bucket); + } + + /* RULE: Both must be specified in all other cases. */ + if (iclr == NULL || imap == NULL) { + prom_printf("Invalid build_irq %d %d %016lx %016lx\n", + pil, inofixup, iclr, imap); prom_halt(); } - if (imap) - buckets->ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup; - else - buckets->ino = 0; - - buckets->iclr = iclr; - buckets->flags = 0; - buckets->imap_off = imap - iclr; - return __irq(buckets++); + + ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup; + if(ino > NUM_IVECS) { + prom_printf("Invalid INO %04x (%d:%d:%016lx:%016lx)\n", + ino, pil, inofixup, iclr, imap); + prom_halt(); + } + + /* Ok, looks good, set it up. Don't touch the irq_chain or + * the pending flag. + */ + bucket = &ivector_table[ino]; + if ((bucket->flags & IBF_ACTIVE) || + (bucket->irq_info != NULL)) { + /* This is a gross fatal error if it happens here. */ + prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n"); + prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n", + ino, pil, inofixup, iclr, imap); + prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n", + bucket->pil, bucket->iclr, bucket->imap); + prom_printf("IRQ: Cannot continue, halting...\n"); + prom_halt(); + } + bucket->imap = imap; + bucket->iclr = iclr; + bucket->pil = pil; + bucket->flags = 0; + + bucket->irq_info = NULL; + + return __irq(bucket); } unsigned int sbus_build_irq(void *buscookie, unsigned int ino) @@ -382,8 +441,44 @@ unsigned int psycho_build_irq(void *buscookie, int imap_off, int ino, int need_d if(!(ino & 0x20)) inofixup = ino & 0x03; - bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); - + /* First check for sharing. */ + ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup; + if (ino > NUM_IVECS) { + prom_printf("PSYCHO: Invalid INO %04x (%d:%d:%016lx:%016lx)\n", + ino, pil, inofixup, iclr, imap); + prom_halt(); + } + bucket = &ivector_table[ino]; + if(bucket->flags & IBF_ACTIVE) { + void *old_handler = bucket->irq_info; + unsigned long flags; + + if(old_handler == NULL) { + prom_printf("PSYCHO: Active bucket, but no handler.\n"); + prom_halt(); + } + save_and_cli(flags); + if((bucket->flags & IBF_MULTI) == 0) { + void **vector; + + vector = kmalloc(sizeof(void *) * 4, + GFP_KERNEL); + + /* We might have slept. */ + if((bucket->flags & IBF_MULTI) != 0) { + kfree(vector); + } else { + vector[0] = old_handler; + vector[1] = vector[2] = vector[3] = NULL; + bucket->irq_info = vector; + bucket->flags |= IBF_MULTI; + } + } + restore_flags(flags); + } else { + /* Just init the bucket */ + bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); + } if (need_dma_sync) bucket->flags |= IBF_DMA_SYNC; @@ -392,6 +487,20 @@ unsigned int psycho_build_irq(void *buscookie, int imap_off, int ino, int need_d } #endif +static void atomic_bucket_insert(struct ino_bucket *bucket) +{ + unsigned long pstate; + unsigned int *ent; + + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + __asm__ __volatile__("wrpr %0, %1, %%pstate" + : : "r" (pstate), "i" (PSTATE_IE)); + ent = irq_work(smp_processor_id(), bucket->pil); + bucket->irq_chain = *ent; + *ent = __irq(bucket); + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); +} + int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *name, void *dev_id) { @@ -400,11 +509,16 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) unsigned long flags; int pending = 0; - if (irq < 0x400000 || (irq & 0x80000000)) { - prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler); - prom_halt(); - } - + if ((bucket != &pil0_dummy_bucket) && + (bucket < &ivector_table[0] || + bucket >= &ivector_table[NUM_IVECS])) { + unsigned int *caller; + + __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); + printk(KERN_CRIT "request_irq: Old style IRQ registry attempt " + "from %p, irq %08x.\n", caller, irq); + return -EINVAL; + } if(!handler) return -EINVAL; @@ -429,24 +543,26 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) } } + save_and_cli(flags); + action = *(bucket->pil + irq_action); if(action) { if((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) for (tmp = action; tmp->next; tmp = tmp->next) ; - else + else { + restore_flags(flags); return -EBUSY; - + } if((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) { printk("Attempt to mix fast and slow interrupts on IRQ%d " "denied\n", bucket->pil); + restore_flags(flags); return -EBUSY; } action = NULL; /* Or else! */ } - save_and_cli(flags); - /* If this is flagged as statically allocated then we use our * private struct which is never freed. */ @@ -466,12 +582,65 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) return -ENOMEM; } - if (irqflags & SA_IMAP_MASKED) { - pending = ((ivector_to_mask[bucket->ino] & 0x80000000) != 0); - ivector_to_mask[bucket->ino] = (1 << bucket->pil); - if(pending) - ivector_to_mask[bucket->ino] |= 0x80000000; + if ((irqflags & SA_IMAP_MASKED) == 0) { + bucket->irq_info = action; bucket->flags |= IBF_ACTIVE; + } else { + if((bucket->flags & IBF_ACTIVE) != 0) { + void *orig = bucket->irq_info; + void **vector = NULL; + + if((bucket->flags & IBF_PCI) == 0) { + printk("IRQ: Trying to share non-PCI bucket.\n"); + goto free_and_ebusy; + } + if((bucket->flags & IBF_MULTI) == 0) { + vector = kmalloc(sizeof(void *) * 4, GFP_KERNEL); + if(vector == NULL) + goto free_and_enomem; + + /* We might have slept. */ + if ((bucket->flags & IBF_MULTI) != 0) { + int ent; + + kfree(vector); + vector = (void **)bucket->irq_info; + for(ent = 0; ent < 4; ent++) { + if (vector[ent] == NULL) { + vector[ent] = action; + break; + } + } + if (ent == 4) + goto free_and_ebusy; + } else { + vector[0] = orig; + vector[1] = action; + vector[2] = NULL; + vector[3] = NULL; + bucket->irq_info = vector; + bucket->flags |= IBF_MULTI; + } + } else { + int ent; + + vector = (void **)orig; + for(ent = 0; ent < 4; ent++) { + if(vector[ent] == NULL) { + vector[ent] = action; + break; + } + } + if (ent == 4) + goto free_and_ebusy; + } + } else { + bucket->irq_info = action; + bucket->flags |= IBF_ACTIVE; + } + pending = bucket->pending; + if(pending) + bucket->pending = 0; } action->mask = (unsigned long) bucket; @@ -489,15 +658,26 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) enable_irq(irq); /* We ate the IVEC already, this makes sure it does not get lost. */ - if(pending) + if(pending) { + atomic_bucket_insert(bucket); set_softint(1 << bucket->pil); - + } restore_flags(flags); + #ifdef __SMP__ - if(irqs_have_been_distributed) - distribute_irqs(); + distribute_irqs(); #endif return 0; + +free_and_ebusy: + kfree(action); + restore_flags(flags); + return -EBUSY; + +free_and_enomem: + kfree(action); + restore_flags(flags); + return -ENOMEM; } void free_irq(unsigned int irq, void *dev_id) @@ -507,9 +687,15 @@ void free_irq(unsigned int irq, void *dev_id) unsigned long flags; struct ino_bucket *bucket = __bucket(irq), *bp; - if (irq < 0x400000 || (irq & 0x80000000)) { - prom_printf("free_irq with old style irq %08x\n", irq); - prom_halt(); + if ((bucket != &pil0_dummy_bucket) && + (bucket < &ivector_table[0] || + bucket >= &ivector_table[NUM_IVECS])) { + unsigned int *caller; + + __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); + printk(KERN_CRIT "free_irq: Old style IRQ removal attempt " + "from %p, irq %08x.\n", caller, irq); + return; } action = *(bucket->pil + irq_action); @@ -545,27 +731,59 @@ void free_irq(unsigned int irq, void *dev_id) *(bucket->pil + irq_action) = action->next; if(action->flags & SA_IMAP_MASKED) { - unsigned int *imap = __imap(bucket); + unsigned int *imap = bucket->imap; + void **vector, *orig; + int ent; + + orig = bucket->irq_info; + vector = (void **)orig; + + if ((bucket->flags & IBF_MULTI) != 0) { + int other = 0; + void *orphan = NULL; + for(ent = 0; ent < 4; ent++) { + if(vector[ent] == action) + vector[ent] = NULL; + else if(vector[ent] != NULL) { + orphan = vector[ent]; + other++; + } + } - /* - * Only free when no other shared irq uses this bucket. - */ - tmp = *(bucket->pil + irq_action); - for( ; tmp; tmp = tmp->next) - if ((struct ino_bucket *)tmp->mask == bucket) + /* Only free when no other shared irq + * uses this bucket. + */ + if(other) { + if (other == 1) { + /* Convert back to non-shared bucket. */ + bucket->irq_info = orphan; + bucket->flags &= ~(IBF_MULTI); + kfree(vector); + } goto out; + } + } else { + bucket->irq_info = NULL; + } - ivector_to_mask[bucket->ino] = 0; - + /* This unique interrupt source is now inactive. */ bucket->flags &= ~IBF_ACTIVE; - for (bp = bucket_base; bp < endbuckets; bp++) - if (__imap(bp) == imap && (bp->flags & IBF_ACTIVE)) + + /* See if any other buckets share this bucket's IMAP + * and are still active. + */ + for(ent = 0; ent < NUM_IVECS; ent++) { + bp = &ivector_table[ent]; + if(bp != bucket && + bp->imap == imap && + (bp->flags & IBF_ACTIVE) != 0) break; - /* - * Only disable when no other sub-irq levels of - * the same imap are active. + } + + /* Only disable when no other sub-irq levels of + * the same IMAP are active. */ - if (bp == endbuckets) + if (ent == NUM_IVECS) disable_irq(irq); } @@ -607,10 +825,10 @@ static void show(char * str) int cpu = smp_processor_id(); printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [%d %d]\n", + printk("irq: %d [%ld %ld]\n", atomic_read(&global_irq_count), cpu_data[0].irq_count, cpu_data[1].irq_count); - printk("bh: %d [%d %d]\n", + printk("bh: %d [%ld %ld]\n", (spin_is_locked(&global_bh_count) ? 1 : 0), cpu_data[0].bh_count, cpu_data[1].bh_count); } @@ -755,57 +973,56 @@ void __global_restore_flags(unsigned long flags) #endif /* __SMP__ */ -void report_spurious_ivec(struct pt_regs *regs) +void catch_disabled_ivec(struct pt_regs *regs) { - extern unsigned long ivec_spurious_cookie; - -#if 0 - printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n", - ivec_spurious_cookie, regs->tpc); -#endif + int cpu = smp_processor_id(); + struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0)); /* We can actually see this on Ultra/PCI PCI cards, which are bridges * to other devices. Here a single IMAP enabled potentially multiple * unique interrupt sources (which each do have a unique ICLR register. * * So what we do is just register that the IVEC arrived, when registered - * for real the request_irq() code will check the high bit and signal + * for real the request_irq() code will check the bit and signal * a local CPU interrupt for it. */ - ivector_to_mask[ivec_spurious_cookie] |= (0x80000000); +#if 0 + printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n", + bucket - &ivector_table[0], regs->tpc); +#endif + *irq_work(cpu, 0) = 0; + bucket->pending = 1; } -void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs) -{ - int i; - struct irqaction *action; - unsigned int cpu_irq; - - cpu_irq = irq & NR_IRQS; - action = *(cpu_irq + irq_action); - - prom_printf("Unexpected IRQ[%d]: ", irq); - prom_printf("PC[%016lx] NPC[%016lx] FP[%016lx]\n", - regs->tpc, regs->tnpc, regs->u_regs[14]); - - if(action) { - prom_printf("Expecting: "); - for(i = 0; i < 16; i++) { - if(action->handler) - prom_printf("[%s:%d:0x%016lx] ", action->name, - i, (unsigned long) action->handler); - } - } - prom_printf("AIEEE\n"); - prom_printf("bogus interrupt received\n"); - prom_cmdline (); -} +/* Tune this... */ +#define FORWARD_VOLUME 12 void handler_irq(int irq, struct pt_regs *regs) { - struct ino_bucket *bucket = NULL; - struct irqaction *action, *act; + struct ino_bucket *bp, *nbp; int cpu = smp_processor_id(); +#ifdef __SMP__ + extern int this_is_starfire; + int should_forward = (this_is_starfire == 0 && + irq < 10 && + current->pid != 0); + unsigned int buddy = 0; + + /* 'cpu' is the MID (ie. UPAID), calculate the MID + * of our buddy. + */ + if(should_forward != 0) { + buddy = cpu_number_map[cpu] + 1; + if (buddy >= NR_CPUS || + (buddy = cpu_logical_map(buddy)) == -1) + buddy = cpu_logical_map(0); + + /* Voo-doo programming. */ + if(cpu_data[buddy].idle_volume < FORWARD_VOLUME) + should_forward = 0; + buddy <<= 26; + } +#endif #ifndef __SMP__ /* @@ -817,30 +1034,55 @@ void handler_irq(int irq, struct pt_regs *regs) clear_softint(1 << irq); irq_enter(cpu, irq); - action = *(irq + irq_action); kstat.irqs[cpu][irq]++; - if(!action) { - unexpected_irq(irq, 0, regs); - } else { - act = action; - do { - if(act->flags & SA_IMAP_MASKED) { - bucket = (struct ino_bucket *)act->mask; - if(!(ivector_to_mask[bucket->ino] & 0x80000000)) - continue; + + /* Sliiiick... */ +#ifndef __SMP__ + bp = ((irq != 0) ? + __bucket(xchg32(irq_work(cpu, irq), 0)) : + &pil0_dummy_bucket); +#else + bp = __bucket(xchg32(irq_work(cpu, irq), 0)); +#endif + for( ; bp != NULL; bp = nbp) { + unsigned char flags = bp->flags; + + nbp = __bucket(bp->irq_chain); + if((flags & IBF_ACTIVE) != 0) { + if((flags & IBF_MULTI) == 0) { + struct irqaction *ap = bp->irq_info; + ap->handler(__irq(bp), ap->dev_id, regs); + } else { + void **vector = (void **)bp->irq_info; + int ent; + for(ent = 0; ent < 4; ent++) { + struct irqaction *ap = vector[ent]; + if(ap != NULL) + ap->handler(__irq(bp), ap->dev_id, regs); + } } - act->handler(__irq(bucket), 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; + /* Only the dummy bucket lacks IMAP/ICLR. */ + if(bp->pil != 0) { +#ifdef __SMP__ + /* Ok, here is what is going on: + * 1) Retargeting IRQs on Starfire is very + * expensive so just forget about it on them. + * 2) Moving around very high priority interrupts + * is a losing game. + * 3) If the current cpu is idle, interrupts are + * useful work, so keep them here. But do not + * pass to our neighbour if he is not very idle. + */ + if (should_forward != 0) { + /* Push it to our buddy. */ + should_forward = 0; + *(bp->imap) = (buddy | SYSIO_IMAP_VALID); + } +#endif + *(bp->iclr) = SYSIO_ICLR_IDLE; } - } while((act = act->next) != NULL); + } else + bp->pending = 1; } irq_exit(cpu, irq); } @@ -856,10 +1098,13 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; + + *(irq_work(cpu, irq)) = 0; bucket = (struct ino_bucket *)action->mask; + floppy_interrupt(irq, dev_cookie, regs); - ivector_to_mask[bucket->ino] &= ~(0x80000000); *(bucket->iclr) = SYSIO_ICLR_IDLE; + irq_exit(cpu, irq); } #endif @@ -897,11 +1142,21 @@ int request_fast_irq(unsigned int irq, struct ino_bucket *bucket = __bucket(irq); unsigned long flags; - if (irq < 0x400000 || (irq & 0x80000000)) { - prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler); - prom_halt(); - } + /* No pil0 dummy buckets allowed here. */ + if (bucket < &ivector_table[0] || + bucket >= &ivector_table[NUM_IVECS]) { + unsigned int *caller; + + __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); + printk(KERN_CRIT "request_fast_irq: Old style IRQ registry attempt " + "from %p, irq %08x.\n", caller, irq); + return -EINVAL; + } + /* Only IMAP style interrupts can be registered as fast. */ + if(bucket->pil == 0) + return -EINVAL; + if(!handler) return -EINVAL; @@ -919,6 +1174,7 @@ int request_fast_irq(unsigned int irq, printk("request_fast_irq: Trying to register yet already owned.\n"); return -EBUSY; } + save_and_cli(flags); if(irqflags & SA_STATIC_ALLOC) { if(static_irq_count < MAX_STATIC_ALLOC) @@ -936,7 +1192,8 @@ int request_fast_irq(unsigned int irq, } install_fast_irq(bucket->pil, handler); - ivector_to_mask[bucket->ino] = (1 << bucket->pil); + bucket->irq_info = action; + bucket->flags |= IBF_ACTIVE; action->mask = (unsigned long) bucket; action->handler = handler; @@ -949,9 +1206,9 @@ int request_fast_irq(unsigned int irq, enable_irq(irq); restore_flags(flags); + #ifdef __SMP__ - if(irqs_have_been_distributed) - distribute_irqs(); + distribute_irqs(); #endif return 0; } @@ -1025,50 +1282,51 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *), } #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. - */ -/* #define SMP_IRQ_VERBOSE */ -void distribute_irqs(void) +static int retarget_one_irq(struct irqaction *p, int goal_cpu) +{ + extern int this_is_starfire; + struct ino_bucket *bucket = __bucket(p->mask); + unsigned int *imap = bucket->imap; + unsigned int tid; + + /* Never change this, it causes problems on Ex000 systems. */ + if (bucket->pil == 12) + return goal_cpu; + + if(this_is_starfire == 0) { + tid = __cpu_logical_map[goal_cpu] << 26; + } else { + extern unsigned int starfire_translate(unsigned int *imap, + unsigned int upaid); + + tid = (starfire_translate(imap, __cpu_logical_map[goal_cpu]) << 26); + } + *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); + + goal_cpu++; + if(goal_cpu >= NR_CPUS || + __cpu_logical_map[goal_cpu] == -1) + goal_cpu = 0; + return goal_cpu; +} + +/* Called from request_irq. */ +static void distribute_irqs(void) { unsigned long flags; int cpu, level; -#ifdef SMP_IRQ_VERBOSE - printk("SMP: redistributing interrupts...\n"); -#endif 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 = __imap(bucket); - unsigned int val; - unsigned long tid = __cpu_logical_map[cpu] << 26; - - val = *imap; - *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); - -#ifdef SMP_IRQ_VERBOSE - printk("SMP: Redirecting IGN[%x] INO[%x] " - "to cpu %d [%s]\n", - (val & SYSIO_IMAP_IGN) >> 6, - (val & SYSIO_IMAP_INO), cpu, - p->name); -#endif - - cpu++; - if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1) - cpu = 0; - } + if(p->flags & SA_IMAP_MASKED) + cpu = retarget_one_irq(p, cpu); p = p->next; } } restore_flags(flags); - irqs_have_been_distributed = 1; } #endif @@ -1146,13 +1404,13 @@ __initfunc(void init_IRQ(void)) static int called = 0; if (called == 0) { - int i; - called = 1; map_prom_timers(); kill_prom_timer(); - for(i = 0; i < NUM_IVECS; i++) - ivector_to_mask[i] = 0; + memset(&ivector_table[0], 0, sizeof(ivector_table)); +#ifndef __SMP__ + memset(&__up_workvec[0], 0, sizeof(__up_workvec)); +#endif } /* We need to clear any IRQ's pending in the soft interrupt diff --git a/arch/sparc64/kernel/itlb_base.S b/arch/sparc64/kernel/itlb_base.S index 34a542ac5..eefc1c074 100644 --- a/arch/sparc64/kernel/itlb_base.S +++ b/arch/sparc64/kernel/itlb_base.S @@ -1,4 +1,4 @@ -/* $Id: itlb_base.S,v 1.5 1998/06/15 16:59:32 jj Exp $ +/* $Id: itlb_base.S,v 1.7 1999/03/02 15:42:12 jj Exp $ * itlb_base.S: Front end to ITLB miss replacement strategy. * This is included directly into the trap table. * @@ -15,11 +15,9 @@ * 2) All user instruction misses. * * All real page faults merge their code paths to the - * sparc64_realfault_* labels below. + * sparc64_realfault_common label below. */ - .globl sparc64_vpte_patchme - /* ITLB ** ICACHE line 1: Quick user TLB misses */ ldxa [%g1 + %g1] ASI_IMMU, %g4 ! Get TAG_ACCESS srax %g4, VPTE_SHIFT, %g6 ! Create VPTE offset @@ -42,28 +40,25 @@ /* ITLB ** ICACHE line 3: Real faults */ rdpr %tpc, %g5 ! And load faulting VA + clr %g4 ! It was read sparc64_realfault_common: ! Called by TL0 dtlb_miss too sethi %hi(1f), %g7 ! Save state ba,pt %xcc, etrap ! ... 1: or %g7, %lo(1b), %g7 ! ... - clr %o2 ! It was read -sparc64_realfault_continue: ! Called by dtlb_prot handler + mov %l4, %o2 ! Read/Write/No idea srlx %l5, PAGE_SHIFT, %o1 ! Page align faulting VA add %sp, STACK_BIAS + REGWIN_SZ, %o0! Compute pt_regs arg - call do_sparc64_fault ! Call fault handler /* ITLB ** ICACHE line 4: Call fault processing code */ + call do_sparc64_fault ! Call fault handler sllx %o1, PAGE_SHIFT, %o1 ! Finish page alignment ba,a,pt %xcc, rtrap_clr_l6 ! Restore cpu state + nop winfix_trampoline: rdpr %tpc, %g3 ! Prepare winfixup TNPC or %g3, 0x7c, %g3 ! Compute offset to branch wrpr %g3, %tnpc ! Write it into TNPC done ! Do it to it -sparc64_vpte_nucleus: - ba,pt %xcc, sparc64_vpte_continue ! Part of dtlb_backend -sparc64_vpte_patchme: - sethi %hi(0), %g5 ! This has to be patched #undef TAG_CONTEXT_BITS #undef VPTE_SHIFT diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index c5c7061f7..0d4871132 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.82 1998/10/19 21:52:23 davem Exp $ +/* $Id: process.c,v 1.92 1999/05/08 23:04:48 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -53,11 +53,19 @@ asmlinkage int sys_idle(void) /* endless idle loop with no priority at all */ current->priority = 0; - current->counter = 0; + current->counter = -100; + init_idle(); + for (;;) { - check_pgt_cache(); - run_task_queue(&tq_scheduler); + /* If current->need_resched is zero we should really + * setup for a system wakup event and execute a shutdown + * instruction. + * + * But this requires writing back the contents of the + * L2 cache etc. so implement this later. -DaveM + */ schedule(); + check_pgt_cache(); } return 0; } @@ -67,20 +75,27 @@ asmlinkage int sys_idle(void) /* * the idle loop on a UltraMultiPenguin... */ +#define idle_me_harder() (cpu_data[current->processor].idle_volume += 1) +#define unidle_me() (cpu_data[current->processor].idle_volume = 0) asmlinkage int cpu_idle(void) { current->priority = 0; - while(1) { - struct task_struct *p; + current->counter = -100; + init_idle(); - check_pgt_cache(); - run_task_queue(&tq_scheduler); - current->counter = 0; - if (current->need_resched != 0 || - ((p = init_task.next_run) != NULL && - (p->processor == smp_processor_id() || - (p->tss.flags & SPARC_FLAG_NEWCHILD) != 0))) + while(1) { + if (current->need_resched != 0) { + unidle_me(); schedule(); + check_pgt_cache(); + } + idle_me_harder(); + + /* The store ordering is so that IRQ handlers on + * other cpus see our increasing idleness for the buddy + * redistribution algorithm. -DaveM + */ + membar("#StoreStore | #StoreLoad"); } } @@ -158,12 +173,12 @@ static void show_regwindow32(struct pt_regs *regs) } rw = &r_w; set_fs (old_fs); - printk("l0: %016x l1: %016x l2: %016x l3: %016x\n" - "l4: %016x l5: %016x l6: %016x l7: %016x\n", + printk("l0: %08x l1: %08x l2: %08x l3: %08x " + "l4: %08x l5: %08x l6: %08x l7: %08x\n", rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); - printk("i0: %016x i1: %016x i2: %016x i3: %016x\n" - "i4: %016x i5: %016x i6: %016x i7: %016x\n", + printk("i0: %08x i1: %08x i2: %08x i3: %08x " + "i4: %08x i5: %08x i6: %08x i7: %08x\n", rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); } @@ -340,13 +355,13 @@ void show_regs32(struct pt_regs32 *regs) { printk("PSR: %08x PC: %08x NPC: %08x Y: %08x\n", regs->psr, regs->pc, regs->npc, regs->y); - printk("g0: %08x g1: %08x g2: %08x g3: %08x\n", + printk("g0: %08x g1: %08x g2: %08x g3: %08x ", regs->u_regs[0], regs->u_regs[1], regs->u_regs[2], regs->u_regs[3]); printk("g4: %08x g5: %08x g6: %08x g7: %08x\n", regs->u_regs[4], regs->u_regs[5], regs->u_regs[6], regs->u_regs[7]); - printk("o0: %08x o1: %08x o2: %08x o3: %08x\n", + printk("o0: %08x o1: %08x o2: %08x o3: %08x ", regs->u_regs[8], regs->u_regs[9], regs->u_regs[10], regs->u_regs[11]); printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n", @@ -427,9 +442,7 @@ void flush_thread(void) /* exec_mmap() set context to NO_CONTEXT, here is * where we grab a new one. */ - current->mm->cpu_vm_mask = 0; activate_context(current); - current->mm->cpu_vm_mask = (1UL<<smp_processor_id()); } if (current->tss.flags & SPARC_FLAG_32BIT) __asm__ __volatile__("stxa %%g0, [%0] %1" @@ -447,6 +460,11 @@ static unsigned long clone_stackframe(unsigned long csp, unsigned long psp) { unsigned long fp, distance, rval; + /* do_fork() grabs the parent semaphore, we must release it + * temporarily so we can build the child clone stack frame + * without deadlocking. + */ + up(¤t->mm->mmap_sem); if(!(current->tss.flags & SPARC_FLAG_32BIT)) { csp += STACK_BIAS; psp += STACK_BIAS; @@ -463,17 +481,20 @@ static unsigned long clone_stackframe(unsigned long csp, unsigned long psp) distance = fp - psp; rval = (csp - distance); if(copy_in_user(rval, psp, distance)) - return 0; - if(current->tss.flags & SPARC_FLAG_32BIT) { + rval = 0; + else if(current->tss.flags & SPARC_FLAG_32BIT) { if(put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6]))) - return 0; - return rval; + rval = 0; } else { if(put_user(((u64)csp - STACK_BIAS), &(((struct reg_window *)rval)->ins[6]))) - return 0; - return rval - STACK_BIAS; + rval = 0; + else + rval = rval - STACK_BIAS; } + down(¤t->mm->mmap_sem); + + return rval; } /* Standard stuff. */ @@ -624,6 +645,37 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, } /* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + + __asm__ __volatile("mov %1, %%g1\n\t" + "mov %2, %%o0\n\t" /* Clone flags. */ + "mov 0, %%o1\n\t" /* usp arg == 0 */ + "t 0x6d\n\t" /* Linux/Sparc clone(). */ + "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */ + " mov %%o0, %0\n\t" + "jmpl %4, %%o7\n\t" /* Call the function. */ + " mov %5, %%o0\n\t" /* Set arg in delay. */ + "mov %3, %%g1\n\t" + "t 0x6d\n\t" /* Linux/Sparc exit(). */ + /* Notreached by child. */ + "1:" : + "=r" (retval) : + "i" (__NR_clone), "r" (flags | CLONE_VM), + "i" (__NR_exit), "r" (fn), "r" (arg) : + "g1", "o0", "o1", "memory", "cc"); + return retval; +} + +/* * fill in the user structure for a core dump.. */ void dump_thread(struct pt_regs * regs, struct user * dump) diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c index 96b1ac2e9..7df9a5882 100644 --- a/arch/sparc64/kernel/psycho.c +++ b/arch/sparc64/kernel/psycho.c @@ -1,8 +1,9 @@ -/* $Id: psycho.c,v 1.66 1998/11/02 22:27:45 davem Exp $ +/* $Id: psycho.c,v 1.85 1999/04/02 14:54:28 davem Exp $ * psycho.c: Ultra/AX U2P PCI controller support. * * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) */ #include <linux/config.h> @@ -29,14 +30,13 @@ #define dprintf printk #endif - unsigned long pci_dvma_offset = 0x00000000UL; unsigned long pci_dvma_mask = 0xffffffffUL; +#define PCI_DVMA_HASH_NONE 0xffffffffffffffffUL unsigned long pci_dvma_v2p_hash[PCI_DVMA_HASHSZ]; unsigned long pci_dvma_p2v_hash[PCI_DVMA_HASHSZ]; - #ifndef CONFIG_PCI int pcibios_present(void) @@ -74,9 +74,12 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, #include <asm/apb.h> #include <asm/uaccess.h> +#define PSYCHO_REORDER_ONBOARDFIRST 1 + struct linux_psycho *psycho_root = NULL; int linux_num_psycho = 0; static struct linux_pbm_info *bus2pbm[256]; +static int psycho_reorder __initdata = 0; static int pbm_read_config_byte(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn, @@ -112,8 +115,10 @@ static __inline__ void set_dvma_hash(unsigned long paddr, unsigned long daddr) pci_dvma_p2v_hash[pci_dvma_ahashfn(dvma_addr)] = vaddr - dvma_addr; } -__initfunc(static void psycho_iommu_init(struct linux_psycho *psycho, int tsbsize)) +static void __init psycho_iommu_init(struct linux_psycho *psycho, int tsbsize) { + extern int this_is_starfire; + extern void *starfire_hookup(int); struct linux_mlist_p1275 *mlist; unsigned long tsbbase; unsigned long control, i, n; @@ -137,37 +142,77 @@ __initfunc(static void psycho_iommu_init(struct linux_psycho *psycho, int tsbsiz break; } tsbbase = __get_free_pages(GFP_DMA, order); + if (!tsbbase) { + prom_printf("IOMMU: Error, kmalloc(tsb) failed.\n"); + prom_halt(); + } iopte = (unsigned long *)tsbbase; - memset(pci_dvma_v2p_hash, 0, sizeof(pci_dvma_v2p_hash)); - memset(pci_dvma_p2v_hash, 0, sizeof(pci_dvma_p2v_hash)); + /* Initialize to "none" settings. */ + for(i = 0; i < PCI_DVMA_HASHSZ; i++) { + pci_dvma_v2p_hash[i] = PCI_DVMA_HASH_NONE; + pci_dvma_p2v_hash[i] = PCI_DVMA_HASH_NONE; + } n = 0; mlist = *prom_meminfo()->p1275_totphys; while (mlist) { unsigned long paddr = mlist->start_adr; + unsigned long num_bytes = mlist->num_bytes; - for (i = 0; i < (mlist->num_bytes >> 16); i++) { + if(paddr >= (((unsigned long) high_memory) - PAGE_OFFSET)) + goto next; + + if((paddr + num_bytes) >= (((unsigned long) high_memory) - PAGE_OFFSET)) + num_bytes = (((unsigned long) high_memory) - PAGE_OFFSET) - paddr; + + /* Align base and length so we map whole hash table sized chunks + * at a time (and therefore full 64K IOMMU pages). + */ + paddr &= ~((1UL << 24UL) - 1); + num_bytes = (num_bytes + ((1UL << 24UL) - 1)) & ~((1UL << 24) - 1); + + /* Move up the base for mappings already created. */ + while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] != + PCI_DVMA_HASH_NONE) { + paddr += (1UL << 24UL); + num_bytes -= (1UL << 24UL); + if(num_bytes == 0UL) + goto next; + } + + /* Move down the size for tail mappings already created. */ + while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr + num_bytes - (1UL << 24UL))] != + PCI_DVMA_HASH_NONE) { + num_bytes -= (1UL << 24UL); + if(num_bytes == 0UL) + goto next; + } + /* Now map the rest. */ + for (i = 0; i < ((num_bytes + ((1 << 16) - 1)) >> 16); i++) { *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); *iopte |= paddr; if (!(n & 0xff)) set_dvma_hash(paddr, (n << 16)); - + if (++n > (tsbsize * 1024)) goto out; paddr += (1 << 16); iopte++; } - + next: mlist = mlist->theres_more; } out: - if (mlist) - printk("WARNING: not all physical memory mapped in IOMMU\n"); + if (mlist) { + prom_printf("WARNING: not all physical memory mapped in IOMMU\n"); + prom_printf("Try booting with mem=xxxM or similar\n"); + prom_halt(); + } psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase); @@ -193,6 +238,12 @@ out: break; } psycho->psycho_regs->iommu_control = control; + + /* If necessary, hook us up for starfire IRQ translations. */ + if(this_is_starfire) + psycho->starfire_cookie = starfire_hookup(psycho->upa_portid); + else + psycho->starfire_cookie = NULL; } extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm); @@ -201,7 +252,7 @@ extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm); /* * Poor man's PCI... */ -__initfunc(void sabre_init(int pnode)) +void __init sabre_init(int pnode) { struct linux_prom64_registers pr_regs[2]; struct linux_psycho *sabre; @@ -213,6 +264,10 @@ __initfunc(void sabre_init(int pnode)) int bus; sabre = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC); + if (!sabre) { + prom_printf("SABRE: Error, kmalloc(sabre) failed.\n"); + prom_halt(); + } portid = prom_getintdefault(pnode, "upa-portid", 0xff); @@ -248,9 +303,11 @@ __initfunc(void sabre_init(int pnode)) prom_halt(); } - printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); + printk("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n", + sabre->psycho_regs, sabre->psycho_regs->control); #ifdef PROM_DEBUG - dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); + dprintf("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n", + sabre->psycho_regs, sabre->psycho_regs->control); #endif ctrl = sabre->psycho_regs->pci_a_control; @@ -382,7 +439,7 @@ apb_present(struct linux_psycho *psycho) return psycho->pci_bus ? 1 : 0; } -__initfunc(void pcibios_init(void)) +void __init pcibios_init(void) { struct linux_prom64_registers pr_regs[3]; struct linux_psycho *psycho; @@ -408,8 +465,6 @@ __initfunc(void pcibios_init(void)) goto next_pci; } - psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC); - portid = prom_getintdefault(node, "upa-portid", 0xff); for(search = psycho_root; search; search = search->next) { if(search->upa_portid == portid) { @@ -424,6 +479,11 @@ __initfunc(void pcibios_init(void)) } } + psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC); + if (!psycho) { + prom_printf("PSYCHO: Error, kmalloc(psycho) failed.\n"); + prom_halt(); + } memset(psycho, 0, sizeof(*psycho)); psycho->next = psycho_root; @@ -494,8 +554,14 @@ __initfunc(void pcibios_init(void)) is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); /* Enable arbitration for all PCI slots. */ - psycho->psycho_regs->pci_a_control |= 0x3f; - psycho->psycho_regs->pci_b_control |= 0x3f; + psycho->psycho_regs->pci_a_control |= PSYCHO_PCICTRL_AEN; + psycho->psycho_regs->pci_b_control |= PSYCHO_PCICTRL_AEN; + + /* Disable DMA write / PIO rd synchronization on both + * PCI bus segments. + */ + psycho->psycho_regs->pci_a_diag |= PSYCHO_PCIDIAG_DDWSYNC; + psycho->psycho_regs->pci_b_diag |= PSYCHO_PCIDIAG_DDWSYNC; other_pbm: if(is_pbm_a) @@ -609,25 +675,28 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void) } -__initfunc(static void -pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)) +static void __init +pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) { unsigned int devfn, l, class; unsigned char hdr_type = 0; + int is_multi = 0; for (devfn = 0; devfn < 0xff; ++devfn) { - if (PCI_FUNC(devfn) == 0) { - pbm_read_config_byte(pbm, bus, devfn, - PCI_HEADER_TYPE, &hdr_type); - } else if (!(hdr_type & 0x80)) { + if (PCI_FUNC(devfn) != 0 && is_multi == 0) { /* not a multi-function device */ continue; } + pbm_read_config_byte(pbm, bus, devfn, + PCI_HEADER_TYPE, &hdr_type); + if (PCI_FUNC(devfn) == 0) + is_multi = hdr_type & 0x80; /* Check if there is anything here. */ pbm_read_config_dword(pbm, bus, devfn, PCI_VENDOR_ID, &l); - if (l == 0xffffffff || l == 0x00000000) { - hdr_type = 0; + if (l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000) { + is_multi = 0; continue; } @@ -657,7 +726,7 @@ pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)) } } -__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)) +static void __init pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) { unsigned int nbus; @@ -682,8 +751,7 @@ __initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char } while (nbus--); } - -__initfunc(static void apb_init(struct linux_psycho *sabre)) +static void __init apb_init(struct linux_psycho *sabre) { struct pci_dev *pdev; unsigned short stmp; @@ -692,21 +760,20 @@ __initfunc(static void apb_init(struct linux_psycho *sabre)) for(pdev = pci_devices; pdev; pdev = pdev->next) { if(pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SABRE) { - /* Increase latency timer on top level bridge. */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128); break; } } 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); + /* Status register bits are "write 1 to clear". */ pci_write_config_word(pdev, PCI_STATUS, 0xffff); pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff); @@ -721,28 +788,25 @@ __initfunc(static void apb_init(struct linux_psycho *sabre)) APB_PCI_CTL_HIGH_ARBITER_EN; pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp); + /* Systems with SIMBA are usually workstations, so + * we configure to park to SIMBA not to the previous + * bus owner. + */ pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp); - itmp = APB_PCI_CTL_LOW_ARB_PARK | - APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; + itmp = APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp); - /* - * Setup Registers for Guaranteed Completion. + /* Don't mess with the retry limit and PIO/DMA latency + * timer settings. But do set primary and secondary + * latency timers. */ - 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); - - /* Increase primary latency timer. */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128); + pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 128); } } } -__initfunc(static void sabre_probe(struct linux_psycho *sabre)) +static void __init sabre_probe(struct linux_psycho *sabre) { struct pci_bus *pbus = sabre->pci_bus; static unsigned char busno = 0; @@ -764,7 +828,7 @@ __initfunc(static void sabre_probe(struct linux_psycho *sabre)) } -__initfunc(static void pbm_probe(struct linux_pbm_info *pbm)) +static void __init pbm_probe(struct linux_pbm_info *pbm) { static struct pci_bus *pchain = NULL; struct pci_bus *pbus = &pbm->pci_bus; @@ -803,9 +867,9 @@ __initfunc(static void pbm_probe(struct linux_pbm_info *pbm)) } } -__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - int pnode)) +static int __init pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + int pnode) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; int node; @@ -827,8 +891,8 @@ __initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, return 0; } -__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, - struct pci_dev *pdev, int pnode)) +static void __init pdev_cookie_fillin(struct linux_pbm_info *pbm, + struct pci_dev *pdev, int pnode) { struct pcidev_cookie *pcp; int node; @@ -846,9 +910,9 @@ __initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, #endif } -__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus, - struct linux_pbm_info *pbm, - int node)) +static void __init fill_in_pbm_cookies(struct pci_bus *pbus, + struct linux_pbm_info *pbm, + int node) { struct pci_dev *pdev; @@ -868,7 +932,7 @@ __initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus, } } -__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre)) +static void __init sabre_cookie_fillin(struct linux_psycho *sabre) { struct pci_bus *pbus = sabre->pci_bus; @@ -886,9 +950,9 @@ __initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre)) * properties, and recording them in pci_vma's linked in via * PBM->assignments. */ -__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)) +static int __init gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs) { - struct linux_prom_ebus_ranges erng[PROMREG_MAX]; + struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX]; int err, iter; err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng)); @@ -911,7 +975,7 @@ __initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_reg return err; } -__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node)) +static void __init assignment_process(struct linux_pbm_info *pbm, int node) { struct linux_prom_pci_registers aregs[PROMREG_MAX]; char pname[256]; @@ -968,7 +1032,7 @@ __initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node)) } } -__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)) +static void __init assignment_walk_siblings(struct linux_pbm_info *pbm, int node) { while(node) { int child = prom_getchild(node); @@ -1077,12 +1141,12 @@ static inline void record_assignments(struct linux_pbm_info *pbm) #endif } -__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)) +static void __init 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; @@ -1173,12 +1237,13 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev, } if (bsreg == PCI_ROM_ADDRESS) { pdev->rom_address = (unsigned long)__va(pci_addr); - pdev->rom_address |= 1; + pdev->rom_address &= ~1UL; + /* - * Enable access to the ROM. + * Disable access to the ROM. */ pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp); - pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp & ~1); } else pdev->base_address[brindex] = (unsigned long)__va(pci_addr); @@ -1347,7 +1412,7 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev, rtmp = new_base; pci_read_config_dword(pdev, breg, &base); - rtmp |= (base & ~PCI_ROM_ADDRESS_MASK); + rtmp &= ~(base & ~PCI_ROM_ADDRESS_MASK); pci_write_config_dword(pdev, breg, rtmp); /* Apply PBM ranges and update pci_dev. */ @@ -1370,8 +1435,7 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev, "PBM ranges\n"); } pdev->rom_address = (unsigned long)__va(pci_addr); - - pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK); + pdev->rom_address &= ~(base & ~PCI_ROM_ADDRESS_MASK); MEM_seen = 1; } rom_address_done: @@ -1415,7 +1479,7 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev, #define imap_offset(__member) \ ((unsigned long)(&(((struct psycho_regs *)0)->__member))) -__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino)) +static unsigned long __init psycho_pcislot_imap_offset(unsigned long ino) { unsigned int bus, slot; @@ -1431,11 +1495,8 @@ __initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino)) 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(); + return imap_offset(imap_a_slot3); } } else { switch(slot) { @@ -1446,19 +1507,16 @@ __initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino)) case 2: return imap_offset(imap_b_slot2); case 3: - return imap_offset(imap_b_slot3); default: - prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", - bus, slot); - prom_halt(); + return imap_offset(imap_b_slot3); } } } /* Exported for EBUS probing layer. */ -__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int ino)) +unsigned int __init psycho_irq_build(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + unsigned int ino) { unsigned long imap_off; int need_dma_sync = 0; @@ -1533,6 +1591,36 @@ __initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, imap_off = imap_offset(imap_ser); break; + case 0x2c: + /* Onboard Timer 0 */ + imap_off = imap_offset(imap_tim0); + break; + + case 0x2d: + /* Onboard Timer 1 */ + imap_off = imap_offset(imap_tim1); + break; + + case 0x2e: + /* Psycho UE Interrupt */ + imap_off = imap_offset(imap_ue); + break; + + case 0x2f: + /* Psycho CE Interrupt */ + imap_off = imap_offset(imap_ce); + break; + + case 0x30: + /* Psycho PCI A Error Interrupt */ + imap_off = imap_offset(imap_a_err); + break; + + case 0x31: + /* Psycho PCI B Error Interrupt */ + imap_off = imap_offset(imap_b_err); + break; + case 0x32: /* Power Management */ imap_off = imap_offset(imap_pmgmt); @@ -1554,37 +1642,78 @@ __initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, return psycho_build_irq(pbm->parent, imap_off, ino, need_dma_sync); } -__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - struct linux_prom_pci_registers *preg, - unsigned int *interrupt)) +static int __init 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) +#ifdef FIXUP_IRQ_DEBUG + dprintf("pbm_intmap_match: "); +#endif + if (!pbm->num_pbm_intmap) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No intmap UPA[%x:%c]\n", + pbm->parent->upa_portid, + (pbm == &pbm->parent->pbm_A) ? 'A' : 'B'); +#endif 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; + struct pcidev_cookie *pcp; + int node, offset; + char prom_name[64]; - if (!pcp) +#ifdef FIXUP_IRQ_DEBUG + dprintf("UnderBridge, "); +#endif + pcp = pdev->bus->self->sysdata; + if (!pcp) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No bus PCP\n"); +#endif goto out; - + } node = pcp->prom_node; i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg)); - if(i == 0 || i == -1) + if(i == 0 || i == -1) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No reg property.\n"); +#endif goto out; + } + /* + * Did PROM know better and assign an interrupt different + * to #INTA to the device? - We test here for presence of + * FCODE on the card, in this case we assume PROM has set + * correct 'interrupts' property, unless it is quadhme. + */ + pcp = pdev->sysdata; + if (!pcp) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No dev PCP\n"); +#endif + goto out; + } + node = pcp->prom_node; - /* Use low slot number bits of child as IRQ line. */ - *interrupt = ((pdev->devfn >> 3) & 3) + 1; - + offset = prom_getint(node, "fcode-rom-offset"); + prom_getstring(node, "name", prom_name, sizeof(prom_name)); + if (offset == -1 || + !strcmp(prom_name, "SUNW,qfe") || + !strcmp(prom_name, "qfe")) { + /* + * No, use low slot number bits of child as IRQ line. + */ + *interrupt = ((*interrupt - 1 + PCI_SLOT(pdev->devfn)) & 3) + 1; + } preg = &ppreg; } @@ -1618,13 +1747,12 @@ out: prom_halt(); } -__initfunc(static void fixup_irq(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *preg, - int node)) +static void __init 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; int err; #ifdef FIXUP_IRQ_DEBUG @@ -1668,7 +1796,25 @@ __initfunc(static void fixup_irq(struct pci_dev *pdev, unsigned int bus, slot, line; bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0; - line = (pci_irq_line) & 3; + + /* Use the given interrupt property value as the line if it + * is non-zero and legal. Legal encodings are INTA=1, INTB=2, + * INTC=3, INTD=4 as per PCI OBP binding spec version 2.1 -DaveM + */ + if(prom_irq > 0 && prom_irq < 5) { + line = ((prom_irq - 1) & 3); + } else { + unsigned char pci_irq_line; + + /* The generic PCI probing layer will read the + * interrupt line into pdev->irq if the interrupt + * pin is non-zero, so we have to explicitly fetch + * the pin here to be certain (the interrupt line is + * typically left at zero by OBP). + */ + pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line); + line = ((pci_irq_line - 1) & 3); + } /* Slot determination is only slightly complex. Handle * the easy case first. @@ -1721,11 +1867,11 @@ __initfunc(static void fixup_irq(struct pci_dev *pdev, #endif } -__initfunc(static void fixup_doit(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - int node)) +static void __init 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; @@ -1745,9 +1891,9 @@ __initfunc(static void fixup_doit(struct pci_dev *pdev, fixup_irq(pdev, pbm, &pregs[0], node); } -__initfunc(static void fixup_pci_dev(struct pci_dev *pdev, - struct pci_bus *pbus, - struct linux_pbm_info *pbm)) +static void __init 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; @@ -1762,8 +1908,12 @@ __initfunc(static void fixup_pci_dev(struct pci_dev *pdev, cmd |= PCI_COMMAND_MASTER; pci_write_config_word(pdev, PCI_COMMAND, cmd); - /* Now, set cache line size to 64-bytes. */ - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64); + /* Now, set cache line size to 64-bytes. + * NOTE: Cache line size is in 32-bit word units. + */ + pci_write_config_byte(pdev, + PCI_CACHE_LINE_SIZE, + (64 / sizeof(u32))); } /* Ignore if this is one of the PBM's, EBUS, or a @@ -1808,7 +1958,7 @@ __initfunc(static void fixup_pci_dev(struct pci_dev *pdev, } } -__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)) +static void __init fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm) { struct pci_dev *pdev; @@ -1819,7 +1969,7 @@ __initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info fixup_pci_bus(pbus, pbm); } -__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm)) +static void __init fixup_addr_irq(struct linux_pbm_info *pbm) { struct pci_bus *pbus = &pbm->pci_bus; @@ -1832,7 +1982,7 @@ __initfunc(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. */ -__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho)) +static void __init psycho_final_fixup(struct linux_psycho *psycho) { /* Second, fixup base address registers and IRQ lines... */ if (psycho->pbm_A.parent) @@ -1841,7 +1991,33 @@ __initfunc(static void psycho_final_fixup(struct linux_psycho *psycho)) fixup_addr_irq(&psycho->pbm_B); } -__initfunc(void pcibios_fixup(void)) +/* Reorder the pci_dev chain, so that onboard devices come first + and then come the pluggable cards. */ +void __init psycho_reorder_devs(void) +{ + struct pci_dev **pci_onboard = &pci_devices; + struct pci_dev **pci_tail = &pci_devices; + struct pci_dev *pdev = pci_devices, *pci_other = NULL; + + while (pdev) { + if (pdev->irq && (__irq_ino(pdev->irq) & 0x20)) { + if (pci_other) { + *pci_onboard = pdev; + pci_onboard = &pdev->next; + pdev = pdev->next; + *pci_onboard = pci_other; + *pci_tail = pdev; + continue; + } else + pci_onboard = &pdev->next; + } else if (!pci_other) + pci_other = pdev; + pci_tail = &pdev->next; + pdev = pdev->next; + } +} + +void __init pcibios_fixup(void) { struct linux_psycho *psycho; @@ -1861,9 +2037,9 @@ __initfunc(void pcibios_fixup(void)) for (psycho = psycho_root; psycho; psycho = psycho->next) { /* Probe bus on builtin PCI. */ - if (apb_present(psycho)) + if (apb_present(psycho)) { sabre_probe(psycho); - else { + } else { /* Probe busses under PBM B. */ pbm_probe(&psycho->pbm_B); @@ -1896,6 +2072,9 @@ __initfunc(void pcibios_fixup(void)) psycho_final_fixup(psycho); } + if (psycho_reorder & PSYCHO_REORDER_ONBOARDFIRST) + psycho_reorder_devs(); + return ebus_init(); } @@ -2418,12 +2597,20 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, return err; } -__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +void __init pcibios_fixup_bus(struct pci_bus *bus) { } -__initfunc(char *pcibios_setup(char *str)) +char * __init pcibios_setup(char *str) { + if (!strcmp(str, "onboardfirst")) { + psycho_reorder |= PSYCHO_REORDER_ONBOARDFIRST; + return NULL; + } + if (!strcmp(str, "noreorder")) { + psycho_reorder = 0; + return NULL; + } return str; } diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 4063a1e86..4dd9651b3 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -591,6 +591,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs) if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH)) || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) { + unsigned long flags; + if(child == current) { /* Try this under SunOS/Solaris, bwa haha * You'll never be able to kill the process. ;-) @@ -602,8 +604,9 @@ asmlinkage void do_ptrace(struct pt_regs *regs) (current->uid != child->euid) || (current->uid != child->uid) || (current->gid != child->egid) || - (current->gid != child->gid)) && - !capable(CAP_SYS_PTRACE)) { + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); goto out; } @@ -613,15 +616,13 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } child->flags |= PF_PTRACED; + write_lock_irqsave(&tasklist_lock, flags); if(child->p_pptr != current) { - unsigned long flags; - - write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); send_sig(SIGSTOP, child, 1); pt_succ_return(regs, 0); goto out; @@ -670,14 +671,18 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = read_int(child, addr, &x); + up(&child->mm->mmap_sem); tmp = x; } else { if(addr & (sizeof(unsigned long) - 1)) { pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); } if (res < 0) { pt_error_return(regs, -res); @@ -709,13 +714,17 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = write_int(child, addr, data); + up(&child->mm->mmap_sem); } else { if(addr & (sizeof(unsigned long) - 1)) { pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = write_long(child, addr, data); + up(&child->mm->mmap_sem); } if(res < 0) pt_error_return(regs, -res); @@ -944,12 +953,15 @@ asmlinkage void do_ptrace(struct pt_regs *regs) unsigned long page; while(len) { + down(&child->mm->mmap_sem); vma = find_extend_vma(child, src); if (!vma) { + up(&child->mm->mmap_sem); pt_error_return(regs, EIO); goto flush_and_out; } pgtable = get_page (child, vma, src, 0); + up(&child->mm->mmap_sem); if (src & ~PAGE_MASK) { curlen = PAGE_SIZE - (src & ~PAGE_MASK); if (curlen > len) curlen = len; @@ -988,12 +1000,15 @@ asmlinkage void do_ptrace(struct pt_regs *regs) unsigned long page; while(len) { + down(&child->mm->mmap_sem); vma = find_extend_vma(child, dest); if (!vma) { + up(&child->mm->mmap_sem); pt_error_return(regs, EIO); goto flush_and_out; } pgtable = get_page (child, vma, dest, 1); + up(&child->mm->mmap_sem); if (dest & ~PAGE_MASK) { curlen = PAGE_SIZE - (dest & ~PAGE_MASK); if (curlen > len) curlen = len; diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 570322eec..caa1d99ef 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.37 1998/10/14 15:49:09 ecd Exp $ +/* $Id: setup.c,v 1.43 1999/04/12 08:08:24 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -277,6 +277,22 @@ static int console_fb __initdata = 0; #endif static unsigned long memory_size = 0; +#ifdef PROM_DEBUG_CONSOLE +static struct console prom_debug_console = { + "debug", + prom_console_write, + NULL, + NULL, + NULL, + NULL, + NULL, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; +#endif + /* XXX Implement this at some point... */ void kernel_enter_debugger(void) { @@ -397,13 +413,12 @@ __initfunc(static void boot_flags_init(char *commands)) extern int prom_probe_memory(void); extern unsigned long start, end; extern void panic_setup(char *, int *); -extern unsigned long sun_serial_setup(unsigned long); extern unsigned short root_flags; extern unsigned short root_dev; extern unsigned short ram_flags; -extern unsigned int ramdisk_image; -extern unsigned int ramdisk_size; +extern unsigned int sparc_ramdisk_image; +extern unsigned int sparc_ramdisk_size; #define RAMDISK_IMAGE_START_MASK 0x07FF #define RAMDISK_PROMPT_FLAG 0x8000 #define RAMDISK_LOAD_FLAG 0x4000 @@ -430,6 +445,10 @@ __initfunc(void setup_arch(char **cmdline_p, *cmdline_p = prom_getbootargs(); strcpy(saved_command_line, *cmdline_p); +#ifdef PROM_DEBUG_CONSOLE + register_console(&prom_debug_console); +#endif + printk("ARCH: SUN4U\n"); #ifdef CONFIG_DUMMY_CONSOLE @@ -489,13 +508,13 @@ __initfunc(void setup_arch(char **cmdline_p, rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0); #endif #ifdef CONFIG_BLK_DEV_INITRD - if (ramdisk_image) { + if (sparc_ramdisk_image) { unsigned long start = 0; - if (ramdisk_image >= (unsigned long)&end - 2 * PAGE_SIZE) - ramdisk_image -= KERNBASE; - initrd_start = ramdisk_image + phys_base + PAGE_OFFSET; - initrd_end = initrd_start + ramdisk_size; + if (sparc_ramdisk_image >= (unsigned long)&end - 2 * PAGE_SIZE) + sparc_ramdisk_image -= KERNBASE; + initrd_start = sparc_ramdisk_image + phys_base + PAGE_OFFSET; + initrd_end = initrd_start + sparc_ramdisk_size; if (initrd_end > *memory_end_p) { printk(KERN_CRIT "initrd extends beyond end of memory " "(0x%016lx > 0x%016lx)\ndisabling initrd\n", @@ -503,10 +522,10 @@ __initfunc(void setup_arch(char **cmdline_p, initrd_start = 0; } if (initrd_start) - start = ramdisk_image + KERNBASE; + start = sparc_ramdisk_image + KERNBASE; if (start >= *memory_start_p && start < *memory_start_p + 2 * PAGE_SIZE) { initrd_below_start_ok = 1; - *memory_start_p = PAGE_ALIGN (start + ramdisk_size); + *memory_start_p = PAGE_ALIGN (start + sparc_ramdisk_size); } } #endif @@ -531,7 +550,7 @@ __initfunc(void setup_arch(char **cmdline_p, ic_servaddr = sv; if (gw) ic_gateway = gw; - ic_bootp_flag = ic_rarp_flag = 0; + ic_proto_enabled = 0; } } #endif @@ -566,7 +585,6 @@ __initfunc(void setup_arch(char **cmdline_p, serial_console = 2; break; } - *memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */ #else serial_console = 0; #endif diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 4bdfca1b7..27344f4b6 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -5,6 +5,8 @@ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/mm.h> +#include <linux/pagemap.h> #include <linux/tasks.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -34,24 +36,23 @@ 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; -int smp_num_cpus = 1; -int smp_threads_ready = 0; +struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); -struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); +volatile int cpu_number_map[NR_CPUS] __attribute__ ((aligned (64))); +volatile int __cpu_logical_map[NR_CPUS] __attribute__ ((aligned (64))); -/* Please don't make this initdata!!! --DaveM */ +/* Please don't make this stuff initdata!!! --DaveM */ static unsigned char boot_cpu_id = 0; - static int smp_activated = 0; -volatile int cpu_number_map[NR_CPUS]; -volatile int __cpu_logical_map[NR_CPUS]; - /* Kernel spinlock */ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; +volatile int smp_processors_ready = 0; +unsigned long cpu_present_map = 0; +int smp_num_cpus = 1; +int smp_threads_ready = 0; + __initfunc(void smp_setup(char *str, int *ints)) { /* XXX implement me XXX */ @@ -84,6 +85,8 @@ int smp_bogo(char *buf) __initfunc(void smp_store_cpu_info(int id)) { + int i; + cpu_data[id].irq_count = 0; cpu_data[id].bh_count = 0; /* multiplier and counter set by @@ -94,16 +97,18 @@ __initfunc(void smp_store_cpu_info(int id)) cpu_data[id].pte_cache = NULL; cpu_data[id].pgdcache_size = 0; cpu_data[id].pgd_cache = NULL; -} + cpu_data[id].idle_volume = 1; -extern void distribute_irqs(void); + for(i = 0; i < 16; i++) + cpu_data[id].irq_worklists[i] = 0; +} __initfunc(void smp_commence(void)) { - distribute_irqs(); } static void smp_setup_percpu_timer(void); +static void smp_tune_scheduling(void); static volatile unsigned long callin_flag = 0; @@ -173,10 +178,16 @@ void cpu_panic(void) panic("SMP bolixed\n"); } -extern struct prom_cpuinfo linux_cpus[NR_CPUS]; +extern struct prom_cpuinfo linux_cpus[64]; extern unsigned long smp_trampoline; +/* The OBP cpu startup callback truncates the 3rd arg cookie to + * 32-bits (I think) so to be safe we have it read the pointer + * contained here so we work on >4GB machines. -DaveM + */ +static struct task_struct *cpu_new_task = NULL; + __initfunc(void smp_boot_cpus(void)) { int cpucount = 0, i; @@ -184,6 +195,8 @@ __initfunc(void smp_boot_cpus(void)) printk("Entering UltraSMPenguin Mode...\n"); __sti(); smp_store_cpu_info(boot_cpu_id); + smp_tune_scheduling(); + init_idle(); if(linux_num_cpus == 1) return; @@ -194,21 +207,25 @@ __initfunc(void smp_boot_cpus(void)) if(cpu_present_map & (1UL << i)) { unsigned long entry = (unsigned long)(&smp_trampoline); + unsigned long cookie = (unsigned long)(&cpu_new_task); struct task_struct *p; int timeout; int no; extern unsigned long phys_base; entry += phys_base - KERNBASE; + cookie += phys_base - KERNBASE; kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; + p->has_cpu = 1; /* we schedule the first task manually */ callin_flag = 0; for (no = 0; no < linux_num_cpus; no++) if (linux_cpus[no].mid == i) break; + cpu_new_task = p; prom_startcpu(linux_cpus[no].prom_node, - entry, ((unsigned long)p)); + entry, cookie); for(timeout = 0; timeout < 5000000; timeout++) { if(callin_flag) break; @@ -216,8 +233,8 @@ __initfunc(void smp_boot_cpus(void)) } if(callin_flag) { cpu_number_map[i] = cpucount; - prom_cpu_nodes[i] = linux_cpus[no].prom_node; __cpu_logical_map[cpucount] = i; + prom_cpu_nodes[i] = linux_cpus[no].prom_node; } else { cpucount--; printk("Processor %d is stuck.\n", i); @@ -228,6 +245,7 @@ __initfunc(void smp_boot_cpus(void)) cpu_number_map[i] = -1; } } + cpu_new_task = NULL; if(cpucount == 0) { printk("Error: only one processor found.\n"); cpu_present_map = (1UL << smp_processor_id()); @@ -249,17 +267,6 @@ __initfunc(void smp_boot_cpus(void)) membar("#StoreStore | #StoreLoad"); } -/* We don't even need to do anything, the only generic message pass done - * anymore is to stop all cpus during a panic(). When the user drops to - * the PROM prompt, the firmware will send the other cpu's it's MONDO - * vector anyways, so doing anything special here is pointless. - * - * This whole thing should go away anyways... - */ -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, unsigned long cpu) @@ -342,6 +349,17 @@ extern unsigned long xcall_flush_tlb_all; extern unsigned long xcall_tlbcachesync; extern unsigned long xcall_flush_cache_all; extern unsigned long xcall_report_regs; +extern unsigned long xcall_receive_signal; + +void smp_receive_signal(int cpu) +{ + if(smp_processors_ready && + (cpu_present_map & (1UL<<cpu)) != 0) { + u64 pstate, data0 = (((u64)&xcall_receive_signal) & 0xffffffff); + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + xcall_deliver(data0, 0, 0, pstate, cpu); + } +} void smp_report_regs(void) { @@ -364,37 +382,51 @@ void smp_flush_tlb_all(void) * to the stack before we get here because all callers of us * are flush_tlb_*() routines, and these run after flush_cache_*() * which performs the flushw. + * + * The SMP TLB coherency scheme we use works as follows: + * + * 1) mm->cpu_vm_mask is a bit mask of which cpus an address + * space has (potentially) executed on, this is the heuristic + * we use to avoid doing cross calls. + * + * 2) TLB context numbers are shared globally across all processors + * in the system, this allows us to play several games to avoid + * cross calls. + * + * One invariant is that when a cpu switches to a process, and + * that processes tsk->mm->cpu_vm_mask does not have the current + * cpu's bit set, that tlb context is flushed locally. + * + * If the address space is non-shared (ie. mm->count == 1) we avoid + * cross calls when we want to flush the currently running process's + * tlb state. This is done by clearing all cpu bits except the current + * processor's in current->mm->cpu_vm_mask and performing the flush + * locally only. This will force any subsequent cpus which run this + * task to flush the context from the local tlb if the process migrates + * to another cpu (again). + * + * 3) For shared address spaces (threads) and swapping we bite the + * bullet for most cases and perform the cross call. + * + * The performance gain from "optimizing" away the cross call for threads is + * questionable (in theory the big win for threads is the massive sharing of + * address space state across processors). + * + * For the swapping case the locking is difficult to get right, we'd have to + * enforce strict ordered access to mm->cpu_vm_mask via a spinlock for example. + * Then again one could argue that when you are swapping, the cost of a cross + * call won't even show up on the performance radar. But in any case we do get + * rid of the cross-call when the task has a dead context or the task has only + * ever run on the local cpu. */ -static void smp_cross_call_avoidance(struct mm_struct *mm) -{ - u32 ctx; - - spin_lock(&scheduler_lock); - get_new_mmu_context(mm); - mm->cpu_vm_mask = (1UL << smp_processor_id()); - current->tss.ctx = ctx = mm->context & 0x3ff; - spitfire_set_secondary_context(ctx); - __asm__ __volatile__("flush %g6"); - spitfire_flush_dtlb_secondary_context(); - spitfire_flush_itlb_secondary_context(); - __asm__ __volatile__("flush %g6"); - if(!segment_eq(current->tss.current_ds,USER_DS)) { - /* Rarely happens. */ - current->tss.ctx = 0; - spitfire_set_secondary_context(0); - __asm__ __volatile__("flush %g6"); - } - spin_unlock(&scheduler_lock); -} - void smp_flush_tlb_mm(struct mm_struct *mm) { u32 ctx = mm->context & 0x3ff; if(mm == current->mm && atomic_read(&mm->count) == 1) { - if(mm->cpu_vm_mask == (1UL << smp_processor_id())) - goto local_flush_and_out; - return smp_cross_call_avoidance(mm); + if(mm->cpu_vm_mask != (1UL << smp_processor_id())) + mm->cpu_vm_mask = (1UL << smp_processor_id()); + goto local_flush_and_out; } smp_cross_call(&xcall_flush_tlb_mm, ctx, 0, 0); @@ -410,9 +442,9 @@ void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start, start &= PAGE_MASK; end &= PAGE_MASK; if(mm == current->mm && atomic_read(&mm->count) == 1) { - if(mm->cpu_vm_mask == (1UL << smp_processor_id())) - goto local_flush_and_out; - return smp_cross_call_avoidance(mm); + if(mm->cpu_vm_mask != (1UL << smp_processor_id())) + mm->cpu_vm_mask = (1UL << smp_processor_id()); + goto local_flush_and_out; } smp_cross_call(&xcall_flush_tlb_range, ctx, start, end); @@ -426,22 +458,26 @@ void smp_flush_tlb_page(struct mm_struct *mm, unsigned long page) page &= PAGE_MASK; if(mm == current->mm && atomic_read(&mm->count) == 1) { - if(mm->cpu_vm_mask == (1UL << smp_processor_id())) - goto local_flush_and_out; - return smp_cross_call_avoidance(mm); - } -#if 0 /* XXX Disabled until further notice... */ - else if(atomic_read(&mm->count) == 1) { + if(mm->cpu_vm_mask != (1UL << smp_processor_id())) + mm->cpu_vm_mask = (1UL << smp_processor_id()); + goto local_flush_and_out; + } else { /* Try to handle two special cases to avoid cross calls * in common scenerios where we are swapping process * pages out. */ - if((mm->context ^ tlb_context_cache) & CTX_VERSION_MASK) + if(((mm->context ^ tlb_context_cache) & CTX_VERSION_MASK) || + (mm->cpu_vm_mask == 0)) { + /* A dead context cannot ever become "alive" until + * a task switch is done to it. + */ return; /* It's dead, nothing to do. */ - if(mm->cpu_vm_mask == (1UL << smp_processor_id())) - goto local_flush_and_out; + } + if(mm->cpu_vm_mask == (1UL << smp_processor_id())) { + __flush_tlb_page(ctx, page, SECONDARY_CONTEXT); + return; /* Only local flush is necessary. */ + } } -#endif smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0); local_flush_and_out: @@ -644,6 +680,100 @@ __initfunc(void smp_tick_init(void)) prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1; } +static inline unsigned long find_flush_base(unsigned long size) +{ + struct page *p = mem_map; + unsigned long found, base; + + size = PAGE_ALIGN(size); + found = size; + base = page_address(p); + while(found != 0) { + /* Failure. */ + if(p >= (mem_map + max_mapnr)) + return 0UL; + if(PageSkip(p)) { + p = p->next_hash; + base = page_address(p); + found = size; + } else { + found -= PAGE_SIZE; + p++; + } + } + return base; +} + +cycles_t cacheflush_time; + +__initfunc(static void smp_tune_scheduling (void)) +{ + unsigned long flush_base, flags, *p; + unsigned int ecache_size; + cycles_t tick1, tick2, raw; + + /* Approximate heuristic for SMP scheduling. It is an + * estimation of the time it takes to flush the L2 cache + * on the local processor. + * + * The ia32 chooses to use the L1 cache flush time instead, + * and I consider this complete nonsense. The Ultra can service + * a miss to the L1 with a hit to the L2 in 7 or 8 cycles, and + * L2 misses are what create extra bus traffic (ie. the "cost" + * of moving a process from one cpu to another). + */ + printk("SMP: Calibrating ecache flush... "); + ecache_size = prom_getintdefault(linux_cpus[0].prom_node, + "ecache-size", (512 *1024)); + flush_base = find_flush_base(ecache_size << 1); + + if(flush_base != 0UL) { + __save_and_cli(flags); + + /* Scan twice the size once just to get the TLB entries + * loaded and make sure the second scan measures pure misses. + */ + for(p = (unsigned long *)flush_base; + ((unsigned long)p) < (flush_base + (ecache_size<<1)); + p += (64 / sizeof(unsigned long))) + *((volatile unsigned long *)p); + + /* Now the real measurement. */ + __asm__ __volatile__(" + b,pt %%xcc, 1f + rd %%tick, %0 + + .align 64 +1: ldx [%2 + 0x000], %%g1 + ldx [%2 + 0x040], %%g2 + ldx [%2 + 0x080], %%g3 + ldx [%2 + 0x0c0], %%g5 + add %2, 0x100, %2 + cmp %2, %4 + bne,pt %%xcc, 1b + nop + + rd %%tick, %1" + : "=&r" (tick1), "=&r" (tick2), "=&r" (flush_base) + : "2" (flush_base), "r" (flush_base + ecache_size) + : "g1", "g2", "g3", "g5"); + + __restore_flags(flags); + + raw = (tick2 - tick1); + + /* Dampen it a little, considering two processes + * sharing the cache and fitting. + */ + cacheflush_time = (raw - (raw >> 2)); + } else + cacheflush_time = ((ecache_size << 2) + + (ecache_size << 1)); + + printk("Using heuristic of %d cycles.\n", + (int) cacheflush_time); +} + int __init setup_profiling_timer(unsigned int multiplier) { unsigned long flags; diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index a42505edc..305f37ad8 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,8 +1,9 @@ -/* $Id: sparc64_ksyms.c,v 1.49 1998/10/28 08:11:28 jj Exp $ +/* $Id: sparc64_ksyms.c,v 1.58 1999/05/08 03:00:31 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) */ /* Tell string.h we don't want memcpy etc. as cpp defines */ @@ -52,8 +53,9 @@ struct poll { short revents; }; -extern unsigned prom_cpu_nodes[NR_CPUS]; +extern unsigned prom_cpu_nodes[64]; extern void die_if_kernel(char *str, struct pt_regs *regs); +extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); void _sigpause_common (unsigned int set, struct pt_regs *); @@ -88,7 +90,6 @@ extern int __ashrdi3(int, int); extern void dump_thread(struct pt_regs *, struct user *); #ifdef __SMP__ -extern spinlock_t scheduler_lock; extern spinlock_t kernel_flag; extern int smp_num_cpus; #ifdef SPIN_LOCK_DEBUG @@ -102,6 +103,8 @@ extern void _do_write_unlock(rwlock_t *rw); #endif #endif +extern unsigned long phys_base; + /* One thing to note is that the way the symbols of the mul/div * support routines are named is a mess, they all start with * a '.' which makes it a bitch to export, here is the trick: @@ -116,7 +119,6 @@ __attribute__((section("__ksymtab"))) = \ /* used by various drivers */ #ifdef __SMP__ /* Kernel wide locking */ -EXPORT_SYMBOL(scheduler_lock); EXPORT_SYMBOL(kernel_flag); /* Software-IRQ BH locking */ @@ -155,6 +157,8 @@ EXPORT_SYMBOL(_do_write_unlock); EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(local_bh_count); #endif + +EXPORT_SYMBOL(ivector_table); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); @@ -171,6 +175,7 @@ EXPORT_SYMBOL(sparc_dvma_malloc); EXPORT_SYMBOL(mmu_release_scsi_one); EXPORT_SYMBOL(mmu_release_scsi_sgl); #if CONFIG_SBUS +EXPORT_SYMBOL(mmu_set_sbus64); EXPORT_SYMBOL(SBus_chain); EXPORT_SYMBOL(dma_chain); #endif @@ -199,6 +204,9 @@ EXPORT_SYMBOL(dump_thread); /* math-emu wants this */ EXPORT_SYMBOL(die_if_kernel); +/* Kernel thread creation. */ +EXPORT_SYMBOL(kernel_thread); + /* prom symbols */ EXPORT_SYMBOL(idprom); EXPORT_SYMBOL(prom_root_node); @@ -214,6 +222,7 @@ EXPORT_SYMBOL(prom_setprop); EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); +EXPORT_SYMBOL(prom_getbool); EXPORT_SYMBOL(prom_getstring); EXPORT_SYMBOL(prom_apply_sbus_ranges); EXPORT_SYMBOL(prom_getint); @@ -257,7 +266,6 @@ EXPORT_SYMBOL(svr4_setcontext); EXPORT_SYMBOL(prom_cpu_nodes); EXPORT_SYMBOL(sys_ioctl); EXPORT_SYMBOL(sys32_ioctl); -EXPORT_SYMBOL(get_unmapped_area); EXPORT_SYMBOL(move_addr_to_kernel); EXPORT_SYMBOL(move_addr_to_user); #endif @@ -281,6 +289,10 @@ EXPORT_SYMBOL(__copy_from_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__bzero_noasi); +/* Various address conversion macros use this. */ +EXPORT_SYMBOL(phys_base); +EXPORT_SYMBOL(sparc64_valid_addr_bitmap); + /* No version information on this, heavily used in inline asm, * and will always be 'void __ret_efault(void)'. */ diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c new file mode 100644 index 000000000..38f33ecd6 --- /dev/null +++ b/arch/sparc64/kernel/starfire.c @@ -0,0 +1,121 @@ +/* $Id: starfire.c,v 1.2 1998/12/09 18:53:11 davem Exp $ + * starfire.c: Starfire/E10000 support. + * + * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> + +#include <asm/page.h> +#include <asm/oplib.h> +#include <asm/smp.h> + +/* A few places around the kernel check this to see if + * they need to call us to do things in a Starfire specific + * way. + */ +int this_is_starfire = 0; + +void starfire_check(void) +{ + int ssnode = prom_finddevice("/ssp-serial"); + + if(ssnode != 0 && ssnode != -1) { + int i; + + this_is_starfire = 1; + + /* Now must fixup cpu MIDs. OBP gave us a logical + * linear cpuid number, not the real upaid. + */ + for(i = 0; i < linux_num_cpus; i++) { + unsigned int mid = linux_cpus[i].mid; + + mid = (((mid & 0x3c) << 1) | + ((mid & 0x40) >> 4) | + (mid & 0x3)); + + linux_cpus[i].mid = mid; + } + } +} + +int starfire_hard_smp_processor_id(void) +{ + return *((unsigned int *) __va(0x1fff40000d0)); +} + +/* Each Starfire board has 32 registers which perform translation + * and delivery of traditional interrupt packets into the extended + * Starfire hardware format. Essentially UPAID's now have 2 more + * bits than in all previous Sun5 systems. + */ +struct starfire_irqinfo { + unsigned int *imap_slots[32]; + unsigned int *tregs[32]; + struct starfire_irqinfo *next; + int upaid, hwmid; +}; + +static struct starfire_irqinfo *sflist = NULL; + +/* Beam me up Scott(McNeil)y... */ +void *starfire_hookup(int upaid) +{ + struct starfire_irqinfo *p; + unsigned long treg_base, hwmid, i; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if(!p) { + prom_printf("starfire_hookup: No memory, this is insane.\n"); + prom_halt(); + } + treg_base = 0x100fc000000UL; + hwmid = ((upaid & 0x3c) << 1) | + ((upaid & 0x40) >> 4) | + (upaid & 0x3); + p->hwmid = hwmid; + treg_base += (hwmid << 33UL); + treg_base += 0x200UL; + for(i = 0; i < 32; i++) { + p->imap_slots[i] = NULL; + p->tregs[i] = __va(treg_base + (i * 0x10)); + } + p->upaid = upaid; + p->next = sflist; + sflist = p; + + return (void *) p; +} + +unsigned int starfire_translate(unsigned int *imap, + unsigned int upaid) +{ + struct starfire_irqinfo *p; + unsigned int bus_hwmid; + unsigned int i; + + bus_hwmid = (((unsigned long)imap) >> 33) & 0x7f; + for(p = sflist; p != NULL; p = p->next) + if(p->hwmid == bus_hwmid) + break; + if(p == NULL) { + prom_printf("XFIRE: Cannot find irqinfo for imap %016lx\n", + ((unsigned long)imap)); + prom_halt(); + } + for(i = 0; i < 32; i++) { + if(p->imap_slots[i] == imap || + p->imap_slots[i] == NULL) + break; + } + if(i == 32) { + printk("starfire_translate: Are you kidding me?\n"); + panic("Lucy in the sky...."); + } + p->imap_slots[i] = imap; + *(p->tregs[i]) = upaid; + + return i; +} diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 08ce07244..8d11f10b8 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.25 1998/10/21 03:21:15 davem Exp $ +/* $Id: sys_sparc.c,v 1.26 1999/01/07 19:07:01 jj Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -27,6 +27,8 @@ #include <asm/utrap.h> #include <asm/perfctr.h> +/* #define DEBUG_UNIMP_SYSCALL */ + /* XXX Make this per-binary type, this way we can detect the type of * XXX a binary. Every Sparc executable calls this very early on. */ @@ -200,11 +202,14 @@ asmlinkage unsigned long c_sys_nis_syscall (struct pt_regs *regs) { static int count=0; + + /* Don't make the system unusable, if someone goes stuck */ + if (count++ > 5) return -ENOSYS; lock_kernel(); - if (++count <= 20) { /* Don't make the system unusable, if someone goes stuck */ - printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]); - show_regs (regs); - } + printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]); +#ifdef DEBUG_UNIMP_SYSCALL + show_regs (regs); +#endif unlock_kernel(); return -ENOSYS; } diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 1a49380f1..a7f85ca58 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.100 1998/11/08 11:14:00 davem Exp $ +/* $Id: sys_sparc32.c,v 1.107 1999/03/05 13:21:02 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -43,6 +43,7 @@ #include <linux/poll.h> #include <linux/personality.h> #include <linux/stat.h> +#include <linux/timex.h> #include <asm/types.h> #include <asm/ipc.h> @@ -50,6 +51,8 @@ #include <asm/fpumacro.h> #include <asm/semaphore.h> +#include <net/scm.h> + /* Use this to get at 32-bit user passed pointers. */ /* Things to consider: the low-level assembly stub does srl x, 0, x for first four arguments, so if you have @@ -74,15 +77,6 @@ __ret; \ }) -static inline char * get_page(void) -{ - char * res; - res = (char *)__get_free_page(GFP_KERNEL); - return res; -} - -#define putname32 putname - /* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. @@ -109,13 +103,13 @@ char * getname32(const char *filename) char *tmp, *result; result = ERR_PTR(-ENOMEM); - tmp = get_page(); + tmp = (char *)__get_free_page(GFP_KERNEL); if (tmp) { int retval = do_getname32(filename, tmp); result = tmp; if (retval < 0) { - putname32(tmp); + putname(tmp); result = ERR_PTR(retval); } } @@ -243,7 +237,10 @@ static int do_sys32_semctl(int first, int second, int third, void *uptr) err = -EFAULT; if (get_user (pad, (u32 *)uptr)) goto out; - fourth.__pad = (void *)A(pad); + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); if (IPCOP_MASK (third) & (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) | IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) | @@ -652,7 +649,7 @@ asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned lon set_fs (KERNEL_DS); err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); set_fs (old_fs); - putname32 (spec); + putname (spec); if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; ((struct dqblk32 *)&d)->dqb_itime = i; @@ -696,7 +693,7 @@ asmlinkage int sys32_statfs(const char * path, struct statfs32 *buf) set_fs (KERNEL_DS); ret = sys_statfs((const char *)pth, &s); set_fs (old_fs); - putname32 (pth); + putname (pth); if (put_statfs(buf, &s)) return -EFAULT; } @@ -744,7 +741,7 @@ asmlinkage int sys32_utime(char * filename, struct utimbuf32 *times) set_fs (KERNEL_DS); ret = sys_utime(filenam, &t); set_fs (old_fs); - putname32 (filenam); + putname (filenam); } return ret; } @@ -796,8 +793,9 @@ static long do_readv_writev32(int type, struct file *file, } inode = file->f_dentry->d_inode; - retval = locks_verify_area((type == VERIFY_READ) ? - FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), inode, file, file->f_pos, tot_len); if (retval) { if (iov != iovstack) @@ -1106,13 +1104,17 @@ set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) __put_user(*fdset, ufdset); } +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) { - fd_set_buffer *fds; + fd_set_bits fds; struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x); + char *bits; unsigned long nn; long timeout; - int ret; + int ret, size; timeout = MAX_SCHEDULE_TIMEOUT; if (tvp) { @@ -1123,30 +1125,47 @@ asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) || (ret = __get_user(usec, &tvp->tv_usec))) goto out_nofds; - timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); - timeout += sec * HZ; + ret = -EINVAL; + if(sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } } + ret = -EINVAL; + if (n < 0 || n > KFDS_NR) + goto out_nofds; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ ret = -ENOMEM; - fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL); - if (!fds) + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) goto out_nofds; - ret = -EINVAL; - if (n < 0) - goto out; - if (n > KFDS_NR) - n = KFDS_NR; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); - if ((ret = get_fd_set32(nn, fds->in, inp)) || - (ret = get_fd_set32(nn, fds->out, outp)) || - (ret = get_fd_set32(nn, fds->ex, exp))) + if ((ret = get_fd_set32(nn, fds.in, inp)) || + (ret = get_fd_set32(nn, fds.out, outp)) || + (ret = get_fd_set32(nn, fds.ex, exp))) goto out; - zero_fd_set(n, fds->res_in); - zero_fd_set(n, fds->res_out); - zero_fd_set(n, fds->res_ex); + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); - ret = do_select(n, fds, &timeout); + ret = do_select(n, &fds, &timeout); if (tvp && !(current->personality & STICKY_TIMEOUTS)) { time_t sec = 0, usec = 0; @@ -1168,12 +1187,12 @@ asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) ret = 0; } - set_fd_set32(nn, inp, fds->res_in); - set_fd_set32(nn, outp, fds->res_out); - set_fd_set32(nn, exp, fds->res_ex); + set_fd_set32(nn, inp, fds.res_in); + set_fd_set32(nn, outp, fds.res_out); + set_fd_set32(nn, exp, fds.res_ex); out: - free_page ((unsigned long)fds); + kfree(bits); out_nofds: return ret; } @@ -1213,7 +1232,7 @@ asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf) set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat (statbuf, &s)) return -EFAULT; } @@ -1235,7 +1254,7 @@ asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf) set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat (statbuf, &s)) return -EFAULT; } @@ -2010,74 +2029,6 @@ asmlinkage int sys32_getrusage(int who, struct rusage32 *ru) return ret; } -struct timex32 { - unsigned int modes; - s32 offset; - s32 freq; - s32 maxerror; - s32 esterror; - int status; - s32 constant; - s32 precision; - s32 tolerance; - struct timeval32 time; - s32 tick; - s32 ppsfreq; - s32 jitter; - int shift; - s32 stabil; - s32 jitcnt; - s32 calcnt; - s32 errcnt; - s32 stbcnt; - int :32; int :32; int :32; int :32; - int :32; int :32; int :32; int :32; - int :32; int :32; int :32; int :32; -}; - -extern int do_adjtimex(struct timex *); - -asmlinkage int sys32_adjtimex(struct timex32 *txc_p) -{ - struct timex t; - int ret; - - ret = get_user (t.modes, &txc_p->modes); - ret |= __get_user (t.offset, &txc_p->offset); - ret |= __get_user (t.freq, &txc_p->freq); - ret |= __get_user (t.maxerror, &txc_p->maxerror); - ret |= __get_user (t.esterror, &txc_p->esterror); - ret |= __get_user (t.status, &txc_p->status); - ret |= __get_user (t.constant, &txc_p->constant); - ret |= __get_user (t.tick, &txc_p->tick); - ret |= __get_user (t.shift, &txc_p->shift); - if (ret || (ret = do_adjtimex(&t))) - return ret; - ret = __put_user (t.modes, &txc_p->modes); - ret |= __put_user (t.offset, &txc_p->offset); - ret |= __put_user (t.freq, &txc_p->freq); - ret |= __put_user (t.maxerror, &txc_p->maxerror); - ret |= __put_user (t.esterror, &txc_p->esterror); - ret |= __put_user (t.status, &txc_p->status); - ret |= __put_user (t.constant, &txc_p->constant); - ret |= __put_user (t.precision, &txc_p->precision); - ret |= __put_user (t.tolerance, &txc_p->tolerance); - ret |= __put_user (t.time.tv_sec, &txc_p->time.tv_sec); - ret |= __put_user (t.time.tv_usec, &txc_p->time.tv_usec); - ret |= __put_user (t.tick, &txc_p->tick); - ret |= __put_user (t.ppsfreq, &txc_p->ppsfreq); - ret |= __put_user (t.jitter, &txc_p->jitter); - ret |= __put_user (t.shift, &txc_p->shift); - ret |= __put_user (t.stabil, &txc_p->stabil); - ret |= __put_user (t.jitcnt, &txc_p->jitcnt); - ret |= __put_user (t.calcnt, &txc_p->calcnt); - ret |= __put_user (t.errcnt, &txc_p->errcnt); - ret |= __put_user (t.stbcnt, &txc_p->stbcnt); - if (!ret) - ret = time_state; - return ret; -} - /* XXX This really belongs in some header file... -DaveM */ #define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, @@ -2131,9 +2082,44 @@ struct cmsghdr32 { __kernel_size_t32 cmsg_len; int cmsg_level; int cmsg_type; - unsigned char cmsg_data[0]; }; +/* Bleech... */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) + +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + static inline int iov_from_user32_to_kern(struct iovec *kiov, struct iovec32 *uiov32, int niov) @@ -2175,6 +2161,7 @@ static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, kmsg->msg_control = (void *)A(tmp3); err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); err |= get_user(kmsg->msg_flags, &umsg->msg_flags); @@ -2217,6 +2204,165 @@ static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, return tot_len; } +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + fp[i]->f_count++; + current->files->fd[new_fd] = fp[i]; + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) { struct socket *sock; @@ -2237,25 +2383,10 @@ asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_fl total_len = err; if(kern_msg.msg_controllen) { - struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control; - unsigned long *kcmsg; - __kernel_size_t32 cmlen; - - 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) - 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))) - goto out_freectl; - kern_msg.msg_control = ctl_buf; + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; } kern_msg.msg_flags = user_flags; @@ -2269,7 +2400,6 @@ asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_fl } unlock_kernel(); -out_freectl: /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ if(ctl_buf != ctl) kfree(ctl_buf); @@ -2310,26 +2440,43 @@ asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int use lock_kernel(); sock = sockfd_lookup(fd, &err); if (sock != NULL) { + struct scm_cookie scm; + if (sock->file->f_flags & O_NONBLOCK) user_flags |= MSG_DONTWAIT; - err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); - if(err >= 0) + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } sockfd_put(sock); } unlock_kernel(); if(uaddr != NULL && err >= 0) err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); - if(err >= 0) { - err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); - if(!err) { - /* XXX Convert cmsg back into userspace 32-bit format... */ - err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr, - &user_msg->msg_controllen); - } + if(cmsg_ptr != 0 && err >= 0) { + u32 ucmsg_ptr = ((u32)(unsigned long)kern_msg.msg_control); + err = __put_user(ucmsg_ptr, &user_msg->msg_control); + err |= __put_user(kern_msg.msg_controllen, &user_msg->msg_controllen); } - + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); if(kern_msg.msg_iov != iov) kfree(kern_msg.msg_iov); out: @@ -2653,7 +2800,7 @@ asmlinkage int sparc32_execve(struct pt_regs *regs) error = do_execve32(filename, (u32 *)AA((u32)regs->u_regs[base + UREG_I1]), (u32 *)AA((u32)regs->u_regs[base + UREG_I2]), regs); - putname32(filename); + putname(filename); if(!error) { fprs_write(0); @@ -2943,8 +3090,10 @@ qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) info.addr = (unsigned long)mod; info.size = mod->size; info.flags = mod->flags; - info.usecount = (mod_member_present(mod, can_unload) - && mod->can_unload ? -1 : mod->usecount); + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); if (copy_to_user(buf, &info, sizeof(struct module_info32))) return -EFAULT; @@ -3452,7 +3601,7 @@ asmlinkage int sys32_utimes(char *filename, struct timeval32 *tvs) ret = sys_utimes(kfilename, &ktvs[0]); set_fs(old_fs); - putname32(kfilename); + putname(kfilename); } return ret; } @@ -3577,3 +3726,76 @@ asmlinkage int sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s return ret; } + +/* Handle adjtimex compatability. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage int sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 11c86ef5f..d2a75033a 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.50 1998/10/07 01:27:27 davem Exp $ +/* $Id: systbls.S,v 1.53 1999/04/07 17:14:11 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -26,7 +26,7 @@ sys_call_table32: /*30*/ .word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice .word sys_nis_syscall, sys_sync, sys_kill, sys32_newstat, sys32_sendfile /*40*/ .word sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_nis_syscall - .word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid + .word sys_umount, sys_setgid, sys_getgid, sys_signal, sys_geteuid /*50*/ .word sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys32_execve /*60*/ .word sys_umask, sys_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize @@ -48,7 +48,7 @@ sys_call_table32: /*140*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit .word sys32_setrlimit, sys_nis_syscall, sys32_prctl, sys32_pciconfig_read, sys32_pciconfig_write /*150*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall - .word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys_umount + .word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys_oldumount /*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_nis_syscall .word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_nis_syscall /*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents @@ -85,7 +85,7 @@ sys_call_table: /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice .word sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_sendfile /*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall - .word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid + .word sys_umount, sys_setgid, sys_getgid, sys_signal, sys_geteuid /*50*/ .word sys_getegid, sys_acct, sys_memory_ordering, sys_nis_syscall, sys_ioctl .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve /*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize @@ -107,7 +107,7 @@ sys_call_table: /*140*/ .word sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit .word sys_setrlimit, sys_nis_syscall, sys_prctl, sys_pciconfig_read, sys_pciconfig_write /*150*/ .word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall - .word sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount + .word sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_oldumount /*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_utrap_install .word sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall /*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents @@ -188,7 +188,7 @@ sunos_sys_table: /*150*/ .word sys_getsockname, sunos_nosys, sunos_nosys .word sys_poll, sunos_nosys, sunos_nosys .word sunos_getdirentries, sys32_statfs, sys32_fstatfs - .word sys_umount, sunos_nosys, sunos_nosys + .word sys_oldumount, sunos_nosys, sunos_nosys .word sys_getdomainname, sys_setdomainname .word sunos_nosys, sys32_quotactl, sunos_nosys .word sunos_mount, sys_ustat, sunos_semsys diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index c826ce56d..0b72e6e0b 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.16 1998/09/05 17:25:28 jj Exp $ +/* $Id: time.c,v 1.20 1999/03/15 22:13:40 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -31,6 +31,8 @@ #include <asm/pbm.h> #include <asm/ebus.h> +extern rwlock_t xtime_lock; + struct mostek48t02 *mstk48t02_regs = 0; static struct mostek48t08 *mstk48t08_regs = 0; static struct mostek48t59 *mstk48t59_regs = 0; @@ -69,6 +71,8 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) { unsigned long ticks; + write_lock(&xtime_lock); + do { do_timer(regs); @@ -82,11 +86,15 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) } while (ticks >= timer_tick_compare); timer_check_rtc(); + + write_unlock(&xtime_lock); } #ifdef __SMP__ void timer_tick_interrupt(struct pt_regs *regs) { + write_lock(&xtime_lock); + do_timer(regs); /* @@ -99,6 +107,8 @@ void timer_tick_interrupt(struct pt_regs *regs) : "r" (timer_tick_offset)); timer_check_rtc(); + + write_unlock(&xtime_lock); } #endif @@ -256,13 +266,17 @@ void __init clock_probe(void) node = prom_getchild(busnd); while(1) { - prom_getstring(node, "model", model, sizeof(model)); + if (!node) + model[0] = 0; + else + prom_getstring(node, "model", model, sizeof(model)); if(strcmp(model, "mk48t02") && strcmp(model, "mk48t08") && strcmp(model, "mk48t59")) { - node = prom_getsibling(node); + if (node) + node = prom_getsibling(node); #ifdef CONFIG_PCI - if ((node == 0) && ebus) { + while ((node == 0) && ebus) { ebus = ebus->next; if (ebus) { busnd = ebus->prom_node; @@ -397,6 +411,9 @@ static __inline__ unsigned long do_gettimeoffset(void) return ticks / timer_ticks_per_usec; } +/* This need not obtain the xtime_lock as it is coded in + * an implicitly SMP safe way already. + */ void do_gettimeofday(struct timeval *tv) { /* Load doubles must be used on xtime so that what we get @@ -450,7 +467,7 @@ void do_gettimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv) { - cli(); + write_lock_irq(&xtime_lock); tv->tv_usec -= do_gettimeoffset(); if(tv->tv_usec < 0) { @@ -461,10 +478,10 @@ void do_settimeofday(struct timeval *tv) xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + + write_unlock_irq(&xtime_lock); } static int set_rtc_mmss(unsigned long nowtime) diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S index 9c0498348..2c19cc39d 100644 --- a/arch/sparc64/kernel/trampoline.S +++ b/arch/sparc64/kernel/trampoline.S @@ -1,4 +1,4 @@ -/* $Id: trampoline.S,v 1.6 1998/10/11 06:58:23 davem Exp $ +/* $Id: trampoline.S,v 1.8 1998/12/09 21:01:15 davem Exp $ * trampoline.S: Jump start slave processors on sparc64. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -34,8 +34,8 @@ sparc64_cpu_startup: sllx %g4, 32, %g4 /* XXX Buggy PROM... */ - srl %o0, 0, %g6 - add %g6, %g4, %g6 + srl %o0, 0, %o0 + ldx [%o0], %g6 sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 sllx %g5, 32, %g5 @@ -197,10 +197,18 @@ bounce: #undef KERN_LOWBITS #undef VPTE_BASE + /* Setup interrupt globals, we are always SMP. */ wrpr %o1, (PSTATE_IG | PSTATE_IE), %pstate - sethi %hi(ivector_to_mask), %g5 - or %g5, %lo(ivector_to_mask), %g1 - mov 0x40, %g2 + + /* Get our UPA MID. */ + lduw [%o2 + AOFF_task_processor], %g1 + sethi %hi(cpu_data), %g5 + or %g5, %lo(cpu_data), %g5 + + /* In theory this is: &(cpu_data[this_upamid].irq_worklists[0]) */ + sllx %g1, 7, %g1 + add %g5, %g1, %g1 + add %g1, 64, %g1 wrpr %g0, 0, %wstate or %o1, PSTATE_IE, %o1 diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index cb5180fff..5f7049822 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,8 +1,8 @@ -/* $Id: traps.c,v 1.55 1998/10/11 06:58:22 davem Exp $ +/* $Id: traps.c,v 1.58 1999/03/29 12:38:10 jj Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* @@ -406,8 +406,6 @@ void do_fpe_common(struct pt_regs *regs) void do_fpieee(struct pt_regs *regs) { #ifdef DEBUG_FPU - save_and_clear_fpu(); - printk("fpieee %016lx\n", current->tss.xfsr[0]); #endif do_fpe_common(regs); @@ -420,7 +418,6 @@ void do_fpother(struct pt_regs *regs) struct fpustate *f = FPUSTATE; int ret = 0; - save_and_clear_fpu(); switch ((current->tss.xfsr[0] & 0x1c000)) { case (2 << 14): /* unfinished_FPop */ case (3 << 14): /* unimplemented_FPop */ @@ -428,7 +425,7 @@ void do_fpother(struct pt_regs *regs) break; } if (ret) return; -#ifdef DEBUG_FPU +#ifdef DEBUG_FPU printk("fpother %016lx\n", current->tss.xfsr[0]); #endif do_fpe_common(regs); @@ -462,6 +459,9 @@ void instruction_dump (unsigned int *pc) void die_if_kernel(char *str, struct pt_regs *regs) { + extern void __show_regs(struct pt_regs * regs); + extern void smp_report_regs(void); + /* Amuse the user. */ printk( " \\|/ ____ \\|/\n" @@ -471,7 +471,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("%s(%d): %s\n", current->comm, current->pid, str); __asm__ __volatile__("flushw"); - show_regs(regs); + __show_regs(regs); { struct reg_window *rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); @@ -491,6 +491,10 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("Instruction DUMP:"); instruction_dump ((unsigned int *) regs->tpc); } +#ifdef __SMP__ + smp_report_regs(); +#endif + lock_kernel(); /* Or else! */ if(regs->tstate & TSTATE_PRIV) do_exit(SIGKILL); @@ -498,7 +502,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) } extern int handle_popc(u32 insn, struct pt_regs *regs); -extern int handle_ldq_stq(u32 insn, struct pt_regs *regs); +extern int handle_ldf_stq(u32 insn, struct pt_regs *regs); void do_illegal_instruction(struct pt_regs *regs) { @@ -515,7 +519,7 @@ void do_illegal_instruction(struct pt_regs *regs) if (handle_popc(insn, regs)) return; } else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ { - if (handle_ldq_stq(insn, regs)) + if (handle_ldf_stq(insn, regs)) return; } } diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 656d29454..3a9fdf4d2 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.27 1998/09/25 01:09:10 davem Exp $ +/* $Id: ttable.S,v 1.28 1999/03/29 12:38:10 jj Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -21,8 +21,8 @@ tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0 tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d) tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f) tl0_fpdis: TRAP_NOSAVE(do_fpdis) -tl0_fpieee: TRAP(do_fpieee) -tl0_fpother: TRAP(do_fpother) +tl0_fpieee: TRAP_SAVEFPU(do_fpieee) +tl0_fpother: TRAP_SAVEFPU(do_fpother) tl0_tof: TRAP(do_tof) tl0_cwin: CLEAN_WINDOW tl0_div0: TRAP(do_div0) diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index d32309b9d..f4599bbdb 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.13 1998/10/07 22:43:13 davem Exp $ +/* $Id: unaligned.c,v 1.15 1999/04/03 11:36:21 anton Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -374,7 +374,6 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u enum direction dir = decode_direction(insn); int size = decode_access_size(insn); - lock_kernel(); if(!ok_for_kernel(insn) || dir == both) { printk("Unsupported unaligned load/store trap for kernel at <%016lx>.\n", regs->tpc); @@ -423,7 +422,6 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u } advance(regs); } - unlock_kernel(); } static char popc_helper[] = { @@ -470,7 +468,7 @@ extern void do_fpother(struct pt_regs *regs); extern void do_privact(struct pt_regs *regs); extern void data_access_exception(struct pt_regs *regs); -int handle_ldq_stq(u32 insn, struct pt_regs *regs) +int handle_ldf_stq(u32 insn, struct pt_regs *regs) { unsigned long addr = compute_effective_address(regs, insn, 0); int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20); @@ -522,8 +520,10 @@ int handle_ldq_stq(u32 insn, struct pt_regs *regs) return 1; } } else { - /* LDQ */ - u32 first, second, third, fourth; + /* LDF, LDDF, LDQF */ + u32 data[4] __attribute__ ((aligned(8))); + int size, i; + int err; if (asi < 0x80) { do_privact(regs); @@ -532,25 +532,35 @@ int handle_ldq_stq(u32 insn, struct pt_regs *regs) data_access_exception(regs); return 1; } - if (get_user (first, (u32 *)addr) || - __get_user (second, (u32 *)(addr + 4)) || - __get_user (third, (u32 *)(addr + 8)) || - __get_user (fourth, (u32 *)(addr + 12))) { - if (asi & 0x2) /* NF */ { - first = 0; second = 0; third = 0; fourth = 0; - } else { - data_access_exception(regs); - return 1; - } + switch (insn & 0x180000) { + case 0x000000: size = 1; break; + case 0x100000: size = 4; break; + default: size = 2; break; + } + for (i = 0; i < size; i++) + data[i] = 0; + + err = get_user (data[0], (u32 *)addr); + if (!err) { + for (i = 1; i < size; i++) + err |= __get_user (data[i], (u32 *)(addr + 4*i)); + } + if (err && !(asi & 0x2 /* NF */)) { + data_access_exception(regs); + return 1; } if (asi & 0x8) /* Little */ { - u32 tmp = le32_to_cpup(&first); - - first = le32_to_cpup(&fourth); - fourth = tmp; - tmp = le32_to_cpup(&second); - second = le32_to_cpup(&third); - third = tmp; + u64 tmp; + + switch (size) { + case 1: data[0] = le32_to_cpup(data + 0); break; + default:*(u64 *)(data + 0) = le64_to_cpup((u64 *)(data + 0)); + break; + case 4: tmp = le64_to_cpup((u64 *)(data + 0)); + *(u64 *)(data + 0) = le64_to_cpup((u64 *)(data + 2)); + *(u64 *)(data + 2) = tmp; + break; + } } if (!(current->tss.fpsaved[0] & FPRS_FEF)) { current->tss.fpsaved[0] = FPRS_FEF; @@ -562,16 +572,27 @@ int handle_ldq_stq(u32 insn, struct pt_regs *regs) else memset(f->regs+32, 0, 32*sizeof(u32)); } - f->regs[freg] = first; - f->regs[freg+1] = second; - f->regs[freg+2] = third; - f->regs[freg+3] = fourth; + memcpy(f->regs + freg, data, size * 4); current->tss.fpsaved[0] |= flag; } advance(regs); return 1; } +void handle_ld_nf(u32 insn, struct pt_regs *regs) +{ + int rd = ((insn >> 25) & 0x1f); + int from_kernel = (regs->tstate & TSTATE_PRIV) != 0; + unsigned long *reg; + + maybe_flush_windows(0, 0, rd, from_kernel); + reg = fetch_reg_addr(rd, regs); + if ((insn & 0x780000) == 0x180000) + reg[1] = 0; + reg[0] = 0; + advance(regs); +} + void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr) { unsigned long pc = regs->tpc; diff --git a/arch/sparc64/math-emu/fabsq.c b/arch/sparc64/math-emu/fabsq.c index e01b02046..62a7e1839 100644 --- a/arch/sparc64/math-emu/fabsq.c +++ b/arch/sparc64/math-emu/fabsq.c @@ -2,5 +2,5 @@ int FABSQ(unsigned long *rd, unsigned long *rs2) { rd[0] = rs2[0] & 0x7fffffffffffffffUL; rd[1] = rs2[1]; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/faddd.c b/arch/sparc64/math-emu/faddd.c index 69b9f7c24..7c755b872 100644 --- a/arch/sparc64/math-emu/faddd.c +++ b/arch/sparc64/math-emu/faddd.c @@ -8,6 +8,5 @@ int FADDD(void *rd, void *rs2, void *rs1) __FP_UNPACK_D(A, rs1); __FP_UNPACK_D(B, rs2); FP_ADD_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/faddq.c b/arch/sparc64/math-emu/faddq.c index 07245eb95..052c6c9cd 100644 --- a/arch/sparc64/math-emu/faddq.c +++ b/arch/sparc64/math-emu/faddq.c @@ -8,6 +8,5 @@ int FADDQ(void *rd, void *rs2, void *rs1) __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); FP_ADD_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fadds.c b/arch/sparc64/math-emu/fadds.c index 71295ae47..35bb2030e 100644 --- a/arch/sparc64/math-emu/fadds.c +++ b/arch/sparc64/math-emu/fadds.c @@ -8,6 +8,5 @@ int FADDS(void *rd, void *rs2, void *rs1) __FP_UNPACK_S(A, rs1); __FP_UNPACK_S(B, rs2); FP_ADD_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc64/math-emu/fcmpeq.c b/arch/sparc64/math-emu/fcmpeq.c index e74b1b06b..e99864f55 100644 --- a/arch/sparc64/math-emu/fcmpeq.c +++ b/arch/sparc64/math-emu/fcmpeq.c @@ -21,5 +21,5 @@ int FCMPEQ(void *rd, void *rs2, void *rs1) case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; } *(unsigned long *)rd = fsr; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fcmpq.c b/arch/sparc64/math-emu/fcmpq.c index 9effefb1f..54ec4492f 100644 --- a/arch/sparc64/math-emu/fcmpq.c +++ b/arch/sparc64/math-emu/fcmpq.c @@ -21,5 +21,5 @@ int FCMPQ(void *rd, void *rs2, void *rs1) case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; } *(unsigned long *)rd = fsr; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fdivd.c b/arch/sparc64/math-emu/fdivd.c index 2984290fc..a19dbdd78 100644 --- a/arch/sparc64/math-emu/fdivd.c +++ b/arch/sparc64/math-emu/fdivd.c @@ -4,10 +4,16 @@ int FDIVD(void *rd, void *rs2, void *rs1) { FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + int ret = 0; __FP_UNPACK_D(A, rs1); __FP_UNPACK_D(B, rs2); + if(B_c == FP_CLS_ZERO && + A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if(__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } FP_DIV_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return (ret | __FP_PACK_D(rd, R)); } diff --git a/arch/sparc64/math-emu/fdivq.c b/arch/sparc64/math-emu/fdivq.c index e5858c0af..9bc0987f5 100644 --- a/arch/sparc64/math-emu/fdivq.c +++ b/arch/sparc64/math-emu/fdivq.c @@ -4,10 +4,16 @@ int FDIVQ(void *rd, void *rs2, void *rs1) { FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + int ret; __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); + if(B_c == FP_CLS_ZERO && + A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if(__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } FP_DIV_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return (ret | __FP_PACK_Q(rd, R)); } diff --git a/arch/sparc64/math-emu/fdivs.c b/arch/sparc64/math-emu/fdivs.c index 704f218c9..41095dc4c 100644 --- a/arch/sparc64/math-emu/fdivs.c +++ b/arch/sparc64/math-emu/fdivs.c @@ -4,10 +4,17 @@ int FDIVS(void *rd, void *rs2, void *rs1) { FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); + int ret = 0; __FP_UNPACK_S(A, rs1); __FP_UNPACK_S(B, rs2); + if(B_c == FP_CLS_ZERO && + A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if(__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } FP_DIV_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return (ret | __FP_PACK_S(rd, R)); } + diff --git a/arch/sparc64/math-emu/fdmulq.c b/arch/sparc64/math-emu/fdmulq.c index 7862a0039..4d4b5916a 100644 --- a/arch/sparc64/math-emu/fdmulq.c +++ b/arch/sparc64/math-emu/fdmulq.c @@ -11,6 +11,5 @@ int FDMULQ(void *rd, void *rs2, void *rs1) __FP_UNPACK_D(IN, rs2); FP_CONV(Q,D,2,1,B,IN); FP_MUL_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fdtoi.c b/arch/sparc64/math-emu/fdtoi.c index f7ba0f1f2..1b12a6395 100644 --- a/arch/sparc64/math-emu/fdtoi.c +++ b/arch/sparc64/math-emu/fdtoi.c @@ -9,5 +9,5 @@ int FDTOI(unsigned *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_TO_INT_D(r, A, 32, 1); *rd = r; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fdtoq.c b/arch/sparc64/math-emu/fdtoq.c index 42e4009c6..b37b31198 100644 --- a/arch/sparc64/math-emu/fdtoq.c +++ b/arch/sparc64/math-emu/fdtoq.c @@ -8,6 +8,5 @@ int FDTOQ(void *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_CONV(Q,D,2,1,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fdtos.c b/arch/sparc64/math-emu/fdtos.c index fb7fede54..ae9f382ed 100644 --- a/arch/sparc64/math-emu/fdtos.c +++ b/arch/sparc64/math-emu/fdtos.c @@ -8,6 +8,5 @@ int FDTOS(void *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_CONV(S,D,1,1,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc64/math-emu/fdtox.c b/arch/sparc64/math-emu/fdtox.c index 1a93b585c..062651a47 100644 --- a/arch/sparc64/math-emu/fdtox.c +++ b/arch/sparc64/math-emu/fdtox.c @@ -9,5 +9,5 @@ int FDTOX(unsigned long *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_TO_INT_D(r, A, 64, 1); *rd = r; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fitoq.c b/arch/sparc64/math-emu/fitoq.c index 5d08bbe18..68ba2839b 100644 --- a/arch/sparc64/math-emu/fitoq.c +++ b/arch/sparc64/math-emu/fitoq.c @@ -7,6 +7,5 @@ int FITOQ(void *rd, void *rs2) int a = *(int *)rs2; FP_FROM_INT_Q(R, a, 32, int); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fmuld.c b/arch/sparc64/math-emu/fmuld.c index 04d1f38df..707bb45c1 100644 --- a/arch/sparc64/math-emu/fmuld.c +++ b/arch/sparc64/math-emu/fmuld.c @@ -8,6 +8,5 @@ int FMULD(void *rd, void *rs2, void *rs1) __FP_UNPACK_D(A, rs1); __FP_UNPACK_D(B, rs2); FP_MUL_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/fmulq.c b/arch/sparc64/math-emu/fmulq.c index 4d099be6f..94400d023 100644 --- a/arch/sparc64/math-emu/fmulq.c +++ b/arch/sparc64/math-emu/fmulq.c @@ -8,6 +8,5 @@ int FMULQ(void *rd, void *rs2, void *rs1) __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); FP_MUL_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fmuls.c b/arch/sparc64/math-emu/fmuls.c index 8a358d030..b16509964 100644 --- a/arch/sparc64/math-emu/fmuls.c +++ b/arch/sparc64/math-emu/fmuls.c @@ -8,6 +8,5 @@ int FMULS(void *rd, void *rs2, void *rs1) __FP_UNPACK_S(A, rs1); __FP_UNPACK_S(B, rs2); FP_MUL_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc64/math-emu/fnegq.c b/arch/sparc64/math-emu/fnegq.c index 2251e3308..745020ee3 100644 --- a/arch/sparc64/math-emu/fnegq.c +++ b/arch/sparc64/math-emu/fnegq.c @@ -2,6 +2,6 @@ int FNEGQ(unsigned long *rd, unsigned long *rs2) { rd[0] = rs2[0] ^ 0x8000000000000000UL; rd[1] = rs2[1]; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fqtod.c b/arch/sparc64/math-emu/fqtod.c index 1f9161999..29e41cf6a 100644 --- a/arch/sparc64/math-emu/fqtod.c +++ b/arch/sparc64/math-emu/fqtod.c @@ -8,6 +8,5 @@ int FQTOD(void *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_CONV(D,Q,1,2,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/fqtoi.c b/arch/sparc64/math-emu/fqtoi.c index 06d67ff81..1bb213f19 100644 --- a/arch/sparc64/math-emu/fqtoi.c +++ b/arch/sparc64/math-emu/fqtoi.c @@ -9,5 +9,5 @@ int FQTOI(unsigned *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_TO_INT_Q(r, A, 32, 1); *rd = r; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fqtos.c b/arch/sparc64/math-emu/fqtos.c index 833f10618..382912d11 100644 --- a/arch/sparc64/math-emu/fqtos.c +++ b/arch/sparc64/math-emu/fqtos.c @@ -8,6 +8,5 @@ int FQTOS(void *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_CONV(S,Q,1,2,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc64/math-emu/fqtox.c b/arch/sparc64/math-emu/fqtox.c index 6cdabc8cd..484ca6900 100644 --- a/arch/sparc64/math-emu/fqtox.c +++ b/arch/sparc64/math-emu/fqtox.c @@ -9,5 +9,5 @@ int FQTOX(unsigned long *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_TO_INT_Q(r, A, 64, 1); *rd = r; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fsmuld.c b/arch/sparc64/math-emu/fsmuld.c index 1a0eefd06..b6bf61b0f 100644 --- a/arch/sparc64/math-emu/fsmuld.c +++ b/arch/sparc64/math-emu/fsmuld.c @@ -11,6 +11,5 @@ int FSMULD(void *rd, void *rs2, void *rs1) __FP_UNPACK_S(IN, rs2); FP_CONV(D,S,1,1,B,IN); FP_MUL_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/fsqrtd.c b/arch/sparc64/math-emu/fsqrtd.c index 59f5e3bf3..b022f7d37 100644 --- a/arch/sparc64/math-emu/fsqrtd.c +++ b/arch/sparc64/math-emu/fsqrtd.c @@ -7,6 +7,5 @@ int FSQRTD(void *rd, void *rs2) __FP_UNPACK_D(A, rs2); FP_SQRT_D(R, A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/fsqrtq.c b/arch/sparc64/math-emu/fsqrtq.c index 8f84aa850..7e620079f 100644 --- a/arch/sparc64/math-emu/fsqrtq.c +++ b/arch/sparc64/math-emu/fsqrtq.c @@ -7,6 +7,5 @@ int FSQRTQ(void *rd, void *rs2) __FP_UNPACK_Q(A, rs2); FP_SQRT_Q(R, A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fsqrts.c b/arch/sparc64/math-emu/fsqrts.c index d57cdc98e..62ff0b782 100644 --- a/arch/sparc64/math-emu/fsqrts.c +++ b/arch/sparc64/math-emu/fsqrts.c @@ -7,6 +7,5 @@ int FSQRTS(void *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_SQRT_S(R, A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc64/math-emu/fstod.c b/arch/sparc64/math-emu/fstod.c index 60f1bc8a4..91935f78e 100644 --- a/arch/sparc64/math-emu/fstod.c +++ b/arch/sparc64/math-emu/fstod.c @@ -8,6 +8,5 @@ int FSTOD(void *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_CONV(D,S,1,1,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/fstoi.c b/arch/sparc64/math-emu/fstoi.c index 4adc7d170..5ff3854cc 100644 --- a/arch/sparc64/math-emu/fstoi.c +++ b/arch/sparc64/math-emu/fstoi.c @@ -9,5 +9,5 @@ int FSTOI(unsigned *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_TO_INT_S(r, A, 32, 1); *rd = r; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fstoq.c b/arch/sparc64/math-emu/fstoq.c index 06313a77f..1a56e0055 100644 --- a/arch/sparc64/math-emu/fstoq.c +++ b/arch/sparc64/math-emu/fstoq.c @@ -8,6 +8,5 @@ int FSTOQ(void *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_CONV(Q,S,2,1,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fstox.c b/arch/sparc64/math-emu/fstox.c index 555fba2ee..fa2135f8b 100644 --- a/arch/sparc64/math-emu/fstox.c +++ b/arch/sparc64/math-emu/fstox.c @@ -9,5 +9,5 @@ int FSTOX(unsigned long *rd, void *rs2) __FP_UNPACK_S(A, rs2); FP_TO_INT_S(r, A, 64, 1); *rd = r; - return 1; + return 0; } diff --git a/arch/sparc64/math-emu/fsubd.c b/arch/sparc64/math-emu/fsubd.c index b0f451b32..bbd11c611 100644 --- a/arch/sparc64/math-emu/fsubd.c +++ b/arch/sparc64/math-emu/fsubd.c @@ -10,6 +10,5 @@ int FSUBD(void *rd, void *rs2, void *rs1) if (B_c != FP_CLS_NAN) B_s ^= 1; FP_ADD_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff --git a/arch/sparc64/math-emu/fsubq.c b/arch/sparc64/math-emu/fsubq.c index ef006a540..d5cc260cf 100644 --- a/arch/sparc64/math-emu/fsubq.c +++ b/arch/sparc64/math-emu/fsubq.c @@ -10,6 +10,5 @@ int FSUBQ(void *rd, void *rs2, void *rs1) if (B_c != FP_CLS_NAN) B_s ^= 1; FP_ADD_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/fsubs.c b/arch/sparc64/math-emu/fsubs.c index d060f095a..ff8fa12b1 100644 --- a/arch/sparc64/math-emu/fsubs.c +++ b/arch/sparc64/math-emu/fsubs.c @@ -10,6 +10,5 @@ int FSUBS(void *rd, void *rs2, void *rs1) if (B_c != FP_CLS_NAN) B_s ^= 1; FP_ADD_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff --git a/arch/sparc64/math-emu/fxtoq.c b/arch/sparc64/math-emu/fxtoq.c index 7c2f7df48..411e51571 100644 --- a/arch/sparc64/math-emu/fxtoq.c +++ b/arch/sparc64/math-emu/fxtoq.c @@ -7,6 +7,5 @@ int FXTOQ(void *rd, void *rs2) long a = *(long *)rs2; FP_FROM_INT_Q(R, a, 64, long); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff --git a/arch/sparc64/math-emu/math.c b/arch/sparc64/math-emu/math.c index 5a71804b9..f4493e224 100644 --- a/arch/sparc64/math-emu/math.c +++ b/arch/sparc64/math-emu/math.c @@ -1,7 +1,8 @@ -/* $Id: math.c,v 1.5 1998/06/12 14:54:27 jj Exp $ +/* $Id: math.c,v 1.7 1999/02/10 14:16:26 davem Exp $ * arch/sparc64/math-emu/math.c * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1999 David S. Miller (davem@redhat.com) * * Emulation routines originate from soft-fp package, which is part * of glibc and has appropriate copyrights in it. @@ -14,6 +15,8 @@ #include <asm/ptrace.h> #include <asm/uaccess.h> +#include "soft-fp.h" + #define FLOATFUNC(x) extern int x(void *,void *,void *); FLOATFUNC(FMOVQ) @@ -54,6 +57,91 @@ FLOATFUNC(FSTOD) FLOATFUNC(FSTOI) FLOATFUNC(FDTOI) +#define FSR_TEM_SHIFT 23UL +#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) +#define FSR_AEXC_SHIFT 5UL +#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT) +#define FSR_CEXC_SHIFT 0UL +#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT) + +/* All routines returning an exception to raise should detect + * such exceptions _before_ rounding to be consistant with + * the behavior of the hardware in the implemented cases + * (and thus with the recommendations in the V9 architecture + * manual). + * + * We return 0 if a SIGFPE should be sent, 1 otherwise. + */ +static int record_exception(struct pt_regs *regs, int eflag) +{ + u64 fsr = current->tss.xfsr[0]; + int would_trap; + + /* Determine if this exception would have generated a trap. */ + would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL; + + /* If trapping, we only want to signal one bit. */ + if(would_trap != 0) { + eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); + if((eflag & (eflag - 1)) != 0) { + if(eflag & EFLAG_INVALID) + eflag = EFLAG_INVALID; + else if(eflag & EFLAG_DIVZERO) + eflag = EFLAG_DIVZERO; + else if(eflag & EFLAG_INEXACT) + eflag = EFLAG_INEXACT; + } + } + + /* Set CEXC, here are the rules: + * + * 1) In general all FPU ops will set one and only one + * bit in the CEXC field, this is always the case + * when the IEEE exception trap is enabled in TEM. + * + * 2) As a special case, if an overflow or underflow + * is being signalled, AND the trap is not enabled + * in TEM, then the inexact field shall also be set. + */ + fsr &= ~(FSR_CEXC_MASK); + if(would_trap || + (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) { + fsr |= ((long)eflag << FSR_CEXC_SHIFT); + } else { + fsr |= (((long)eflag << FSR_CEXC_SHIFT) | + (EFLAG_INEXACT << FSR_CEXC_SHIFT)); + } + + /* Set the AEXC field, rules are: + * + * 1) If a trap would not be generated, the + * CEXC just generated is OR'd into the + * existing value of AEXC. + * + * 2) When a trap is generated, AEXC is cleared. + */ + if(would_trap == 0) + fsr |= ((long)eflag << FSR_AEXC_SHIFT); + else + fsr &= ~(FSR_AEXC_MASK); + + /* If trapping, indicate fault trap type IEEE. */ + if(would_trap != 0) + fsr |= (1UL << 14); + + current->tss.xfsr[0] = fsr; + + /* If we will not trap, advance the program counter over + * the instruction being handled. + */ + if(would_trap == 0) { + regs->tpc = regs->tnpc; + regs->tnpc += 4; + } + + return (would_trap ? 0 : 1); +} + int do_mathemu(struct pt_regs *regs, struct fpustate *f) { unsigned long pc = regs->tpc; @@ -175,7 +263,12 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f) current->tss.fpsaved[0] |= flags; break; } - func(rd, rs2, rs1); + flags = func(rd, rs2, rs1); + if(flags != 0) + return record_exception(regs, flags); + + /* Success and no exceptions detected. */ + current->tss.xfsr[0] &= ~(FSR_CEXC_MASK); regs->tpc = regs->tnpc; regs->tnpc += 4; return 1; diff --git a/arch/sparc64/math-emu/op-2.h b/arch/sparc64/math-emu/op-2.h index 5999cfc3b..8ac63188c 100644 --- a/arch/sparc64/math-emu/op-2.h +++ b/arch/sparc64/math-emu/op-2.h @@ -190,14 +190,14 @@ \ __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _b_f1, _b_f0, 0, \ _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ - 0, _b_f1, _b_f0, 0); \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _c_f1, _c_f0, 0, \ _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ - 0, _c_f1, _c_f0, 0); \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ \ /* Normalize since we know where the msb of the multiplicands \ were (bit B), we know that the msb of the of the product is \ diff --git a/arch/sparc64/math-emu/op-common.h b/arch/sparc64/math-emu/op-common.h index ac49cd6c2..6090e0213 100644 --- a/arch/sparc64/math-emu/op-common.h +++ b/arch/sparc64/math-emu/op-common.h @@ -53,14 +53,14 @@ do { \ */ #define _FP_PACK_CANONICAL(fs, wc, X) \ -do { \ +({int __ret = 0; \ switch (X##_c) \ { \ case FP_CLS_NORMAL: \ X##_e += _FP_EXPBIAS_##fs; \ if (X##_e > 0) \ { \ - _FP_ROUND(wc, X); \ + __ret |= _FP_ROUND(wc, X); \ if (_FP_FRAC_OVERP_##wc(fs, X)) \ { \ _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1)); \ @@ -73,6 +73,7 @@ do { \ /* overflow to infinity */ \ X##_e = _FP_EXPMAX_##fs; \ _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + __ret |= EFLAG_OVERFLOW; \ } \ } \ else \ @@ -82,7 +83,7 @@ do { \ if (X##_e <= _FP_WFRACBITS_##fs) \ { \ _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ - _FP_ROUND(wc, X); \ + __ret |= _FP_ROUND(wc, X); \ _FP_FRAC_SLL_##wc(X, 1); \ if (_FP_FRAC_OVERP_##wc(fs, X)) \ { \ @@ -93,6 +94,7 @@ do { \ { \ X##_e = 0; \ _FP_FRAC_SRL_##wc(X, _FP_WORKBITS+1); \ + __ret |= EFLAG_UNDERFLOW; \ } \ } \ else \ @@ -100,6 +102,7 @@ do { \ /* underflow to zero */ \ X##_e = 0; \ _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + __ret |= EFLAG_UNDERFLOW; \ } \ } \ break; \ @@ -125,7 +128,8 @@ do { \ _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs; \ break; \ } \ -} while (0) + __ret; \ +}) /* @@ -424,11 +428,19 @@ do { \ } \ else \ { \ - /* Force -0 -> +0 */ \ - if (!X##_e && _FP_FRAC_ZEROP_##wc(X)) X##_s = 0; \ - if (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) X##_s = 0; \ + int __is_zero_x; \ + int __is_zero_y; \ + \ + __is_zero_x = (!X##_e && _FP_FRAC_ZEROP_##wc(X)) ? 1 : 0; \ + __is_zero_y = (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) ? 1 : 0; \ \ - if (X##_s != Y##_s) \ + if (__is_zero_x && __is_zero_y) \ + ret = 0; \ + else if (__is_zero_x) \ + ret = Y##_s ? 1 : -1; \ + else if (__is_zero_y) \ + ret = X##_s ? -1 : 1; \ + else if (X##_s != Y##_s) \ ret = X##_s ? -1 : 1; \ else if (X##_e > Y##_e) \ ret = X##_s ? -1 : 1; \ diff --git a/arch/sparc64/math-emu/sfp-machine.h b/arch/sparc64/math-emu/sfp-machine.h index f15a5ea4a..3846ac4f9 100644 --- a/arch/sparc64/math-emu/sfp-machine.h +++ b/arch/sparc64/math-emu/sfp-machine.h @@ -52,16 +52,6 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_RAW_2(fs, X, val) \ do { \ union _FP_UNION_##fs *_flo = \ @@ -73,53 +63,80 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_S(X,val) \ do { \ __FP_UNPACK_RAW_1(S,X,val); \ _FP_UNPACK_CANONICAL(S,1,X); \ } while (0) -#define __FP_PACK_S(val,X) \ - do { \ - _FP_PACK_CANONICAL(S,1,X); \ - __FP_PACK_RAW_1(S,val,X); \ - } while (0) - #define __FP_UNPACK_D(X,val) \ do { \ __FP_UNPACK_RAW_1(D,X,val); \ _FP_UNPACK_CANONICAL(D,1,X); \ } while (0) -#define __FP_PACK_D(val,X) \ - do { \ - _FP_PACK_CANONICAL(D,1,X); \ - __FP_PACK_RAW_1(D,val,X); \ - } while (0) - #define __FP_UNPACK_Q(X,val) \ do { \ __FP_UNPACK_RAW_2(Q,X,val); \ _FP_UNPACK_CANONICAL(Q,2,X); \ } while (0) -#define __FP_PACK_Q(val,X) \ - do { \ - _FP_PACK_CANONICAL(Q,2,X); \ - __FP_PACK_RAW_2(Q,val,X); \ +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ } while (0) +#include <linux/kernel.h> +#include <linux/sched.h> + +/* We only actually write to the destination register + * if exceptions signalled (if any) will not trap. + */ +#define __FPU_TEM \ + (((current->tss.xfsr[0])>>23)&0x1f) +#define __FPU_TRAP_P(bits) \ + ((__FPU_TEM & (bits)) != 0) + +#define __FP_PACK_S(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(S,val,X); \ + __exc; \ +}) + +#define __FP_PACK_D(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(D,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(D,val,X); \ + __exc; \ +}) + +#define __FP_PACK_Q(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(Q,2,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_2(Q,val,X); \ + __exc; \ +}) + +/* Obtain the current rounding mode. */ +#define FP_ROUNDMODE ((current->tss.xfsr[0] >> 30) & 0x3) + #include <linux/types.h> #include <asm/byteorder.h> @@ -153,28 +170,24 @@ #define umul_ppmm(wh, wl, u, v) \ do { \ - long tmp1 = 0, tmp2 = 0, tmp3 = 0; \ __asm__ ("mulx %2,%3,%1 - srlx %2,32,%4 - srl %3,0,%5 - mulx %4,%5,%6 - srlx %3,32,%4 - srl %2,0,%5 - mulx %4,%5,%5 - srlx %2,32,%4 - add %5,%6,%6 - srlx %3,32,%5 - mulx %4,%5,%4 - srlx %6,32,%5 - add %4,%5,%0" \ + srlx %2,32,%%g1 + srl %3,0,%%g2 + mulx %%g1,%%g2,%%g3 + srlx %3,32,%%g1 + srl %2,0,%%g2 + mulx %%g1,%%g2,%%g2 + srlx %2,32,%%g1 + add %%g2,%%g3,%%g3 + srlx %3,32,%%g2 + mulx %%g1,%%g2,%%g1 + srlx %%g3,32,%%g2 + add %%g1,%%g2,%0" \ : "=r" ((UDItype)(wh)), \ "=&r" ((UDItype)(wl)) \ : "r" ((UDItype)(u)), \ - "r" ((UDItype)(v)), \ - "r" ((UDItype)(tmp1)), \ - "r" ((UDItype)(tmp2)), \ - "r" ((UDItype)(tmp3)) \ - : "cc"); \ + "r" ((UDItype)(v)) \ + : "g1", "g2", "g3", "cc"); \ } while (0) #define udiv_qrnnd(q, r, n1, n0, d) \ @@ -223,3 +236,10 @@ #else #define __BYTE_ORDER __LITTLE_ENDIAN #endif + +/* Exception flags. */ +#define EFLAG_INVALID (1 << 4) +#define EFLAG_OVERFLOW (1 << 3) +#define EFLAG_UNDERFLOW (1 << 2) +#define EFLAG_DIVZERO (1 << 1) +#define EFLAG_INEXACT (1 << 0) diff --git a/arch/sparc64/math-emu/soft-fp.h b/arch/sparc64/math-emu/soft-fp.h index a4f72e78f..b54f86217 100644 --- a/arch/sparc64/math-emu/soft-fp.h +++ b/arch/sparc64/math-emu/soft-fp.h @@ -14,45 +14,56 @@ # define FP_RND_ZERO 1 # define FP_RND_PINF 2 # define FP_RND_MINF 3 +#ifndef FP_ROUNDMODE # define FP_ROUNDMODE FP_RND_NEAREST #endif +#endif #define _FP_ROUND_NEAREST(wc, X) \ - do { \ +({ int __ret = EFLAG_INEXACT; \ if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND) \ _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \ - } while(0) + else __ret = 0; \ + __ret; \ +}) -#define _FP_ROUND_ZERO(wc, X) +#define _FP_ROUND_ZERO(wc, X) 0 /* XXX */ #define _FP_ROUND_PINF(wc, X) \ - do { \ +({ int __ret = EFLAG_INEXACT; \ if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ - } while (0) + else __ret = 0; \ + __ret; \ +}) #define _FP_ROUND_MINF(wc, X) \ - do { \ +({ int __ret = EFLAG_INEXACT; \ if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ - } while (0) + else __ret = 0; \ + __ret; \ +}) #define _FP_ROUND(wc, X) \ +({ int __ret = 0; \ switch (FP_ROUNDMODE) \ { \ case FP_RND_NEAREST: \ - _FP_ROUND_NEAREST(wc,X); \ + __ret |= _FP_ROUND_NEAREST(wc,X); \ break; \ case FP_RND_ZERO: \ - _FP_ROUND_ZERO(wc,X); \ + __ret |= _FP_ROUND_ZERO(wc,X); \ break; \ case FP_RND_PINF: \ - _FP_ROUND_PINF(wc,X); \ + __ret |= _FP_ROUND_PINF(wc,X); \ break; \ case FP_RND_MINF: \ - _FP_ROUND_MINF(wc,X); \ + __ret |= _FP_ROUND_MINF(wc,X); \ break; \ - } + }; \ + __ret; \ +}) #define FP_CLS_NORMAL 0 #define FP_CLS_ZERO 1 diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 737872fb2..88fd55e03 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -1,8 +1,8 @@ -/* $Id: fault.c,v 1.26 1998/11/08 11:14:03 davem Exp $ +/* $Id: fault.c,v 1.34 1999/03/16 12:12:28 jj Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz) */ #include <asm/head.h> @@ -14,6 +14,8 @@ #include <linux/signal.h> #include <linux/mm.h> #include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/interrupt.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -26,7 +28,7 @@ extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; /* Nice, simple, prom library does all the sweating for us. ;) */ -unsigned long prom_probe_memory (void) +unsigned long __init prom_probe_memory (void) { register struct linux_mlist_p1275 *mlist; register unsigned long bytes, base_paddr, tally; @@ -35,7 +37,7 @@ unsigned long prom_probe_memory (void) i = 0; mlist = *prom_meminfo()->p1275_available; bytes = tally = mlist->num_bytes; - base_paddr = (unsigned int) mlist->start_adr; + base_paddr = mlist->start_adr; sp_banks[0].base_addr = base_paddr; sp_banks[0].num_bytes = bytes; @@ -55,12 +57,12 @@ unsigned long prom_probe_memory (void) break; } - sp_banks[i].base_addr = (unsigned long) mlist->start_adr; + sp_banks[i].base_addr = mlist->start_adr; sp_banks[i].num_bytes = mlist->num_bytes; } i++; - sp_banks[i].base_addr = 0xdeadbeef; + sp_banks[i].base_addr = 0xdeadbeefbeefdeadUL; sp_banks[i].num_bytes = 0; /* Now mask all bank sizes on a page boundary, it is all we can @@ -72,26 +74,12 @@ unsigned long prom_probe_memory (void) return tally; } -/* Traverse the memory lists in the prom to see how much physical we - * have. - */ -unsigned long -probe_memory(void) -{ - unsigned long total; - - total = prom_probe_memory(); - - /* Oh man, much nicer, keep the dirt in promlib. */ - return total; -} - void unhandled_fault(unsigned long address, struct task_struct *tsk, struct pt_regs *regs) { if((unsigned long) address < PAGE_SIZE) { printk(KERN_ALERT "Unable to handle kernel NULL " - "pointer dereference"); + "pointer dereference\n"); } else { printk(KERN_ALERT "Unable to handle kernel paging request " "at virtual address %016lx\n", (unsigned long)address); @@ -100,22 +88,74 @@ void unhandled_fault(unsigned long address, struct task_struct *tsk, (unsigned long) tsk->mm->context); printk(KERN_ALERT "tsk->mm->pgd = %016lx\n", (unsigned long) tsk->mm->pgd); - lock_kernel(); die_if_kernel("Oops", regs); - unlock_kernel(); } /* #define DEBUG_EXCEPTIONS */ /* #define DEBUG_LOCKUPS */ +/* #define INSN_VPTE_LOOKUP */ + +static inline u32 get_user_insn(unsigned long tpc) +{ + u32 insn; +#ifndef INSN_VPTE_LOOKUP + pgd_t *pgdp = pgd_offset(current->mm, tpc); + pmd_t *pmdp; + pte_t *ptep; + + if(pgd_none(*pgdp)) + return 0; + pmdp = pmd_offset(pgdp, tpc); + if(pmd_none(*pmdp)) + return 0; + ptep = pte_offset(pmdp, tpc); + if(!pte_present(*ptep)) + return 0; + insn = *(unsigned int *)(pte_page(*ptep) + (tpc & ~PAGE_MASK)); +#else + register unsigned long pte asm("l1"); + + /* So that we don't pollute TLB, we read the instruction + * using PHYS bypass. For that, we of course need + * to know its page table entry. Do this by simulating + * dtlb_miss handler. -jj */ + pte = ((((long)tpc) >> (PAGE_SHIFT-3)) & ~7); + asm volatile (" + rdpr %%pstate, %%l0 + wrpr %%l0, %2, %%pstate + wrpr %%g0, 1, %%tl + mov %%l1, %%g6 + ldxa [%%g3 + %%l1] %3, %%g5 + mov %%g5, %%l1 + wrpr %%g0, 0, %%tl + wrpr %%l0, 0, %%pstate + " : "=r" (pte) : "0" (pte), "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_S) : "l0"); + + if ((long)pte >= 0) return 0; + + pte = (pte & _PAGE_PADDR) + (tpc & ~PAGE_MASK); + asm ("lduwa [%1] %2, %0" : "=r" (insn) : "r" (pte), "i" (ASI_PHYS_USE_EC)); +#endif + + return insn; +} + asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; + unsigned int insn = 0; #ifdef DEBUG_LOCKUPS static unsigned long lastaddr, lastpc; static int lastwrite, lockcnt; #endif + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto do_kernel_fault; down(&mm->mmap_sem); #ifdef DEBUG_LOCKUPS @@ -135,6 +175,21 @@ asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, in vma = find_vma(mm, address); if(!vma) goto bad_area; +#ifndef INSN_VPTE_LOOKUP + write &= 0xf; +#else + if (write & 0x10) { + write = 0; + if((vma->vm_flags & VM_WRITE)) { + if (regs->tstate & TSTATE_PRIV) + insn = *(unsigned int *)regs->tpc; + else + insn = get_user_insn(regs->tpc); + if ((insn & 0xc0200000) == 0xc0200000 && (insn & 0x1780000) != 0x1680000) + write = 1; + } + } +#endif if(vma->vm_start <= address) goto good_area; if(!(vma->vm_flags & VM_GROWSDOWN)) @@ -168,16 +223,44 @@ bad_area: do_kernel_fault: { - unsigned long g2 = regs->u_regs[UREG_G2]; + unsigned long g2; + unsigned char asi = ASI_P; + + if (!insn) { + if (regs->tstate & TSTATE_PRIV) + insn = *(unsigned int *)regs->tpc; + else + insn = get_user_insn(regs->tpc); + } + if (write != 1 && (insn & 0xc0800000) == 0xc0800000) { + if (insn & 0x2000) + asi = (regs->tstate >> 24); + else + asi = (insn >> 5); + if ((asi & 0xf2) == 0x82) { + /* This was a non-faulting load. Just clear the + destination register(s) and continue with the next + instruction. -jj */ + if (insn & 0x1000000) { + extern int handle_ldf_stq(u32, struct pt_regs *); + + handle_ldf_stq(insn, regs); + } else { + extern int handle_ld_nf(u32, struct pt_regs *); + + handle_ld_nf(insn, regs); + } + return; + } + } + + g2 = regs->u_regs[UREG_G2]; /* Is this in ex_table? */ if (regs->tstate & TSTATE_PRIV) { - unsigned char asi = ASI_P; - unsigned int insn; unsigned long fixup; - - insn = *(unsigned int *)regs->tpc; - if ((insn & 0xc0800000) == 0xc0800000) { + + if (asi == ASI_P && (insn & 0xc0800000) == 0xc0800000) { if (insn & 0x2000) asi = (regs->tstate >> 24); else diff --git a/arch/sparc64/mm/generic.c b/arch/sparc64/mm/generic.c index 0b869a2f2..ccb0951cc 100644 --- a/arch/sparc64/mm/generic.c +++ b/arch/sparc64/mm/generic.c @@ -1,4 +1,4 @@ -/* $Id: generic.c,v 1.3 1998/10/27 23:28:07 davem Exp $ +/* $Id: generic.c,v 1.8 1999/03/12 06:51:50 davem Exp $ * generic.c: Generic Sparc mm routines that are not dependent upon * MMU type but are Sparc specific. * @@ -56,6 +56,10 @@ static inline void forget_pte(pte_t page) * * They use a pgprot that sets PAGE_IO and does not check the * mem_map table as this is independent of normal memory. + * + * As a special hack if the lowest bit of offset is set the + * side-effect bit will be turned off. This is used as a + * performance improvement on FFB/AFB. -DaveM */ static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsigned long size, unsigned long offset, pgprot_t prot, int space) @@ -71,23 +75,32 @@ static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsign pte_t entry; unsigned long curend = address + PAGE_SIZE; - entry = mk_pte_io(offset, prot, space); - offset += PAGE_SIZE; + entry = mk_pte_io((offset & ~(0x1UL)), prot, space); if (!(address & 0xffff)) { - if (!(address & 0x3fffff) && !(offset & 0x3fffff) && end >= address + 0x400000) { - entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), space); + if (!(address & 0x3fffff) && !(offset & 0x3ffffe) && end >= address + 0x400000) { + entry = mk_pte_io((offset & ~(0x1UL)), + __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), + space); curend = address + 0x400000; - offset += 0x400000 - PAGE_SIZE; - } else if (!(address & 0x7ffff) && !(offset & 0x7ffff) && end >= address + 0x80000) { - entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ512K), space); + offset += 0x400000; + } else if (!(address & 0x7ffff) && !(offset & 0x7fffe) && end >= address + 0x80000) { + entry = mk_pte_io((offset & ~(0x1UL)), + __pgprot(pgprot_val (prot) | _PAGE_SZ512K), + space); curend = address + 0x80000; - offset += 0x80000 - PAGE_SIZE; - } else if (!(offset & 0xffff) && end >= address + 0x10000) { - entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ64K), space); + offset += 0x80000; + } else if (!(offset & 0xfffe) && end >= address + 0x10000) { + entry = mk_pte_io((offset & ~(0x1UL)), + __pgprot(pgprot_val (prot) | _PAGE_SZ64K), + space); curend = address + 0x10000; - offset += 0x10000 - PAGE_SIZE; + offset += 0x10000; } - } + } else + offset += PAGE_SIZE; + + if (offset & 0x1UL) + pte_val(entry) &= ~(_PAGE_E); do { oldpage = *pte; pte_clear(pte); diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 236c866d6..aeb4b26b7 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,8 +1,8 @@ -/* $Id: init.c,v 1.103 1998/10/20 03:09:12 jj Exp $ +/* $Id: init.c,v 1.127 1999/05/08 03:00:38 davem Exp $ * arch/sparc64/mm/init.c * - * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -23,6 +23,7 @@ #include <asm/io.h> #include <asm/mmu_context.h> #include <asm/vaddrs.h> +#include <asm/dma.h> /* Turn this off if you suspect some place in some physical memory hole might get into page tables (something would be broken very much). */ @@ -34,6 +35,8 @@ extern unsigned long device_scan(unsigned long); struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; +unsigned long *sparc64_valid_addr_bitmap; + /* Ugly, but necessary... -DaveM */ unsigned long phys_base; @@ -69,7 +72,7 @@ int do_check_pgt_cache(int low, int high) page->next_hash = NULL; page->pprev_hash = NULL; pgd_cache_size -= 2; - free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + __free_page(page); freed++; if (page2) page = page2->next_hash; @@ -139,9 +142,9 @@ void show_mem(void) printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); - printk("%ld pages in page table cache\n",pgtable_cache_size); + printk("%d pages in page table cache\n",pgtable_cache_size); #ifndef __SMP__ - printk("%ld entries in page dir cache\n",pgd_cache_size); + printk("%d entries in page dir cache\n",pgd_cache_size); #endif show_buffers(); #ifdef CONFIG_NET @@ -160,27 +163,31 @@ static int dvma_pages_current_index; static unsigned long dvmaiobase = 0; static unsigned long dvmaiosz __initdata = 0; -/* #define E3000_DEBUG */ - __initfunc(void dvmaio_init(void)) { - int i; + long i; if (!dvmaiobase) { for (i = 0; sp_banks[i].num_bytes != 0; i++) if (sp_banks[i].base_addr + sp_banks[i].num_bytes > dvmaiobase) dvmaiobase = sp_banks[i].base_addr + sp_banks[i].num_bytes; + + /* We map directly phys_base to phys_base+(4GB-DVMAIO_SIZE). */ + dvmaiobase -= phys_base; + dvmaiobase = (dvmaiobase + DVMAIO_SIZE + 0x400000 - 1) & ~(0x400000 - 1); for (i = 0; i < 6; i++) - if (dvmaiobase <= ((1024 * 64 * 1024) << i)) + if (dvmaiobase <= ((1024L * 64 * 1024) << i)) break; - dvmaiobase = ((1024 * 64 * 1024) << i) - DVMAIO_SIZE; + dvmaiobase = ((1024L * 64 * 1024) << i) - DVMAIO_SIZE; dvmaiosz = i; } } __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) { + extern int this_is_starfire; + extern void *starfire_hookup(int); struct iommu_struct *iommu; struct sysio_regs *sregs; struct linux_prom64_registers rprop; @@ -191,10 +198,6 @@ __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) int err, i, j; dvmaio_init(); -#ifdef E3000_DEBUG - prom_printf("\niommu_init: [%x:%p] ", - iommu_node, sbus); -#endif err = prom_getproperty(iommu_node, "reg", (char *)&rprop, sizeof(rprop)); if(err == -1) { @@ -203,19 +206,16 @@ __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) } sregs = (struct sysio_regs *) __va(rprop.phys_addr); -#ifdef E3000_DEBUG - prom_printf("sregs[%p]\n"); -#endif if(!sregs) { prom_printf("iommu_init: Fatal error, sysio regs not mapped\n"); prom_halt(); } iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC); - -#ifdef E3000_DEBUG - prom_printf("iommu_init: iommu[%p] ", iommu); -#endif + if (!iommu) { + prom_printf("iommu_init: Fatal error, kmalloc(iommu) failed\n"); + prom_halt(); + } spin_lock_init(&iommu->iommu_lock); iommu->sysio_regs = sregs; @@ -224,14 +224,19 @@ __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) control = sregs->iommu_control; impl = (control & IOMMU_CTRL_IMPL) >> 60; vers = (control & IOMMU_CTRL_VERS) >> 56; -#ifdef E3000_DEBUG - prom_printf("sreg_control[%08x]\n", control); - prom_printf("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n", - (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs); -#endif printk("IOMMU(SBUS): IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n", (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs); + /* Streaming buffer is unreliable on VERS 0 of SYSIO, + * although such parts were never shipped in production + * Sun hardware, I check just to be robust. --DaveM + */ + vers = ((sregs->control & SYSIO_CONTROL_VER) >> 56); + if (vers == 0) + iommu->strbuf_enabled = 0; + else + iommu->strbuf_enabled = 1; + control &= ~(IOMMU_CTRL_TSBSZ); control |= ((IOMMU_TSBSZ_2K * dvmaiosz) | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); @@ -281,9 +286,14 @@ __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) /* Setup aliased mappings... */ for(i = 0; i < (dvmaiobase >> 16); i++) { - *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_STBUF | - IOPTE_CACHE | IOPTE_WRITE); - *iopte |= (i << 16); + unsigned long val = ((((unsigned long)i) << 16UL) + phys_base); + + val |= IOPTE_VALID | IOPTE_64K | IOPTE_WRITE; + if (iommu->strbuf_enabled) + val |= IOPTE_STBUF; + else + val |= IOPTE_CACHE; + *iopte = val; iopte++; } @@ -291,38 +301,34 @@ __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) for( ; i < ((dvmaiobase + DVMAIO_SIZE) >> 16); i++) *iopte++ = 0; -#ifdef E3000_DEBUG - prom_printf("IOMMU: pte's mapped, enabling IOMMU... "); -#endif sregs->iommu_tsbbase = __pa(tsbbase); sregs->iommu_control = control; -#ifdef E3000_DEBUG - prom_printf("done\n"); -#endif /* Get the streaming buffer going. */ control = sregs->sbuf_control; impl = (control & SYSIO_SBUFCTRL_IMPL) >> 60; vers = (control & SYSIO_SBUFCTRL_REV) >> 56; -#ifdef E3000_DEBUG - prom_printf("IOMMU: enabling streaming buffer, control[%08x]... ", - control); -#endif - printk("IOMMU: Streaming Buffer IMPL[%x] REV[%x] ", + printk("IOMMU: Streaming Buffer IMPL[%x] REV[%x] ... ", (unsigned int)impl, (unsigned int)vers); - iommu->sbuf_flushflag_va = kmalloc(sizeof(unsigned long), GFP_DMA); - printk("FlushFLAG[%016lx] ... ", (iommu->sbuf_flushflag_pa = __pa(iommu->sbuf_flushflag_va))); - *(iommu->sbuf_flushflag_va) = 0; - - sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN); + iommu->flushflag = 0; -#ifdef E3000_DEBUG - prom_printf("done, returning\n"); -#endif - printk("ENABLED\n"); + if (iommu->strbuf_enabled != 0) { + sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN); + printk("ENABLED\n"); + } else { + sregs->sbuf_control = (control & ~(SYSIO_SBUFCTRL_SB_EN)); + printk("DISABLED\n"); + } /* Finally enable DVMA arbitration for all devices, just in case. */ sregs->sbus_control |= SYSIO_SBCNTRL_AEN; + + /* If necessary, hook us up for starfire IRQ translations. */ + sbus->upaid = prom_getintdefault(sbus->prom_node, "upa-portid", -1); + if(this_is_starfire) + sbus->starfire_cookie = starfire_hookup(sbus->upaid); + else + sbus->starfire_cookie = NULL; } void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr, @@ -397,19 +403,35 @@ void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr, __u32 mmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus) { - __u32 sbus_addr = (__u32) __pa(vaddr); - -#ifndef DEBUG_IOMMU - return sbus_addr; -#else - if((sbus_addr < dvmaiobase) && - ((sbus_addr + len) < dvmaiobase)) - return sbus_addr; - - /* "can't happen"... GFP_DMA assures this. */ - panic("Very high scsi_one mappings should never happen."); - return (__u32)0; -#endif + struct iommu_struct *iommu = sbus->iommu; + struct sysio_regs *sregs = iommu->sysio_regs; + unsigned long start = (unsigned long) vaddr; + unsigned long end = PAGE_ALIGN(start + len); + unsigned long flags, tmp; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; + + start &= PAGE_MASK; + if (end > MAX_DMA_ADDRESS) { + printk("mmu_get_scsi_one: Bogus DMA buffer address [%016lx:%d]\n", + (unsigned long) vaddr, (int)len); + panic("DMA address too large, tell DaveM"); + } + + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); + iommu->flushflag = 0; + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + tmp = *sbctrl; + while(iommu->flushflag == 0) + membar("#LoadLoad"); + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } + + return sbus_dvma_addr(vaddr); } void mmu_release_scsi_one(u32 vaddr, unsigned long len, struct linux_sbus *sbus) @@ -418,43 +440,89 @@ void mmu_release_scsi_one(u32 vaddr, unsigned long len, struct linux_sbus *sbus) struct sysio_regs *sregs = iommu->sysio_regs; unsigned long start = (unsigned long) vaddr; unsigned long end = PAGE_ALIGN(start + len); - unsigned long flags; - unsigned int *sync_word; + unsigned long flags, tmp; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; start &= PAGE_MASK; - spin_lock_irqsave(&iommu->iommu_lock, flags); + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); + + /* 1) Clear the flush flag word */ + iommu->flushflag = 0; - while(start < end) { - sregs->sbuf_pflush = start; - start += PAGE_SIZE; + /* 2) Tell the streaming buffer which entries + * we want flushed. + */ + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + + /* 3) Initiate flush sequence. */ + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + + /* 4) Guarentee completion of all previous writes + * by reading SYSIO's SBUS control register. + */ + tmp = *sbctrl; + + /* 5) Wait for flush flag to get set. */ + while(iommu->flushflag == 0) + membar("#LoadLoad"); + + spin_unlock_irqrestore(&iommu->iommu_lock, flags); } - sync_word = iommu->sbuf_flushflag_va; - sregs->sbuf_fsync = iommu->sbuf_flushflag_pa; - membar("#StoreLoad | #MemIssue"); - while((*sync_word & 0x1) == 0) - membar("#LoadLoad"); - *sync_word = 0; - - spin_unlock_irqrestore(&iommu->iommu_lock, flags); } void mmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) { - while(sz >= 0) { - __u32 page = (__u32) __pa(((unsigned long) sg[sz].addr)); -#ifndef DEBUG_IOMMU - sg[sz].dvma_addr = page; -#else - if((page < dvmaiobase) && - (page + sg[sz].len) < dvmaiobase) { - sg[sz].dvma_addr = page; - } else { - /* "can't happen"... GFP_DMA assures this. */ - panic("scsi_sgl high mappings should never happen."); + struct iommu_struct *iommu = sbus->iommu; + struct sysio_regs *sregs = iommu->sysio_regs; + unsigned long flags, tmp; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; + + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); + iommu->flushflag = 0; + + while(sz >= 0) { + unsigned long start = (unsigned long)sg[sz].addr; + unsigned long end = PAGE_ALIGN(start + sg[sz].len); + + if (end > MAX_DMA_ADDRESS) { + printk("mmu_get_scsi_sgl: Bogus DMA buffer address " + "[%016lx:%d]\n", start, (int) sg[sz].len); + panic("DMA address too large, tell DaveM"); + } + + sg[sz--].dvma_addr = sbus_dvma_addr(start); + start &= PAGE_MASK; + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + } + + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + tmp = *sbctrl; + while(iommu->flushflag == 0) + membar("#LoadLoad"); + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } else { + /* Just verify the addresses and fill in the + * dvma_addr fields in this case. + */ + while(sz >= 0) { + unsigned long start = (unsigned long)sg[sz].addr; + unsigned long end = PAGE_ALIGN(start + sg[sz].len); + if (end > MAX_DMA_ADDRESS) { + printk("mmu_get_scsi_sgl: Bogus DMA buffer address " + "[%016lx:%d]\n", start, (int) sg[sz].len); + panic("DMA address too large, tell DaveM"); + } + sg[sz--].dvma_addr = sbus_dvma_addr(start); } -#endif - sz--; } } @@ -462,30 +530,98 @@ void mmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus { struct iommu_struct *iommu = sbus->iommu; struct sysio_regs *sregs = iommu->sysio_regs; - unsigned long flags; - unsigned int *sync_word; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; + unsigned long flags, tmp; + + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); + + /* 1) Clear the flush flag word */ + iommu->flushflag = 0; + + /* 2) Tell the streaming buffer which entries + * we want flushed. + */ + while(sz >= 0) { + unsigned long start = sg[sz].dvma_addr; + unsigned long end = PAGE_ALIGN(start + sg[sz].len); + + start &= PAGE_MASK; + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + sz--; + } - spin_lock_irqsave(&iommu->iommu_lock, flags); + /* 3) Initiate flush sequence. */ + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); - while(sz >= 0) { - unsigned long start = sg[sz].dvma_addr; - unsigned long end = PAGE_ALIGN(start + sg[sz].len); + /* 4) Guarentee completion of previous writes + * by reading SYSIO's SBUS control register. + */ + tmp = *sbctrl; - start &= PAGE_MASK; - while(start < end) { - sregs->sbuf_pflush = start; - start += PAGE_SIZE; - } - sz--; + /* 5) Wait for flush flag to get set. */ + while(iommu->flushflag == 0) + membar("#LoadLoad"); + + spin_unlock_irqrestore(&iommu->iommu_lock, flags); } - sync_word = iommu->sbuf_flushflag_va; - sregs->sbuf_fsync = iommu->sbuf_flushflag_pa; - membar("#StoreLoad | #MemIssue"); - while((*sync_word & 0x1) == 0) - membar("#LoadLoad"); - *sync_word = 0; - - spin_unlock_irqrestore(&iommu->iommu_lock, flags); +} + +void mmu_set_sbus64(struct linux_sbus_device *sdev, int bursts) +{ + struct linux_sbus *sbus = sdev->my_bus; + struct sysio_regs *sregs = sbus->iommu->sysio_regs; + int slot = sdev->slot; + u64 *cfg, tmp; + + switch(slot) { + case 0: + cfg = &sregs->sbus_s0cfg; + break; + case 1: + cfg = &sregs->sbus_s1cfg; + break; + case 2: + cfg = &sregs->sbus_s2cfg; + break; + case 3: + cfg = &sregs->sbus_s3cfg; + break; + + case 13: + cfg = &sregs->sbus_s4cfg; + break; + case 14: + cfg = &sregs->sbus_s5cfg; + break; + case 15: + cfg = &sregs->sbus_s6cfg; + break; + + default: + return; + }; + + /* ETM already enabled? If so, we're done. */ + tmp = *cfg; + if ((tmp & SYSIO_SBSCFG_ETM) != 0) + return; + + /* Set burst bits. */ + if (bursts & DMA_BURST8) + tmp |= SYSIO_SBSCFG_BA8; + if (bursts & DMA_BURST16) + tmp |= SYSIO_SBSCFG_BA16; + if (bursts & DMA_BURST32) + tmp |= SYSIO_SBSCFG_BA32; + if (bursts & DMA_BURST64) + tmp |= SYSIO_SBSCFG_BA64; + + /* Finally turn on ETM and set register. */ + *cfg = (tmp | SYSIO_SBSCFG_ETM); } int mmu_info(char *buf) @@ -562,16 +698,9 @@ static inline void inherit_prom_mappings(void) */ static void __flush_nucleus_vptes(void) { - unsigned long pstate; unsigned long prom_reserved_base = 0xfffffffc00000000UL; int i; - __asm__ __volatile__("flushw\n\t" - "rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (pstate) - : "i" (PSTATE_IE)); - /* Only DTLB must be checked for VPTE entries. */ for(i = 0; i < 63; i++) { unsigned long tag = spitfire_get_dtlb_tag(i); @@ -586,8 +715,6 @@ static void __flush_nucleus_vptes(void) membar("#Sync"); } } - __asm__ __volatile__("wrpr %0, 0, %%pstate" - : : "r" (pstate)); } static int prom_ditlb_set = 0; @@ -600,10 +727,19 @@ struct prom_tlb_entry prom_itlb[8], prom_dtlb[8]; void prom_world(int enter) { + unsigned long pstate; int i; if (!prom_ditlb_set) return; + + /* Make sure the following runs atomically. */ + __asm__ __volatile__("flushw\n\t" + "rdpr %%pstate, %0\n\t" + "wrpr %0, %1, %%pstate" + : "=r" (pstate) + : "i" (PSTATE_IE)); + if (enter) { /* Kick out nucleus VPTEs. */ __flush_nucleus_vptes(); @@ -648,6 +784,8 @@ void prom_world(int enter) } } } + __asm__ __volatile__("wrpr %0, 0, %%pstate" + : : "r" (pstate)); } void inherit_locked_prom_mappings(int save_p) @@ -680,7 +818,7 @@ void inherit_locked_prom_mappings(int save_p) unsigned long data; data = spitfire_get_dtlb_data(i); - if(data & _PAGE_L) { + if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) { unsigned long tag = spitfire_get_dtlb_tag(i); if(save_p) { @@ -703,7 +841,7 @@ void inherit_locked_prom_mappings(int save_p) unsigned long data; data = spitfire_get_itlb_data(i); - if(data & _PAGE_L) { + if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) { unsigned long tag = spitfire_get_itlb_tag(i); if(save_p) { @@ -811,8 +949,7 @@ void __flush_tlb_all(void) #define CTX_BMAP_SLOTS (1UL << (CTX_VERSION_SHIFT - 6)) unsigned long mmu_context_bmap[CTX_BMAP_SLOTS]; -/* We are always protected by scheduler_lock under SMP. - * Caller does TLB context flushing on local CPU if necessary. +/* Caller does TLB context flushing on local CPU if necessary. * * We must be careful about boundary cases so that we never * let the user have CTX 0 (nucleus) or we ever use a CTX @@ -867,15 +1004,11 @@ out: struct pgtable_cache_struct pgt_quicklists; #endif -/* XXX Add __GFP_HIGH to these calls to "fool" page allocator - * XXX so we don't go to swap so quickly... then do the same - * XXX for get_user_page as well -DaveM - */ pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset) { pmd_t *pmd; - pmd = (pmd_t *) __get_free_page(GFP_DMA|GFP_KERNEL); + pmd = (pmd_t *) __get_free_page(GFP_KERNEL); if(pmd) { memset(pmd, 0, PAGE_SIZE); pgd_set(pgd, pmd); @@ -888,7 +1021,7 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; - pte = (pte_t *) __get_free_page(GFP_DMA|GFP_KERNEL); + pte = (pte_t *) __get_free_page(GFP_KERNEL); if(pte) { memset(pte, 0, PAGE_SIZE); pmd_set(pmd, pte); @@ -993,15 +1126,16 @@ void sparc_ultra_dump_dtlb(void) /* paging_init() sets up the page tables */ extern unsigned long free_area_init(unsigned long, unsigned long); +extern unsigned long sun_serial_setup(unsigned long); __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)) { - extern void setup_tba(void); extern pmd_t swapper_pmd_dir[1024]; - extern unsigned long irq_init(unsigned long start_mem, unsigned long end_mem); - extern unsigned int sparc64_vpte_patchme[1]; + extern unsigned int sparc64_vpte_patchme1[1]; + extern unsigned int sparc64_vpte_patchme2[1]; unsigned long alias_base = phys_base + PAGE_OFFSET; + unsigned long second_alias_page = 0; unsigned long pt; unsigned long flags; unsigned long shift = alias_base - ((unsigned long)&empty_zero_page); @@ -1026,6 +1160,21 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pt), "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3) : "memory"); + if (start_mem >= KERNBASE + 0x340000) { + second_alias_page = alias_base + 0x400000; + __asm__ __volatile__(" + stxa %1, [%0] %3 + stxa %2, [%5] %4 + membar #Sync + flush %%g6 + nop + nop + nop" + : /* No outputs */ + : "r" (TLB_TAG_ACCESS), "r" (second_alias_page), "r" (pt + 0x400000), + "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (60 << 3) + : "memory"); + } __restore_flags(flags); /* Now set kernel pgd to upper alias so physical page computations @@ -1038,22 +1187,40 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) /* Now can init the kernel/bad page tables. */ pgd_set(&swapper_pg_dir[0], swapper_pmd_dir + (shift / sizeof(pgd_t))); - sparc64_vpte_patchme[0] |= (init_mm.pgd[0] >> 10); + sparc64_vpte_patchme1[0] |= (init_mm.pgd[0] >> 10); + sparc64_vpte_patchme2[0] |= (init_mm.pgd[0] & 0x3ff); + flushi((long)&sparc64_vpte_patchme1[0]); - start_mem = irq_init(start_mem, end_mem); - /* We use mempool to create page tables, therefore adjust it up * such that __pa() macros etc. work. */ mempool = PAGE_ALIGN(start_mem) + shift; + +#ifdef CONFIG_SUN_SERIAL + /* This does not logically belong here, but is the first place + we can initialize it at, so that we work in the PAGE_OFFSET+ + address space. */ + mempool = sun_serial_setup(mempool); +#endif /* Allocate 64M for dynamic DVMA mapping area. */ allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000); inherit_prom_mappings(); - - /* Ok, we can use our TLB miss and window trap handlers safely. */ - setup_tba(); + /* Ok, we can use our TLB miss and window trap handlers safely. + * We need to do a quick peek here to see if we are on StarFire + * or not, so setup_tba can setup the IRQ globals correctly (it + * needs to get the hard smp processor id correctly). + */ + { + extern void setup_tba(int); + int is_starfire = prom_finddevice("/ssp-serial"); + if(is_starfire != 0 && is_starfire != -1) + is_starfire = 1; + else + is_starfire = 0; + setup_tba(is_starfire); + } /* Really paranoid. */ flushi((long)&empty_zero_page); @@ -1064,6 +1231,8 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) */ /* We only created DTLB mapping of this stuff. */ spitfire_flush_dtlb_nucleus_page(alias_base); + if (second_alias_page) + spitfire_flush_dtlb_nucleus_page(second_alias_page); membar("#Sync"); /* Paranoid */ @@ -1079,10 +1248,6 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) return device_scan (PAGE_ALIGN (start_mem)); } -/* XXX Add also PG_Hole flag, set it in the page structs here, - * XXX remove FREE_UNUSED_MEM_MAP code, and the nfsd file handle - * problems will all be gone. -DaveM - */ __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem)) { unsigned long tmp = 0, paddr, endaddr; @@ -1096,14 +1261,24 @@ __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long e if (!sp_banks[tmp].num_bytes) { mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip); mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (phys_base >> PAGE_SHIFT); + mem_map[(paddr>>PAGE_SHIFT)+1UL].flags |= (1<<PG_skip); + mem_map[(paddr>>PAGE_SHIFT)+1UL].next_hash = mem_map + (phys_base >> PAGE_SHIFT); return; } if (sp_banks[tmp].base_addr > paddr) { - /* Making a one or two pages PG_skip holes is not necessary */ - if (sp_banks[tmp].base_addr - paddr > 2 * PAGE_SIZE) { + /* Making a one or two pages PG_skip holes + * is not necessary. We add one more because + * we must set the PG_skip flag on the first + * two mem_map[] entries for the hole. Go and + * see the mm/filemap.c:shrink_mmap() loop for + * details. -DaveM + */ + if (sp_banks[tmp].base_addr - paddr > 3 * PAGE_SIZE) { mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip); mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT); + mem_map[(paddr>>PAGE_SHIFT)+1UL].flags |= (1<<PG_skip); + mem_map[(paddr>>PAGE_SHIFT)+1UL].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT); } paddr = sp_banks[tmp].base_addr; } @@ -1111,7 +1286,8 @@ __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long e endaddr = sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes; while (paddr < endaddr) { mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_reserved); - if (paddr >= dvmaiobase) + set_bit(paddr >> 22, sparc64_valid_addr_bitmap); + if (paddr >= (MAX_DMA_ADDRESS - PAGE_OFFSET)) mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_DMA); paddr += PAGE_SIZE; } @@ -1131,6 +1307,12 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) end_mem &= PAGE_MASK; max_mapnr = MAP_NR(end_mem); high_memory = (void *) end_mem; + + sparc64_valid_addr_bitmap = (unsigned long *)start_mem; + i = max_mapnr >> ((22 - PAGE_SHIFT) + 6); + i += 1; + memset(sparc64_valid_addr_bitmap, 0, i << 3); + start_mem += i << 3; start_mem = PAGE_ALIGN(start_mem); num_physpages = 0; @@ -1138,6 +1320,8 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) if (phys_base) { mem_map[0].flags |= (1<<PG_skip) | (1<<PG_reserved); mem_map[0].next_hash = mem_map + (phys_base >> PAGE_SHIFT); + mem_map[1].flags |= (1<<PG_skip) | (1<<PG_reserved); + mem_map[1].next_hash = mem_map + (phys_base >> PAGE_SHIFT); } addr = PAGE_OFFSET + phys_base; @@ -1148,6 +1332,7 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) else #endif mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved); + set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap); addr += PAGE_SIZE; } @@ -1159,6 +1344,9 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) if (PageSkip(page)) { unsigned long low, high; + /* See taint_real_pages() for why this is done. -DaveM */ + page++; + low = PAGE_ALIGN((unsigned long)(page+1)); if (page->next_hash < page) high = ((unsigned long)end) & PAGE_MASK; @@ -1255,7 +1443,7 @@ void si_meminfo(struct sysinfo *val) val->totalram = 0; val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; + val->freeram = ((unsigned long)nr_free_pages) << PAGE_SHIFT; val->bufferram = buffermem; for (page = mem_map, end = mem_map + max_mapnr; page < end; page++) { diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S index 4362a15b4..3d3d1a289 100644 --- a/arch/sparc64/mm/ultra.S +++ b/arch/sparc64/mm/ultra.S @@ -1,4 +1,4 @@ -/* $Id: ultra.S,v 1.31 1998/11/07 06:39:21 davem Exp $ +/* $Id: ultra.S,v 1.32 1999/03/28 08:39:34 davem Exp $ * ultra.S: Don't expand these all over the place... * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -261,6 +261,21 @@ xcall_capture: b,pt %xcc, rtrap clr %l6 + .globl xcall_receive_signal +xcall_receive_signal: + rdpr %pstate, %g2 + wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate + rdpr %tstate, %g1 + andcc %g1, TSTATE_PRIV, %g0 + /* If we did not trap from user space, just ignore. */ + bne,pn %xcc, 99f + sethi %hi(109f), %g7 + b,pt %xcc, etrap +109: or %g7, %lo(109b), %g7 + b,pt %xcc, rtrap + clr %l6 +99: retry + /* These two are not performance critical... */ .globl xcall_flush_tlb_all xcall_flush_tlb_all: diff --git a/arch/sparc64/prom/memory.c b/arch/sparc64/prom/memory.c index bb3e50a92..3523c3b1d 100644 --- a/arch/sparc64/prom/memory.c +++ b/arch/sparc64/prom/memory.c @@ -1,4 +1,4 @@ -/* $Id: memory.c,v 1.3 1997/03/04 16:27:10 jj Exp $ +/* $Id: memory.c,v 1.4 1998/11/25 10:04:06 jj Exp $ * memory.c: Prom routine for acquiring various bits of information * about RAM on the machine, both virtual and physical. * @@ -41,8 +41,8 @@ __initfunc(static void prom_sortmemlist(struct linux_mlist_p1275 *thislist)) { int swapi = 0; - int i, mitr, tmpsize; - unsigned long tmpaddr; + int i, mitr; + unsigned long tmpaddr, tmpsize; unsigned long lowest; for(i=0; thislist[i].theres_more != 0; i++) { @@ -79,7 +79,7 @@ __initfunc(void prom_meminit(void)) prom_phys_avail[iter].start_adr = prom_reg_memlist[iter].phys_addr; prom_phys_avail[iter].num_bytes = - (unsigned long) prom_reg_memlist[iter].reg_size; + prom_reg_memlist[iter].reg_size; prom_phys_avail[iter].theres_more = &prom_phys_avail[iter+1]; } @@ -93,7 +93,7 @@ __initfunc(void prom_meminit(void)) prom_phys_total[iter].start_adr = prom_reg_memlist[iter].phys_addr; prom_phys_total[iter].num_bytes = - (unsigned long) prom_reg_memlist[iter].reg_size; + prom_reg_memlist[iter].reg_size; prom_phys_total[iter].theres_more = &prom_phys_total[iter+1]; } @@ -112,7 +112,7 @@ __initfunc(void prom_meminit(void)) prom_prom_taken[iter].start_adr = prom_reg_memlist[iter].phys_addr; prom_prom_taken[iter].num_bytes = - (unsigned long) prom_reg_memlist[iter].reg_size; + prom_reg_memlist[iter].reg_size; prom_prom_taken[iter].theres_more = &prom_phys_total[iter+1]; } @@ -130,7 +130,7 @@ __initfunc(void prom_meminit(void)) prom_prom_taken[iter].start_adr; } prom_prom_taken[iter-1].num_bytes = - ((unsigned long)-1) - (unsigned long) prom_prom_taken[iter-1].start_adr; + -1UL - prom_prom_taken[iter-1].start_adr; /* Sort the other two lists. */ prom_sortmemlist(prom_phys_total); diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index 340c27223..83af773df 100644 --- a/arch/sparc64/prom/misc.c +++ b/arch/sparc64/prom/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.13 1998/10/13 14:03:49 davem Exp $ +/* $Id: misc.c,v 1.14 1998/12/18 10:01:59 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -122,9 +122,123 @@ void prom_set_trap_table(unsigned long tba) p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba); } +/* This is only used internally below. */ +static int prom_get_mmu_ihandle(void) +{ + int node; + int ret; + + node = prom_finddevice("/chosen"); + ret = prom_getint(node, "mmu"); + if(ret == -1 || ret == 0) { + prom_printf("PROMLIB: Fatal error, cannot get mmu ihandle.\n"); + prom_halt(); + } + return ret; +} + +/* Load explicit I/D TLB entries. */ +long prom_itlb_load(unsigned long index, + unsigned long tte_data, + unsigned long vaddr) +{ + return p1275_cmd("call-method", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)), + "SUNW,itlb-load", + prom_get_mmu_ihandle(), + /* And then our actual args are pushed backwards. */ + vaddr, + tte_data, + index); +} + +long prom_dtlb_load(unsigned long index, + unsigned long tte_data, + unsigned long vaddr) +{ + return p1275_cmd("call-method", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)), + "SUNW,dtlb-load", + prom_get_mmu_ihandle(), + /* And then our actual args are pushed backwards. */ + vaddr, + tte_data, + index); +} + +/* Set aside physical memory which is not touched or modified + * across soft resets. + */ +unsigned long prom_retain(char *name, + unsigned long pa_low, unsigned long pa_high, + long size, long align) +{ + /* XXX I don't think we return multiple values correctly. + * XXX OBP supposedly returns pa_low/pa_high here, how does + * XXX it work? + */ + + /* If align is zero, the pa_low/pa_high args are passed, + * else they are not. + */ + if(align == 0) + return p1275_cmd("SUNW,retain", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)), + name, pa_low, pa_high, size, align); + else + return p1275_cmd("SUNW,retain", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)), + name, size, align); +} + +/* Get "Unumber" string for the SIMM at the given + * memory address. Usually this will be of the form + * "Uxxxx" where xxxx is a decimal number which is + * etched into the motherboard next to the SIMM slot + * in question. + */ +int prom_getunumber(unsigned long phys_lo, unsigned long phys_hi, + char *buf, int buflen) +{ + return p1275_cmd("SUNW,get-unumber", + (P1275_ARG(2, P1275_ARG_OUT_BUF) | P1275_INOUT(4, 1)), + phys_lo, phys_hi, buf, buflen); +} + +/* Power management extensions. */ +void prom_sleepself(void) +{ + p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0)); +} + +int prom_sleepsystem(void) +{ + return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1)); +} + +int prom_wakeupsystem(void) +{ + return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1)); +} + #ifdef __SMP__ void prom_startcpu(int cpunode, unsigned long pc, unsigned long o0) { p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0); } + +void prom_stopself(void) +{ + p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0)); +} + +void prom_idleself(void) +{ + p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0)); +} + +void prom_resumecpu(int cpunode) +{ + p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode); +} #endif diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c index 3631d43b2..f0921ab9f 100644 --- a/arch/sparc64/solaris/fs.c +++ b/arch/sparc64/solaris/fs.c @@ -1,4 +1,4 @@ -/* $Id: fs.c,v 1.11 1998/10/28 08:12:04 jj Exp $ +/* $Id: fs.c,v 1.12 1999/01/02 16:46:06 davem Exp $ * fs.c: fs related syscall emulation for Solaris * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -7,6 +7,7 @@ #include <linux/types.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/mm.h> #include <linux/file.h> #include <linux/stat.h> #include <linux/smp_lock.h> @@ -21,7 +22,6 @@ #include "conv.h" extern char * getname32(u32 filename); -#define putname32 putname #define R4_DEV(DEV) ((DEV & 0xff) | ((DEV & 0xff00) << 10)) #define R4_MAJOR(DEV) (((DEV) >> 18) & 0x3fff) @@ -138,7 +138,7 @@ asmlinkage int solaris_stat(u32 filename, u32 statbuf) set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat ((struct sol_stat *)A(statbuf), &s)) return -EFAULT; } @@ -166,7 +166,7 @@ asmlinkage int solaris_stat64(u32 filename, u32 statbuf) set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) return -EFAULT; } @@ -188,7 +188,7 @@ asmlinkage int solaris_lstat(u32 filename, u32 statbuf) set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat ((struct sol_stat *)A(statbuf), &s)) return -EFAULT; } @@ -215,7 +215,7 @@ asmlinkage int solaris_lstat64(u32 filename, u32 statbuf) set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) return -EFAULT; } diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S index f47470e4c..7c7c7b8b1 100644 --- a/arch/sparc64/solaris/systbl.S +++ b/arch/sparc64/solaris/systbl.S @@ -1,4 +1,4 @@ -/* $Id: systbl.S,v 1.7 1998/10/28 08:11:49 jj Exp $ +/* $Id: systbl.S,v 1.8 1999/02/11 18:34:02 davem Exp $ * systbl.S: System call entry point table for Solaris compatibility. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -72,7 +72,7 @@ solaris_sys_table: .word CHAIN(dup) /* dup d 41 */ .word CHAIN(pipe) /* pipe 42 */ .word CHAIN(times) /* times p 43 */ - .word CHAIN(profil) /* prof xxxx 44 */ + .word 44 /*CHAIN(profil)*/ /* prof xxxx 44 */ .word solaris_unimplemented /* lock/plock 45 */ .word CHAIN(setgid) /* setgid d 46 */ .word solaris_getgid /* getgid 47 */ |