diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-09-12 01:29:55 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-09-12 01:29:55 +0000 |
commit | 545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch) | |
tree | e9ce4bc598d06374bda906f18365984bf22a526a /arch | |
parent | 4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff) |
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'arch')
203 files changed, 23642 insertions, 4743 deletions
diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 6ac472f08..71c310aa1 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -94,9 +94,9 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC - tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA + tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86 tristate 'Parallel port support' CONFIG_PARPORT diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index a36ec98a9..22adc7f62 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -43,7 +43,7 @@ CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_EM86=y -# CONFIG_PNP_PARPORT is not set +# CONFIG_PARPORT is not set # # Plug and Play support @@ -67,7 +67,6 @@ CONFIG_BLK_DEV_FD=y # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_MD is not set # CONFIG_BLK_DEV_RAM is not set -# CONFIG_BLK_DEV_INITRD is not set # CONFIG_BLK_DEV_XD is not set # CONFIG_BLK_DEV_EZ is not set # CONFIG_BLK_DEV_HD is not set @@ -142,6 +141,7 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set +# CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_QLOGIC_FAS is not set CONFIG_SCSI_QLOGIC_ISP=y @@ -150,6 +150,8 @@ CONFIG_SCSI_QLOGIC_ISP=y # CONFIG_SCSI_T128 is not set # CONFIG_SCSI_U14_34F is not set # CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set # # Network device support @@ -174,6 +176,7 @@ CONFIG_DE4X5=y # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set +# CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set @@ -215,6 +218,7 @@ CONFIG_ISO9660_FS=y # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_MAC_PARTITION is not set # # Character devices @@ -230,12 +234,15 @@ CONFIG_MOUSE=y # CONFIG_MS_BUSMOUSE is not set CONFIG_PSMOUSE=y # CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set # CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set # # Sound diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c index 16d7474d1..385fc9494 100644 --- a/arch/alpha/kernel/bios32.c +++ b/arch/alpha/kernel/bios32.c @@ -186,6 +186,7 @@ static void layout_dev(struct pci_dev *dev) pcibios_read_config_dword(bus->number, dev->devfn, reg, &base); if (!base) { /* this base-address register is unused */ + dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2] = 0; continue; } diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index 8f896195d..e22313036 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -437,8 +437,8 @@ entUnaUser: stq $13,32($30) stq $14,40($30) stq $15,48($30) - bis $31,$30,$19 lda $8,0x3fff + addq $30,56,$19 bic $30,$8,$8 jsr $26,do_entUnaUser ldq $9,0($30) diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index c3838a8bb..12bee3fbc 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -491,8 +491,22 @@ static inline unsigned long s_reg_to_mem (unsigned long s_reg) | 1L << 0x2c | 1L << 0x2d /* stl stq */ \ | 1L << 0xd ) /* stw */ +#define R(x) ((size_t) &((struct pt_regs *)0)->x) + +static int unauser_reg_offsets[32] = { + R(r0), R(r1), R(r2), R(r3), R(r4), R(r5), R(r6), R(r7), R(r8), + /* r9 ... r15 are stored in front of regs. */ + -56, -48, -40, -32, -24, -16, -8, + R(r16), R(r17), R(r18), + R(r19), R(r20), R(r21), R(r22), R(r23), R(r24), R(r25), R(r26), + R(r27), R(r28), R(gp), + 0, 0 +}; + +#undef R + asmlinkage void do_entUnaUser(void * va, unsigned long opcode, - unsigned long reg, unsigned long * frame) + unsigned long reg, struct pt_regs *regs) { extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); extern unsigned long alpha_read_fp_reg (unsigned long reg); @@ -501,12 +515,10 @@ asmlinkage void do_entUnaUser(void * va, unsigned long opcode, static long last_time = 0; unsigned long tmp1, tmp2, tmp3, tmp4; - unsigned long *reg_addr, *pc_addr, fake_reg; + unsigned long fake_reg, *reg_addr = &fake_reg; unsigned long uac_bits; long error; - pc_addr = frame + 7 + 20 + 1; /* pc in PAL frame */ - /* Check the UAC bits to decide what the user wants us to do with the unaliged access. */ @@ -519,7 +531,7 @@ asmlinkage void do_entUnaUser(void * va, unsigned long opcode, lock_kernel(); printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n", current->comm, current->pid, - *pc_addr - 4, va, opcode, reg); + regs->pc - 4, va, opcode, reg); unlock_kernel(); } last_time = jiffies; @@ -540,51 +552,19 @@ asmlinkage void do_entUnaUser(void * va, unsigned long opcode, ++unaligned[1].count; unaligned[1].va = (unsigned long)va; - unaligned[1].pc = *pc_addr - 4; + unaligned[1].pc = regs->pc - 4; - reg_addr = frame; if ((1L << opcode) & OP_INT_MASK) { /* it's an integer load/store */ - switch (reg) { - case 0: case 1: case 2: case 3: case 4: - case 5: case 6: case 7: case 8: - /* v0-t7 in SAVE_ALL frame */ - reg_addr += 7 + reg; - break; - - case 9: case 10: case 11: case 12: - case 13: case 14: case 15: - /* s0-s6 in entUna frame */ - reg_addr += (reg - 9); - break; - - case 16: case 17: case 18: - /* a0-a2 in PAL frame */ - reg_addr += 7 + 20 + 3 + (reg - 16); - break; - - case 19: case 20: case 21: case 22: case 23: - case 24: case 25: case 26: case 27: case 28: - /* a3-at in SAVE_ALL frame */ - reg_addr += 7 + 9 + (reg - 19); - break; - - case 29: - /* gp in PAL frame */ - reg_addr += 7 + 20 + 2; - break; - - case 30: + if (reg < 30) { + reg_addr = (unsigned long *) + ((char *)regs + unauser_reg_offsets[reg]); + } else if (reg == 30) { /* usp in PAL regs */ fake_reg = rdusp(); - reg_addr = &fake_reg; - break; - - case 31: + } else { /* zero "register" */ fake_reg = 0; - reg_addr = &fake_reg; - break; } } @@ -728,7 +708,6 @@ asmlinkage void do_entUnaUser(void * va, unsigned long opcode, case 0x26: /* sts */ fake_reg = s_reg_to_mem(alpha_read_fp_reg(reg)); - reg_addr = &fake_reg; /* FALLTHRU */ case 0x2c: /* stl */ @@ -763,7 +742,6 @@ asmlinkage void do_entUnaUser(void * va, unsigned long opcode, case 0x27: /* stt */ fake_reg = alpha_read_fp_reg(reg); - reg_addr = &fake_reg; /* FALLTHRU */ case 0x2d: /* stq */ @@ -807,14 +785,14 @@ asmlinkage void do_entUnaUser(void * va, unsigned long opcode, return; give_sigsegv: - *pc_addr -= 4; /* make pc point to faulting insn */ + regs->pc -= 4; /* make pc point to faulting insn */ lock_kernel(); force_sig(SIGSEGV, current); unlock_kernel(); return; give_sigbus: - *pc_addr -= 4; + regs->pc -= 4; lock_kernel(); force_sig(SIGBUS, current); unlock_kernel(); diff --git a/arch/i386/config.in b/arch/i386/config.in index 887407c61..50d9de2a7 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -23,8 +23,10 @@ comment 'General setup' bool 'Kernel math emulation' CONFIG_MATH_EMULATION bool 'Networking support' CONFIG_NET -bool 'PCI bios support' CONFIG_PCI +bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then + bool ' PCI BIOS support' CONFIG_PCI_BIOS + bool ' PCI direct access support' CONFIG_PCI_DIRECT if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE fi @@ -36,7 +38,7 @@ tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA + tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi choice 'Processor type' \ diff --git a/arch/i386/defconfig b/arch/i386/defconfig index e3e50886c..93f300de7 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -20,6 +20,8 @@ CONFIG_MODULES=y # CONFIG_MATH_EMULATION is not set CONFIG_NET=y CONFIG_PCI=y +CONFIG_PCI_BIOS=y +# CONFIG_PCI_DIRECT is not set # CONFIG_MCA is not set CONFIG_SYSVIPC=y CONFIG_SYSCTL=y @@ -139,7 +141,6 @@ CONFIG_SCSI_OMIT_FLASHPOINT=y # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set -# CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set @@ -160,6 +161,7 @@ CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y # CONFIG_PCNET32 is not set @@ -172,7 +174,6 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set @@ -214,6 +215,7 @@ CONFIG_ISO9660_FS=y # CONFIG_ROMFS_FS is not set CONFIG_AUTOFS_FS=y # CONFIG_UFS_FS is not set +# CONFIG_MAC_PARTITION is not set # # Character devices diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 8f7a796fe..d1d7a85e0 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -1,7 +1,7 @@ /* * bios32.c - BIOS32, PCI BIOS functions. * - * $Id: bios32.c,v 1.1.1.1 1997/06/01 03:16:32 ralf Exp $ + * $Id: bios32.c,v 1.2 1997/07/20 14:57:08 ralf Exp $ * * Sponsored by * iX Multiuser Multitasking Magazine @@ -61,6 +61,9 @@ * * Jun 20, 1997 : Corrected problems in "conf1" type accesses. * (paubert@iram.es) + * + * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts + * and cleaned it up... Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ #include <linux/config.h> @@ -75,69 +78,12 @@ #include <asm/system.h> #include <asm/io.h> -#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX -#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 -#define PCIBIOS_FIND_PCI_DEVICE 0xb102 -#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 -#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 -#define PCIBIOS_READ_CONFIG_BYTE 0xb108 -#define PCIBIOS_READ_CONFIG_WORD 0xb109 -#define PCIBIOS_READ_CONFIG_DWORD 0xb10a -#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b -#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c -#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d - - -/* BIOS32 signature: "_32_" */ -#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) - -/* PCI signature: "PCI " */ -#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) - -/* PCI service signature: "$PCI" */ -#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) - -/* - * This is the standard structure used to identify the entry point - * to the BIOS32 Service Directory, as documented in - * Standard BIOS 32-bit Service Directory Proposal - * Revision 0.4 May 24, 1993 - * Phoenix Technologies Ltd. - * Norwood, MA - * and the PCI BIOS specification. - */ - -union bios32 { - struct { - unsigned long signature; /* _32_ */ - unsigned long entry; /* 32 bit physical address */ - unsigned char revision; /* Revision level, 0 */ - unsigned char length; /* Length in paragraphs should be 01 */ - unsigned char checksum; /* All bytes must add up to zero */ - unsigned char reserved[5]; /* Must be zero */ - } fields; - char chars[16]; -}; - -#ifdef CONFIG_PCI /* - * Physical address of the service directory. I don't know if we're - * allowed to have more than one of these or not, so just in case - * we'll make pcibios_present() take a memory start parameter and store - * the array there. + * Generic PCI access -- indirect calls according to detected HW. */ -static unsigned long bios32_entry = 0; -static struct { - unsigned long address; - unsigned short segment; -} bios32_indirect = { 0, KERNEL_CS }; - - -/* - * function table for accessing PCI configuration space - */ struct pci_access { + int pci_present; int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *); int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *); int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *); @@ -148,363 +94,137 @@ struct pci_access { int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int); }; -/* - * pointer to selected PCI access function table - */ -static struct pci_access *access_pci = NULL; - - - -/* - * Returns the entry point for the given service, NULL on error - */ - -static unsigned long bios32_service(unsigned long service) +static int pci_stub(void) { - unsigned char return_code; /* %al */ - unsigned long address; /* %ebx */ - unsigned long length; /* %ecx */ - unsigned long entry; /* %edx */ - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%edi)" - : "=a" (return_code), - "=b" (address), - "=c" (length), - "=d" (entry) - : "0" (service), - "1" (0), - "D" (&bios32_indirect)); - restore_flags(flags); - - switch (return_code) { - case 0: - return address + entry; - case 0x80: /* Not present */ - printk("bios32_service(0x%lx) : not present\n", service); - return 0; - default: /* Shouldn't happen */ - printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n", - service, return_code); - return 0; - } + return PCIBIOS_FUNC_NOT_SUPPORTED; } -static long pcibios_entry = 0; -static struct { - unsigned long address; - unsigned short segment; -} pci_indirect = { 0, KERNEL_CS }; +static struct pci_access pci_access_none = { + 0, /* No PCI present */ + (void *) pci_stub, /* No functions implemented */ + (void *) pci_stub, + (void *) pci_stub, + (void *) pci_stub, + (void *) pci_stub, + (void *) pci_stub, + (void *) pci_stub, + (void *) pci_stub +}; +static struct pci_access *access_pci = &pci_access_none; -__initfunc(static int check_pcibios(void)) +int pcibios_present(void) { - unsigned long signature; - unsigned char present_status; - unsigned char major_revision; - unsigned char minor_revision; - unsigned long flags; - int pack; - - if ((pcibios_entry = bios32_service(PCI_SERVICE))) { - pci_indirect.address = pcibios_entry | PAGE_OFFSET; - - save_flags(flags); cli(); - __asm__("lcall (%%edi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:\tshl $8, %%eax\n\t" - "movw %%bx, %%ax" - : "=d" (signature), - "=a" (pack) - : "1" (PCIBIOS_PCI_BIOS_PRESENT), - "D" (&pci_indirect) - : "bx", "cx"); - restore_flags(flags); - - present_status = (pack >> 16) & 0xff; - major_revision = (pack >> 8) & 0xff; - minor_revision = pack & 0xff; - if (present_status || (signature != PCI_SIGNATURE)) { - printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n" - " but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n" - " and signature of 0x%08lx (%c%c%c%c). mail drew@Colorado.EDU\n", - (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR", - present_status, signature, - (char) (signature >> 0), (char) (signature >> 8), - (char) (signature >> 16), (char) (signature >> 24)); - - if (signature != PCI_SIGNATURE) - pcibios_entry = 0; - } - if (pcibios_entry) { - printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n", - major_revision, minor_revision, pcibios_entry); - return 1; - } - } - return 0; + return access_pci->pci_present; } - -static int pci_bios_find_class (unsigned int class_code, unsigned short index, +int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *device_fn) { - unsigned long bx; - unsigned long ret; - unsigned long flags; - - save_flags(flags); cli(); - __asm__ ("lcall (%%edi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=b" (bx), - "=a" (ret) - : "1" (PCIBIOS_FIND_PCI_CLASS_CODE), - "c" (class_code), - "S" ((int) index), - "D" (&pci_indirect)); - restore_flags(flags); - *bus = (bx >> 8) & 0xff; - *device_fn = bx & 0xff; - return (int) (ret & 0xff00) >> 8; + return access_pci->find_class(class_code, index, bus, device_fn); } - -static int pci_bios_find_device (unsigned short vendor, unsigned short device_id, +int pcibios_find_device (unsigned short vendor, unsigned short device_id, unsigned short index, unsigned char *bus, unsigned char *device_fn) { - unsigned short bx; - unsigned short ret; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%edi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=b" (bx), - "=a" (ret) - : "1" (PCIBIOS_FIND_PCI_DEVICE), - "c" (device_id), - "d" (vendor), - "S" ((int) index), - "D" (&pci_indirect)); - restore_flags(flags); - *bus = (bx >> 8) & 0xff; - *device_fn = bx & 0xff; - return (int) (ret & 0xff00) >> 8; + return access_pci->find_device(vendor, device_id, index, bus, device_fn); } -static int pci_bios_read_config_byte(unsigned char bus, +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char *value) { - unsigned long ret; - unsigned long bx = (bus << 8) | device_fn; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%esi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=c" (*value), - "=a" (ret) - : "1" (PCIBIOS_READ_CONFIG_BYTE), - "b" (bx), - "D" ((long) where), - "S" (&pci_indirect)); - restore_flags(flags); - return (int) (ret & 0xff00) >> 8; + return access_pci->read_config_byte(bus, device_fn, where, value); } -static int pci_bios_read_config_word (unsigned char bus, +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned short *value) { - unsigned long ret; - unsigned long bx = (bus << 8) | device_fn; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%esi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=c" (*value), - "=a" (ret) - : "1" (PCIBIOS_READ_CONFIG_WORD), - "b" (bx), - "D" ((long) where), - "S" (&pci_indirect)); - restore_flags(flags); - return (int) (ret & 0xff00) >> 8; + return access_pci->read_config_word(bus, device_fn, where, value); } -static int pci_bios_read_config_dword (unsigned char bus, +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned int *value) { - unsigned long ret; - unsigned long bx = (bus << 8) | device_fn; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%esi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=c" (*value), - "=a" (ret) - : "1" (PCIBIOS_READ_CONFIG_DWORD), - "b" (bx), - "D" ((long) where), - "S" (&pci_indirect)); - restore_flags(flags); - return (int) (ret & 0xff00) >> 8; + return access_pci->read_config_dword(bus, device_fn, where, value); } -static int pci_bios_write_config_byte (unsigned char bus, +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char value) { - unsigned long ret; - unsigned long bx = (bus << 8) | device_fn; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%esi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (ret) - : "0" (PCIBIOS_WRITE_CONFIG_BYTE), - "c" (value), - "b" (bx), - "D" ((long) where), - "S" (&pci_indirect)); - restore_flags(flags); - return (int) (ret & 0xff00) >> 8; + return access_pci->write_config_byte(bus, device_fn, where, value); } -static int pci_bios_write_config_word (unsigned char bus, +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned short value) { - unsigned long ret; - unsigned long bx = (bus << 8) | device_fn; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%esi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (ret) - : "0" (PCIBIOS_WRITE_CONFIG_WORD), - "c" (value), - "b" (bx), - "D" ((long) where), - "S" (&pci_indirect)); - restore_flags(flags); - return (int) (ret & 0xff00) >> 8; + return access_pci->write_config_word(bus, device_fn, where, value); } -static int pci_bios_write_config_dword (unsigned char bus, +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned int value) { - unsigned long ret; - unsigned long bx = (bus << 8) | device_fn; - unsigned long flags; - - save_flags(flags); cli(); - __asm__("lcall (%%esi)\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (ret) - : "0" (PCIBIOS_WRITE_CONFIG_DWORD), - "c" (value), - "b" (bx), - "D" ((long) where), - "S" (&pci_indirect)); - restore_flags(flags); - return (int) (ret & 0xff00) >> 8; + return access_pci->write_config_dword(bus, device_fn, where, value); } /* - * function table for BIOS32 access + * Direct access to PCI hardware... */ -static struct pci_access pci_bios_access = { - pci_bios_find_device, - pci_bios_find_class, - pci_bios_read_config_byte, - pci_bios_read_config_word, - pci_bios_read_config_dword, - pci_bios_write_config_byte, - pci_bios_write_config_word, - pci_bios_write_config_dword -}; - - /* * Given the vendor and device ids, find the n'th instance of that device * in the system. */ + +#ifdef CONFIG_PCI_DIRECT + static int pci_direct_find_device (unsigned short vendor, unsigned short device_id, unsigned short index, unsigned char *bus, unsigned char *devfn) { unsigned int curr = 0; struct pci_dev *dev; - unsigned long flags; - save_flags(flags); for (dev = pci_devices; dev; dev = dev->next) { if (dev->vendor == vendor && dev->device == device_id) { if (curr == index) { *devfn = dev->devfn; *bus = dev->bus->number; - restore_flags(flags); return PCIBIOS_SUCCESSFUL; } ++curr; } } - restore_flags(flags); return PCIBIOS_DEVICE_NOT_FOUND; } - /* * Given the class, find the n'th instance of that device * in the system. */ + static int pci_direct_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *devfn) { unsigned int curr = 0; struct pci_dev *dev; - unsigned long flags; - save_flags(flags); cli(); for (dev = pci_devices; dev; dev = dev->next) { if (dev->class == class_code) { if (curr == index) { *devfn = dev->devfn; *bus = dev->bus->number; - restore_flags(flags); return PCIBIOS_SUCCESSFUL; } ++curr; } } - restore_flags(flags); return PCIBIOS_DEVICE_NOT_FOUND; } /* * Functions for accessing PCI configuration space with type 1 accesses */ + #define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3)) static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn, @@ -585,10 +305,8 @@ static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device #undef CONFIG_CMD -/* - * functiontable for type 1 - */ static struct pci_access pci_direct_conf1 = { + 1, pci_direct_find_device, pci_direct_find_class, pci_conf1_read_config_byte, @@ -602,6 +320,7 @@ static struct pci_access pci_direct_conf1 = { /* * Functions for accessing PCI configuration space with type 2 accesses */ + #define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where) #define FUNC(devfn) (((devfn & 7) << 1) | 0xf0) @@ -698,10 +417,8 @@ static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device #undef IOADDR #undef FUNC -/* - * functiontable for type 2 - */ static struct pci_access pci_direct_conf2 = { + 1, pci_direct_find_device, pci_direct_find_class, pci_conf2_read_config_byte, @@ -712,8 +429,7 @@ static struct pci_access pci_direct_conf2 = { pci_conf2_write_config_dword }; - -__initfunc(static struct pci_access *check_direct_pci(void)) +__initfunc(static struct pci_access *pci_check_direct(void)) { unsigned int tmp; unsigned long flags; @@ -721,7 +437,7 @@ __initfunc(static struct pci_access *check_direct_pci(void)) save_flags(flags); cli(); /* - * check if configuration type 1 works + * Check if configuration type 1 works. */ outb (0x01, 0xCFB); tmp = inl (0xCF8); @@ -729,153 +445,385 @@ __initfunc(static struct pci_access *check_direct_pci(void)) if (inl (0xCF8) == 0x80000000) { outl (tmp, 0xCF8); restore_flags(flags); - printk("pcibios_init: Using configuration type 1\n"); + printk("PCI: Using configuration type 1\n"); return &pci_direct_conf1; } outl (tmp, 0xCF8); /* - * check if configuration type 2 works + * Check if configuration type 2 works. */ outb (0x00, 0xCFB); outb (0x00, 0xCF8); outb (0x00, 0xCFA); if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) { restore_flags(flags); - printk("pcibios_init: Using configuration type 2\n"); + printk("PCI: Using configuration type 2\n"); return &pci_direct_conf2; } restore_flags(flags); - printk("pcibios_init: Not supported chipset for direct PCI access !\n"); + printk("PCI: PCI hardware not found (i.e., not present or not supported).\n"); return NULL; } +#endif /* - * access defined pcibios functions via - * the function table + * BIOS32 and PCI BIOS handling. */ -int pcibios_present(void) +#ifdef CONFIG_PCI_BIOS + +#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d + +/* BIOS32 signature: "_32_" */ +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +/* + * This is the standard structure used to identify the entry point + * to the BIOS32 Service Directory, as documented in + * Standard BIOS 32-bit Service Directory Proposal + * Revision 0.4 May 24, 1993 + * Phoenix Technologies Ltd. + * Norwood, MA + * and the PCI BIOS specification. + */ + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +/* + * Physical address of the service directory. I don't know if we're + * allowed to have more than one of these or not, so just in case + * we'll make pcibios_present() take a memory start parameter and store + * the array there. + */ + +static unsigned long bios32_entry = 0; +static struct { + unsigned long address; + unsigned short segment; +} bios32_indirect = { 0, KERNEL_CS }; + +/* + * Returns the entry point for the given service, NULL on error + */ + +static unsigned long bios32_service(unsigned long service) { - return access_pci ? 1 : 0; + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%edi)" + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "D" (&bios32_indirect)); + restore_flags(flags); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + printk("bios32_service(0x%lx): not present\n", service); + return 0; + default: /* Shouldn't happen */ + printk("bios32_service(0x%lx): returned 0x%x, mail drew@colorado.edu\n", + service, return_code); + return 0; + } } -int pcibios_find_class (unsigned int class_code, unsigned short index, +static long pcibios_entry = 0; +static struct { + unsigned long address; + unsigned short segment; +} pci_indirect = { 0, KERNEL_CS }; + +__initfunc(static int check_pcibios(void)) +{ + unsigned long signature; + unsigned char present_status; + unsigned char major_revision; + unsigned char minor_revision; + unsigned long flags; + int pack; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + pci_indirect.address = pcibios_entry | PAGE_OFFSET; + + save_flags(flags); cli(); + __asm__("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:\tshl $8, %%eax\n\t" + "movw %%bx, %%ax" + : "=d" (signature), + "=a" (pack) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "D" (&pci_indirect) + : "bx", "cx"); + restore_flags(flags); + + present_status = (pack >> 16) & 0xff; + major_revision = (pack >> 8) & 0xff; + minor_revision = pack & 0xff; + if (present_status || (signature != PCI_SIGNATURE)) { + printk ("PCI: %s: BIOS32 Service Directory says PCI BIOS is present,\n" + " but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n" + " and signature of 0x%08lx (%c%c%c%c). Mail drew@Colorado.EDU\n", + (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR", + present_status, signature, + (char) (signature >> 0), (char) (signature >> 8), + (char) (signature >> 16), (char) (signature >> 24)); + + if (signature != PCI_SIGNATURE) + pcibios_entry = 0; + } + if (pcibios_entry) { + printk ("PCI: PCI BIOS revision %x.%02x entry at 0x%lx\n", + major_revision, minor_revision, pcibios_entry); + return 1; + } + } + return 0; +} + +static int pci_bios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *device_fn) { - if (access_pci && access_pci->find_class) - return access_pci->find_class(class_code, index, bus, device_fn); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + unsigned long bx; + unsigned long ret; + unsigned long flags; + + save_flags(flags); cli(); + __asm__ ("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_CLASS_CODE), + "c" (class_code), + "S" ((int) index), + "D" (&pci_indirect)); + restore_flags(flags); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; } -int pcibios_find_device (unsigned short vendor, unsigned short device_id, +static int pci_bios_find_device (unsigned short vendor, unsigned short device_id, unsigned short index, unsigned char *bus, unsigned char *device_fn) { - if (access_pci && access_pci->find_device) - return access_pci->find_device(vendor, device_id, index, bus, device_fn); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + unsigned short bx; + unsigned short ret; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_DEVICE), + "c" (device_id), + "d" (vendor), + "S" ((int) index), + "D" (&pci_indirect)); + restore_flags(flags); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; } -int pcibios_read_config_byte (unsigned char bus, +static int pci_bios_read_config_byte(unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char *value) { - if (access_pci && access_pci->read_config_byte) - return access_pci->read_config_byte(bus, device_fn, where, value); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; } -int pcibios_read_config_word (unsigned char bus, +static int pci_bios_read_config_word (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned short *value) { - if (access_pci && access_pci->read_config_word) - return access_pci->read_config_word(bus, device_fn, where, value); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; } -int pcibios_read_config_dword (unsigned char bus, +static int pci_bios_read_config_dword (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned int *value) { - if (access_pci && access_pci->read_config_dword) - return access_pci->read_config_dword(bus, device_fn, where, value); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; } -int pcibios_write_config_byte (unsigned char bus, +static int pci_bios_write_config_byte (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char value) { - if (access_pci && access_pci->write_config_byte) - return access_pci->write_config_byte(bus, device_fn, where, value); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; } -int pcibios_write_config_word (unsigned char bus, +static int pci_bios_write_config_word (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned short value) { - if (access_pci && access_pci->write_config_word) - return access_pci->write_config_word(bus, device_fn, where, value); - - return PCIBIOS_FUNC_NOT_SUPPORTED; -} + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; -int pcibios_write_config_dword (unsigned char bus, - unsigned char device_fn, unsigned char where, unsigned int value) -{ - if (access_pci && access_pci->write_config_dword) - return access_pci->write_config_dword(bus, device_fn, where, value); - - return PCIBIOS_FUNC_NOT_SUPPORTED; + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; } -const char *pcibios_strerror (int error) +static int pci_bios_write_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned int value) { - static char buf[80]; - - switch (error) { - case PCIBIOS_SUCCESSFUL: - return "SUCCESSFUL"; - - case PCIBIOS_FUNC_NOT_SUPPORTED: - return "FUNC_NOT_SUPPORTED"; - - case PCIBIOS_BAD_VENDOR_ID: - return "SUCCESSFUL"; - - case PCIBIOS_DEVICE_NOT_FOUND: - return "DEVICE_NOT_FOUND"; - - case PCIBIOS_BAD_REGISTER_NUMBER: - return "BAD_REGISTER_NUMBER"; - - case PCIBIOS_SET_FAILED: - return "SET_FAILED"; - - case PCIBIOS_BUFFER_TOO_SMALL: - return "BUFFER_TOO_SMALL"; + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; - default: - sprintf (buf, "UNKNOWN RETURN 0x%x", error); - return buf; - } + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; } +/* + * Function table for BIOS32 access + */ -__initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) -{ - return mem_start; -} +static struct pci_access pci_bios_access = { + 1, + pci_bios_find_device, + pci_bios_find_class, + pci_bios_read_config_byte, + pci_bios_read_config_word, + pci_bios_read_config_dword, + pci_bios_write_config_byte, + pci_bios_write_config_word, + pci_bios_write_config_dword +}; -#endif +/* + * Try to find PCI BIOS. + */ -__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)) +__initfunc(static struct pci_access *pci_find_bios(void)) { -#ifdef CONFIG_PCI union bios32 *check; unsigned char sum; int i, length; @@ -884,7 +832,6 @@ __initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long * Follow the standard procedure for locating the BIOS32 Service * directory by scanning the permissible address range from * 0xe0000 through 0xfffff for a valid BIOS32 structure. - * */ for (check = (union bios32 *) __va(0xe0000); @@ -901,24 +848,59 @@ __initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long if (sum != 0) continue; if (check->fields.revision != 0) { - printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n", + printk("PCI: unsupported BIOS32 revision %d at 0x%p, mail drew@colorado.edu\n", check->fields.revision, check); continue; } - printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check); - if (!bios32_entry) { - if (check->fields.entry >= 0x100000) { - printk("pcibios_init: entry in high memory, trying direct PCI access\n"); - access_pci = check_direct_pci(); - } else { - bios32_entry = check->fields.entry; - printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); - bios32_indirect.address = bios32_entry + PAGE_OFFSET; - } + printk ("PCI: BIOS32 Service Directory structure at 0x%p\n", check); + if (check->fields.entry >= 0x100000) { +#ifdef CONFIG_PCI_DIRECT + printk("PCI: BIOS32 entry in high memory, trying direct PCI access.\n"); + return pci_check_direct(); +#else + printk("PCI: BIOS32 entry in high memory, cannot use.\n"); +#endif + } else { + bios32_entry = check->fields.entry; + printk ("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + bios32_indirect.address = bios32_entry + PAGE_OFFSET; + if (check_pcibios()) + return &pci_bios_access; } + break; /* Hopefully more than one BIOS32 cannot happen... */ } - if (bios32_entry && check_pcibios()) - access_pci = &pci_bios_access; + + return NULL; +} + #endif + +/* + * No fixup function used. + */ + +__initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) +{ + return mem_start; +} + +/* + * Initialization. Try all known PCI access methods. + */ + +__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)) +{ + struct pci_access *a = NULL; + +#ifdef CONFIG_PCI_BIOS + a = pci_find_bios(); +#else +#ifdef CONFIG_PCI_DIRECT + a = pci_check_direct(); +#endif +#endif + if (a) + access_pci = a; + return memory_start; } diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index e7b9e0779..21bf04709 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -373,7 +373,7 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_mknod) .long SYMBOL_NAME(sys_chmod) /* 15 */ .long SYMBOL_NAME(sys_chown) - .long SYMBOL_NAME(sys_break) + .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ .long SYMBOL_NAME(sys_stat) .long SYMBOL_NAME(sys_lseek) .long SYMBOL_NAME(sys_getpid) /* 20 */ @@ -387,11 +387,11 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_fstat) .long SYMBOL_NAME(sys_pause) .long SYMBOL_NAME(sys_utime) /* 30 */ - .long SYMBOL_NAME(sys_stty) - .long SYMBOL_NAME(sys_gtty) + .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ .long SYMBOL_NAME(sys_access) .long SYMBOL_NAME(sys_nice) - .long SYMBOL_NAME(sys_ftime) /* 35 */ + .long SYMBOL_NAME(sys_ni_syscall) /* 35 */ /* old ftime syscall holder */ .long SYMBOL_NAME(sys_sync) .long SYMBOL_NAME(sys_kill) .long SYMBOL_NAME(sys_rename) @@ -400,7 +400,7 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_dup) .long SYMBOL_NAME(sys_pipe) .long SYMBOL_NAME(sys_times) - .long SYMBOL_NAME(sys_prof) + .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ .long SYMBOL_NAME(sys_brk) /* 45 */ .long SYMBOL_NAME(sys_setgid) .long SYMBOL_NAME(sys_getgid) @@ -408,13 +408,13 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_geteuid) .long SYMBOL_NAME(sys_getegid) /* 50 */ .long SYMBOL_NAME(sys_acct) - .long SYMBOL_NAME(sys_phys) - .long SYMBOL_NAME(sys_lock) + .long SYMBOL_NAME(sys_ni_syscall) /* old phys syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ .long SYMBOL_NAME(sys_ioctl) .long SYMBOL_NAME(sys_fcntl) /* 55 */ - .long SYMBOL_NAME(sys_mpx) + .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ .long SYMBOL_NAME(sys_setpgid) - .long SYMBOL_NAME(sys_ulimit) + .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ .long SYMBOL_NAME(sys_olduname) .long SYMBOL_NAME(sys_umask) /* 60 */ .long SYMBOL_NAME(sys_chroot) @@ -454,7 +454,7 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_fchown) /* 95 */ .long SYMBOL_NAME(sys_getpriority) .long SYMBOL_NAME(sys_setpriority) - .long SYMBOL_NAME(sys_profil) + .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ .long SYMBOL_NAME(sys_statfs) .long SYMBOL_NAME(sys_fstatfs) /* 100 */ .long SYMBOL_NAME(sys_ioperm) diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index bd4bf56cf..ef2cfa9a2 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -312,15 +312,16 @@ ignore_int: pushl %eax pushl %ecx pushl %edx + push %es push %ds movl $(KERNEL_DS),%eax mov %ax,%ds mov %ax,%es - mov %ax,%fs pushl $int_msg call SYMBOL_NAME(printk) popl %eax pop %ds + pop %es popl %edx popl %ecx popl %eax diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index a711d9a40..b740eb0ce 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -7,6 +7,7 @@ #include <linux/sched.h> #include <linux/in6.h> #include <linux/interrupt.h> +#include <linux/smp_lock.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -49,6 +50,7 @@ EXPORT_SYMBOL_NOVERS(kernel_flag); EXPORT_SYMBOL_NOVERS(active_kernel_processor); EXPORT_SYMBOL(smp_invalidate_needed); EXPORT_SYMBOL_NOVERS(__lock_kernel); +EXPORT_SYMBOL(lk_lockmsg); /* Global SMP irq stuff */ EXPORT_SYMBOL(synchronize_irq); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 2e0f3e084..62d074508 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -42,8 +42,6 @@ extern volatile unsigned long smp_local_timer_ticks[1+NR_CPUS]; #endif -#define CR0_NE 32 - unsigned int local_irq_count[NR_CPUS]; #ifdef __SMP__ atomic_t __intel_bh_counter; @@ -55,6 +53,8 @@ int __intel_bh_counter; static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},}; #endif +atomic_t nmi_counter; + /* * This contains the irq mask for both irq controllers */ @@ -199,7 +199,6 @@ static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } * be shot. */ - static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) { outb(0,0xF0); @@ -224,28 +223,26 @@ static struct irqaction *irq_action[16] = { int get_irq_list(char *buf) { - int i, len = 0; + int i; struct irqaction * action; + char *p = buf; for (i = 0 ; i < NR_IRQS ; i++) { action = irq_action[i]; if (!action) continue; - len += sprintf(buf+len, "%2d: %10u %s", + p += sprintf(p, "%3d: %10u %s", i, kstat.interrupts[i], action->name); for (action=action->next; action; action = action->next) { - len += sprintf(buf+len, ", %s", action->name); + p += sprintf(p, ", %s", action->name); } - len += sprintf(buf+len, "\n"); + *p++ = '\n'; } -/* - * Linus - should you add NMI counts here ????? - */ + p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter)); #ifdef __SMP_PROF__ - len+=sprintf(buf+len, "IPI: %8lu received\n", - ipi_count); + p += sprintf(p, "IPI: %10lu\n", ipi_count); #endif - return len; + return p - buf; } #ifdef __SMP_PROF__ diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index d187128e1..eef030375 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -85,7 +85,7 @@ extern void update_one_process( struct task_struct *p, * 5AP. Remote read is never used * 9AP. XXX NEED TO CHECK WE HANDLE THIS XXX * 10AP. XXX NEED TO CHECK WE HANDLE THIS XXX - * 11AP. Linux read the APIC between writes to avoid this, as per + * 11AP. Linux reads the APIC between writes to avoid this, as per * the documentation. Make sure you preserve this as it affects * the C stepping chips too. * @@ -887,10 +887,14 @@ __initfunc(void smp_boot_cpus(void)) /* * Initialize the logical to physical cpu number mapping + * and the per-CPU profiling counter/multiplier */ - for (i = 0; i < NR_CPUS; i++) + for (i = 0; i < NR_CPUS; i++) { cpu_number_map[i] = -1; + prof_counter[i] = 1; + prof_multiplier[i] = 1; + } /* * Setup boot CPU information @@ -917,15 +921,8 @@ __initfunc(void smp_boot_cpus(void)) * of here now! */ - if (!smp_found_config) { - /* - * For SMP-simulation on one CPU to work, we must initialize these - * values for the single CPU here: - */ - prof_counter[0] = prof_multiplier[0] = 1; - + if (!smp_found_config) return; - } /* * Map the local APIC into kernel space @@ -1625,7 +1622,6 @@ static unsigned int calibration_result; __initfunc(void setup_APIC_clock (void)) { - int cpu = smp_processor_id(); unsigned long flags; static volatile int calibration_lock; @@ -1658,11 +1654,10 @@ __initfunc(void setup_APIC_clock (void)) } /* - * Now set up the timer for real. Profiling multiplier is 1. + * Now set up the timer for real. */ - setup_APIC_timer (calibration_result); - prof_counter[cpu] = prof_multiplier[cpu] = 1; + setup_APIC_timer (calibration_result); /* * We ACK the APIC, just in case there is something pending. diff --git a/arch/i386/kernel/sys_i386.c b/arch/i386/kernel/sys_i386.c index b1eb2911d..f43dfe726 100644 --- a/arch/i386/kernel/sys_i386.c +++ b/arch/i386/kernel/sys_i386.c @@ -17,6 +17,7 @@ #include <linux/stat.h> #include <linux/mman.h> #include <linux/file.h> +#include <linux/utsname.h> #include <asm/uaccess.h> #include <asm/ipc.h> @@ -203,3 +204,45 @@ out: unlock_kernel(); return ret; } + +/* + * Old cruft + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + return 0; + return -EFAULT; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + error = error ? -EFAULT : 0; + + return error; +} + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index b397fc76d..6fb18dc35 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -29,6 +29,7 @@ #include <asm/uaccess.h> #include <asm/io.h> #include <asm/spinlock.h> +#include <asm/atomic.h> asmlinkage int system_call(void); asmlinkage void lcall7(void); @@ -271,7 +272,9 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { unsigned char reason = inb(0x61); + extern atomic_t nmi_counter; + atomic_inc(&nmi_counter); if (reason & 0x80) mem_parity_error(reason, regs); if (reason & 0x40) @@ -348,7 +351,10 @@ asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, long error_code) { +#if 0 + /* No need to warn about this any longer. */ printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); +#endif } /* diff --git a/arch/i386/lib/checksum.c b/arch/i386/lib/checksum.c index 4a0f82440..2293a6c5e 100644 --- a/arch/i386/lib/checksum.c +++ b/arch/i386/lib/checksum.c @@ -111,13 +111,13 @@ unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) #define SRC(y...) \ " 9999: "#y"; \n \ .section __ex_table, \"a\"; \n \ - .long 9999b, src_access_fault \n \ + .long 9999b, 6001f \n \ .previous" #define DST(y...) \ " 9999: "#y"; \n \ .section __ex_table, \"a\"; \n \ - .long 9999b, dst_access_fault \n \ + .long 9999b, 6002f \n \ .previous" unsigned int csum_partial_copy_generic (const char *src, char *dst, @@ -203,28 +203,28 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst, adcl $0, %%eax 7: -end_of_body: +5000: # Exception handler: ################################################ # .section .fixup, \"a\" # # -common_fixup: # +6000: # # movl %7, (%%ebx) # # # FIXME: do zeroing of rest of the buffer here. # # - jmp end_of_body # + jmp 5000b # # -src_access_fault: # +6001: # movl %1, %%ebx # - jmp common_fixup # + jmp 6000b # # -dst_access_fault: # +6002: # movl %2, %%ebx # - jmp common_fixup # + jmp 6000b # # .previous # # diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index e4847c070..8181fd6c7 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -186,16 +186,15 @@ bad_area: * * First we check if it was the bootup rw-test, though.. */ - if (wp_works_ok < 0 && address == 0xc0000000 && (error_code & 1)) { + if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) { wp_works_ok = 1; - pg0[0] = pte_val(mk_pte(0, PAGE_SHARED)); + pg0[0] = pte_val(mk_pte(TASK_SIZE, PAGE_SHARED)); flush_tlb(); goto out; } - if (address < PAGE_SIZE) { + if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - pg0[0] = pte_val(mk_pte(0, PAGE_SHARED)); - } else + else printk(KERN_ALERT "Unable to handle kernel paging request"); printk(" at virtual address %08lx\n",address); __asm__("movl %%cr3,%0" : "=r" (page)); diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 8f2852a48..6ed47e2ef 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -261,6 +261,43 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ return free_area_init(start_mem, end_mem); } +/* + * Test if the WP bit works in supervisor mode. It isn't supported on 386's + * and also on some strange 486's (NexGen etc.). All 586+'s are OK. The jumps + * before and after the test are here to work-around some nasty CPU bugs. + */ + +__initfunc(void test_wp_bit(void)) +{ + unsigned char tmp_reg; + unsigned long old = pg0[0]; + + printk("Checking if this processor honours the WP bit even in supervisor mode... "); + pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY)); + local_flush_tlb(); + current->mm->mmap->vm_start += PAGE_SIZE; + __asm__ __volatile__( + "jmp 1f; 1:\n" + "movb %0,%1\n" + "movb %1,%0\n" + "jmp 1f; 1:\n" + :"=m" (*(char *) __va(0)), + "=q" (tmp_reg) + :/* no inputs */ + :"memory"); + pg0[0] = old; + local_flush_tlb(); + current->mm->mmap->vm_start -= PAGE_SIZE; + if (wp_works_ok < 0) { + wp_works_ok = 0; + printk("No.\n"); +#ifndef CONFIG_M386 + panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!"); +#endif + } else + printk("Ok.\n"); +} + __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) { unsigned long start_low_mem = PAGE_SIZE; @@ -339,30 +376,9 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) reservedpages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10)); -/* test if the WP bit is honoured in supervisor mode */ - if (wp_works_ok < 0) { - unsigned char tmp_reg; - unsigned long old = pg0[0]; - printk("Checking if this processor honours the WP bit even in supervisor mode... "); - pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY)); - local_flush_tlb(); - current->mm->mmap->vm_start += PAGE_SIZE; - __asm__ __volatile__( - "movb %0,%1 ; movb %1,%0" - :"=m" (*(char *) __va(0)), - "=q" (tmp_reg) - :/* no inputs */ - :"memory"); - pg0[0] = old; - local_flush_tlb(); - current->mm->mmap->vm_start -= PAGE_SIZE; - if (wp_works_ok < 0) { - wp_works_ok = 0; - printk("No.\n"); - } else - printk("Ok.\n"); - } - return; + + if (wp_works_ok < 0) + test_wp_bit(); } void free_initmem(void) diff --git a/arch/m68k/config.in b/arch/m68k/config.in index 2390fb6fc..25d6ded04 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -52,9 +52,7 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC -fi +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_AMIGA" = "y" ]; then bool 'Amiga AutoConfig Identification' CONFIG_ZORRO diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 67d87c1b9..a4fc10ab0 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile @@ -13,7 +13,7 @@ all: kernel.o head.o O_TARGET := kernel.o O_OBJS := entry.o process.o traps.o ints.o signal.o ptrace.o \ - setup.o bios32.o sys_m68k.o time.o + setup.o sys_m68k.o time.o ifdef CONFIG_VT O_OBJS += console.o endif diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 27883a86e..f07a6a038 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -65,7 +65,7 @@ asmlinkage int sys_idle(void) current->priority = -100; current->counter = -100; for (;;){ - if (!need_resched) + if (!resched_needed()) #if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC) /* block out HSYNC on the atari (falcon) */ __asm__("stop #0x2200" : : : "cc"); diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index be4149cbb..6b660118a 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -526,7 +526,7 @@ asmlinkage void syscall_trace(void) goto out; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index 52c13445d..ab887801c 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -441,7 +441,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs) regs->pc -= 2; } } - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) { discard_frame: @@ -489,7 +489,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs) current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 516baec29..b1df65c86 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -13,21 +13,21 @@ # Copyright (C) 1994, 1995, 1996 by Ralf Baechle # DECStation modifications by Paul M. Antoine, 1996 # -# $Id: Makefile,v 1.5 1997/07/01 08:54:40 ralf Exp $ +# $Id: Makefile,v 1.7 1997/08/30 04:51:27 ralf Exp $ # # # Select the object file format to substitute into the linker script. # ifdef CONFIG_CPU_LITTLE_ENDIAN -CROSS_COMPILE = mipsel-linux- +tool-prefix = mipsel-linux- ifdef CONFIG_MIPS_ECOFF oformat = ecoff-littlemips else oformat = elf32-littlemips endif else -CROSS_COMPILE = mips-linux- +tool-prefix = mips-linux- ifdef CONFIG_MIPS_ECOFF oformat = ecoff-bigmips else @@ -35,6 +35,10 @@ oformat = elf32-bigmips endif endif +ifdef CONFIG_CROSSCOMPILE +CROSS_COMPILE = $(tool-prefix) +endif + LINKFLAGS = -static -N MODFLAGS += -mlong-calls diff --git a/arch/mips/config.in b/arch/mips/config.in index 35903797a..9e820f29e 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -21,10 +21,6 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then fi bool 'Support for Mips Magnum 4000' CONFIG_MIPS_MAGNUM_4000 bool 'Support for Olivetti M700-10' CONFIG_OLIVETTI_M700 -if [ "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \ - "$CONFIG_OLIVETTI_M700" = "y" ]; then - define_bool CONFIG_VIDEO_G364 y -fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Support for SGI workstations' CONFIG_SGI if [ "$CONFIG_SGI" = "y" ]; then @@ -32,6 +28,21 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then fi fi bool 'Support for SNI RM200 PCI' CONFIG_SNI_RM200_PCI + +# +# Select some configuration options automatically for certain systems. +# +unset CONFIG_PCI +unset CONFIG_MIPS_JAZZ +unset CONFIG_VIDEO_G364 + +if [ "$CONFIG_ALGOR_P4032" = "y" ]; then + define_bool CONFIG_PCI y +fi +if [ "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \ + "$CONFIG_OLIVETTI_M700" = "y" ]; then + define_bool CONFIG_VIDEO_G364 y +fi if [ "$CONFIG_DESKSTATION_RPC44" = "y" -o \ "$CONFIG_DESKSTATION_TYNE" = "y" ]; then define_bool CONFIG_MIPS_ARC y @@ -41,9 +52,7 @@ if [ "$CONFIG_ACER_PICA_61" = "y" -o \ "$CONFIG_MIPS_MAGNUM_4000" = "y" ]; then define_bool CONFIG_MIPS_JAZZ y fi -unset CONFIG_PCI -if [ "$CONFIG_SNI_RM200_PCI" = "y" -o \ - "$CONFIG_ALGOR_P4032" = "y" ]; then +if [ "$CONFIG_SNI_RM200_PCI" = "y" ]; then define_bool CONFIG_PCI y fi endmenu @@ -70,15 +79,19 @@ else define_bool CONFIG_ELF_KERNEL y bool 'Generate little endian code' CONFIG_CPU_LITTLE_ENDIAN fi + if [ "$CONFIG_CPU_LITTLE_ENDIAN" = "n" ]; then define_bool CONFIG_BINFMT_IRIX y define_bool CONFIG_FORWARD_KEYBOARD y fi -define_bool CONFIG_BINFMT_ELF y define_bool CONFIG_BINFMT_AOUT n +define_bool CONFIG_BINFMT_ELF y +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi + bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL @@ -208,6 +221,10 @@ mainmenu_option next_comment comment 'Kernel hacking' #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Are you using a crosscompiler' CONFIG_CROSSCOMPILE +if [ "$CONFIG_MODULES" = "y" ]; then + bool ' Build fp execption handler module' CONFIG_MIPS_FPE_MODULE +fi bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then diff --git a/arch/mips/dec/decstation.S b/arch/mips/dec/decstation.S index a5a4f9b6d..612fcea0b 100644 --- a/arch/mips/dec/decstation.S +++ b/arch/mips/dec/decstation.S @@ -14,6 +14,8 @@ * the end hasn't been modified to suit the DECStation's interrupts. * (Paul, you need to fix this file to comply with NAPS. Won't be * too hard - Ralf) + * + * $Id:$ */ #include <asm/asm.h> #include <asm/mipsconfig.h> @@ -223,12 +225,12 @@ loc_scsi: PANIC("Unimplemented loc_scsi handler") * comment - Ralf) * Andy, 6/16/95 */ -loc_ethernet: PANIC("Unimplemented loc_ethernet\n") +loc_ethernet: PANIC("Unimplemented loc_ethernet") /* * Keyboard interrupt, remapped to level 1 */ -loc_keyboard: PANIC("Unimplemented loc_keyboard\n") +loc_keyboard: PANIC("Unimplemented loc_keyboard") loc_mouse: PANIC("Unimplemented loc_mouse handler") @@ -291,7 +293,7 @@ ll_rtc: PANIC("Unimplemented RTC interrupt handler") * Timer IRQ * We remap the timer irq to be more similar to a IBM compatible */ -ll_timer: PANIC("Timer interrupt!\n"); +ll_timer: PANIC("Timer interrupt!"); /* * CPU count/compare IRQ (unused) */ diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S index 9191574d2..1706dc975 100644 --- a/arch/mips/dec/int-handler.S +++ b/arch/mips/dec/int-handler.S @@ -11,6 +11,8 @@ * * FIXME: still plenty to do in this file, as much of the code hasn't been * modified to suit the DECStation's interrupts. + * + * $Id:$ */ #include <asm/asm.h> #include <asm/regdef.h> @@ -114,12 +116,12 @@ loc_scsi: PANIC("Unimplemented loc_scsi handler") * Only god knows why... Tell me if you find the reason! * Andy, 6/16/95 */ -loc_ethernet: PANIC("Unimplemented loc_ethernet\n") +loc_ethernet: PANIC("Unimplemented loc_ethernet") /* * Keyboard interrupt, remapped to level 1 */ -loc_keyboard: PANIC("Unimplemented loc_keyboard\n") +loc_keyboard: PANIC("Unimplemented loc_keyboard") loc_mouse: PANIC("Unimplemented loc_mouse handler") @@ -182,7 +184,7 @@ ll_rtc: PANIC("Unimplemented RTC interrupt handler") * Timer IRQ * We remap the timer irq to be more similar to a IBM compatible */ -ll_timer: PANIC("Timer interrupt!\n"); +ll_timer: PANIC("Timer interrupt!"); /* * CPU count/compare IRQ (unused) */ diff --git a/arch/mips/defconfig b/arch/mips/defconfig index b1892eba6..ae3ba1f9d 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -33,12 +33,13 @@ CONFIG_CPU_R4X00=y # CONFIG_ELF_KERNEL=y CONFIG_CPU_LITTLE_ENDIAN=y -CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set CONFIG_NET=y CONFIG_SYSVIPC=y CONFIG_SYSCTL=y -# CONFIG_PARPORT is not set +# CONFIG_PNP_PARPORT is not set # # Loadable module support @@ -121,6 +122,7 @@ CONFIG_MIPS_JAZZ_SONIC=y # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=y @@ -133,7 +135,6 @@ CONFIG_PCNET32=y # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set @@ -177,6 +178,7 @@ CONFIG_ISO9660_FS=y # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_MAC_PARTITION is not set # # Character devices @@ -192,6 +194,7 @@ CONFIG_SERIAL=y # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set # @@ -202,5 +205,6 @@ CONFIG_SERIAL=y # # Kernel hacking # +# CONFIG_CROSSCOMPILE is not set # CONFIG_REMOTE_DEBUG is not set # CONFIG_PROFILE is not set diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 21dd3610b..b3a550177 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -18,6 +18,10 @@ O_OBJS := branch.o process.o signal.o entry.o traps.o ptrace.o vm86.o \ r2300_misc.o r2300_scall.o r2300_fpu.o r6000_fpu.o unaligned.o OX_OBJS := mips_ksyms.o +ifdef CONFIG_MIPS_FPE_MODULE +M_OBJS += fpe.o +endif + # # SGI's have very different interrupt/timer hardware. # diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index 00c321a35..c955b964e 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -6,6 +6,8 @@ * for more details. * * Copyright (C) 1994, 1995 by Ralf Baechle + * + * $Id: entry.S,v 1.2 1997/08/11 04:21:31 ralf Exp $ */ /* @@ -38,24 +40,17 @@ .set noreorder .set mips3 .align 4 -/* XXX cli/sti ??? */ handle_bottom_half: - mfc0 s3,CP0_STATUS # Enable IRQs - ori t0,s3, 0x1f - xori t0,0x1e - jal do_bottom_half - mtc0 t0,CP0_STATUS - + nop b 9f - mtc0 s3,CP0_STATUS # Restore old IRQ state + nop -reschedule: - jal schedule +reschedule: jal schedule nop EXPORT(ret_from_sys_call) - lw t0,bh_mask + lw t0,bh_mask lw t1,bh_active # unused delay slot and t0,t1 bnez t0,handle_bottom_half diff --git a/arch/mips/kernel/fpe.c b/arch/mips/kernel/fpe.c new file mode 100644 index 000000000..46ff43bb4 --- /dev/null +++ b/arch/mips/kernel/fpe.c @@ -0,0 +1,55 @@ +/* + * The real floating point exception handler. While it doesn't really + * make sense to have this in a module, it makes debugging of this code + * in the kernel space a lot easier. So far this handler in the released + * kernel source is just a dummy. + * + * Copyright (C) 1997 Ralf Baechle + * + * $Id:$ + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/branch.h> +#include <asm/ptrace.h> + +MODULE_AUTHOR("Ralf Baechle <ralf@gnu.ai.mit.edu>"); +MODULE_DESCRIPTION("Experimental floating point exception handler"); +MODULE_SUPPORTED_DEVICE("MIPS FPU"); + +static void do_fpe(struct pt_regs *regs, unsigned int fcr31) +{ + lock_kernel(); +#ifdef CONF_DEBUG_EXCEPTIONS + show_regs(regs); +#endif + printk("Caught floating exception at epc == %08lx, fcr31 == %08x\n", + regs->cp0_epc, fcr31); + if (compute_return_epc(regs)) + goto out; + force_sig(SIGFPE, current); +out: + unlock_kernel(); +} + +/* + * For easier experimentation we never increment/decrement + * the module useable counter. + */ +int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); +int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); + +int init_module(void) +{ + return register_fpe(do_fpe); +} + +void cleanup_module(void) +{ + unregister_fpe(do_fpe); +} diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index b2d112373..89ff7a3b0 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -1,17 +1,10 @@ /* - * linux/arch/mips/kernel/irq.c + * Code to handle x86 style IRQs plus some generic interrupt stuff. * - * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle * - * This file contains the code used by various IRQ handling routines: - * asking for different IRQ's should be done through these routines - * instead of just grabbing them. Thus setups with different IRQ numbers - * shouldn't result in any weird surprises, and installing new handlers - * should be easier. - * - * Mips support by Ralf Baechle and Andreas Busse - * - * $Id: irq.c,v 1.2 1997/07/01 08:59:07 ralf Exp $ + * $Id: irq.c,v 1.4 1997/09/11 20:35:50 ralf Exp $ */ #include <linux/config.h> #include <linux/errno.h> @@ -30,13 +23,9 @@ #include <asm/bootinfo.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/jazz.h> #include <asm/mipsregs.h> #include <asm/system.h> #include <asm/vector.h> -#ifdef CONFIG_SGI -#include <asm/sgialib.h> -#endif unsigned char cache_21 = 0xff; unsigned char cache_A1 = 0xff; @@ -51,9 +40,6 @@ unsigned long spurious_count = 0; static inline void mask_irq(unsigned int irq_nr) { unsigned char mask; - - if (irq_nr >= 16) - return; mask = 1 << (irq_nr & 7); if (irq_nr < 8) { @@ -69,9 +55,6 @@ static inline void unmask_irq(unsigned int irq_nr) { unsigned char mask; - if (irq_nr >= 16) - return; - mask = ~(1 << (irq_nr & 7)); if (irq_nr < 8) { cache_21 &= mask; @@ -139,13 +122,6 @@ int get_irq_list(char *buf) atomic_t __mips_bh_counter; -#ifdef __SMP__ -#error Send superfluous SMP boxes to ralf@uni-koblenz.de -#else -#define irq_enter(cpu, irq) (++local_irq_count[cpu]) -#define irq_exit(cpu, irq) (--local_irq_count[cpu]) -#endif - /* * do_IRQ handles IRQ's that have been installed without the * SA_INTERRUPT flag: it uses the full signal-handling return @@ -255,7 +231,7 @@ int request_irq(unsigned int irq, int retval; struct irqaction * action; - if (irq > 31) + if (irq >= 32) return -EINVAL; if (!handler) return -EINVAL; diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index d6657ac89..83712649c 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -7,7 +7,7 @@ * * Copyright (C) 1996, 1997 by Ralf Baechle * - * $Id: mips_ksyms.c,v 1.2 1997/07/26 19:31:40 ralf Exp $ + * $Id: mips_ksyms.c,v 1.2 1997/07/29 03:58:54 ralf Exp $ */ #include <linux/config.h> #include <linux/module.h> @@ -71,3 +71,19 @@ EXPORT_SYMBOL(port_base); #ifdef CONFIG_SGI EXPORT_SYMBOL(hpc3c0); #endif + +/* + * Kernel hacking ... + */ +#include <asm/branch.h> +#include <linux/sched.h> + +int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); +int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); + +#ifdef CONFIG_MIPS_FPE_MODULE +EXPORT_SYMBOL(force_sig); +EXPORT_SYMBOL(__compute_return_epc); +EXPORT_SYMBOL(register_fpe); +EXPORT_SYMBOL(unregister_fpe); +#endif diff --git a/arch/mips/kernel/pci.c b/arch/mips/kernel/pci.c index 5e71233af..25ea56fdd 100644 --- a/arch/mips/kernel/pci.c +++ b/arch/mips/kernel/pci.c @@ -82,32 +82,6 @@ int pcibios_find_class (unsigned int class_code, unsigned short index, return PCIBIOS_DEVICE_NOT_FOUND; } -const char *pcibios_strerror (int error) -{ - static char buf[80]; - - switch (error) { - case PCIBIOS_SUCCESSFUL: - return "SUCCESSFUL"; - - case PCIBIOS_FUNC_NOT_SUPPORTED: - return "FUNC_NOT_SUPPORTED"; - - case PCIBIOS_BAD_VENDOR_ID: - return "SUCCESSFUL"; - - case PCIBIOS_DEVICE_NOT_FOUND: - return "DEVICE_NOT_FOUND"; - - case PCIBIOS_BAD_REGISTER_NUMBER: - return "BAD_REGISTER_NUMBER"; - - default: - sprintf (buf, "UNKNOWN RETURN 0x%x", error); - return buf; - } -} - /* * The functions below are machine specific and must be reimplented for * each PCI chipset configuration. We just run the hook to the machine diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 97a11abc4..b5a5a4428 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -59,12 +59,15 @@ repeat: goto repeat; } page = pte_page(*pgtable); -/* this is a hack for non-kernel-mapped video buffers and similar */ + /* This is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) >= MAP_NR(high_memory)) return 0; page += addr & ~PAGE_MASK; + /* We can't use flush_page_to_ram() since we're running in + * another context ... + */ + flush_cache_all(); retval = *(unsigned long *) page; - flush_page_to_ram(page); return retval; } @@ -117,14 +120,17 @@ repeat: handle_mm_fault(tsk, vma, addr, 1); goto repeat; } -/* this is a hack for non-kernel-mapped video buffers and similar */ - flush_cache_page(vma, addr); - if (MAP_NR(page) < MAP_NR(high_memory)) { - *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + /* This is a hack for non-kernel-mapped video buffers and similar */ + if (MAP_NR(page) < MAP_NR(high_memory)) + flush_cache_page(vma, addr); + *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + if (MAP_NR(page) < MAP_NR(high_memory)) flush_page_to_ram(page); - } -/* we're bypassing pagetables, so we have to set the dirty bit ourselves */ -/* this should also re-instate whatever read-only mode there was before */ + /* + * We're bypassing pagetables, so we have to set the dirty bit + * ourselves this should also re-instate whatever read-only mode + * there was before + */ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); flush_tlb_page(vma, addr); } @@ -369,7 +375,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case 5: tmp = child->tss.fpu.hard.control; break; - case 6: + case 6: /* implementation / version register */ tmp = 0; break; default: diff --git a/arch/mips/kernel/r4k_misc.S b/arch/mips/kernel/r4k_misc.S index 0cbc2222f..090b3109f 100644 --- a/arch/mips/kernel/r4k_misc.S +++ b/arch/mips/kernel/r4k_misc.S @@ -1,10 +1,12 @@ -/* $Id: r4k_misc.S,v 1.8 1996/07/22 22:32:52 dm Exp $ +/* * r4k_misc.S: Misc. exception handling code for r4k. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse * * Multi-cpu abstraction and reworking: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r4k_misc.S,v 1.2 1997/06/09 06:20:52 ralf Exp $ */ #include <asm/asm.h> #include <asm/current.h> @@ -21,7 +23,7 @@ #include <asm/regdef.h> #include <asm/stackframe.h> -#define NOTLB_OPTIMIZE /* If you are paranoid, define this. */ +#undef NOTLB_OPTIMIZE /* If you are paranoid, define this. */ /* ABUSE of CPP macros 101. */ @@ -108,10 +110,32 @@ .set noreorder .set mips3 - /* Note for many R4k variants tlb probes cannot be executed out +/* + * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: + * 2. A timing hazard exists for the TLBP instruction. + * + * stalling_instruction + * TLBP + * + * The JTLB is being read for the TLBP throughout the stall generated by the + * previous instruction. This is not really correct as the stalling instruction + * can modify the address used to access the JTLB. The failure symptom is that + * the TLBP instruction will use an address created for the stalling instruction + * and not the address held in C0_ENHI and thus report the wrong results. + * + * The software work-around is to not allow the instruction preceding the TLBP + * to stall - make it an NOP or some other instruction guaranteed not to stall. + * + * Errata 2 will not be fixed. This errata is also on the R5000. + * + * As if we MIPS hackers wouldn't know how to nop pipelines happy ... + */ +#define R5K_HAZARD nop + + /* + * Note for many R4k variants tlb probes cannot be executed out * of the instruction cache else you get bogus results. */ - .align 5 NESTED(r4k_handle_tlbl, PT_SIZE, sp) .set noat @@ -120,6 +144,7 @@ invalid_tlbl: #ifndef NOTLB_OPTIMIZE /* Test present bit in entry. */ LOAD_PTE(k0, k1) + R5K_HAZARD tlbp PTE_PRESENT(k0, k1, nopage_tlbl) PTE_MAKEVALID(k0, k1) @@ -141,6 +166,7 @@ nopage_tlbl: .set noat #ifndef NOTLB_OPTIMIZE LOAD_PTE(k0, k1) + R5K_HAZARD tlbp # find faulting entry PTE_WRITABLE(k0, k1, nopage_tlbs) PTE_MAKEWRITE(k0, k1) @@ -162,6 +188,7 @@ nopage_tlbs: .set noat #ifndef NOTLB_OPTIMIZE LOAD_PTE(k0, k1) + R5K_HAZARD tlbp # find faulting entry andi k0, k0, _PAGE_WRITE beqz k0, nowrite_mod diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 1b00365a6..3ebf269f5 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -4,6 +4,8 @@ * Copyright (C) 1995 Linus Torvalds * Copyright (C) 1995, 1996 Ralf Baechle * Copyright (C) 1996 Stoned Elipot + * + * $Id:$ */ #include <linux/config.h> #include <linux/errno.h> @@ -168,9 +170,10 @@ __initfunc(void setup_arch(char **cmdline_p, break; #endif #if defined(CONFIG_MIPS_ARC) -/* Perhaps arch/mips/deskstation should be renommed arch/mips/arc. - * For now CONFIG_MIPS_ARC means DeskStation. -Stoned. - */ + /* + * Perhaps arch/mips/deskstation should be renamed to arch/mips/arc. + * For now CONFIG_MIPS_ARC means DeskStation. -Stoned. + */ case MACH_GROUP_ARC: deskstation_setup(); break; @@ -196,9 +199,6 @@ __initfunc(void setup_arch(char **cmdline_p, atag = bi_TagFind(tag_drive_info); memcpy(&drive_info, TAGVALPTR(atag), atag->size); -#if 0 - aux_device_present = AUX_DEVICE_INFO; -#endif memory_end = mips_memory_upper; /* diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 34ed87fb6..b2e76e8a7 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -4,7 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, 1995, 1996 Ralf Baechle * - * $Id: signal.c,v 1.3 1997/06/25 20:08:49 ralf Exp $ + * $Id: signal.c,v 1.4 1997/08/06 19:15:07 miguel Exp $ */ #include <linux/config.h> #include <linux/sched.h> @@ -42,7 +42,7 @@ asmlinkage int sys_sigsuspend(struct pt_regs *regs) unsigned long mask; sigset_t *uset, set; - uset = (sigset_t *)(long) regs->regs[4]; + uset = (sigset_t *) regs->regs[4]; if (get_user(set, uset)) return -EFAULT; @@ -392,3 +392,13 @@ asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler) { return -ENOSYS; } + +/* + * Compatibility syscall. Can be replaced in libc. + */ +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index f1b117a63..0a190e3ab 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -21,6 +21,7 @@ #include <linux/smp_lock.h> #include <linux/mman.h> #include <linux/sched.h> +#include <linux/utsname.h> #include <linux/unistd.h> #include <asm/branch.h> #include <asm/ptrace.h> @@ -149,6 +150,43 @@ out: } /* + * Compacrapability ... + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + return 0; + return -EFAULT; +} + +/* + * Compacrapability ... + */ +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + error = error ? -EFAULT : 0; + + return error; +} + +/* * Do the indirect syscall syscall. * Don't care about kernel locking; the actual syscall will do it. */ diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h index fd1b39bca..3232af01c 100644 --- a/arch/mips/kernel/syscalls.h +++ b/arch/mips/kernel/syscalls.h @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * - * $Id: syscalls.h,v 1.5 1997/07/20 14:57:27 ralf Exp $ + * $Id: syscalls.h,v 1.6 1997/08/06 19:15:08 miguel Exp $ */ /* @@ -35,7 +35,7 @@ SYS(sys_time, 1) SYS(sys_mknod, 3) SYS(sys_chmod, 2) /* 4015 */ SYS(sys_chown, 3) -SYS(sys_break, 0) +SYS(sys_ni_syscall, 0) SYS(sys_stat, 2) SYS(sys_lseek, 3) SYS(sys_getpid, 0) /* 4020 */ @@ -49,11 +49,11 @@ SYS(sys_alarm, 1) SYS(sys_fstat, 2) SYS(sys_pause, 0) SYS(sys_utime, 2) /* 4030 */ -SYS(sys_stty, 0) -SYS(sys_gtty, 0) +SYS(sys_ni_syscall, 0) +SYS(sys_ni_syscall, 0) SYS(sys_access, 2) SYS(sys_nice, 1) -SYS(sys_ftime, 0) /* 4035 */ +SYS(sys_ni_syscall, 0) /* 4035 */ SYS(sys_sync, 0) SYS(sys_kill, 2) SYS(sys_rename, 2) @@ -62,7 +62,7 @@ SYS(sys_rmdir, 1) /* 4040 */ SYS(sys_dup, 1) SYS(sys_pipe, 0) SYS(sys_times, 1) -SYS(sys_prof, 0) +SYS(sys_ni_syscall, 0) SYS(sys_brk, 1) /* 4045 */ SYS(sys_setgid, 1) SYS(sys_getgid, 0) @@ -70,13 +70,13 @@ SYS(sys_signal, 2) SYS(sys_geteuid, 0) SYS(sys_getegid, 0) /* 4050 */ SYS(sys_acct, 0) -SYS(sys_phys, 0) -SYS(sys_lock, 0) +SYS(sys_ni_syscall, 0) +SYS(sys_ni_syscall, 0) SYS(sys_ioctl, 3) SYS(sys_fcntl, 3) /* 4055 */ -SYS(sys_mpx, 2) +SYS(sys_ni_syscall, 2) SYS(sys_setpgid, 2) -SYS(sys_ulimit, 0) +SYS(sys_ni_syscall, 0) SYS(sys_olduname, 1) SYS(sys_umask, 1) /* 4060 */ SYS(sys_chroot, 1) @@ -116,7 +116,7 @@ SYS(sys_fchmod, 2) SYS(sys_fchown, 3) /* 4095 */ SYS(sys_getpriority, 2) SYS(sys_setpriority, 3) -SYS(sys_profil, 0) +SYS(sys_ni_syscall, 0) SYS(sys_statfs, 2) SYS(sys_fstatfs, 2) /* 4100 */ SYS(sys_ioperm, 3) diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index b7ceea080..c38fcf549 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -12,8 +12,6 @@ * to mainly kill the offending process (probably by giving it a signal, * but possibly by killing it outright if necessary). * - * FIXME: This is the place for a fpu emulator. - * * Modified for R3000 by Paul M. Antoine, 1995, 1996 */ #include <linux/config.h> @@ -228,8 +226,34 @@ out: unlock_kernel(); } +#ifdef CONFIG_MIPS_FPE_MODULE +static void (*fpe_handler)(struct pt_regs *regs, unsigned int fcr31); + +/* + * Register_fpe/unregister_fpe are for debugging purposes only. To make + * this hack work a bit better there is no error checking. + */ +int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)) +{ + fpe_handler = handler; + return 0; +} + +int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)) +{ + fpe_handler = NULL; + return 0; +} +#endif + void do_fpe(struct pt_regs *regs, unsigned int fcr31) { +#ifdef CONFIG_MIPS_FPE_MODULE + if (fpe_handler != NULL) { + fpe_handler(regs, fcr31); + return; + } +#endif lock_kernel(); #ifdef CONF_DEBUG_EXCEPTIONS show_regs(regs); diff --git a/arch/mips/lib/copy_user.S b/arch/mips/lib/copy_user.S index 30fa6d6e9..a7fdc74e5 100644 --- a/arch/mips/lib/copy_user.S +++ b/arch/mips/lib/copy_user.S @@ -1,13 +1,15 @@ /* - * arch/mips/mips1/memcpy.S + * arch/mips/lib/copy_user.S * * 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) 1996 by Ralf Baechle + * Copyright (c) 1996, 1997 by Ralf Baechle * - * Less stupid memcpy/user_copy implementation for 32 bit MIPS CPUs. + * Less stupid user_copy implementation for 32 bit MIPS CPUs. + * + * $Id: copy_user.S,v 1.2 1997/08/11 04:26:12 ralf Exp $ */ #include <asm/asm.h> #include <asm/regdef.h> @@ -37,8 +39,8 @@ not_even_the_same_alignment: andi v1,3 sltu t0,v0,v1 MOVN(v1,v0,t0) - beqz v1,3f # -> finished - LONG_ADDU v1,a0 # delay slot + beqz v1,src_aligned + LONG_ADDU v1,a0 1: lb $1,(a1) EX(1b, fault) LONG_ADDIU a1,1 @@ -46,20 +48,22 @@ not_even_the_same_alignment: EX(2b, fault) LONG_ADDIU a0,1 bne a0,v1,1b - LONG_SUBU v0,1 # delay slot -3: + LONG_SUBU v0,1 +src_aligned: + /* * Ok. We've fixed the alignment of the copy src for this case. * Now let's copy in the usual BLOCK_SIZE byte blocks using unaligned * stores. * XXX Align the destination address. This is better if the __copy_user * encounters an access fault because we never have to deal with an - * only partially modified destination word. + * only partially modified destination word. This is required to + * keep the semantics of the result of copy_*_user(). */ ori v1,v0,BLOCK_SIZE-1 xori v1,BLOCK_SIZE-1 beqz v1,copy_left_over - nop # delay slot + nop LONG_SUBU v0,v1 LONG_ADDU v1,a0 @@ -81,16 +85,16 @@ not_even_the_same_alignment: UEX(2b, fault_plus_12) LONG_ADDIU a0,BLOCK_SIZE bne a0,v1,1b - LONG_ADDIU a1,BLOCK_SIZE # delay slot + LONG_ADDIU a1,BLOCK_SIZE 9: b copy_left_over # < BLOCK_SIZE bytes left - nop # delay slot + nop /* ---------------------------------------------------------------------- */ not_w_aligned: /* - * Ok, src or destination are not 8-byte aligned. + * Ok, src or destination are not 4-byte aligned. * Try to fix that. Do at least both addresses have the same alignment? */ xor t0,a0,a1 @@ -107,8 +111,8 @@ not_w_aligned: andi v1,3 sltu t0,v0,v1 MOVN(v1,v0,t0) - beqz v1,align4 # -> finished - LONG_ADDU v1,a0 # delay slot + beqz v1,__copy_user + LONG_ADDU v1,a0 1: lb $1,(a1) EX(1b, fault) LONG_ADDIU a1,1 @@ -116,23 +120,23 @@ not_w_aligned: EX(2b, fault) LONG_ADDIU a0,1 bne a0,v1,1b - LONG_SUBU v0,1 # delay slot + LONG_SUBU v0,1 b align4 - nop # delay slot + nop /* ---------------------------------------------------------------------- */ LEAF(__copy_user) or t1,a0,a1 andi t1,3 - bnez t1,not_w_aligned - move v0,a2 # delay slot + bnez t1,not_w_aligned # not word alignment + move v0,a2 align4: ori v1,v0,BLOCK_SIZE-1 xori v1,BLOCK_SIZE-1 beqz v1,copy_left_over - nop # delay slot + nop LONG_SUBU v0,v1 LONG_ADDU v1,a0 @@ -154,7 +158,7 @@ align4: EX(2b, fault_plus_12) LONG_ADDIU a0,BLOCK_SIZE bne a0,v1,1b - LONG_ADDIU a1,BLOCK_SIZE # delay slot + LONG_ADDIU a1,BLOCK_SIZE 9: /* @@ -162,7 +166,7 @@ align4: */ copy_left_over: beqz v0,3f - nop # delay slot + nop 1: lb $1,(a1) EX(1b, fault) LONG_ADDIU a1,1 @@ -170,9 +174,11 @@ copy_left_over: EX(2b, fault) LONG_SUBU v0,1 bnez v0,1b - LONG_ADDIU a0,1 -3: jr ra - nop # delay slot + LONG_ADDIU a0,1 +3: + +done: jr ra + nop END(__copy_user) .set at @@ -193,14 +199,3 @@ fault_plus_8: LONG_ADDIU v0,8 jr ra fault_plus_12: LONG_ADDIU v0,12 jr ra - -/* ---------------------------------------------------------------------- */ - -/* - * For now we use __copy_user for __memcpy, too. This is effizient (one - * instruction penatly) and smaller but adds unwanted error checking we don't - * need. This hopefully doesn't cover any bugs. The memcpy() wrapper in - * <asm/string.h> takes care of the return value in a way GCC can optimize. - */ - .globl __memcpy -__memcpy = __copy_user diff --git a/arch/mips/mm/loadmmu.c b/arch/mips/mm/loadmmu.c index 70597201a..c4b4258b7 100644 --- a/arch/mips/mm/loadmmu.c +++ b/arch/mips/mm/loadmmu.c @@ -1,7 +1,9 @@ -/* $Id: loadmmu.c,v 1.1.1.1 1997/06/01 03:16:38 ralf Exp $ +/* * loadmmu.c: Setup cpu/cache specific function ptrs at boot time. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: loadmmu.c,v 1.1.1.1 1997/06/01 03:16:38 ralf Exp $ */ #include <linux/kernel.h> @@ -76,6 +78,7 @@ void loadmmu(void) case CPU_R4700: case CPU_R5000: case CPU_R5000A: + case CPU_NEVADA: printk("Loading R4000 MMU routines.\n"); ld_mmu_r4xx0(); break; diff --git a/arch/mips/mm/r4xx0.c b/arch/mips/mm/r4xx0.c index 2b32ca455..2586a961c 100644 --- a/arch/mips/mm/r4xx0.c +++ b/arch/mips/mm/r4xx0.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r4xx0.c,v 1.5 1997/07/29 22:54:52 tsbogend Exp $ + * $Id: r4xx0.c,v 1.6 1997/08/06 19:15:10 miguel Exp $ */ #include <linux/config.h> @@ -39,7 +39,16 @@ static scache_size, sc_lsize; /* Again, in bytes */ #undef DEBUG_CACHE /* - * Zero an entire page. + * On processors with QED R4600 style two set assosicative cache + * this is the bit which selects the way in the cache for the + * indexed cachops. + */ +#define waybit 0x2000 + +/* + * Zero an entire page. We have three flavours of the routine available. + * One for CPU with 16byte, with 32byte cachelines plus a special version + * with nops which handles the buggy R4600 v1.x. */ static void r4k_clear_page_d16(unsigned long page) @@ -1465,11 +1474,8 @@ static void r4k_flush_cache_page_d16i16(struct vm_area_struct *vma, */ page = (KSEG0 + (page & (dcache_size - 1))); blast_dcache16_page_indexed(page); - blast_dcache16_page_indexed(page ^ 0x2000); - if(text) { + if(text) blast_icache16_page_indexed(page); - blast_icache16_page_indexed(page ^ 0x2000); - } } out: restore_flags(flags); @@ -1583,10 +1589,10 @@ static void r4k_flush_cache_page_d32i32_r4600(struct vm_area_struct *vma, */ page = (KSEG0 + (page & (dcache_size - 1))); blast_dcache32_page_indexed(page); - blast_dcache32_page_indexed(page ^ 0x2000); + blast_dcache32_page_indexed(page ^ waybit); if(text) { blast_icache32_page_indexed(page); - blast_icache32_page_indexed(page ^ 0x2000); + blast_icache32_page_indexed(page ^ waybit); } } out: @@ -1776,7 +1782,6 @@ static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page) unsigned long flags; #ifdef DEBUG_CACHE - /* #if 1 */ printk("r4600_cram[%08lx]", page); #endif /* @@ -1786,7 +1791,6 @@ static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page) save_and_cli(flags); blast_dcache32_page(page); - blast_dcache32_page(page ^ 0x2000); #ifdef CONFIG_SGI { unsigned long tmp1, tmp2; @@ -1824,6 +1828,7 @@ static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page) static void r4k_flush_cache_sigtramp(unsigned long addr) { + /* XXX protect like uaccess.h loads/stores */ addr &= ~(dc_lsize - 1); __asm__ __volatile__("nop;nop;nop;nop"); flush_dcache_line(addr); @@ -2534,7 +2539,7 @@ void ld_mmu_r4xx0(void) unsigned long cfg = read_32bit_cp0_register(CP0_CONFIG); int sc_present = 0; - printk("CPU REVISION IS: %08x\n", read_32bit_cp0_register(CP0_PRID)); + printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); probe_icache(cfg); probe_dcache(cfg); @@ -2546,7 +2551,6 @@ void ld_mmu_r4xx0(void) case CPU_R4400PC: case CPU_R4400SC: case CPU_R4400MC: -try_again: probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache)); sc_present = probe_scache_kseg1(cfg); break; @@ -2554,38 +2558,40 @@ try_again: case CPU_R4600: case CPU_R4640: case CPU_R4700: - case CPU_R5000: + case CPU_R5000: /* XXX: We don't handle the true R5000 SCACHE */ + case CPU_NEVADA: probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache_eeprom)); sc_present = probe_scache_eeprom(cfg); - /* Try using tags if eeprom give us bogus data. */ - if(sc_present == -1) - goto try_again; + /* Try using tags if eeprom gives us bogus data. */ + if(sc_present == -1) { + probe_scache_kseg1 = + (probe_func_t) (KSEG1ADDR(&probe_scache)); + sc_present = probe_scache_kseg1(cfg); + } break; }; - if(!sc_present) { - /* Lacks secondary cache. */ - setup_noscache_funcs(); + if(sc_present == 1 + && (mips_cputype == CPU_R4000SC + || mips_cputype == CPU_R4000MC + || mips_cputype == CPU_R4400SC + || mips_cputype == CPU_R4400MC)) { + /* Has a true secondary cache. */ + setup_scache_funcs(); } else { - /* Has a secondary cache. */ - if(mips_cputype != CPU_R4600 && - mips_cputype != CPU_R4640 && - mips_cputype != CPU_R4700 && - mips_cputype != CPU_R5000) { - setup_scache_funcs(); - } else { - setup_noscache_funcs(); - if((mips_cputype != CPU_R5000)) { - flush_cache_page = - r4k_flush_cache_page_d32i32_r4600; - flush_page_to_ram = - r4k_flush_page_to_ram_d32i32_r4600; - } + /* Lacks true secondary cache. */ + setup_noscache_funcs(); + if((mips_cputype != CPU_R5000)) { /* XXX */ + flush_cache_page = + r4k_flush_cache_page_d32i32_r4600; + flush_page_to_ram = + r4k_flush_page_to_ram_d32i32_r4600; } } + /* XXX Handle true second level cache w/ split I/D */ flush_cache_sigtramp = r4k_flush_cache_sigtramp; flush_tlb_all = r4k_flush_tlb_all; diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c index 2d23372ae..343a71f9e 100644 --- a/arch/mips/sgi/kernel/indy_int.c +++ b/arch/mips/sgi/kernel/indy_int.c @@ -4,7 +4,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_int.c,v 1.2 1997/07/01 09:00:58 ralf Exp $ + * $Id: indy_int.c,v 1.3 1997/08/26 04:34:55 miguel Exp $ */ #include <linux/config.h> @@ -260,13 +260,6 @@ int get_irq_list(char *buf) atomic_t __mips_bh_counter; -#ifdef __SMP__ -#error Send superfluous SMP boxes to ralf@uni-koblenz.de -#else -#define irq_enter(cpu, irq) (++local_irq_count[cpu]) -#define irq_exit(cpu, irq) (--local_irq_count[cpu]) -#endif - /* * do_IRQ handles IRQ's that have been installed without the * SA_INTERRUPT flag: it uses the full signal-handling return @@ -434,7 +427,7 @@ void indy_local0_irqdispatch(struct pt_regs *regs) struct irqaction *action; unsigned char mask = ioc_icontrol->istat0; unsigned char mask2 = 0; - int irq; + int irq, cpu = smp_processor_id();; mask &= ioc_icontrol->imask0; if(mask & ISTAT0_LIO2) { @@ -446,13 +439,11 @@ void indy_local0_irqdispatch(struct pt_regs *regs) irq = lc0msk_to_irqnr[mask]; action = local_irq_action[irq]; } -#if 0 - printk("local0_dispatch: got irq %d mask %2x mask2 %2x\n", - irq, mask, mask2); - prom_getchar(); -#endif + + irq_enter(cpu, irq); kstat.interrupts[irq + 16]++; action->handler(irq, action->dev_id, regs); + irq_exit(cpu, irq); } void indy_local1_irqdispatch(struct pt_regs *regs) @@ -460,7 +451,7 @@ void indy_local1_irqdispatch(struct pt_regs *regs) struct irqaction *action; unsigned char mask = ioc_icontrol->istat1; unsigned char mask2 = 0; - int irq; + int irq, cpu = smp_processor_id();; mask &= ioc_icontrol->imask1; if(mask & ISTAT1_LIO3) { @@ -473,23 +464,24 @@ void indy_local1_irqdispatch(struct pt_regs *regs) irq = lc1msk_to_irqnr[mask]; action = local_irq_action[irq]; } -#if 0 - printk("local1_dispatch: got irq %d mask %2x mask2 %2x\n", - irq, mask, mask2); - prom_getchar(); -#endif + irq_enter(cpu, irq); kstat.interrupts[irq + 24]++; action->handler(irq, action->dev_id, regs); + irq_exit(cpu, irq); } void indy_buserror_irq(struct pt_regs *regs) { - kstat.interrupts[6]++; + int cpu = smp_processor_id(); + int irq = 6; + + irq_enter(cpu, irq); + kstat.interrupts[irq]++; printk("Got a bus error IRQ, shouldn't happen yet\n"); show_regs(regs); printk("Spinning...\n"); - while(1) - ; + while(1); + irq_exit(cpu, irq); } /* Misc. crap just to keep the kernel linking... */ diff --git a/arch/mips/sgi/kernel/indy_timer.c b/arch/mips/sgi/kernel/indy_timer.c index 43c1c76c6..d0ba84929 100644 --- a/arch/mips/sgi/kernel/indy_timer.c +++ b/arch/mips/sgi/kernel/indy_timer.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_timer.c,v 1.2 1997/06/28 23:27:29 ralf Exp $ + * $Id: indy_timer.c,v 1.2 1997/07/01 09:00:59 ralf Exp $ */ #include <linux/errno.h> @@ -101,10 +101,12 @@ static long last_rtc_update = 0; void indy_timer_interrupt(struct pt_regs *regs) { + int irq = 7; + /* Ack timer and compute new compare. */ r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset); ack_r4ktimer(r4k_cur); - kstat.interrupts[7]++; + kstat.interrupts[irq]++; do_timer(regs); /* We update the Dallas time of day approx. every 11 minutes, @@ -272,10 +274,15 @@ void indy_timer_init(void) void indy_8254timer_irq(void) { - kstat.interrupts[4]++; + int cpu = smp_processor_id(); + int irq = 4; + + irq_enter(cpu, irq); + kstat.interrupts[irq]++; printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n"); prom_getchar(); prom_imode(); + irq_exit(cpu, irq); } void do_gettimeofday(struct timeval *tv) diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index ac2719977..6c81701d9 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -1,6 +1,3 @@ -# -# ppc/Makefile -# # This file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. Remember to do have actions # for "archclean" and "archdep" for cleaning up and making dependencies for @@ -12,29 +9,28 @@ # # Copyright (C) 1994 by Linus Torvalds # Changes for PPC by Gary Thomas -# Modified by Cort Dougan +# Rewritten by Cort Dougan and Paul Mackerras # +ifeq ($(CONFIG_PMAC),y) +KERNELBASE =0xc0000000 +else +KERNELBASE =0x90000000 +endif + # PowerPC (cross) tools -SUFFIX = -AS = as$(SUFFIX) -ASFLAGS = -LD = ld$(SUFFIX) -LINKFLAGS = -T arch/ppc/ld.script -Ttext 0x90000000 -HOSTCC = gcc -CC = gcc$(SUFFIX) +ifneq ($(shell uname -m),ppc) +CROSS_COMPILE = ppc-linux-elf- +else +CHECKS = checks +endif + +ASFLAGS = +LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELBASE) -Bstatic CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ -CFLAGS = $(CFLAGSINC) \ - -Wstrict-prototypes -fomit-frame-pointer \ - -fno-builtin \ - -finhibit-size-directive \ - -O2 -fsigned-char -pipe -ffixed-r2 -mstring -mmultiple -msoft-float -# -fverbose-asm +CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \ + -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring CPP = $(CC) -E $(CFLAGS) -AR = ar$(SUFFIX) -RANLIB = ranlib$(SUFFIX) -STRIP = strip$(SUFFIX) -NM = nm$(SUFFIX) ifdef CONFIG_601 CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601 @@ -55,59 +51,35 @@ SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) ARCHIVES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(ARCHIVES) CORE_FILES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(CORE_FILES) +ifdef CONFIG_XMON +SUBDIRS += arch/ppc/xmon +CORE_FILES += arch/ppc/xmon/x.o +endif + +ifdef CONFIG_PMAC +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/coffboot +else +# PReP and CHRP systems MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +endif checks: @$(MAKE) -C arch/$(ARCH)/kernel checks -netboot: checks vmlinux - @$(MAKEBOOT) netboot - -znetboot: checks vmlinux - @$(MAKEBOOT) znetboot +BOOT_TARGETS = netboot znetboot zImage floppy install \ + vmlinux.coff znetboot.initrd zImage.initrd -#rcpboot: checks vmlinux -# @$(MAKEBOOT) rcpboot +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKEBOOT) $@ -zImage: checks vmlinux - @$(MAKEBOOT) zImage - -floppy: checks vmlinux - @$(MAKEBOOT) floppy - -install: checks vmlinux - @$(MAKEBOOT) install - -vmlinux.coff : checks vmlinux - $(MAKE) -C arch/ppc/coffboot/ vmlinux.coff - -arch/ppc/kernel: dummy - $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/kernel - -arch/ppc/mm: dummy - $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/mm - -arch/ppc/lib: dummy - $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/lib - -diffs: - arch/ppc/mkdiff - -tar: - arch/ppc/mktar +tags: + etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h} archclean: - rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h arch/ppc/kernel/checks TAGS - rm -f `find arch/ppc/ \( -name '*.[oas]' -o -name '*~' -o -name '#*#' \) -print` - rm -f `find include/asm-ppc/ \( -name '*.[oas]' -o -name '*~' -o -name '#*#' \) -print` + rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h + rm -f arch/ppc/kernel/checks @$(MAKEBOOT) clean archdep: - $(MAKE) -C arch/ppc/boot fastdep - $(MAKE) -C arch/ppc/kernel fastdep - $(MAKE) -C arch/ppc/mm fastdep - $(MAKE) -C arch/ppc/lib fastdep - -tags : - etags arch/ppc/*/*.c arch/ppc/*/*.S include/asm/* */*.c + $(MAKEBOOT) fastdep diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index 5dcac750e..a731ec2a0 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -22,58 +22,81 @@ $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< -ZLINKFLAGS = -T ../ld.script -Ttext 0x00800000 +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 GZIP_FLAGS = -v9 SYSTEM = $(TOPDIR)/vmlinux - -OBJECTS = head.o inflate.o unzip.o misc.o vreset.o - +OBJECTS := head.o inflate.o unzip.o misc.o vreset.o kbd.o CFLAGS = -O2 -DSTDC_HEADERS -I$(TOPDIR)/include +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJCOPY_ARGS = -O elf32-powerpc + all: $(TOPDIR)/zImage mkprep : mkprep.c $(HOSTCC) $(CFLAGSINC) -o mkprep mkprep.c +piggyback : piggyback.c + $(HOSTCC) $(CFLAGSINC) -o piggyback piggyback.c + find_name : find_name.c $(HOSTCC) $(CFLAGSINC) -o find_name find_name.c -mk_type41: mk_type41.c - $(HOSTCC) $(CFLAGSINC) -o mk_type41 mk_type41.c - -piggyback: piggyback.c - $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c - floppy: $(TOPDIR)/vmlinux zImage dd if=$(TOPDIR)/zImage of=/dev/fd0H1440 bs=64b -netboot : $(TOPDIR)/vmlinux mkprep - mkprep $(TOPDIR)/vmlinux $(TOPDIR)/netboot +floppy.initrd: $(TOPDIR)/vmlinux zImage + dd if=$(TOPDIR)/zImage.initrd of=/dev/fd0H1440 bs=64b + +znetboot : zImage mkprep + cp $(TOPDIR)/zImage /usr/local/tftpboot/vmlinux -znetboot : zvmlinux mkprep - mkprep zvmlinux $(TOPDIR)/znetboot - cp $(TOPDIR)/znetboot /usr/local/tftpboot/vmlinux +znetboot.initrd : zImage.initrd mkprep + cp $(TOPDIR)/zImage.initrd /usr/local/tftpboot/vmlinux -rcpboot : znetboot - rcp $(TOPDIR)/znetboot charon:/usr/tftpboot/vmlinux +# +# This really needs to go away. Perhaps a +# zImage.prep and zImage.chrp might be better. +# Once we're able to get a lilo-ish program +# on prep systems this won't be a problem. +# -- Cort +# +ifdef CONFIG_CHRP +zImage: zvmlinux + cp zvmlinux $(TOPDIR)/zImage +zImage.initrd: zvmlinux.initrd + cp zvmlinux.initrd $(TOPDIR)/zImage.initrd + +zvmlinux: $(OBJECTS) $(SYSTEM) find_name vmlinux.gz piggyback + ./piggyback < vmlinux.gz | $(AS) -o piggy.o + $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) piggy.o + rm -f piggy.o +else zImage: zvmlinux mkprep mkprep -pbp zvmlinux $(TOPDIR)/zImage -install: zImage - dd if=$(TOPDIR)/zImage of=/dev/sda4 - ln -s /dev/sda4 $(INSTALL_PATH)/vmlinuz - cp $(TOPDIR)/System.map $(INSTALL_PATH)/ +zImage.initrd: zvmlinux.initrd mkprep + mkprep -pbp zvmlinux.initrd $(TOPDIR)/zImage.initrd -zvmlinux: $(OBJECTS) $(SYSTEM) mkprep find_name - mkprep $(TOPDIR)/vmlinux -|gzip ${GZIP_FLAGS}|mkprep -asm - -|$(AS) -o piggy.o - $(LD) $(ZLINKFLAGS) -o zvmlinux $(OBJECTS) piggy.o - rm -f piggy.o +zvmlinux: $(OBJECTS) $(SYSTEM) mkprep find_name vmlinux.gz + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=vmlinux.gz \ + zvmlinux.tmp $@ + rm zvmlinux.tmp +endif + +vmlinux.gz: $(TOPDIR)/vmlinux + dd bs=64k skip=1 if=$(TOPDIR)/vmlinux | gzip -vf9 - > vmlinux.gz + +zvmlinux.initrd: zvmlinux + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \ + zvmlinux $@ clean: - rm -f piggyback zvmlinux mk_type41 mkprep mkboot find_name - rm -f $(TOPDIR)/{zImage,znetboot,netboot} + rm -f vmlinux* znetboot* zImage* zvmlinux* mkprep find_name + rm -f $(TOPDIR)/{zImage*,znetboot*,zvmlinux*,vmlinux*} fastdep: $(TOPDIR)/scripts/mkdep *.[Sch] > .depend diff --git a/arch/ppc/boot/find_name.c b/arch/ppc/boot/find_name.c new file mode 100644 index 000000000..23646fb69 --- /dev/null +++ b/arch/ppc/boot/find_name.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <asm/page.h> +#include <sys/mman.h> +/* + * Finds a given address in the System.map and prints it out + * with its neighbors. -- Cort + */ + +void main(int argc, char **argv) +{ + unsigned long addr, cmp, i; + FILE *f; + char *ptr; + char s[256], last[256]; + + if ( argc < 2 ) + { + fprintf(stderr, "Usage: %s <address>\n", argv[0]); + exit(-1); + } + + for ( i = 1 ; argv[i] ; i++ ) + { + sscanf( argv[i], "%0x", &addr ); + /* adjust if addr is relative to kernelbase */ + if ( addr < PAGE_OFFSET ) + addr += PAGE_OFFSET; + + if ( (f = fopen( "System.map", "r" )) == NULL ) + { + perror("fopen()\n"); + exit(-1); + } + + while ( !feof(f) ) + { + fgets(s, 255 , f); + sscanf( s, "%0x", &cmp ); + if ( addr < cmp ) + break; + strcpy( last, s); + } + + printf( "%s", last); + } + fclose(f); +} diff --git a/arch/ppc/boot/head.S b/arch/ppc/boot/head.S index 6b25cb157..2cfda5cbe 100644 --- a/arch/ppc/boot/head.S +++ b/arch/ppc/boot/head.S @@ -13,17 +13,6 @@ start: bl start_ start_: -/* TEMP - No residual data on BeBox (yet) */ -#if 0 -#define IS_BE_BOX 0x42654278 /* 'BeBx' */ - lis r2,IS_BE_BOX>>16 - ori r2,r2,IS_BE_BOX&0xFFFF - cmp 0,r30,r2 - bne notBeBox - li r3,0 -#endif -notBeBox: -/* TEMP */ mr r11,r3 /* Save pointer to residual data */ mfmsr r3 /* Turn off interrupts */ li r4,0 @@ -48,7 +37,9 @@ notBeBox: mtlr r21 mtctr r22 bctr /* Jump to code */ -/* Relocate code to final resting spot */ +/* + * no matter where we're loaded, move ourselves to -Ttext address + */ relocate: mflr r3 /* Compute code bias */ subi r3,r3,4 @@ -98,13 +89,26 @@ start_ldr: mr r5,r6 /* Checksum */ mr r6,r11 /* Residual data */ bl decompress_kernel - /*mr r29,r3*/ /* R3 = TotalMemory */ - /*lis r28,hold_residual@h - ori r28,r28,hold_residual@l*/ + /* changed to use r3 (as firmware does) for kernel as ptr to residual -- Cort*/ - li r5,0x100 /* Kernel code starts here */ - mtlr r5 + lis r6,cmd_line@h + ori r6,r6,cmd_line@l + 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) + + li r9,0x00c /* Kernel code starts here */ + mtlr r9 blr hang: b hang @@ -142,6 +146,45 @@ _put_HID0: blr /* + * 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 + +/* * This space [buffer] is used to forceably flush the data cache when * running in copyback mode. This is necessary IFF the data cache could * contain instructions for which the instruction cache has stale data. diff --git a/arch/ppc/boot/kbd.c b/arch/ppc/boot/kbd.c new file mode 100644 index 000000000..6c0065b00 --- /dev/null +++ b/arch/ppc/boot/kbd.c @@ -0,0 +1,166 @@ +/* Keyboard handler */ + +#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */ + +#define L 0x0001 /* locking function */ +#define SHF 0x0002 /* keyboard shift */ +#define ALT 0x0004 /* alternate shift -- alternate chars */ +#define NUM 0x0008 /* numeric shift cursors vs. numeric */ +#define CTL 0x0010 /* control shift -- allows ctl function */ +#define CPS 0x0020 /* caps shift -- swaps case of letter */ +#define ASCII 0x0040 /* ascii code for this key */ +#define STP 0x0080 /* stop output */ +#define FUNC 0x0100 /* function key */ +#define SCROLL 0x0200 /* scroll lock key */ + +unsigned char shfts, ctls, alts, caps, num, stp; + +#define KBDATAP 0x60 /* kbd data port */ +#define KBSTATUSPORT 0x61 /* kbd status */ +#define KBSTATP 0x64 /* kbd status port */ +#define KBINRDY 0x01 +#define KBOUTRDY 0x02 + +#define _x__ 0x00 /* Unknown / unmapped */ + +const unsigned short action[] = { + 0, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 0- 7 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 8-15 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 16-23 */ + ASCII, ASCII, ASCII, ASCII, ASCII, CTL, ASCII, ASCII, /* scan 24-31 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 32-39 */ + ASCII, ASCII, SHF, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 40-47 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, SHF, ASCII, /* scan 48-55 */ + ALT, ASCII, CPS, FUNC, FUNC, FUNC, FUNC, FUNC, /* scan 56-63 */ + FUNC, FUNC, FUNC, FUNC, FUNC, NUM,SCROLL, ASCII, /* scan 64-71 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 72-79 */ + ASCII, ASCII, ASCII, ASCII, 0, 0, 0, 0, /* scan 80-87 */ + 0,0,0,0,0,0,0,0, /* scan 88-95 */ + 0,0,0,0,0,0,0,0, /* scan 96-103 */ + 0,0,0,0,0,0,0,0, /* scan 104-111 */ + 0,0,0,0,0,0,0,0, /* scan 112-119 */ + 0,0,0,0,0,0,0,0, /* scan 120-127 */ +}; + +static int +kbd(noblock) + int noblock; +{ + unsigned char dt, brk, act; + int first = 1; +loop: + if (noblock) { + if ((inb(KBSTATP) & KBINRDY) == 0) + return (-1); + } else while((inb(KBSTATP) & KBINRDY) == 0) ; + + dt = inb(KBDATAP); + + brk = dt & 0x80; /* brk == 1 on key release */ + dt = dt & 0x7f; /* keycode */ + + act = action[dt]; + if (/*act&SHF*/ dt == 54) + shfts = brk ? 0 : 1; + if (/*act&ALT*/ dt == 48) + alts = brk ? 0 : 1; + if (/*act&NUM*/ dt == 69) + if (act&L) { + /* NUM lock */ + if(!brk) + num = !num; + } else + num = brk ? 0 : 1; + if (/*act&CTL*/ dt == 29) + ctls = brk ? 0 : 1; + if (/*act&CPS*/ dt == 58) + if (act&L) { + /* CAPS lock */ + if(!brk) + caps = !caps; + } else + caps = brk ? 0 : 1; + if (0/*act&STP*/) + if (act&L) { + if(!brk) + stp = !stp; + } else + stp = brk ? 0 : 1; + + if ((act&ASCII) && !brk) { + unsigned char chr; + if (shfts) + chr = shift_map[dt]; + else if (ctls) + chr = ctrl_map[dt]; + else + chr = plain_map[dt]; + if (alts) + chr |= 0x80; + + if (caps && (chr >= 'a' && chr <= 'z')) + chr -= 'a' - 'A' ; + if ( chr == 0x01 ) chr = '\n'; /* hack */ +#define CTRL(s) (s & 0x1F) + if ((chr == '\r') || (chr == '\n') || (chr == CTRL('A')) || (chr == CTRL('S'))) + { + /* Wait for key up */ + while (1) + { + while((inb(KBSTATP) & KBINRDY) == 0) ; + dt = inb(KBDATAP); + if (dt & 0x80) /* key up */ break; + } + } + return (chr); + } + if (first && brk) return (0); /* Ignore initial 'key up' codes */ + goto loop; +} + +static +scankbd(void) { + return (kbd(1) != -1); +} + +static +kbdreset(void) +{ + unsigned char c; + int i; + + /* Send self-test */ + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0xAA); + while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */ + if ((c = inb(KBDATAP)) != 0x55) + { + puts("Keyboard self test failed - result:"); + puthex(c); + puts("\n"); + } + /* Enable interrupts and keyboard controller */ + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0x60); + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBDATAP,0x45); + for (i = 0; i < 10000; i++) udelay(1); + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0xAE); +} + +static int kbd_reset = 0; + +CRT_getc(void) +{ + int c; + if (!kbd_reset) {kbdreset(); kbd_reset++; } + while ((c = kbd(0)) == 0) ; + return(c); +} + +CRT_tstc(void) +{ + if (!kbd_reset) {kbdreset(); kbd_reset++; } + return ((inb(KBSTATP) & KBINRDY) != 0); +} diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index a983ea106..4d2a723f5 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -8,13 +8,23 @@ * puts by Nick Holloway 1993 * * Adapted for PowerPC by Gary Thomas + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) */ #include "gzip.h" #include "lzw.h" #include "asm/residual.h" +#include <elf.h> RESIDUAL hold_residual; +unsigned long initrd_start = 0, initrd_end = 0; +char *zimage_start; +int zimage_size; +extern char input_data[]; +extern int input_len; +void cksum_text(void); +void verify_data(unsigned long load_addr); + void dump_buf(unsigned char *p, int s); #define EOF -1 @@ -26,8 +36,7 @@ unsigned outcnt; unsigned insize; unsigned inptr; -extern char input_data[]; -extern int input_len; +char cmd_line[256]; int input_ptr; @@ -56,6 +65,9 @@ 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 *malloc(int size) { @@ -77,7 +89,7 @@ void *malloc(int size) */ if (free_mem_ptr < (long)&end) { - if (free_mem_ptr > (long)&input_data[input_ptr]) + if (free_mem_ptr > (long)&zimage_start[input_ptr]) error("\nOut of memory\n"); return p; @@ -87,7 +99,7 @@ void *malloc(int size) #endif return p; puts("large kernel, low 1M tight..."); - free_mem_ptr = (long)input_data; + free_mem_ptr = (long)zimage_start; } } @@ -115,6 +127,53 @@ static void scroll() vidmem[i] = ' '; } +tstc(void) +{ + return (CRT_tstc() ); +} + +getc(void) +{ + while (1) { + if (CRT_tstc()) return (CRT_getc()); + } +} + +void +putc(const char c) +{ + int x,y; + + x = orig_x; + y = orig_y; + + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else if (c == '\b') { + if (x > 0) { + x--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + + cursor(x, y); + + orig_x = x; + orig_y = y; +} + void puts(const char *s) { int x,y; @@ -229,10 +288,10 @@ puts("*"); insize = 0; do { len = INBUFSIZ-insize; - if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1; + if (len > (zimage_size-input_ptr+1)) len=zimage_size-input_ptr+1; if (len == 0 || len == EOF) break; - for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i]; + for (i=0;i<len;i++) inbuf[insize+i] = zimage_start[input_ptr+i]; insize += len; input_ptr += len; } while (insize < INBUFSIZ); @@ -313,7 +372,15 @@ void error(char *x) unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) { - unsigned long TotalMemory; + int timer; + char *cp, ch; + Elf32_Ehdr *eh; + Elf32_Shdr *sh, *strtab_shdr; + char *strtab; + unsigned long i; + extern unsigned long start(void); + + output_data = (char *)0x0; /* Points to 0 */ lines = 25; cols = 80; @@ -339,33 +406,135 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R clear_bufs(); makecrc(); - puts("Loaded at "); puthex(load_addr); puts(", "); puthex(num_words); puts(" words"); - puts(", cksum = "); puthex(cksum); puts("\n"); + puts("Cksum: "); puthex(cksum); puts("\n"); + puts("Loaded at: "); puthex(load_addr); puts(" "); puthex(num_words+load_addr); + puts("\n"); + puts("Boot code relocated to: "); puthex((unsigned long)start); puts(" "); + puthex((unsigned long)(num_words+start)); + puts("\n"); if (residual) { - _bcopy(residual, &hold_residual, sizeof(hold_residual)); - puts("Residual data at "); puthex(residual); puts("\n"); - show_residual_data(residual); - TotalMemory = residual->TotalMemory; - } else { - TotalMemory = 0x01000000; + _bcopy((char *)residual, (char *)&hold_residual, sizeof(hold_residual)); + puts("Residual data at: "); puthex((unsigned long)residual); puts(" "); + puthex((unsigned long)((unsigned long)(residual->ResidualLength) + residual)); puts("\n"); + puts("Residual data relocated to: "); puthex((unsigned long)&hold_residual); puts("\n"); } - puts("Uncompressing Linux..."); + /* + * Take care of initrd if we have one + */ + /* + * the _actual_ load addr is 64k (elf hdr size) lower but the + * firmware gives us the starting exec point not the load ptr + * -- Cort + */ + eh = (Elf32_Ehdr *)(load_addr - 65536); + sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff ); + /*puts("Entry point: "); puthex(eh->e_entry); puts("\n");*/ + + /* find string table */ + for ( i = 0 ; i <= eh->e_shnum ; i++,sh++) + { + /*puts("Section: "); puthex(i); + puts(" type: "); puthex(sh->sh_type); + puts(" offset: "); puthex(sh->sh_offset); + puts("\n");*/ + + if ( sh->sh_type == SHT_STRTAB ) + { + strtab_shdr = sh; + strtab = (char *)(sh->sh_offset + (unsigned long)eh); + /*puts("Found strtab at: "); puthex((unsigned long)strtab); + puts("\n");*/ + break; + } + } - method = get_method(0); + /* find the initrd and image sections */ + if ( strtab_shdr ) + { + sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff ); + for ( i = 0 ; i <= eh->e_shnum ; i++,sh++) + { + if ( !memcmp("initrd", (char *)(strtab+sh->sh_name), 6 ) ) + { + initrd_start = (unsigned long)eh + sh->sh_offset; + initrd_end = initrd_start + sh->sh_size; + puts("Found initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); + puts("\n"); + } + if ( !memcmp("image", (char *)(strtab+sh->sh_name), 5 ) ) + { + zimage_start = (char *)((unsigned long)eh + sh->sh_offset); + zimage_size = sh->sh_size; + puts("Found zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + } + } + } + else + { + puts("Couldn't find string table for boot image!\n"); + } + + /* relocate initrd */ + if ( initrd_start ) + { + memcpy ((void *)0x00D00000,(void *)initrd_start, + initrd_end - initrd_start ); + initrd_end = 0x00D00000 + initrd_end - initrd_start; + initrd_start = 0x00D00000; + puts("Moved initrd to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } - work(0, 0); + CRT_tstc(); /* Forces keyboard to be initialized */ + puts("\nLinux/PPC load: "); + timer = 0; + cp = cmd_line; + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\n"); + + /* mappings on early boot can only handle 16M */ + if ( (int)(&cmd_line[0]) > (16<<20)) + puts("cmd_line > 16M\n"); + if ( (int)&hold_residual > (16<<20)) + puts("hold_residual > 16M\n"); + if ( initrd_start > (16<<20)) + puts("initrd_start > 16M\n"); + + + puts("Uncompressing Linux..."); + method = get_method(0); + work(0, 0); puts("done.\n"); + puts("Now booting the kernel\n"); - /*return (TotalMemory);*/ /* Later this can be a pointer to saved residual data */ - return &hold_residual; + return (unsigned long)&hold_residual; } show_residual_data(RESIDUAL *res) { puts("Residual data: "); puthex(res->ResidualLength); puts(" bytes\n"); - puts("Total memory: "); puthex(res->TotalMemory); puts("\n"); #if 0 puts("Residual structure = "); puthex(sizeof(*res)); puts(" bytes\n"); dump_buf(&hold_residual, 32); @@ -407,14 +576,14 @@ cksum_data() { unsigned int *ptr, len, cksum, cnt; cksum = cnt = 0; - ptr = input_data; + ptr = (unsigned int *)zimage_start; puts("Checksums: "); - for (len = 0; len < input_len; len += 4) { + for (len = 0; len < zimage_size; len += 4) { cksum ^= *ptr++; if (len && ((len & 0x0FFF) == 0)) { if (cnt == 0) { puts("\n ["); - puthex(ptr-1); + puthex((unsigned long)ptr-1); puts("] "); } puthex(cksum); @@ -429,7 +598,7 @@ cksum_data() puts("Data cksum = "); puthex(cksum); puts("\n"); } -cksum_text() +void cksum_text(void) { extern int start, etext; unsigned int *ptr, len, text_len, cksum, cnt; @@ -442,7 +611,7 @@ cksum_text() if (len && ((len & 0x0FFF) == 0)) { if (cnt == 0) { puts("\n ["); - puthex(ptr-1); + puthex((unsigned long)ptr-1); puts("] "); } puthex(cksum); @@ -457,24 +626,24 @@ cksum_text() puts("TEXT cksum = "); puthex(cksum); puts("\n"); } -verify_data(unsigned long load_addr) +void verify_data(unsigned long load_addr) { extern int start, etext; unsigned int *orig_ptr, *copy_ptr, len, errors; errors = 0; - copy_ptr = input_data; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)input_data - (unsigned int)&start)); - for (len = 0; len < input_len; len += 4) { + copy_ptr = (unsigned int *)zimage_start; + orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); + for (len = 0; len < zimage_size; len += 4) { if (*copy_ptr++ != *orig_ptr++) { errors++; } } - copy_ptr = input_data; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)input_data - (unsigned int)&start)); - for (len = 0; len < input_len; len += 4) { + copy_ptr = (unsigned int *)zimage_start; + orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); + for (len = 0; len < zimage_size; len += 4) { if (*copy_ptr++ != *orig_ptr++) { - dump_buf(copy_ptr-1, 128); - dump_buf(orig_ptr-1, 128); + dump_buf((unsigned char *) (copy_ptr-1), 128); + dump_buf((unsigned char *) (orig_ptr-1), 128); puts("Total errors = "); puthex(errors*4); puts("\n"); while (1) ; } @@ -486,9 +655,9 @@ test_data(unsigned long load_addr) extern int start, etext; unsigned int *orig_ptr, *copy_ptr, len, errors; errors = 0; - copy_ptr = input_data; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)input_data - (unsigned int)&start)); - for (len = 0; len < input_len; len += 4) { + copy_ptr = (unsigned int *)zimage_start; + orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); + for (len = 0; len < zimage_size; len += 4) { if (*copy_ptr++ != *orig_ptr++) { errors++; } @@ -532,7 +701,7 @@ void dump_buf(unsigned char *p, int s) } while (s > 0) { - puthex(p); puts(": "); + puthex((unsigned long)p); puts(": "); for (i = 0; i < 16; i++) { if (i < s) diff --git a/arch/ppc/boot/mkprep.c b/arch/ppc/boot/mkprep.c index 417334670..06b08df98 100644 --- a/arch/ppc/boot/mkprep.c +++ b/arch/ppc/boot/mkprep.c @@ -131,17 +131,18 @@ int main(int argc, char *argv[]) argptr++; /* skip elf header in input file */ - lseek(in_fd, elfhdr_size, SEEK_SET); + if ( !prep ) + lseek(in_fd, elfhdr_size, SEEK_SET); /* write prep partition if necessary */ if ( prep ) - write_prep_partition( in_fd, out_fd ); + write_prep_partition( in_fd, out_fd ); /* write input image to bootimage */ if ( asmoutput ) - write_asm_data( in_fd, out_fd ); + write_asm_data( in_fd, out_fd ); else - copy_image(in_fd, out_fd); + copy_image(in_fd, out_fd); return 0; } @@ -161,28 +162,11 @@ void write_prep_partition(int in, int out) } bzero( block, sizeof block ); - - /* set entry point and boot image size */ - *entry = cpu_to_le32(0x400); - /* need use size - elfheader? */ + /* set entry point and boot image size skipping over elf header */ + *entry = cpu_to_le32(0x400+65536); *length = cpu_to_le32(info.st_size+0x400); - /* - * Writes the "boot record", which contains the partition table, to the - * diskette, followed by the dummy PC boot block and load image descriptor - * block. It returns the number of bytes it has written to the load - * image. - * - * The boot record is the first block of the diskette and identifies the - * "PReP" partition. The "PReP" partition contains the "load image" starting - * at offset zero within the partition. The first block of the load image is - * a dummy PC boot block. The second block is the "load image descriptor" - * which contains the size of the load image and the entry point into the - * image. The actual boot image starts at offset 1024 bytes (third sector) - * in the partition. - */ - /* sets magic number for msdos partition (used by linux) */ block[510] = 0x55; block[511] = 0xAA; @@ -220,7 +204,7 @@ void write_prep_partition(int in, int out) /* This has to be 0 on the PowerStack? */ pe->beginning_sector = cpu_to_le32(0); #endif -/*pe->number_of_sectors = cpu_to_le32(2*18*80-1);*/ + pe->number_of_sectors = cpu_to_le32(2*18*80-1); write( out, block, sizeof(block) ); write( out, entry, sizeof(*entry) ); diff --git a/arch/ppc/coffboot/.cvsignore b/arch/ppc/coffboot/.cvsignore new file mode 100644 index 000000000..45968cd8d --- /dev/null +++ b/arch/ppc/coffboot/.cvsignore @@ -0,0 +1,7 @@ +.depend +vmlinux.coff.initrd +coffboot +elfextract +hack-coff +vmlinux.coff +zImage diff --git a/arch/ppc/coffboot/Makefile b/arch/ppc/coffboot/Makefile new file mode 100644 index 000000000..e145411c6 --- /dev/null +++ b/arch/ppc/coffboot/Makefile @@ -0,0 +1,50 @@ +# Makefile for making XCOFF bootable images for booting on PowerMacs +# using Open Firmware. +# +# Paul Mackerras January 1997 + +# PowerPC (cross) tools +ifneq ($(shell uname -m),ppc) +CROSS_COMPILE =powerpc-eabi- +endif + +HOSTCC = gcc +HOSTCFLAGS = -O -I$(TOPDIR)/include + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +CFLAGS = -O -fno-builtin -I$(TOPDIR)/include +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJCOPY_ARGS = -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment \ + --add-section=image=zImage +LD_ARGS = -e _start -T ld.script -Ttext 500000 -Tdata 510000 -Bstatic +GZIP = gzip -9 + +OBJS = crt0.o start.o main.o misc.o string.o zlib.o +LIBS = $(TOPDIR)/lib/lib.a + +vmlinux.coff: coffboot hack-coff zImage + $(OBJCOPY) $(OBJCOPY_ARGS) coffboot $@ + ./hack-coff $@ + +vmlinux.coff.initrd: coffboot hack-coff zImage ramdisk.image.gz + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \ + coffboot $@ + ./hack-coff $@ + +coffboot: $(OBJS) ld.script + $(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS) + +zImage: $(TOPDIR)/vmlinux elfextract + ./elfextract $(TOPDIR)/vmlinux | $(GZIP) >zImage + +hack-coff: hack-coff.c + $(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c + +elfextract: elfextract.c + $(HOSTCC) $(HOSTCFLAGS) -o elfextract elfextract.c + +clean: + rm -f elfextract hack-coff coffboot zImage vmlinux.coff + +fastdep: diff --git a/arch/ppc/coffboot/crt0.S b/arch/ppc/coffboot/crt0.S new file mode 100644 index 000000000..43ae4e0ec --- /dev/null +++ b/arch/ppc/coffboot/crt0.S @@ -0,0 +1,24 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ + .text + .globl _start +_start: + .long __start,0,0 + + .globl __start +__start: + lis 9,_start@h + lis 8,_etext@ha + addi 8,8,_etext@l +1: dcbf 0,9 + icbi 0,9 + addi 9,9,0x20 + cmplwi 0,9,8 + blt 1b + b start diff --git a/arch/ppc/coffboot/elfextract.c b/arch/ppc/coffboot/elfextract.c new file mode 100644 index 000000000..1b50ca475 --- /dev/null +++ b/arch/ppc/coffboot/elfextract.c @@ -0,0 +1,98 @@ +/* + * Extract the loadable program segment from an elf file. + * + * Copyright 1996 Paul Mackerras. + * + * 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 <stdio.h> +#include <linux/elf.h> + +FILE *fi, *fo; +char *ni, *no; +char buf[65536]; + +void +rd(void *buf, int len) +{ + int nr; + + nr = fread(buf, 1, len, fi); + if (nr == len) + return; + if (ferror(fi)) + fprintf(stderr, "%s: read error\n", ni); + else + fprintf(stderr, "%s: short file\n", ni); + exit(1); +} + +main(int ac, char **av) +{ + unsigned nb, len; + Elf32_Ehdr eh; + Elf32_Phdr ph; + + if (ac > 3 || ac > 1 && av[1][0] == '-') { + fprintf(stderr, "Usage: %s [elf-file [image-file]]\n", av[0]); + exit(0); + } + + fi = stdin; + ni = "(stdin)"; + fo = stdout; + no = "(stdout)"; + + if (ac > 1) { + ni = av[1]; + fi = fopen(ni, "rb"); + if (fi == NULL) { + perror(ni); + exit(1); + } + } + + rd(&eh, sizeof(eh)); + if (eh.e_ident[EI_MAG0] != ELFMAG0 + || eh.e_ident[EI_MAG1] != ELFMAG1 + || eh.e_ident[EI_MAG2] != ELFMAG2 + || eh.e_ident[EI_MAG3] != ELFMAG3) { + fprintf(stderr, "%s: not an ELF file\n", ni); + exit(1); + } + + fseek(fi, eh.e_phoff + (eh.e_phnum - 1) * sizeof(ph), 0); + rd(&ph, sizeof(ph)); + if (ph.p_type != PT_LOAD) { + fprintf(stderr, "%s: doesn't have a loadable segment\n", ni); + exit(1); + } + + if (ac > 2) { + no = av[2]; + fo = fopen(no, "wb"); + if (fo == NULL) { + perror(no); + exit(1); + } + } + + fseek(fi, ph.p_offset, 0); + for (len = ph.p_filesz; len != 0; len -= nb) { + nb = len; + if (nb > sizeof(buf)) + nb = sizeof(buf); + rd(buf, nb); + if (fwrite(buf, 1, nb, fo) != nb) { + fprintf(stderr, "%s: write error\n", no); + exit(1); + } + } + + fclose(fo); + fclose(fi); + exit(0); +} diff --git a/arch/ppc/coffboot/hack-coff.c b/arch/ppc/coffboot/hack-coff.c new file mode 100644 index 000000000..3dfa8d800 --- /dev/null +++ b/arch/ppc/coffboot/hack-coff.c @@ -0,0 +1,79 @@ +/* + * hack-coff.c - hack the header of an xcoff file to fill in + * a few fields needed by the Open Firmware xcoff loader on + * Power Macs but not initialized by objcopy. + * + * Copyright (C) Paul Mackerras 1997. + * + * 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 <stdio.h> +#include "rs6000.h" + +#define AOUT_MAGIC 0x010b + +#define get_16be(x) ((((unsigned char *)(x))[0] << 8) \ + + ((unsigned char *)(x))[1]) +#define put_16be(x, v) (((unsigned char *)(x))[0] = (v) >> 8, \ + ((unsigned char *)(x))[1] = (v) & 0xff) +#define get_32be(x) ((((unsigned char *)(x))[0] << 24) \ + + (((unsigned char *)(x))[1] << 16) \ + + (((unsigned char *)(x))[2] << 8) \ + + ((unsigned char *)(x))[3]) + +main(int ac, char **av) +{ + int fd; + int i, nsect; + int aoutsz; + struct external_filehdr fhdr; + AOUTHDR aout; + struct external_scnhdr shdr; + + if (ac != 2) { + fprintf(stderr, "Usage: hack-coff coff-file\n"); + exit(1); + } + if ((fd = open(av[1], 2)) == -1) { + perror(av[2]); + exit(1); + } + if (read(fd, &fhdr, sizeof(fhdr)) != sizeof(fhdr)) + goto readerr; + i = get_16be(fhdr.f_magic); + if (i != U802TOCMAGIC && i != U802WRMAGIC && i != U802ROMAGIC) { + fprintf(stderr, "%s: not an xcoff file\n", av[1]); + exit(1); + } + aoutsz = get_16be(fhdr.f_opthdr); + if (read(fd, &aout, aoutsz) != aoutsz) + goto readerr; + nsect = get_16be(fhdr.f_nscns); + for (i = 0; i < nsect; ++i) { + if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) + goto readerr; + if (strcmp(shdr.s_name, ".text") == 0) { + put_16be(aout.o_snentry, i+1); + put_16be(aout.o_sntext, i+1); + } else if (strcmp(shdr.s_name, ".data") == 0) { + put_16be(aout.o_sndata, i+1); + } else if (strcmp(shdr.s_name, ".bss") == 0) { + put_16be(aout.o_snbss, i+1); + } + } + put_16be(aout.magic, AOUT_MAGIC); + if (lseek(fd, (long) sizeof(struct external_filehdr), 0) == -1 + || write(fd, &aout, aoutsz) != aoutsz) { + fprintf(stderr, "%s: write error\n", av[1]); + exit(1); + } + close(fd); + exit(0); + +readerr: + fprintf(stderr, "%s: read error or file too short\n", av[1]); + exit(1); +} diff --git a/arch/ppc/coffboot/ld.script b/arch/ppc/coffboot/ld.script new file mode 100644 index 000000000..2469ed65d --- /dev/null +++ b/arch/ppc/coffboot/ld.script @@ -0,0 +1,68 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0 + .plt : { *(.plt) } + .text : + { + *(.text) + *(.rodata) + *(.rodata1) + *(.got1) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + _etext = .; + PROVIDE (etext = .); + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); +} + diff --git a/arch/ppc/coffboot/main.c b/arch/ppc/coffboot/main.c new file mode 100644 index 000000000..ab0ee6c4a --- /dev/null +++ b/arch/ppc/coffboot/main.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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 "nonstdio.h" +#include "rs6000.h" +#include "zlib.h" + +extern void *finddevice(const char *); +extern int getprop(void *, const char *, void *, int); +void gunzip(void *, int, unsigned char *, int *); + +#define get_16be(x) (*(unsigned short *)(x)) +#define get_32be(x) (*(unsigned *)(x)) + +#define RAM_START 0xc0000000 +#define RAM_END 0xc0800000 /* only 8M mapped with BATs */ + +#define RAM_FREE 0xc0540000 /* after image of coffboot */ + +char *avail_ram; +char *end_avail; + +coffboot(int a1, int a2, void *prom) +{ + void *options; + unsigned loadbase; + struct external_filehdr *eh; + struct external_scnhdr *sp; + struct external_scnhdr *isect, *rsect; + int ns, oh, i; + unsigned sa, len; + void *dst; + unsigned char *im; + unsigned initrd_start, initrd_size; + + printf("coffboot starting\n"); + options = finddevice("/options"); + if (options == (void *) -1) + exit(); + if (getprop(options, "load-base", &loadbase, sizeof(loadbase)) + != sizeof(loadbase)) { + printf("error getting load-base\n"); + exit(); + } + setup_bats(); + + eh = (struct external_filehdr *) loadbase; + ns = get_16be(eh->f_nscns); + oh = get_16be(eh->f_opthdr); + + sp = (struct external_scnhdr *) (loadbase + sizeof(struct external_filehdr) + oh); + isect = rsect = NULL; + for (i = 0; i < ns; ++i, ++sp) { + if (strcmp(sp->s_name, "image") == 0) + isect = sp; + else if (strcmp(sp->s_name, "initrd") == 0) + rsect = sp; + } + if (isect == NULL) { + printf("image section not found\n"); + exit(); + } + + if (rsect != NULL && (initrd_size = get_32be(rsect->s_size)) != 0) { + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + printf("initial ramdisk at %x (%u bytes)\n", + initrd_start, initrd_size); + memcpy((char *) initrd_start, + (char *) (loadbase + get_32be(rsect->s_scnptr)), + initrd_size); + end_avail = (char *) initrd_start; + } else { + end_avail = (char *) RAM_END; + } + + im = (unsigned char *)(loadbase + get_32be(isect->s_scnptr)); + len = get_32be(isect->s_size); + dst = (void *) RAM_START; + + if (im[0] == 0x1f && im[1] == 0x8b) { + void *cp = (void *) RAM_FREE; + avail_ram = (void *) (RAM_FREE + ((len + 7) & -8)); + memcpy(cp, im, len); + printf("gunzipping... "); + gunzip(dst, 0x400000, cp, &len); + printf("done\n"); + + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + + sa = *(unsigned *)dst + RAM_START; + printf("start address = 0x%x\n", sa); + +#if 0 + pause(); +#endif + (*(void (*)())sa)(a1, a2, prom); + + printf("returned?\n"); + + pause(); +} + +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) { + printf("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) { + printf("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) { + printf("gunzip: ran out of data in header\n"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n", r); + 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) { + printf("inflate returned %d\n", r); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} diff --git a/arch/ppc/coffboot/misc.S b/arch/ppc/coffboot/misc.S new file mode 100644 index 000000000..ae08ee2fa --- /dev/null +++ b/arch/ppc/coffboot/misc.S @@ -0,0 +1,50 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ + .text + +/* + * Use the BAT0 registers to map the 1st 8MB of RAM to 0xc0000000. + */ + .globl setup_bats +setup_bats: + mfpvr 3 + rlwinm 3,3,16,16,31 /* r3 = 1 for 601, 4 for 604 */ + cmpi 0,3,1 + lis 4,0xc000 + bne 4f + ori 4,4,4 /* set up BAT registers for 601 */ + li 5,0x7f + b 5f +4: ori 4,4,0xff /* set up BAT registers for 604 */ + li 5,2 + mtdbatu 0,4 + mtdbatl 0,5 +5: mtibatu 0,4 + mtibatl 0,5 + isync + blr + +/* + * Flush the dcache and invalidate the icache for a range of addresses. + * + * flush_cache(addr, len) + */ + .global flush_cache +flush_cache: + addi 4,4,0x1f /* len = (len + 0x1f) / 0x20 */ + rlwinm. 4,4,27,5,31 + mtctr 4 + beqlr +1: dcbf 0,3 + icbi 0,3 + addi 3,3,0x20 + bdnz 1b + sync + isync + blr diff --git a/arch/ppc/coffboot/nonstdio.h b/arch/ppc/coffboot/nonstdio.h new file mode 100644 index 000000000..664b8384a --- /dev/null +++ b/arch/ppc/coffboot/nonstdio.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ +typedef int FILE; +extern FILE *stdin, *stdout; +#define NULL ((void *)0) +#define EOF (-1) +#define fopen(n, m) NULL +#define fflush(f) 0 +#define fclose(f) 0 +extern char *fgets(); + +#define perror(s) printf("%s: no files!\n", (s)) diff --git a/arch/ppc/coffboot/rs6000.h b/arch/ppc/coffboot/rs6000.h new file mode 100644 index 000000000..0def1d95d --- /dev/null +++ b/arch/ppc/coffboot/rs6000.h @@ -0,0 +1,243 @@ +/* IBM RS/6000 "XCOFF" file definitions for BFD. + Copyright (C) 1990, 1991 Free Software Foundation, Inc. + FIXME: Can someone provide a transliteration of this name into ASCII? + Using the following chars caused a compiler warning on HIUX (so I replaced + them with octal escapes), and isn't useful without an understanding of what + character set it is. + Written by Mimi Ph\373\364ng-Th\345o V\365 of IBM + and John Gilmore of Cygnus Support. */ + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + char f_magic[2]; /* magic number */ + char f_nscns[2]; /* number of sections */ + char f_timdat[4]; /* time & date stamp */ + char f_symptr[4]; /* file pointer to symtab */ + char f_nsyms[4]; /* number of symtab entries */ + char f_opthdr[2]; /* sizeof(optional hdr) */ + char f_flags[2]; /* flags */ +}; + + /* IBM RS/6000 */ +#define U802WRMAGIC 0730 /* writeable text segments **chh** */ +#define U802ROMAGIC 0735 /* readonly sharable text segments */ +#define U802TOCMAGIC 0737 /* readonly text segments and TOC */ + +#define BADMAG(x) \ + ((x).f_magic != U802ROMAGIC && (x).f_magic != U802WRMAGIC && \ + (x).f_magic != U802TOCMAGIC) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + + +/********************** AOUT "OPTIONAL HEADER" **********************/ + + +typedef struct +{ + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char o_toc[4]; /* address of TOC */ + unsigned char o_snentry[2]; /* section number of entry point */ + unsigned char o_sntext[2]; /* section number of .text section */ + unsigned char o_sndata[2]; /* section number of .data section */ + unsigned char o_sntoc[2]; /* section number of TOC */ + unsigned char o_snloader[2]; /* section number of .loader section */ + unsigned char o_snbss[2]; /* section number of .bss section */ + unsigned char o_algntext[2]; /* .text alignment */ + unsigned char o_algndata[2]; /* .data alignment */ + unsigned char o_modtype[2]; /* module type (??) */ + unsigned char o_cputype[2]; /* cpu type */ + unsigned char o_maxstack[4]; /* max stack size (??) */ + unsigned char o_maxdata[4]; /* max data size (??) */ + unsigned char o_resv2[12]; /* reserved */ +} +AOUTHDR; + +#define AOUTSZ 72 +#define SMALL_AOUTSZ (28) +#define AOUTHDRSZ 72 + +#define RS6K_AOUTHDR_OMAGIC 0x0107 /* old: text & data writeable */ +#define RS6K_AOUTHDR_NMAGIC 0x0108 /* new: text r/o, data r/w */ +#define RS6K_AOUTHDR_ZMAGIC 0x010B /* paged: text r/o, both page-aligned */ + + +/********************** SECTION HEADER **********************/ + + +struct external_scnhdr { + char s_name[8]; /* section name */ + char s_paddr[4]; /* physical address, aliased s_nlib */ + char s_vaddr[4]; /* virtual address */ + char s_size[4]; /* section size */ + char s_scnptr[4]; /* file ptr to raw data for section */ + char s_relptr[4]; /* file ptr to relocation */ + char s_lnnoptr[4]; /* file ptr to line numbers */ + char s_nreloc[2]; /* number of relocation entries */ + char s_nlnno[2]; /* number of line number entries*/ + char s_flags[4]; /* flags */ +}; + +/* + * names of "special" sections + */ +#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" +#define _PAD ".pad" +#define _LOADER ".loader" + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + +/* XCOFF uses a special .loader section with type STYP_LOADER. */ +#define STYP_LOADER 0x1000 + +/* XCOFF uses a special .debug section with type STYP_DEBUG. */ +#define STYP_DEBUG 0x2000 + +/* XCOFF handles line number or relocation overflow by creating + another section header with STYP_OVRFLO set. */ +#define STYP_OVRFLO 0x8000 + +/********************** LINE NUMBERS **********************/ + +/* 1 line number entry for every "breakpointable" source line in a section. + * Line numbers are grouped on a per function basis; first entry in a function + * grouping will have l_lnno = 0 and in place of physical address will be the + * symbol table index of the function name. + */ +struct external_lineno { + union { + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_paddr[4]; /* (physical) address of line number */ + } l_addr; + char l_lnno[2]; /* line number */ +}; + + +#define LINENO struct external_lineno +#define LINESZ 6 + + +/********************** SYMBOLS **********************/ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +struct external_syment +{ + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; +}; + + + +#define N_BTMASK (017) +#define N_TMASK (060) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + + +union external_auxent { + struct { + char x_tagndx[4]; /* str, un, or enum tag indx */ + union { + struct { + char x_lnno[2]; /* declaration line number */ + char x_size[2]; /* str/union/array size */ + } x_lnsz; + char x_fsize[4]; /* size of function */ + } x_misc; + union { + struct { /* if ISFCN, tag, or .bb */ + char x_lnnoptr[4]; /* ptr to fcn line # */ + char x_endndx[4]; /* entry ndx past block end */ + } x_fcn; + struct { /* if ISARY, up to 4 dimen. */ + char x_dimen[E_DIMNUM][2]; + } x_ary; + } x_fcnary; + char x_tvndx[2]; /* tv index */ + } x_sym; + + union { + char x_fname[E_FILNMLEN]; + struct { + char x_zeroes[4]; + char x_offset[4]; + } x_n; + } x_file; + + struct { + char x_scnlen[4]; /* section length */ + char x_nreloc[2]; /* # relocation entries */ + char x_nlinno[2]; /* # line numbers */ + } x_scn; + + struct { + char x_tvfill[4]; /* tv fill value */ + char x_tvlen[2]; /* length of .tv */ + char x_tvran[2][2]; /* tv range */ + } x_tv; /* info about .tv section (in auxent of symbol .tv)) */ + + struct { + unsigned char x_scnlen[4]; + unsigned char x_parmhash[4]; + unsigned char x_snhash[2]; + unsigned char x_smtyp[1]; + unsigned char x_smclas[1]; + unsigned char x_stab[4]; + unsigned char x_snstab[2]; + } x_csect; + +}; + +#define SYMENT struct external_syment +#define SYMESZ 18 +#define AUXENT union external_auxent +#define AUXESZ 18 +#define DBXMASK 0x80 /* for dbx storage mask */ +#define SYMNAME_IN_DEBUG(symptr) ((symptr)->n_sclass & DBXMASK) + + + +/********************** RELOCATION DIRECTIVES **********************/ + + +struct external_reloc { + char r_vaddr[4]; + char r_symndx[4]; + char r_size[1]; + char r_type[1]; +}; + + +#define RELOC struct external_reloc +#define RELSZ 10 + +#define DEFAULT_DATA_SECTION_ALIGNMENT 4 +#define DEFAULT_BSS_SECTION_ALIGNMENT 4 +#define DEFAULT_TEXT_SECTION_ALIGNMENT 4 +/* For new sections we havn't heard of before */ +#define DEFAULT_SECTION_ALIGNMENT 4 diff --git a/arch/ppc/coffboot/start.c b/arch/ppc/coffboot/start.c new file mode 100644 index 000000000..363e65962 --- /dev/null +++ b/arch/ppc/coffboot/start.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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 <stdarg.h> + +int (*prom)(); + +void *chosen_handle; +void *stdin; +void *stdout; +void *stderr; + +void exit(void); +void *finddevice(const char *name); +int getprop(void *phandle, const char *name, void *buf, int buflen); + +void +start(int a1, int a2, void *promptr) +{ + prom = (int (*)()) promptr; + chosen_handle = finddevice("/chosen"); + if (chosen_handle == (void *) -1) + exit(); + if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) + exit(); + stderr = stdout; + if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) + exit(); + + coffboot(a1, a2, promptr); + for (;;) + exit(); +} + +int +write(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +int +read(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +void +exit() +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom)(&args); + } +} + +void +pause() +{ + struct prom_args { + char *service; + } args; + + args.service = "enter"; + (*prom)(&args); +} + +void * +finddevice(const char *name) +{ + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + void *phandle; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.phandle = (void *) -1; + (*prom)(&args); + return args.phandle; +} + +int +getprop(void *phandle, const char *name, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *phandle; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.phandle = phandle; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*prom)(&args); + return args.size; +} + +int +putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + putc('\r', f); + return write(f, &ch, 1) == 1? c: -1; +} + +int +putchar(int c) +{ + return putc(c, stdout); +} + +int +fputs(char *str, void *f) +{ + int n = strlen(str); + + return write(f, str, n) == n? 0: -1; +} + +int +readchar() +{ + char ch; + + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\r\n"); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +getchar() +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +extern int vsprintf(char *buf, const char *fmt, va_list args); +static char sprint_buf[1024]; + +void +printk(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); +} + +int +printf(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); + return n; +} diff --git a/arch/ppc/coffboot/string.S b/arch/ppc/coffboot/string.S new file mode 100644 index 000000000..ba83591b7 --- /dev/null +++ b/arch/ppc/coffboot/string.S @@ -0,0 +1,206 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * 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. + */ +#define r0 0 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr diff --git a/arch/ppc/coffboot/zlib.c b/arch/ppc/coffboot/zlib.c new file mode 100644 index 000000000..12d07df41 --- /dev/null +++ b/arch/ppc/coffboot/zlib.c @@ -0,0 +1,2143 @@ +/* + * This file is derived from various .h and .c files from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. See zlib.h for conditions of + * distribution and use. + * + * Changes that have been made include: + * - changed functions not used outside this file to "local" + * - added minCompression parameter to deflateInit2 + * - added Z_PACKET_FLUSH (see zlib.h for details) + * - added inflateIncomp + * + * $Id: zlib.c,v 1.1 1997/08/30 04:51:48 ralf Exp $ + */ + +/*+++++*/ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */ + +#define _Z_UTIL_H + +#include "zlib.h" + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#define FAR + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern char *z_errmsg[]; /* indexed by 1-zlib_error */ + +#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) +/* To be used only when the state is known to be valid */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + + /* common constants */ + +#define DEFLATED 8 + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + + /* functions */ + +#include <string.h> +#define zmemcpy memcpy +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include <stdio.h> +# ifndef verbose +# define verbose 0 +# endif +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); + +/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ +/* void zcfree OF((voidpf opaque, voidpf ptr)); */ + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr, size) \ + (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) +#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} + +/* deflate.h -- internal compression state + * Copyright (C) 1995 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/*+++++*/ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +local inflate_blocks_statef * inflate_blocks_new OF(( + z_stream *z, + check_func c, /* check function */ + uInt w)); /* window size */ + +local int inflate_blocks OF(( + inflate_blocks_statef *, + z_stream *, + int)); /* initial return code */ + +local void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_addhistory OF(( + inflate_blocks_statef *, + z_stream *)); + +local int inflate_packet_flush OF(( + inflate_blocks_statef *)); + +/*+++++*/ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt Nalloc; /* number of these allocated here */ + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + local uInt inflate_hufts; +#endif + +local int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +local int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_stream *)); /* for zfree function */ + + +/*+++++*/ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +local inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_stream *)); + +local int inflate_codes OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +local void inflate_codes_free OF(( + inflate_codes_statef *, + z_stream *)); + + +/*+++++*/ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state, sizeof(struct internal_state)); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z, w) +z_stream *z; +int w; +{ + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; +/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ +/* if (z->zfree == Z_NULL) z->zfree = zcfree; */ + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit(z) +z_stream *z; +{ + return inflateInit2(z, DEF_WBITS); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_stream *z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) + { + z->state->mode = BAD; + z->msg = "unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = "invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + if ((b = NEXTBYTE) & 0x20) + { + z->state->mode = BAD; + z->msg = "invalid reserved bit"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = "incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + z->state->mode = BLOCKS; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = "incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_stream *z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE + +/*+++++*/ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ + mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + int nblens; /* # elements allocated at blens */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl, *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define DUMPBITS(j) {b>>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (q<s->read?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* And'ing with mask[n] masks the lower n bits */ +local uInt inflate_mask[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +/*+++++*/ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_stream *)); + + +/*+++++*/ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* Table for deflate from PKZIP's appnote.txt. */ +local uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +local void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +local inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_stream *z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +local int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = "invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if (((~b) >> 16) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : TYPE; + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.trees.nblens = t; + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + s->mode = BADB; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + s->mode = BADB; + z->msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window, s->end - s->window); + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +local int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WRAP */ /* expand WRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +local int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} + + +/*+++++*/ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + uIntf *, /* list of base values for non-simple codes */ + uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_stream *)); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +local void ffree OF(( + voidpf q, /* opaque pointer (not used) */ + voidpf p, /* what to free (not used) */ + uInt n)); /* number of bytes (not used) */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* actually lengths - 2; also see note #13 above about 258 */ +local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ +local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local uInt cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +uIntf *d; /* list of base values for non-simple codes */ +uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_stream *zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (all zero length codes or an + over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } + q->word.Nalloc = z + 1; +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +local int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tb, z); + z->msg = "incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_lock = 0; +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local uInt fixed_left = FIXEDH; +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer (not used) */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= fixed_left, + "inflate_trees falloc overflow"); + if (q) s++; /* to make some compilers happy */ + fixed_left -= n; + return (voidpf)(fixed_mem + fixed_left); +} + + +local void ffree(q, p, n) +voidpf q; +voidpf p; +uInt n; +{ + Assert(0, "inflate_trees ffree called!"); + if (q) q = p; /* to make some compilers happy */ +} + + +local int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not built already--lock out other instances */ + while (++fixed_lock > 1) + fixed_lock--; + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = ffree; + z.opaque = Z_NULL; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + fixed_built = 1; + } + fixed_lock--; + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +local int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_stream *z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); + p = q; + } + return Z_OK; +} + +/*+++++*/ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl, *td; +z_stream *z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +local int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_stream *z; +{ + ZFREE(z, c, sizeof(struct inflate_codes_state)); + Tracev((stderr, "inflate: codes free\n")); +} + +/*+++++*/ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt n; + Bytef *p, *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} + + +/*+++++*/ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +local int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl, *td; +inflate_blocks_statef *s; +z_stream *z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = "invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = "invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} + + +/*+++++*/ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ + +char *zlib_version = ZLIB_VERSION; + +char *z_errmsg[] = { +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +""}; + + +/*+++++*/ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf) {s1 += *buf++; s2 += s1;} +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); +#define DO16(buf) DO8(buf); DO8(buf); + +/* ========================================================================= */ +uLong adler32(adler, buf, len) + uLong adler; + Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + k -= 16; + } + if (k != 0) do { + DO1(buf); + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff --git a/arch/ppc/coffboot/zlib.h b/arch/ppc/coffboot/zlib.h new file mode 100644 index 000000000..f4ab77617 --- /dev/null +++ b/arch/ppc/coffboot/zlib.h @@ -0,0 +1,432 @@ +/* $Id: zlib.h,v 1.1 1997/08/30 04:51:49 ralf Exp $ */ + +/* + * This file is derived from zlib.h and zconf.h from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. + */ + +/* + * ==FILEVERSION 960122== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 0.95, Aug 16th, 1995. + + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + */ + +#ifndef _ZLIB_H +#define _ZLIB_H + +/* #include "zconf.h" */ /* included directly here */ + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ + +/* + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. + */ + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints + * at addresses which are not a multiple of their size. + * Under DOS, -DFAR=far or -DFAR=__far may be needed. + */ + +#ifndef STDC +# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) +# define STDC +# endif +#endif + +#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ +# include <unix.h> +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +#ifndef FAR +# define FAR +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef Byte FAR Bytef; +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +/* end of original zconf.h */ + +#define ZLIB_VERSION "0.95P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidp opaque; /* private data object passed to zalloc and zfree */ + + Byte data_type; /* best guess about the data type: ascii or binary */ + +} z_stream; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_FULL_FLUSH 2 +#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ +#define Z_FINISH 4 +#define Z_PACKET_FLUSH 5 +/* See deflate() below for the usage of these constants */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +/* error codes for the compression/decompression functions */ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Used to set the data_type field */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +extern char *zlib_version; +/* The application can compare zlib_version and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + */ + + /* basic functions */ + +extern int inflateInit OF((z_stream *strm)); +/* + Initializes the internal stream state for decompression. The fields + zalloc and zfree must be initialized before by the caller. If zalloc and + zfree are set to Z_NULL, inflateInit updates them to use default allocation + functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory. msg is set to null if there is no error message. + inflateInit does not perform any decompression: this will be done by + inflate(). +*/ + + +extern int inflate OF((z_stream *strm, int flush)); +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() always provides as much output as possible + (until there is no more input data or no more space in the output buffer). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). + + If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, + inflate flushes as much output as possible to the output buffer. The + flushing behavior of inflate is not specified for values of the flush + parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the + current implementation actually flushes as much output as possible + anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data + has been consumed, it is expecting to see the length field of a stored + block; if not, it returns Z_DATA_ERROR. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if + the stream structure was inconsistent (for example if next_in or next_out + was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no + progress is possible or if there was not enough room in the output buffer + when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then + call inflateSync to look for a good compression block. */ + + +extern int inflateEnd OF((z_stream *strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* advanced functions */ + +extern int inflateInit2 OF((z_stream *strm, + int windowBits)); +/* + This is another version of inflateInit with more compression options. The + fields next_out, zalloc and zfree must be initialized before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1<<windowBits bytes. If next_out is null, the + library will allocate its own buffer (and leave next_out null). next_in + need not be provided here but must be provided by the application for the + next call of inflate(). + + If the history buffer is provided by the application, next_out must + never be changed by the application since the decompressor maintains + history information inside this buffer from call to call; the application + can only reset next_out to the beginning of the history buffer when + avail_out is zero and all output has been consumed. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + windowBits < 8). msg is set to null if there is no error message. + inflateInit2 does not perform any decompression: this will be done by + inflate(). +*/ + +extern int inflateSync OF((z_stream *strm)); +/* + Skips invalid compressed data until the special marker (see deflate() + above) can be found, or until all available input is skipped. No output + is provided. + + inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no marker has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +extern int inflateReset OF((z_stream *strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +extern int inflateIncomp OF((z_stream *strm)); +/* + This function adds the data at next_in (avail_in bytes) to the output + history without performing any output. There must be no pending output, + and the decompressor must be expecting to see the start of a block. + Calling this function is equivalent to decompressing a stored block + containing the data at next_in (except that the data is not output). +*/ + + /* checksum functions */ + +/* + This function is not related to compression but is exported + anyway because it might be useful in applications using the + compression library. +*/ + +extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +#ifndef _Z_UTIL_H + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +#endif /* _ZLIB_H */ diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 9df35fa24..1cea7ce3f 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -1,36 +1,44 @@ -# +# $Id: config.in,v 1.19 1997/09/04 01:54:26 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # mainmenu_name "Linux/PowerPC Kernel Configuration" -if [ "`uname`" != "Linux" ]; then +mainmenu_option next_comment +comment 'Platform support' +define_bool CONFIG_PPC y + +if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then define_bool CONFIG_CROSSCOMPILE y else define_bool CONFIG_NATIVE y fi -bool 'Build PowerMac Kernel (not PReP)?' CONFIG_PMAC -bool 'Build PReP Kernel (not PowerMac)?' CONFIG_PREP -bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL -bool 'Used Harddrive LED on IBM 83x workstations as heartbeat?' CONFIG_HEARTBEAT -bool 'Used PowerPC specific powersaving?' CONFIG_POWERSAVING +bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC +bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP +bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP + choice 'Processor type' \ - "Common CONFIG_MCOMMON \ - 601 CONFIG_M601 \ - 603 CONFIG_M603 \ - 604 CONFIG_M604" Common + "Common CONFIG_COMMON \ + 601 CONFIG_601 \ + 603 CONFIG_603 \ + 604 CONFIG_604" Common +endmenu + +mainmenu_option next_comment +comment 'General setup' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD fi - -mainmenu_option next_comment define_bool CONFIG_PCI y -bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE +if [ "$CONFIG_PREP" = "y" ]; then + bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE +fi bool 'Networking support' CONFIG_NET bool 'Sysctl support' CONFIG_SYSCTL bool 'System V IPC' CONFIG_SYSVIPC @@ -39,8 +47,24 @@ bool 'System V IPC' CONFIG_SYSVIPC define_bool CONFIG_BINFMT_ELF y define_bool CONFIG_KERNEL_ELF y tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC -tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA +tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA + +if [ "$CONFIG_PMAC" = "y" ]; then + define_bool CONFIG_PMAC_CONSOLE y + define_bool CONFIG_MAC_KEYBOARD y + define_bool CONFIG_MAC_FLOPPY y + bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE + bool 'Include xmon kernel debugger' CONFIG_XMON +fi + +if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then + bool 'Support for ATI Mach64 display cards' CONFIG_ATY_VIDEO + bool 'Support for IMS Twin Turbo display card' CONFIG_IMSTT_VIDEO +else + define_bool CONFIG_VGA_CONSOLE y +fi +endmenu source drivers/pnp/Config.in source drivers/block/Config.in @@ -83,6 +107,7 @@ fi endmenu source fs/Config.in + source drivers/char/Config.in mainmenu_option next_comment @@ -93,12 +118,12 @@ if [ "$CONFIG_SOUND" != "n" ]; then fi endmenu -mainmenu_option next_comment +#mainmenu_option next_comment #comment 'Kernel hacking' #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC #bool 'Kernel profiling support' CONFIG_PROFILE #if [ "$CONFIG_PROFILE" = "y" ]; then # int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 #fi -endmenu +#endmenu diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig new file mode 100644 index 000000000..ea39e83ea --- /dev/null +++ b/arch/ppc/defconfig @@ -0,0 +1,250 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_NATIVE=y +# CONFIG_PMAC is not set +CONFIG_PREP=y +CONFIG_MCOMMON=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +CONFIG_VGA_CONSOLE=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# 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_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +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_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# 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_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 +CONFIG_SCSI_NCR53C8XX_SYNC=5 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_XTP is not set +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_PATH_MTU_DISCOVERY=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +CONFIG_EL3=y +# CONFIG_VORTEX is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +CONFIG_PCNET32=y +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +# CONFIG_SERIAL_SHARE_IRQ is not set +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +CONFIG_MOUSE=y +# 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_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/ignore b/arch/ppc/ignore new file mode 100644 index 000000000..693fff61a --- /dev/null +++ b/arch/ppc/ignore @@ -0,0 +1,65 @@ +.config +compile.h +.version +.objects +.blurb +*.cort +*.paul +*.old +.defines +version.h +find_name +checks +#* +.objects +.object_files +System.map +asm +.menuconfig* +CVS +ppc_defs.h +mk_defs +mkprep +*.s +.depend +.hdepend +*~ +*.o +znetboot +zvmlinux +vmlinux +zImage +hack-coff +coffboot +vmlinux.coff +.depend +.cvsignore +RCS +SCCS +CVS.adm +RCSLOG +cvslog.* +tags +TAGS +.make.state +.nse_depinfo +*~ +#* +.#* +,* +_$* +*$ +*.old +*.bak +*.BAK +*.orig +*.rej +*.a +*.olb +*.o +*.obj +*.so +*.exe +*.Z +*.elc +*.ln diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 941cd9e38..6fd50a6fb 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -7,39 +7,21 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) $(ASFLAGS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< -.S.s: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s .S.o: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s - $(AS) $(ASFLAGS) -o $*.o $*.s - rm $*.s + $(CC) -D__ASSEMBLY__ -c $< -o $*.o -HOST_CC = gcc - -OBJS = misc.o port_io.o pci.o traps.o process.o \ - signal.o syscalls.o ptrace.o ksyms.o irq.o bitops.o strcase.o ppc_htab.o +O_TARGET := kernel.o +O_OBJS := misc.o traps.o process.o signal.o syscalls.o \ + align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \ + time.o prep_time.o pmac_time.o chrp_time.o \ + setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \ + pci.o prep_pci.o pmac_pci.o chrp_pci.o \ + residual.o prom.o +OX_OBJS := ppc_ksyms.o all: head.o kernel.o -head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h - -ifeq ($(CONFIG_PREP),y) -OBJS += prep_setup.o prep_time.o -endif - -ifeq ($(CONFIG_PMAC),y) -OBJS += pmac_setup.o pmac_support.o align.o pmac_time.o -endif - -ifeq ($(CONFIG_MODULES),y) -OBJS = ksyms.o -endif +head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h ppc_defs.h: mk_defs.c ppc_defs.head \ $(TOPDIR)/include/asm/mmu.h \ @@ -52,25 +34,7 @@ ppc_defs.h: mk_defs.c ppc_defs.head \ rm mk_defs.s checks: checks.c - $(HOSTCC) ${CFLAGS} -o checks checks.c - checks - -kernel.o: $(OBJS) - $(LD) -r -o kernel.o $(OBJS) + $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c + ./checks -fastdep: - $(TOPDIR)/scripts/mkdep *.[Sch] > .depend - -dep: - $(CPP) -M *.S *.c > .depend - -modules: - -dummy: - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c new file mode 100644 index 000000000..39cb04f77 --- /dev/null +++ b/arch/ppc/kernel/align.c @@ -0,0 +1,290 @@ +/* + * align.c - handle alignment exceptions for the Power PC. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au). + */ +#include <linux/kernel.h> +#include <linux/mm.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#define INVALID { 0, 0 } + +#define LD 1 /* load */ +#define ST 2 /* store */ +#define SE 4 /* sign-extend value */ +#define F 8 /* to/from fp regs */ +#define U 0x10 /* update index register */ +#define M 0x20 /* multiple load/store */ +#define S 0x40 /* single-precision fp, or byte-swap value */ +#define HARD 0x80 /* string, stwcx. */ + +/* + * The PowerPC stores certain bits of the instruction that caused the + * alignment exception in the DSISR register. This array maps those + * bits to information about the operand length and what the + * instruction would do. + */ +static struct aligninfo aligninfo[128] = { + { 4, LD }, /* 00 0 0000: lwz / lwarx */ + INVALID, /* 00 0 0001 */ + { 4, ST }, /* 00 0 0010: stw */ + INVALID, /* 00 0 0011 */ + { 2, LD }, /* 00 0 0100: lhz */ + { 2, LD+SE }, /* 00 0 0101: lha */ + { 2, ST }, /* 00 0 0110: sth */ + { 4, LD+M }, /* 00 0 0111: lmw */ + { 4, LD+F+S }, /* 00 0 1000: lfs */ + { 8, LD+F }, /* 00 0 1001: lfd */ + { 4, ST+F+S }, /* 00 0 1010: stfs */ + { 8, ST+F }, /* 00 0 1011: stfd */ + INVALID, /* 00 0 1100 */ + INVALID, /* 00 0 1101 */ + INVALID, /* 00 0 1110 */ + INVALID, /* 00 0 1111 */ + { 4, LD+U }, /* 00 1 0000: lwzu */ + INVALID, /* 00 1 0001 */ + { 4, ST+U }, /* 00 1 0010: stwu */ + INVALID, /* 00 1 0011 */ + { 2, LD+U }, /* 00 1 0100: lhzu */ + { 2, LD+SE+U }, /* 00 1 0101: lhau */ + { 2, ST+U }, /* 00 1 0110: sthu */ + { 4, ST+M }, /* 00 1 0111: stmw */ + { 4, LD+F+S+U }, /* 00 1 1000: lfsu */ + { 8, LD+F+U }, /* 00 1 1001: lfdu */ + { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ + { 8, ST+F+U }, /* 00 1 1011: stfdu */ + INVALID, /* 00 1 1100 */ + INVALID, /* 00 1 1101 */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + INVALID, /* 01 0 0000 */ + INVALID, /* 01 0 0001 */ + INVALID, /* 01 0 0010 */ + INVALID, /* 01 0 0011 */ + INVALID, /* 01 0 0100 */ + INVALID, /* 01 0 0101: lwax?? */ + INVALID, /* 01 0 0110 */ + INVALID, /* 01 0 0111 */ + { 0, LD+HARD }, /* 01 0 1000: lswx */ + { 0, LD+HARD }, /* 01 0 1001: lswi */ + { 0, ST+HARD }, /* 01 0 1010: stswx */ + { 0, ST+HARD }, /* 01 0 1011: stswi */ + INVALID, /* 01 0 1100 */ + INVALID, /* 01 0 1101 */ + INVALID, /* 01 0 1110 */ + INVALID, /* 01 0 1111 */ + INVALID, /* 01 1 0000 */ + INVALID, /* 01 1 0001 */ + INVALID, /* 01 1 0010 */ + INVALID, /* 01 1 0011 */ + INVALID, /* 01 1 0100 */ + INVALID, /* 01 1 0101: lwaux?? */ + INVALID, /* 01 1 0110 */ + INVALID, /* 01 1 0111 */ + INVALID, /* 01 1 1000 */ + INVALID, /* 01 1 1001 */ + INVALID, /* 01 1 1010 */ + INVALID, /* 01 1 1011 */ + INVALID, /* 01 1 1100 */ + INVALID, /* 01 1 1101 */ + INVALID, /* 01 1 1110 */ + INVALID, /* 01 1 1111 */ + INVALID, /* 10 0 0000 */ + INVALID, /* 10 0 0001 */ + { 0, ST+HARD }, /* 10 0 0010: stwcx. */ + INVALID, /* 10 0 0011 */ + INVALID, /* 10 0 0100 */ + INVALID, /* 10 0 0101 */ + INVALID, /* 10 0 0110 */ + INVALID, /* 10 0 0111 */ + { 4, LD+S }, /* 10 0 1000: lwbrx */ + INVALID, /* 10 0 1001 */ + { 4, ST+S }, /* 10 0 1010: stwbrx */ + INVALID, /* 10 0 1011 */ + { 2, LD+S }, /* 10 0 1100: lhbrx */ + INVALID, /* 10 0 1101 */ + { 2, ST+S }, /* 10 0 1110: sthbrx */ + INVALID, /* 10 0 1111 */ + INVALID, /* 10 1 0000 */ + INVALID, /* 10 1 0001 */ + INVALID, /* 10 1 0010 */ + INVALID, /* 10 1 0011 */ + INVALID, /* 10 1 0100 */ + INVALID, /* 10 1 0101 */ + INVALID, /* 10 1 0110 */ + INVALID, /* 10 1 0111 */ + INVALID, /* 10 1 1000 */ + INVALID, /* 10 1 1001 */ + INVALID, /* 10 1 1010 */ + INVALID, /* 10 1 1011 */ + INVALID, /* 10 1 1100 */ + INVALID, /* 10 1 1101 */ + INVALID, /* 10 1 1110 */ + { 0, ST+HARD }, /* 10 1 1111: dcbz */ + { 4, LD }, /* 11 0 0000: lwzx */ + INVALID, /* 11 0 0001 */ + { 4, ST }, /* 11 0 0010: stwx */ + INVALID, /* 11 0 0011 */ + { 2, LD }, /* 11 0 0100: lhzx */ + { 2, LD+SE }, /* 11 0 0101: lhax */ + { 2, ST }, /* 11 0 0110: sthx */ + INVALID, /* 11 0 0111 */ + { 4, LD+F+S }, /* 11 0 1000: lfsx */ + { 8, LD+F }, /* 11 0 1001: lfdx */ + { 4, ST+F+S }, /* 11 0 1010: stfsx */ + { 8, ST+F }, /* 11 0 1011: stfdx */ + INVALID, /* 11 0 1100 */ + INVALID, /* 11 0 1101 */ + INVALID, /* 11 0 1110 */ + INVALID, /* 11 0 1111 */ + { 4, LD+U }, /* 11 1 0000: lwzux */ + INVALID, /* 11 1 0001 */ + { 4, ST+U }, /* 11 1 0010: stwux */ + INVALID, /* 11 1 0011 */ + { 2, LD+U }, /* 11 1 0100: lhzux */ + { 2, LD+SE+U }, /* 11 1 0101: lhaux */ + { 2, ST+U }, /* 11 1 0110: sthux */ + INVALID, /* 11 1 0111 */ + { 4, LD+F+S+U }, /* 11 1 1000: lfsux */ + { 8, LD+F+U }, /* 11 1 1001: lfdux */ + { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ + { 8, ST+F+U }, /* 11 1 1011: stfdux */ + INVALID, /* 11 1 1100 */ + INVALID, /* 11 1 1101 */ + INVALID, /* 11 1 1110 */ + INVALID, /* 11 1 1111 */ +}; + +#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) + +int +fix_alignment(struct pt_regs *regs) +{ + int instr, nb, flags; + int i, t; + int reg, areg; + unsigned char *addr; + union { + long l; + float f; + double d; + unsigned char v[8]; + } data; + + instr = (regs->dsisr >> 10) & 0x7f; + nb = aligninfo[instr].len; + if (nb == 0) + return 0; /* too hard or invalid instruction bits */ + flags = aligninfo[instr].flags; + addr = (unsigned char *) regs->dar; + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + + /* Verify the address of the operand */ + if (user_mode(regs)) { + if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) + return -EFAULT; /* bad address */ + } + + if ((flags & F) && last_task_used_math == current) + giveup_fpu(); + + if (flags & M) + return 0; /* too hard for now */ + + /* If we read the operand, copy it in */ + if (flags & LD) { + if (nb == 2) { + data.v[0] = data.v[1] = 0; + if (__get_user(data.v[2], addr) + || __get_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__get_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + switch (flags & ~U) { + case LD+SE: + if (data.v[2] >= 0x80) + data.v[0] = data.v[1] = -1; + /* fall through */ + case LD: + regs->gpr[reg] = data.l; + break; + case LD+S: + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + regs->gpr[reg] = data.l; + break; + case ST: + data.l = regs->gpr[reg]; + break; + case ST+S: + data.l = regs->gpr[reg]; + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + break; + case LD+F: + current->tss.fpr[reg] = data.d; + break; + case ST+F: + 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: + giveup_fpu(); + cvt_fd(&data.f, ¤t->tss.fpr[reg]); + /* current->tss.fpr[reg] = data.f; */ + break; + case ST+F+S: + giveup_fpu(); + cvt_df(¤t->tss.fpr[reg], &data.f); + /* data.f = current->tss.fpr[reg]; */ + break; + default: + printk("align: can't handle flags=%x\n", flags); + return 0; + } + + if (flags & ST) { + if (nb == 2) { + if (__put_user(data.v[2], addr) + || __put_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__put_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + if (flags & U) { + areg = regs->dsisr & 0x1f; /* register to update */ + regs->gpr[areg] = regs->dar; + } + + return 1; +} diff --git a/arch/ppc/kernel/bitops.c b/arch/ppc/kernel/bitops.c new file mode 100644 index 000000000..fb5a19e3a --- /dev/null +++ b/arch/ppc/kernel/bitops.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ + +#include <linux/kernel.h> +#include <asm/bitops.h> + +/* + * I left these here since the problems with "cc" make it difficult to keep + * them in bitops.h -- Cort + */ +void set_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "set_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + or %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +void clear_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "clear_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + andc %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +void change_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "change_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + xor %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +int test_and_set_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_set_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + or %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_clear_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_clear_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + andc %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_change_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_change_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + xor %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +/* I put it in bitops.h -- Cort */ +#if 0 +int ffz(unsigned int x) +{ + int n; + + x = ~x & (x+1); /* set LS zero to 1, other bits to 0 */ + __asm__ ("cntlzw %0,%1" : "=r" (n) : "r" (x)); + return 31 - n; +} + +/* + * This implementation of find_{first,next}_zero_bit was stolen from + * Linus' asm-alpha/bitops.h. + */ + +int find_first_zero_bit(void * addr, int size) +{ + unsigned int * p = ((unsigned int *) addr); + unsigned int result = 0; + unsigned int tmp; + + if (size == 0) + return 0; + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} + +/* + * Find next zero bit in a bitmap reasonably efficiently.. + */ +int find_next_zero_bit(void * addr, int size, int offset) +{ + unsigned int * p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31UL; + unsigned int tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (32-offset); + if (size < 32) + goto found_first; + if (~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} +#endif diff --git a/arch/ppc/kernel/checks.c b/arch/ppc/kernel/checks.c new file mode 100644 index 000000000..312229552 --- /dev/null +++ b/arch/ppc/kernel/checks.c @@ -0,0 +1,57 @@ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/config.h> + +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/smp_lock.h> + +/* + * Do various before compile checks of data structures + * -- Cort + */ +int main(void) +{ + int ret = 0; +#if 0 + if ( sizeof(struct thread_struct) % 16 ) + { + printf("Thread struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct thread_struct), + sizeof(struct thread_struct)%16); + ret = -1; + } +#endif + + if ( sizeof(struct pt_regs) % 16 ) + { + printf("pt_regs struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct pt_regs), + sizeof(struct pt_regs)%16); + ret = -1; + + } + + printf("Task size : %d bytes\n" + "Tss size : %d bytes\n" + "pt_regs size : %d bytes\n" + "Kernel stack size: %d bytes\n", + sizeof(struct task_struct), sizeof(struct thread_struct), + sizeof(struct pt_regs), + sizeof(union task_union) - sizeof(struct task_struct)); + return ret; +} diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c new file mode 100644 index 000000000..8af9ba9d6 --- /dev/null +++ b/arch/ppc/kernel/chrp_pci.c @@ -0,0 +1,156 @@ +/* + * CHRP pci routines. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/openpic.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/hydra.h> + +/* LongTrail */ +#define pci_config_addr(bus, dev, offset) \ + (0xfec00000 | ((bus)<<16) | ((dev)<<8) | (offset)) + +int chrp_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)); + if (offset == PCI_INTERRUPT_LINE) { + /* PCI interrupts are controlled by the OpenPIC */ + if (*val) + *val = openpic_to_irq(*val); + } + return PCIBIOS_SUCCESSFUL; +} + +int chrp_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; +} + +int chrp_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; +} + +int chrp_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; +} + +int chrp_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; +} + +int chrp_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; +} + +int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int num, devfn; + unsigned int x, vendev; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (devfn = 0; devfn < 32; devfn++) { + chrp_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); + if (x == vendev) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devfn<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int chrp_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int devnr, x, num; + + num = 0; + for (devnr = 0; devnr < 32; devnr++) { + chrp_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); + if ((x>>8) == class_code) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devnr<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +__initfunc(volatile struct Hydra *find_hydra(void)) +{ + u_char bus, dev; + volatile struct Hydra *hydra = 0; + if (chrp_pcibios_find_device(PCI_VENDOR_ID_APPLE, + PCI_DEVICE_ID_APPLE_HYDRA, 0, &bus, &dev) + == PCIBIOS_SUCCESSFUL) + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, + (unsigned int *)&hydra); + return hydra; +} + +__initfunc(void hydra_post_openpic_init(void)) +{ + openpic_set_sense(HYDRA_INT_SCSI_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCA_TX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCA_RX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCB_TX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCB_RX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCSI, 1); + openpic_set_sense(HYDRA_INT_SCCA, 1); + openpic_set_sense(HYDRA_INT_SCCB, 1); + openpic_set_sense(HYDRA_INT_VIA, 1); + openpic_set_sense(HYDRA_INT_ADB, 1); + openpic_set_sense(HYDRA_INT_ADB_NMI, 0); +} diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c new file mode 100644 index 000000000..0a5544b96 --- /dev/null +++ b/arch/ppc/kernel/chrp_setup.c @@ -0,0 +1,180 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +/* for the mac fs */ +kdev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +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 */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + + +extern char saved_command_line[256]; + +long TotalMemory; + +int +chrp_get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + int len; + char *model; + + switch (pvr>>16) + { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + case 9: + model = "604e"; + break; + default: + model = "unknown"; + break; + } + + len = sprintf(buffer, "PowerPC %s rev %d.%d\n", model, + (pvr & 0xff00) >> 8, pvr & 0xff); + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + (loops_per_sec+2500)/500000, + ((loops_per_sec+2500)/5000) % 100); + +#if 0 + /* + * Ooh's and aah's info about zero'd pages in idle task + */ + { + extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; + len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " + "current: %u (%uKb) hits: %u/%u (%lu%%)\n", + zerototal, (zerototal*PAGE_SIZE)>>10, + zerocount, (zerocount*PAGE_SIZE)>>10, + zeropage_hits,zeropage_calls, + /* : 1 below is so we don't div by zero */ + (zeropage_hits*100) / + ((zeropage_calls)?zeropage_calls:1)); + } +#endif + return len; +} + +__initfunc(void +chrp_setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p)) +{ + extern char cmd_line[]; + extern char _etext[], _edata[], _end[]; + extern int panic_timeout; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy( saved_command_line, cmd_line ); + *cmdline_p = cmd_line; + + *memory_start_p = (unsigned long) Hash+Hash_size; + (unsigned long *)*memory_end_p = (unsigned long *)(TotalMemory+KERNELBASE); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_sec = 50000000; + + /* reboot on panic */ + panic_timeout = 180; + + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) _etext; + init_task.mm->end_data = (unsigned long) _edata; + init_task.mm->brk = (unsigned long) _end; + + aux_device_present = 0xaa; + + switch ( _machine ) + { + case _MACH_chrp: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + break; + } + +#ifdef CONFIG_BLK_DEV_RAM +#if 0 + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif + + printk("Boot arguments: %s\n", cmd_line); + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c new file mode 100644 index 000000000..bf58953cd --- /dev/null +++ b/arch/ppc/kernel/chrp_time.c @@ -0,0 +1,132 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PreP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * copied and modified from intel version + * + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> +#include <linux/mc146818rtc.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/nvram.h> + +#include "time.h" + +int chrp_cmos_clock_read(int addr) +{ + outb(addr>>8, NVRAM_AS1); + outb(addr, NVRAM_AS0); + return (inb(NVRAM_DATA)); +} + +void chrp_cmos_clock_write(unsigned long val, int addr) +{ + outb(addr>>8, NVRAM_AS1); + outb(addr, NVRAM_AS0); + outb(val,NVRAM_DATA); + return; +} + +/* + * Set the hardware clock. -- Cort + */ +int chrp_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + to_tm(nowtime, &tm); + + save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ + + chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + + chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + 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); + } + chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); + chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); + chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); + chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); + chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); + chrp_cmos_clock_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 + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + chrp_cmos_clock_write(save_control, RTC_CONTROL); + chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + return 0; +} + +unsigned long chrp_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = chrp_cmos_clock_read(RTC_SECONDS); + min = chrp_cmos_clock_read(RTC_MINUTES); + hour = chrp_cmos_clock_read(RTC_HOURS); + day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); + mon = chrp_cmos_clock_read(RTC_MONTH); + year = chrp_cmos_clock_read(RTC_YEAR); + } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); + if (!(chrp_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); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 25f2dd8a0..3038e8cc1 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -45,14 +45,13 @@ #define TOPHYS(x) (x - KERNELBASE) - /* this is a very kludgey way of loading up the BATs on the prep system. I'll kill this horrible macro and write something clean when I have a chance -- Cort */ #define LOAD_BATS(RA,RB) \ mfspr RA,PVR ; \ - srwi r5,r5,16 ; \ + srwi RA,RA,16 ; \ cmpi 0,RA,1 ; \ beq 199f ; \ /* load bats for 60x */ ; \ @@ -132,21 +131,10 @@ mtspr DBAT3L,RB ; \ 200: - - - .text .globl _stext _stext: -#ifdef CONFIG_PREP - . = 0x100 -_GLOBAL(HardReset) - b _start - -#endif /* CONFIG_PREP */ - -#ifdef CONFIG_PMAC /* * _start is defined this way because the XCOFF loader in the OpenFirmware * on the powermac expects the entry point to be a procedure descriptor. @@ -156,7 +144,7 @@ _GLOBAL(HardReset) _start: .long TOPHYS(__start),0,0 -/* +/* PMAC * Enter here with the kernel text, data and bss loaded starting at * 0, running with virtual == physical mapping. * r5 points to the prom entry point (the client interface handler @@ -165,41 +153,71 @@ _start: * pointer (r1) points to just below the end of the half-meg region * from 0x380000 - 0x400000, which is mapped in already. */ +/* PREP + * This is jumped to on prep systems right after the kernel is relocated + * to its proper place in memory by the boot loader. The expected layout + * of the regs is: + * r3: ptr to residual data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * This just gets a minimal mmu environment setup so we can call + * start_here() to do the real work. + * -- Cort + */ + .globl __start __start: /* - * Use the first pair of BAT registers to map the 1st 8MB + * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. */ - mfspr r9,PVR - rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ - cmpi 0,r9,1 - lis r7,KERNELBASE@h - bne 4f - ori r7,r7,4 /* set up BAT registers for 601 */ - li r8,0x7f - b 5f -4: ori r7,r7,0xff /* set up BAT registers for 604 */ - li r8,2 - mtspr DBAT0U,r7 - mtspr DBAT0L,r8 -5: mtspr IBAT0U,r7 - mtspr IBAT0L,r8 - isync - + mfspr r9,PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpi 0,r9,1 + lis r11,KERNELBASE@h + bne 4f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f + oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ + oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ + mtspr IBAT1U,r9 + mtspr IBAT1L,r10 + b 5f +4: ori r11,r11,0x1ff /* set up BAT registers for 604 */ + li r8,2 + mtspr DBAT0U,r11 + mtspr DBAT0L,r8 +5: mtspr IBAT0U,r11 + mtspr IBAT0L,r8 + isync /* - * Now we have the 1st 8M of RAM mapped at KERNELBASE, so we can - * refer to addresses of data items, procedures, etc. normally. + * we now have the 1st 16M of ram mapped with the bats. + * prep needs the mmu to be turned on here, but pmac already has it on. + * this shouldn't bother the pmac since it just gets turned on again + * as we jump to our code at KERNELBASE. -- Cort */ - lis r7,start_here@ha /* jump up to our copy at KERNELBASE */ - addi r7,r7,start_here@l - mtlr r7 - blr -#endif /* CONFIG_PMAC */ - - - + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ + +/* + * GCC sometimes accesses words at negative offsets from the stack + * pointer, although the SysV ABI says it shouldn't. To cope with + * this, we leave this much untouched space on the stack on exception + * entry. + */ +#define STACK_UNDERHEAD 64 /* * Macros for storing registers into and loading registers from @@ -286,10 +304,8 @@ label: \ .long hdlr; \ .long int_return -#ifndef CONFIG_PREP /* System reset */ STD_EXCEPTION(0x100, Reset, UnknownException) -#endif /* ndef CONFIG_PREP */ /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) @@ -319,7 +335,7 @@ DataAccess: /* Instruction access exception */ . = 0x400 -InstructionAccess: +InstructionAccess: EXCEPTION_PROLOG andis. r0,r23,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ @@ -338,7 +354,7 @@ InstructionAccess: .long int_return /* External interrupt */ - STD_EXCEPTION(0x500, HardwareInterrupt, handle_IRQ) + STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ) /* Alignment exception */ . = 0x600 @@ -376,21 +392,7 @@ FPUnavailable: .long KernelFP .long int_return -/* Decrementer */ -#ifdef CONFIG_PREP -/* - ignored for now... */ -_ORG(0x0900) - mtspr SPRG0,r1 - lis r1,0x7FFF - ori r1,r1,0xFFFF - mtspr DEC,r1 - mfspr r1,SPRG0 - rfi -#endif /* CONFIG_PREP */ -#ifdef CONFIG_PMAC STD_EXCEPTION(0x900, Decrementer, timer_interrupt) -#endif /* CONFIG_PMAC */ - STD_EXCEPTION(0xa00, Trap_0a, UnknownException) STD_EXCEPTION(0xb00, Trap_0b, UnknownException) @@ -596,11 +598,6 @@ transfer_to_handler: andi. r23,r23,MSR_PR mfspr r23,SPRG3 /* if from user, fix up tss */ beq 2f -#ifdef CONFIG_PMAC - lwz r24,GPR1(r21) - stw r22,LAST_PC(r23) - stw r24,USER_STACK(r23) -#endif /* CONFIG_PMAC */ addi r24,r1,STACK_FRAME_OVERHEAD stw r24,PT_REGS(r23) 2: addi r2,r23,-TSS /* set r2 to current */ @@ -630,7 +627,7 @@ load_up_fpu: REST_32FPRS(0, r5) /* use last_task_used_math instead of fpu_tss */ - lis r3,last_task_used_math@h/*a*/ + lis r3,last_task_used_math@ha addis r3,r3,-KERNELBASE@h subi r4,r5,TSS addis r4,r4,KERNELBASE@h @@ -654,7 +651,7 @@ load_up_fpu: REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) - SYNC + SYNC rfi /* @@ -682,14 +679,15 @@ Hash_msk = (((1 << Hash_bits) - 1) * 64) .globl hash_page hash_page: /* Get PTE (linux-style) and check access */ - lwz r5,PG_TABLES(r5) /* task's page tables */ - lis r2,-KERNELBASE@h - add r5,r5,r2 /* convert to phys addr */ + lwz r5,MM-TSS(r5) + addis r5,r5,-KERNELBASE@h /* get physical current->mm */ + lwz r5,PGD(r5) /* get current->mm->pgd */ + addis r5,r5,-KERNELBASE@h /* convert to phys addr */ rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ lwz r5,0(r5) /* get pmd entry */ rlwinm. r5,r5,0,0,19 /* extract address of pte page */ beqlr- /* return if no mapping */ - add r2,r5,r2 + addis r2,r5,-KERNELBASE@h 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 */ @@ -819,58 +817,62 @@ start_here: rlwinm r9,r9,16,16,31 cmpi 0,r9,1 beq 4f /* not needed for 601 */ - mfspr r7,HID0 - andi. r0,r7,HID0_DCE - ori r7,r7,HID0_ICE|HID0_DCE - ori r8,r7,HID0_ICFI + mfspr r11,HID0 + andi. r0,r11,HID0_DCE + ori r11,r11,HID0_ICE|HID0_DCE + ori r8,r11,HID0_ICFI bne 3f /* don't invalidate the D-cache */ ori r8,r8,HID0_DCI /* unless it wasn't enabled */ -3: sync - mtspr HID0,r8 /* enable and invalidate caches */ +3: + /* turn on dpm for 603 */ + cmpi 0,r9,3 + bne 10f + oris r11,r11,HID0_DPM@h +10: sync - mtspr HID0,r7 /* enable caches */ + mtspr HID0,r8 /* enable and invalidate caches */ + mtspr HID0,r11 /* enable caches */ sync isync cmpi 0,r9,4 /* check for 604 */ cmpi 1,r9,9 /* or 604e */ cror 2,2,6 bne 4f - ori r7,r7,HID0_SIED|HID0_BHTE /* for 604[e], enable */ - mtspr HID0,r7 /* superscalar exec & br history tbl */ + ori r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */ + mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l /* ptr to phys current tss */ - addis r3,r2,-KERNELBASE@h - addi r3,r3,TSS /* init task's TSS */ - mtspr SPRG3,r3 + addis r11,r2,-KERNELBASE@h + addi r11,r11,TSS /* init task's TSS */ + mtspr SPRG3,r11 /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 stwu r0,-STACK_FRAME_OVERHEAD(r1) /* Clear out the BSS */ - lis r7,_end@ha - addi r7,r7,_end@l + lis r11,_end@ha + addi r11,r11,_end@l lis r8,__bss_start@ha addi r8,r8,__bss_start@l - subf r7,r8,r7 - addi r7,r7,3 - rlwinm. r7,r7,30,2,31 + subf r11,r8,r11 + addi r11,r11,3 + rlwinm. r11,r11,30,2,31 beq 2f addi r8,r8,-4 - mtctr r7 + mtctr r11 li r0,0 3: stwu r0,4(r8) bdnz 3b 2: /* - * Initialize the prom stuff (powermacs only) and the MMU. + * Initialize the prom stuff and the MMU. */ -#ifdef CONFIG_PMAC + bl identify_machine bl prom_init -#endif /* CONFIG_PMAC */ bl MMU_init /* @@ -889,11 +891,6 @@ start_here: rfi /* Load up the kernel context */ 2: -#ifdef CONFIG_PREP - /* reload the bats now that MMU_init() has setup them up -- Cort */ - LOAD_BATS(r3,r0) -#endif - SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ mtspr SDR1,r6 @@ -905,8 +902,19 @@ start_here: addi r3,r3,1 /* increment VSID */ addis r4,r4,0x1000 /* address of next segment */ bdnz 3b -#ifdef CONFIG_PMAC - li r0,0 /* zot the BATs */ + + lis r3,_machine@ha + addis r3,r3,-KERNELBASE@h + lwz r0,_machine@l(r3) + cmpi 0,r0,_MACH_Pmac + beq 99f + +/* on prep reload the bats now that MMU_init() has setup them up -- Cort */ + LOAD_BATS(r3,r14) + b 100f + +/* on pmac clear the bats out */ +99: li r0,0 /* zot the BATs */ #if 1 mtspr IBAT0U,r0 mtspr IBAT0L,r0 @@ -925,7 +933,7 @@ start_here: mtspr IBAT3L,r0 mtspr DBAT3U,r0 mtspr DBAT3L,r0 -#endif +100: /* Now turn on the MMU for real! */ li r4,MSR_KERNEL lis r3,start_kernel@h @@ -934,60 +942,29 @@ start_here: mtspr SRR1,r4 rfi /* enable MMU and jump to start_kernel */ -#ifdef CONFIG_PREP -/* - * This is jumped to on prep systems right after the kernel is relocated - * to its proper place in memory by the boot loader. The expected layout - * of the regs is: - * R3: End of image - * R4: Start of image - 0x400 - * R11: Start of command line string - * R12: End of command line string - * - * This just gets a minimal mmu environment setup so we can call - * start_here() to do the real work. - * -- Cort - */ - .globl __start -__start: - .globl _start -_start: - lis r7,0xF000 /* To mask upper 4 bits */ -/* save pointer to residual data */ - lis r1,resptr@h - ori r1,r1,resptr@l - addis r1,r1,-KERNELBASE@h - stw r3,0(r1) -/* save argument string */ - li r0,0 /* Null terminate string */ - stb r0,0(r12) - lis r1,cmd_line@h - ori r1,r1,cmd_line@l - addis r1,r1,-KERNELBASE@h - subi r1,r1,1 - subi r11,r11,1 -00: lbzu r0,1(r11) - cmpi 0,r0,0 - stbu r0,1(r1) - bne 00b -/* setup the msr with sane values */ - li r0,MSR_ - mtmsr r0 -/* turn on the mmu with bats covering kernel enough to get started */ - LOAD_BATS(r3,r0) + + .globl reset_SDR1 +reset_SDR1: + lis r6,_SDR1@ha + lwz r6,_SDR1@l(r6) mfmsr r3 - ori r3,r3,MSR_DR|MSR_IR + li r4,MSR_IR|MSR_DR + andc r3,r3,r4 + lis r4,2f@h + addis r4,r4,-KERNELBASE@h + ori r4,r4,2f@l + mtspr SRR0,r4 mtspr SRR1,r3 - lis r3,10f@h - ori r3,r3,10f@l + rfi +2: /* load new SDR1 */ + tlbia + mtspr SDR1,r6 + /* turn the mmu back on */ + li r4,MSR_KERNEL + mflr r3 mtspr SRR0,r3 - SYNC - rfi /* enables MMU */ -10: lis r7,start_here@ha /* jump up to our copy at KERNELBASE */ - addi r7,r7,start_here@l - mtlr r7 - blr -#endif /* CONFIG_PREP */ + mtspr SRR1,r4 + rfi /* * FP unavailable trap from kernel - print a message, but let @@ -1022,31 +999,23 @@ giveup_fpu_unmapped: giveup_fpu: li r6,0 1: - addis r3,r6,last_task_used_math@h/*a*/ + addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) -#if 0 - addis r3,r6,fpu_tss@ha - lwz r4,fpu_tss@l(r3) -#endif mfmsr r5 ori r5,r5,MSR_FP SYNC mtmsr r5 /* enable use of fpu now */ SYNC cmpi 0,r4,0 + beqlr- /* if no previous owner, done */ add r4,r4,r6 - beqlr /* if no previous owner, done */ addi r4,r4,TSS /* want TSS of last_task_used_math */ li r5,0 stw r5,last_task_used_math@l(r3) -#if 0 - stw r5,fpu_tss@l(r3) -#endif SAVE_32FPRS(0, r4) mffs fr0 stfd fr0,TSS_FPSCR-4(r4) lwz r5,PT_REGS(r4) - lwz r5,PT_REGS(r4) add r5,r5,r6 lwz r3,_MSR-STACK_FRAME_OVERHEAD(r5) li r4,MSR_FP @@ -1268,7 +1237,7 @@ int_return: cmpi 0,r4,0 beq+ 1f addi r3,r1,STACK_FRAME_OVERHEAD - bl handle_IRQ + bl do_IRQ b 3b 1: lis r4,bh_mask@ha lwz r4,bh_mask@l(r4) @@ -1341,7 +1310,7 @@ _GLOBAL(fake_interrupt) li r0,0x0fac stw r0,TRAP(r1) addi r3,r1,STACK_FRAME_OVERHEAD - bl handle_IRQ + bl do_IRQ addi r1,r1,INT_FRAME_SIZE+STACK_UNDERHEAD lwz r0,4(r1) mtlr r0 @@ -1384,11 +1353,11 @@ _GLOBAL(flush_instruction_cache) * and invalidate the corresponding instruction cache blocks. * This is a no-op on the 601. * - * store_cache_range(unsigned long start, unsigned long stop) + * flush_icache_range(unsigned long start, unsigned long stop) */ CACHE_LINE_SIZE = 32 LG_CACHE_LINE_SIZE = 5 -_GLOBAL(store_cache_range) +_GLOBAL(flush_icache_range) mfspr r5,PVR rlwinm r5,r5,16,16,31 cmpi 0,r5,1 @@ -1521,7 +1490,6 @@ _GLOBAL(flush_hash_page) _GLOBAL(__main) blr -#ifdef CONFIG_PMAC /* * These exception handlers are used when we have called a prom * routine after we have taken over the exception vectors and MMU. @@ -1716,7 +1684,6 @@ enter_prom: lwz r31,28(r1) lwz r1,0(r1) blr -#endif /* * We put a few things here that have to be page-aligned. @@ -1726,7 +1693,6 @@ enter_prom: .data .globl sdata sdata: - .space 2*4096 .globl empty_zero_page empty_zero_page: .space 4096 @@ -1735,7 +1701,6 @@ empty_zero_page: swapper_pg_dir: .space 4096 -#ifdef CONFIG_PREP /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. @@ -1743,5 +1708,4 @@ swapper_pg_dir: .globl cmd_line cmd_line: .space 512 -#endif /* CONFIG_PREP */ diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c new file mode 100644 index 000000000..bfe1a4146 --- /dev/null +++ b/arch/ppc/kernel/idle.c @@ -0,0 +1,279 @@ +/* + * $Id: idle.c,v 1.4 1997/08/23 22:46:01 cort Exp $ + * + * Idle daemon for PowerPC. Idle daemon will handle any action + * that needs to be taken when the system becomes idle. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define __KERNEL_SYSCALLS__ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/config.h> + +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/smp_lock.h> +#include <asm/processor.h> + +int zero_paged(void *unused); +int power_saved(void *unused); + +asmlinkage int sys_idle(void) +{ + int ret = -EPERM; + + if (current->pid != 0) + goto out; + + /* + * want one per cpu since it would be nice to have all + * processors who aren't doing anything + * zero-ing pages since this daemon is lock-free + * -- Cort + */ + kernel_thread(zero_paged, NULL, 0); + /* no powersaving modes on 601 */ + /*if( (_get_PVR()>>16) != 1 ) + kernel_thread(power_saved, NULL, 0);*/ + + /* endless loop with no priority at all */ + current->priority = -100; + current->counter = -100; + for (;;) + { + schedule(); + } + ret = 0; +out: + return ret; +} + +/* + * vars for idle task zero'ing out pages + */ +unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ +unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ +unsigned long zerocount = 0; /* # currently pre-zero'd pages */ +unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ +unsigned long pageptr = 0; /* current page being zero'd */ +unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ +unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */ +static struct wait_queue * page_zerod_wait = NULL; +#define PAGE_THRESHOLD 96 /* how many pages to keep pre-zero'd */ + +/* + * Returns a pre-zero'd page from the list otherwise returns + * NULL. + */ +unsigned long get_prezerod_page(void) +{ + unsigned long page; + + atomic_inc((atomic_t *)&zeropage_calls); + if ( zero_list ) + { + /* atomically remove this page from the list */ + asm ( "101:lwarx %1,0,%2\n" /* reserve zero_list */ + " lwz %0,0(%1)\n" /* get next -- new zero_list */ + " stwcx. %0,0,%2\n" /* update zero_list */ + " bne- 101b\n" /* if lost reservation try again */ + : "=&r" (zero_list), "=&r" (page) + : "r" (&zero_list) + : "cc" ); + /* 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 effect anything if it zero's one + * less page -- Cort + */ + atomic_inc((atomic_t *)&zeropage_hits); + atomic_dec((atomic_t *)&zerocount); + wake_up(&page_zerod_wait); + resched_force(); + + /* zero out the pointer to next in the page */ + *(unsigned long *)page = 0; + return page; + } + return 0; +} + +/* + * Experimental stuff to zero out pages in the idle task + * to speed up get_free_pages() -- Cort + * Zero's out pages until we need to resched or + * we've reached the limit of zero'd pages. + */ + +int zero_paged(void *unused) +{ + extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + sprintf(current->comm, "zero_paged (idle)"); + current->blocked = ~0UL; + + printk("Started zero_paged\n"); + + __sti(); + while ( 1 ) + { + /* don't want to be pre-empted by swapper or power_saved */ + current->priority = -98; + current->counter = -98; + /* we don't want to run until we have something to do */ + while ( zerocount >= PAGE_THRESHOLD ) + sleep_on(&page_zerod_wait); + /* + * Mark a page as reserved so we can mess with it + * If we're interrupted we keep this page and our place in it + * since we validly hold it and it's reserved for us. + */ + pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); + if ( !pageptr ) + { + printk("!pageptr in zero_paged\n"); + goto retry; + } + + if ( resched_needed() ) + schedule(); + + /* + * Make the page no cache so we don't blow our cache with 0's + */ + dir = pgd_offset( init_task.mm, pageptr ); + if (dir) + { + pmd = pmd_offset(dir, pageptr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) + { + pte = pte_offset(pmd, pageptr & PAGE_MASK); + if (pte && pte_present(*pte)) + { + pte_uncache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + } + } + } + + /* + * Important here to not take time away from real processes. + */ + for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) + { + if ( resched_needed() ) + schedule(); + *(unsigned long *)(bytecount + pageptr) = 0; + } + + /* + * If we finished zero-ing out a page add this page to + * the zero_list atomically -- we can't use + * down/up since we can't sleep in idle. + * Disabling interrupts is also a bad idea since we would + * steal time away from real processes. + * We can also have several zero_paged's running + * on different processors so we can't interfere with them. + * 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 */ + asm ( "101:lwarx %0,0,%1\n" /* reserve zero_list */ + " stw %0,0(%2)\n" /* update *pageptr */ +#ifdef __SMP__ + " sync\n" /* let store settle */ +#endif + " mr %0,%2\n" /* update zero_list in reg */ + " stwcx. %2,0,%1\n" /* update zero_list in mem */ + " bne- 101b\n" /* if lost reservation try again */ + : "=&r" (zero_list) + : "r" (&zero_list), "r" (pageptr) + : "cc" ); + /* + * This variable is used in the above loop and nowhere + * else so the worst that could happen is we would + * zero out one more or one less page than we want + * per processor on the machine. This is because + * we could add our page to the list but not have + * zerocount updated yet when another processor + * reads it. -- Cort + */ + atomic_inc((atomic_t *)&zerocount); + atomic_inc((atomic_t *)&zerototal); +retry: + schedule(); + } +} + +int power_saved(void *unused) +{ + unsigned long msr, hid0; + sprintf(current->comm, "power_saved (idle)"); + current->blocked = ~0UL; + + printk("Power saving daemon started\n"); + + __sti(); + while (1) + { + /* don't want to be pre-empted by swapper */ + current->priority = -99; + current->counter = -99; + /* go ahead and wakeup page_zerod() */ + wake_up(&page_zerod_wait); + schedule(); + asm volatile( + /* clear powersaving modes and set nap mode */ + "mfspr %3,1008 \n\t" + "andc %3,%3,%4 \n\t" + "or %3,%3,%5 \n\t" + "mtspr 1008,%3 \n\t" + /* enter the mode */ + "mfmsr %0 \n\t" + "oris %0,%0,%2 \n\t" + "sync \n\t" + "mtmsr %0 \n\t" + "isync \n\t" + : "=&r" (msr) + : "0" (msr), "i" (MSR_POW>>16), + "r" (hid0), + "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), + "r" (HID0_NAP)); + /* + * The ibm carolina spec says that the eagle memory + * controller will detect the need for a snoop + * and wake up the processor so we don't need to + * check for cache operations that need to be + * snooped. The ppc book says the run signal + * must be asserted while napping for this though. + * + * Paul, what do you know about the pmac here? + * -- Cort + */ + schedule(); + } +} + diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 171bfaa5c..da3c53697 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,13 +1,13 @@ /* * arch/ppc/kernel/irq.c * - * Power Macintosh version - * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) - * * Derived from arch/i386/kernel/irq.c * Copyright (C) 1992 Linus Torvalds * Adapted from arch/i386 by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) * * This file contains the code used by various IRQ handling routines: * asking for different IRQ's should be done through these routines @@ -15,11 +15,7 @@ * shouldn't result in any weird surprises, and installing new handlers * should be easier. */ - -/* - * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * Naturally it's not a 1:1 relation, but there are similarities. - */ + #include <linux/ptrace.h> #include <linux/errno.h> @@ -30,121 +26,164 @@ #include <linux/interrupt.h> #include <linux/timex.h> #include <linux/config.h> - +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/openpic.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/openpic.h> + +#include <asm/hydra.h> #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/bitops.h> -#define IRQ_FLAG ((unsigned *)0xf3000020) -#define IRQ_ENABLE ((unsigned *)0xf3000024) -#define IRQ_ACK ((unsigned *)0xf3000028) -#define IRQ_LEVEL ((unsigned *)0xf300002c) - -#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ - -#undef SHOW_IRQ 1 +#undef SHOW_IRQ +#define OPENPIC_DEBUG unsigned lost_interrupts = 0; - unsigned int local_irq_count[NR_CPUS]; -static struct irqaction irq_action[32]; +static struct irqaction irq_action[NR_IRQS]; +static int spurious_interrupts = 0; +int __ppc_bh_counter; +static unsigned int cached_irq_mask = 0xffffffff; +static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + +#define cached_21 (((char *)(&cached_irq_mask))[3]) +#define cached_A1 (((char *)(&cached_irq_mask))[2]) /* - * This contains the irq mask for both irq controllers + * These are set to the appropriate functions by init_IRQ() */ -static unsigned int cached_irq_mask = 0xffff; +void (*mask_and_ack_irq)(int irq_nr); +void (*set_irq_mask)(int irq_nr); -#define cached_21 (((char *)(&cached_irq_mask))[0]) -#define cached_A1 (((char *)(&cached_irq_mask))[1]) +/* prep */ +#define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21 +extern unsigned long route_pci_interrupts(void); -int __ppc_bh_counter; +/* pmac */ +#define IRQ_FLAG ((unsigned *)0xf3000020) +#define IRQ_ENABLE ((unsigned *)0xf3000024) +#define IRQ_ACK ((unsigned *)0xf3000028) +#define IRQ_LEVEL ((unsigned *)0xf300002c) +#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ +#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE)) -void *null_handler(int,void *,struct pt_regs *); +/* chrp */ +volatile struct Hydra *Hydra = NULL; -/* - * disable and enable intrs in software. This is used - * from the non-realtime parts of Linux to disable interrupts. - * The realtime part disables/enables intrs in the hardware. - * -- Cort - */ -unsigned long soft_intr_enable = 1; -void _soft_cli(void) +void i8259_mask_and_ack_irq(int irq_nr) { - soft_intr_enable = 0; + spin_lock(&irq_controller_lock); + cached_irq_mask |= 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 _soft_sti(void) +void pmac_mask_and_ack_irq(int irq_nr) { - soft_intr_enable = 1; - if ( lost_interrupts ) - { - printk("lost_interrupts from _soft_sti() %x\n",lost_interrupts); - fake_interrupt(); - } + spin_lock(&irq_controller_lock); + cached_irq_mask |= 1 << irq_nr; + out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); + out_le32(IRQ_ACK, 1U << irq_nr); + spin_unlock(&irq_controller_lock); } -void * -null_handler(int a, void *b, struct pt_regs *regs) +void chrp_mask_and_ack_irq(int irq_nr) { - /*printk("irq.c: null_handler() called. Should not have happened.\n");*/ + /* spinlocks are done by i8259_mask_and_ack() - Cort */ + if (is_8259_irq(irq_nr)) + i8259_mask_and_ack_irq(irq_nr); + openpic_eoi(0); } -void -disable_irq(unsigned int irq_nr) -{ - int s = _disable_interrupts(); - unsigned char mask; -#ifdef CONFIG_PMAC - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); -#else /* CONFIG_PMAC */ - mask = 1 << (irq_nr & 7); - if (irq_nr < 8) - { - cached_21 |= mask; - outb(cached_21,0x21); - } else - { - cached_A1 |= mask; +void i8259_set_irq_mask(int irq_nr) +{ + if (irq_nr > 7) { outb(cached_A1,0xA1); - } -#endif /* CONFIG_PMAC */ - _enable_interrupts(s); + } else { + outb(cached_21,0x21); + } } -void -enable_irq(unsigned int irq_nr) +void pmac_set_irq_mask(int irq_nr) { - int s = _disable_interrupts(); -#ifdef CONFIG_PMAC - unsigned bit = 1U << irq_nr; - - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) | bit); - + /* this could be being enabled or disabled - so use cached_irq_mask */ + out_le32(IRQ_ENABLE, ~cached_irq_mask /* enable all unmasked */ ); /* * 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 ((ld_le32(IRQ_LEVEL) & bit) && !(ld_le32(IRQ_FLAG) & bit)) - lost_interrupts |= bit; -#else /* CONFIG_PMAC */ - if (irq_nr < 8) { - cached_21 &= ~(1 << (irq_nr & 7)); - outb(cached_21,0x21); + if ((ld_le32(IRQ_LEVEL) & (1UL<<irq_nr)) && !(ld_le32(IRQ_FLAG) & (1UL<<irq_nr))) + lost_interrupts |= (1UL<<irq_nr); +} + +void chrp_set_irq_mask(int irq_nr) +{ + if (is_8259_irq(irq_nr)) { + i8259_set_irq_mask(irq_nr); } else { - cached_A1 &= ~(1 << (irq_nr-8 & 7)); - outb(cached_A1,0xA1); - } -#endif /* CONFIG_PMAC */ + /* disable? */ + if ( cached_irq_mask & (1UL<<irq_nr) ) + openpic_disable_irq(irq_to_openpic(irq_nr)); + /* enable */ + else + openpic_disable_irq(irq_to_openpic(irq_nr)); + } +} + +/* + * These have to be protected by the spinlock + * before being called. + */ +static inline void mask_irq(unsigned int irq_nr) +{ + cached_irq_mask |= 1 << irq_nr; + set_irq_mask(irq_nr); +} - _enable_interrupts(s); +static inline void unmask_irq(unsigned int irq_nr) +{ + cached_irq_mask &= ~(1 << irq_nr); + set_irq_mask(irq_nr); } +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) { @@ -153,7 +192,7 @@ int get_irq_list(char *buf) for (i = 0 ; i < NR_IRQS ; i++) { action = irq_action + i; - if (!action || !action->handler) + if (!action || !action->handler) continue; len += sprintf(buf+len, "%2d: %10u %s", i, kstat.interrupts[i], action->name); @@ -162,152 +201,143 @@ int get_irq_list(char *buf) } len += sprintf(buf+len, "\n"); } -/* - * Linus - should you add NMI counts here ????? - */ #ifdef __SMP_PROF__ len+=sprintf(buf+len, "IPI: %8lu received\n", ipi_count); -#endif +#endif + len += sprintf(buf+len, "99: %10u spurious or short\n", + spurious_interrupts); return len; } -asmlinkage void handle_IRQ(struct pt_regs *regs) +asmlinkage void do_IRQ(struct pt_regs *regs) { int irq; - unsigned bits; + unsigned long bits; struct irqaction *action; int cpu = smp_processor_id(); + int status; hardirq_enter(cpu); -#ifdef CONFIG_PMAC - bits = ld_le32(IRQ_FLAG) | lost_interrupts; - lost_interrupts = 0; - - for (irq = NR_IRQS; irq >= 0; --irq) - if (bits & (1U << irq)) - break; -#else /* CONFIG_PMAC */ -#if 1 - if ( lost_interrupts ) - { - irq = ffz(~lost_interrupts); - lost_interrupts &= ~irq; - goto retry; - } - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) + + /* + * I'll put this ugly mess of code into a function + * such as get_pending_irq() or some such clear thing + * so we don't have a switch in the irq code and + * the chrp code is merged a bit with the prep. + * -- Cort + */ + switch ( _machine ) { -retry_cascade: - outb(0x0C, 0xA0); - irq = inb(0xA0); - /* if no intr left */ - if ( !(irq & 128 ) ) + case _MACH_Pmac: + bits = ld_le32(IRQ_FLAG) | lost_interrupts; + lost_interrupts = 0; + for (irq = NR_IRQS - 1; irq >= 0; --irq) + if (bits & (1U << irq)) + break; + 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 = (*(volatile unsigned char *)0xfec80000) & 0x0f; + } + else if (irq >= 64) + { + /* + * OpenPIC interrupts >64 will be used for other purposes + * like interprocessor interrupts and hardware errors + */ +#ifdef OPENPIC_DEBUG + printk("OpenPIC interrupt %d\n", irq); +#endif + if (irq==99) + spurious_interrupts++; + } + else { + /* + * Here we should process IPI timer + * for now the interrupt is dismissed. + */ goto out; - irq = (irq&7) + 8; - } -retry: + } + break; + case _MACH_IBM: + case _MACH_Motorola: +#if 1 + 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; #else - /* get the irr from the intr controller */ - outb(0x0A, 0x20); - bits = inb(0x20); - /* handle cascade */ - if ( bits ) - { - bits &= 4; - outb(0x0A, 0xA0); - bits = inb(0xA0)<<8; + /* + * get the isr from the intr controller since + * the bit in the irr has been cleared + */ + outb(0x0a, 0x20); + bits = inb(0x20)&0xff; + /* handle cascade */ + if ( bits & 4 ) + { + bits &= ~4UL; + outb(0x0a, 0xA0); + bits |= inb(0xA0)<<8; + } + /* ignore masked irqs */ + bits &= ~cached_irq_mask; +#endif + break; } - /* get lost interrupts */ - bits |= lost_interrupts; - /* save intrs that are masked out */ - lost_interrupts = bits & cached_irq_mask; - /* get rid of intrs being masked */ - bits &= ~cached_irq_mask; - /* non-specifc eoi */ - outb(0x20,0x20); - if ( bits & 0xff00 ) - outb(0x20,0xA0); - - printk("bits %04X lost %04X mask %04x\n", - bits, lost_interrupts,cached_irq_mask); - - for (irq = NR_IRQS; irq >= 0; --irq) - if (bits & (1U << irq)) - break; -#endif -#endif /* CONFIG_PMAC */ if (irq < 0) { - printk("Bogus interrupt from PC = %lx, irq %d\n",regs->nip,irq); + printk("Bogus interrupt from PC = %lx\n", regs->nip); goto out; } -#ifdef CONFIG_PMAC - out_le32(IRQ_ACK, 1U << irq); -#else /* CONFIG_PMAC */ - /* mask out the irq while handling it */ - disable_irq(irq); - /* - * send eoi to interrupt controller right away or lower - * priority intrs would be ignored even if with intrs enabled - */ - if (irq > 7) - { - outb(0xE0|(irq-8), 0xA0); - outb(0xE2, 0x20); - } else - { - outb(0xE0|irq, 0x20); - } -#endif /* !CONFIG_PMAC */ + mask_and_ack_irq(irq); - /* - * now that we've acked the irq, if intrs are disabled in software - * we're in the real-time system and non-rt linux has disabled them - * so we just queue it up and return -- Cort - */ - if ( ! soft_intr_enable ) - { - lost_interrupts |= 1UL << irq; - /* can't printk - kernel expects intrs off! */ - /*printk("irq %d while intrs soft disabled\n", irq);*/ - goto out; - } - - action = irq + irq_action; + status = 0; + action = irq_action + irq; kstat.interrupts[irq]++; - if (action->handler) { - action->handler(irq, action->dev_id, regs); - _disable_interrupts(); /* in case the handler turned them on */ + if ( action && action->handler) + { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + status |= action->flags; + action->handler(irq, action->dev_id, regs); + /*if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq);*/ + __cli(); /* in case the handler turned them on */ + spin_lock(&irq_controller_lock); + unmask_irq(irq); + spin_unlock(&irq_controller_lock); } else { + if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ + spurious_interrupts++; disable_irq( irq ); } -#ifdef CONFIG_PREP - /* re-enable if the interrupt was good and isn't one-shot */ - if ( action->handler && !(action->flags & SA_ONESHOT) ) - enable_irq(irq); + /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ - if ( irq > 7 ) + if ( is_prep && (irq > 7) ) goto retry_cascade; -#endif - - hardirq_exit(cpu); - /* - * This should be conditional: we should really get - * a return code from the irq handler to tell us - * whether the handler wants us to do software bottom - * half handling or not.. - */ - if (1) - if (bh_active & bh_mask) - do_bottom_half(); - return; + /* do_bottom_half is called if necessary from int_return in head.S */ out: hardirq_exit(cpu); - } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), @@ -315,13 +345,13 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) { struct irqaction * action; unsigned long flags; - -#ifdef SHOW_IRQ + +#ifdef SHOW_IRQ printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n", irq,handler,devname,dev_id); #endif /* SHOW_IRQ */ - - if (irq > NR_IRQS) + + if (irq >= NR_IRQS) return -EINVAL; action = irq + irq_action; if (action->handler) @@ -339,17 +369,16 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) restore_flags(flags); return 0; } - void free_irq(unsigned int irq, void *dev_id) { struct irqaction * action = irq + irq_action; unsigned long flags; -#ifdef SHOW_IRQ +#ifdef SHOW_IRQ printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id); #endif /* SHOW_IRQ */ - - if (irq > NR_IRQS) { + + if (irq >= NR_IRQS) { printk("Trying to free IRQ%d\n",irq); return; } @@ -370,114 +399,119 @@ void free_irq(unsigned int irq, void *dev_id) unsigned long probe_irq_on (void) { - unsigned int i, irqs = 0, irqmask; - unsigned long delay; - - /* first, snaffle up any unassigned irqs */ - for (i = 15; i > 0; i--) { - if (!request_irq(i, null_handler, SA_ONESHOT, "probe", NULL)) { - enable_irq(i); - irqs |= (1 << i); - } - } - - /* wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ - - /* now filter out any obviously spurious interrupts */ - irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i) & irqmask) { - irqs ^= (1 << i); - free_irq(i, NULL); - } - } -#ifdef DEBUG - printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - return irqs; + return 0; } int probe_irq_off (unsigned long irqs) { - unsigned int i, irqmask; - - irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i)) { - free_irq(i, NULL); - } - } -#ifdef SHOW_IRQ - printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - irqs &= irqmask; - if (!irqs) - return 0; - i = ffz(~irqs); - if (irqs != (irqs & (1 << i))) - i = -i; - return i; + return 0; } -void init_IRQ(void) +__initfunc(static void i8259_init(void)) { -#ifdef CONFIG_PMAC - extern void xmon_irq(int, void *, struct pt_regs *); - - *IRQ_ENABLE = 0; - request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); -#else /* CONFIG_PMAC */ - /* Initialize interrupt controllers */ + /* init master interrupt controller */ outb(0x11, 0x20); /* Start init sequence */ outb(0x40, 0x21); /* Vector base */ -#if 1 - outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ -#else + /*outb(0x04, 0x21);*/ /* edge tiggered, Cascade (slave) on IRQ2 */ outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ -#endif - outb(0x01, 0x21); /* Select 8086 mode */ outb(0xFF, 0x21); /* Mask all */ - + + /* init slave interrupt controller */ outb(0x11, 0xA0); /* Start init sequence */ outb(0x48, 0xA1); /* Vector base */ -#if 1 - outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ -#else + /*outb(0x02, 0xA1);*/ /* edge triggered, Cascade (slave) on IRQ2 */ outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ -#endif outb(0x01, 0xA1); /* Select 8086 mode */ outb(0xFF, 0xA1); /* Mask all */ - - /* - * Program level mode for irq's that 'can' be level triggered. - * This does not effect irq's 0,1,2 and 8 since they must be - * edge triggered. This is not the PIC controller. The default - * here is for edge trigger mode. -- Cort - */ -#if 0 - outb(0xf8, 0x4d0); /* level triggered */ - outb(0xff, 0x4d1); -#endif -#if 0 - outb(0x00, 0x4D0); /* All edge triggered */ - outb(0xCF, 0x4D1); /* Trigger mode */ -#endif outb(cached_A1, 0xA1); outb(cached_21, 0x21); - if (request_irq(2, null_handler, SA_INTERRUPT, "cascade", NULL)) - printk("Unable to get IRQ2 for cascade\n"); + if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0) + panic("Could not allocate cascade IRQ!"); enable_irq(2); /* Enable cascade interrupt */ - -#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 */ - - route_pci_interrupts(); -#endif /* CONFIG_PMAC */ +} + +__initfunc(void init_IRQ(void)) +{ + extern void xmon_irq(int, void *, struct pt_regs *); + + switch (_machine) + { + case _MACH_Pmac: + mask_and_ack_irq = pmac_mask_and_ack_irq; + set_irq_mask = pmac_set_irq_mask; + + *IRQ_ENABLE = 0; +#ifdef CONFIG_XMON + request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); +#endif /* CONFIG_XMON */ + break; + case _MACH_Motorola: + case _MACH_IBM: + mask_and_ack_irq = i8259_mask_and_ack_irq; + set_irq_mask = i8259_set_irq_mask; + + i8259_init(); + route_pci_interrupts(); + /* + * According to the Carolina spec from ibm irq's 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 ( _machine == _MACH_IBM ) + irq_mode2 |= 0xa0; + /* + * Sound on the Powerstack reportedly needs to be edge triggered + */ + if ( _machine == _MACH_Motorola ) + { + /*irq_mode2 &= ~0x04L; + outb( irq_mode1 , 0x4d0 ); + outb( irq_mode2 , 0x4d1 );*/ + } + + } + break; + case _MACH_chrp: + mask_and_ack_irq = chrp_mask_and_ack_irq; + set_irq_mask = chrp_set_irq_mask; + if ((Hydra = find_hydra())) { + printk("Hydra Mac I/O at %p\n", Hydra); + out_le32(&Hydra->Feature_Control, HYDRA_FC_SCC_CELL_EN | + HYDRA_FC_SCSI_CELL_EN | + HYDRA_FC_SCCA_ENABLE | + HYDRA_FC_SCCB_ENABLE | + HYDRA_FC_ARB_BYPASS | + HYDRA_FC_MPIC_ENABLE | + HYDRA_FC_SLOW_SCC_PCLK | + HYDRA_FC_MPIC_IS_MASTER); + OpenPIC = (volatile struct OpenPIC *)&Hydra->OpenPIC; + } else if (!OpenPIC /* && find_xxx */) { + printk("Unknown openpic implementation\n"); + /* other OpenPIC implementations */ + /* ... */ + } + if (OpenPIC) + openpic_init(); + else + panic("No OpenPIC found"); + if (Hydra) + hydra_post_openpic_init(); + i8259_init(); + break; + } } diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 430cd7a5d..9c6d013b6 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -64,6 +64,7 @@ _GLOBAL(_hard_sti) mtmsr r3 /* Update machine state */ blr +#if 0 /* * Restore 'flags' * __restore_flags(long val) @@ -79,11 +80,13 @@ _GLOBAL(__restore_flags) mtmsr r3 isync blr - +#endif + /* * We were about to enable interrupts but we have to simulate * some interrupts that were lost by enable_irq first. */ + .globl do_lost_interrupts do_lost_interrupts: stwu r1,-16(r1) mflr r0 @@ -243,7 +246,6 @@ _GLOBAL(_outsl) bdnz 00b blr -#ifdef CONFIG_PMAC _GLOBAL(ide_insw) mtctr r5 subi r4,r4,2 @@ -259,7 +261,6 @@ _GLOBAL(ide_outsw) sthx r5,0,r3 bdnz 00b blr -#endif /* * Extended precision shifts @@ -298,7 +299,7 @@ _GLOBAL(abs) _GLOBAL(_get_SP) mr r3,r1 /* Close enough */ blr - + _GLOBAL(_get_PVR) mfspr r3,PVR blr @@ -308,6 +309,7 @@ cvt_fd: lfs 0,0(r3) stfd 0,0(r4) blr + /* * Fetch the current SR register * get_SR(int index) @@ -317,7 +319,6 @@ _GLOBAL(get_SR) mr r3,r4 blr - _GLOBAL(cvt_df) cvt_df: lfd 0,0(r3) diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index 3f404a37a..80f10c59e 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -29,7 +29,6 @@ void main(void) { - /*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)); @@ -37,54 +36,20 @@ main(void) DEFINE(SIGNAL, offsetof(struct task_struct, signal)); DEFINE(TSS, offsetof(struct task_struct, tss)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); - DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables)); -#ifdef CONFIG_PMAC - DEFINE(LAST_PC, offsetof(struct thread_struct, last_pc)); - DEFINE(USER_STACK, offsetof(struct thread_struct, user_stack)); -#endif + /*DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables));*/ + DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(PGD, offsetof(struct mm_struct, pgd)); DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); DEFINE(PF_TRACESYS, PF_TRACESYS); DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); -#if 0 - DEFINE(TSS_FPR1, offsetof(struct thread_struct, fpr[1])); - DEFINE(TSS_FPR2, offsetof(struct thread_struct, fpr[2])); - DEFINE(TSS_FPR3, offsetof(struct thread_struct, fpr[3])); - DEFINE(TSS_FPR4, offsetof(struct thread_struct, fpr[4])); - DEFINE(TSS_FPR5, offsetof(struct thread_struct, fpr[5])); - DEFINE(TSS_FPR6, offsetof(struct thread_struct, fpr[6])); - DEFINE(TSS_FPR7, offsetof(struct thread_struct, fpr[7])); - DEFINE(TSS_FPR8, offsetof(struct thread_struct, fpr[8])); - DEFINE(TSS_FPR9, offsetof(struct thread_struct, fpr[9])); - DEFINE(TSS_FPR10, offsetof(struct thread_struct, fpr[10])); - DEFINE(TSS_FPR11, offsetof(struct thread_struct, fpr[11])); - DEFINE(TSS_FPR12, offsetof(struct thread_struct, fpr[12])); - DEFINE(TSS_FPR13, offsetof(struct thread_struct, fpr[13])); - DEFINE(TSS_FPR14, offsetof(struct thread_struct, fpr[14])); - DEFINE(TSS_FPR15, offsetof(struct thread_struct, fpr[15])); - DEFINE(TSS_FPR16, offsetof(struct thread_struct, fpr[16])); - DEFINE(TSS_FPR17, offsetof(struct thread_struct, fpr[17])); - DEFINE(TSS_FPR18, offsetof(struct thread_struct, fpr[18])); - DEFINE(TSS_FPR19, offsetof(struct thread_struct, fpr[19])); - DEFINE(TSS_FPR20, offsetof(struct thread_struct, fpr[20])); - DEFINE(TSS_FPR21, offsetof(struct thread_struct, fpr[21])); - DEFINE(TSS_FPR22, offsetof(struct thread_struct, fpr[22])); - DEFINE(TSS_FPR23, offsetof(struct thread_struct, fpr[23])); - DEFINE(TSS_FPR24, offsetof(struct thread_struct, fpr[24])); - DEFINE(TSS_FPR25, offsetof(struct thread_struct, fpr[25])); - DEFINE(TSS_FPR26, offsetof(struct thread_struct, fpr[26])); - DEFINE(TSS_FPR27, offsetof(struct thread_struct, fpr[27])); - DEFINE(TSS_FPR28, offsetof(struct thread_struct, fpr[28])); - DEFINE(TSS_FPR29, offsetof(struct thread_struct, fpr[29])); - DEFINE(TSS_FPR30, offsetof(struct thread_struct, fpr[30])); - DEFINE(TSS_FPR31, offsetof(struct thread_struct, fpr[31])); -#endif DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); /* Interrupt register frame */ DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs)); + /* in fact we only use gpr0 - gpr9 and gpr20 - gpr23 */ DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2])); diff --git a/arch/ppc/kernel/openpic.c b/arch/ppc/kernel/openpic.c new file mode 100644 index 000000000..7eb5e5aa9 --- /dev/null +++ b/arch/ppc/kernel/openpic.c @@ -0,0 +1,546 @@ +/* + * arch/ppc/kernel/openpic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + + +/* + * Note: Interprocessor Interrupt (IPI) and Timer support is incomplete + */ + + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/openpic.h> +#include <asm/ptrace.h> +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/irq.h> + + +#define REGISTER_DEBUG +#undef REGISTER_DEBUG + + +#define VEC_TIMER 0x40 /* and up */ +#define VEC_IPI 0x50 /* and up */ +#define VEC_SOURCE 0x10 /* and up */ +#define VEC_SPURIOUS 99 + + +volatile struct OpenPIC *OpenPIC; + +static u_int Version; +static u_int NumProcessors; +static u_int NumSources; +static u_int VendorID; +static u_int DeviceID; +static u_int Stepping; +static u_int TimerFrequency; + + + /* + * Accesses to the current processor's registers + */ + +#ifndef __powerpc__ +#define THIS_CPU Private +#define CHECK_THIS_CPU do {} while (0) +#else +#define THIS_CPU Processor[cpu] +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#endif + + + /* + * Sanity checks + */ + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("openpic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("openpic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("openpic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri); +#define check_arg_irq(irq) \ + if (irq < 0 || irq >= NumSources) \ + printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq); +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors) \ + printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu); +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + + + /* + * Dummy interrupt handler + */ + +static void no_action(int ir1, void *dev, struct pt_regs *regs) +{} + + + /* + * I/O functions + */ + +#ifdef __i386__ +static inline u_int ld_le32(volatile u_int *addr) +{ + return *addr; +} + +static inline void out_le32(volatile u_int *addr, u_int val) +{ + *addr = val; +} +#endif + +static inline u_int openpic_read(volatile u_int *addr) +{ + u_int val; + + val = ld_le32(addr); +#ifdef REGISTER_DEBUG + printk("openpic_read(0x%08x) = 0x%08x\n", (u_int)addr, val); +#endif + return val; +} + +static inline void openpic_write(volatile u_int *addr, u_int val) +{ +#ifdef REGISTER_DEBUG + printk("openpic_write(0x%08x, 0x%08x)\n", (u_int)addr, val); +#endif + out_le32(addr, val); +} + + +static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +static inline void openpic_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + + + /* + * Update a Vector/Priority register in a safe manner. The interrupt will + * be disabled. + */ + +static void openpic_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + openpic_setfield(addr, OPENPIC_MASK); + /* wait until it's not in use */ + while (openpic_read(addr) & OPENPIC_ACTIVITY); + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + + +/* -------- Global Operations ---------------------------------------------- */ + + + /* + * Initialize the OpenPIC + */ + +void openpic_init(void) +{ + u_int t, i; + const char *version, *vendor, *device; + + if (!OpenPIC) { + printk("No OpenPIC present\n"); + return; + } + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + Version = t & OPENPIC_FEATURE_VERSION_MASK; + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + + switch (Version) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + default: + version = "?.?"; + break; + } + 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; + DeviceID = (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 (DeviceID) { + 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, DeviceID, device, Stepping); + + TimerFrequency = openpic_read(&OpenPIC->Global.Timer_Frequency); + printk("OpenPIC timer frequency is "); + if (TimerFrequency) + printk("%d Hz\n", TimerFrequency); + else + printk("not set\n"); + + /* Initialize timer interrupts */ + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, VEC_TIMER+i); + /* No processor */ + openpic_maptimer(i, 0); + } + + /* Initialize IPI interrupts */ + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 0 */ + openpic_initipi(i, 0, VEC_IPI+i); + } + + /* Initialize external interrupts */ + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, VEC_SOURCE, 1, 1); /* 0,1 gives interrupt storm */ + /* Processor 0 */ + openpic_mapirq(0, 1<<0); + for (i = 1; i < NumSources; i++) { + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, VEC_SOURCE+i, 0, 1); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } + + /* Initialize the spurious interrupt */ + openpic_set_spurious(VEC_SPURIOUS); + + if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, + "OpenPIC cascade", NULL)) + printk("Unable to get OpenPIC IRQ 0 for cascade\n"); + openpic_set_priority(0, 0); + openpic_disable_8259_pass_through(); +} + + + /* + * Reset the OpenPIC + */ + +void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + + + /* + * Enable/disable 8259 Pass Through Mode + */ + +void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + + +#ifndef __i386__ + /* + * Find out the current interrupt + */ + +u_int openpic_irq(u_int cpu) +{ + u_int vec; + + check_arg_cpu(cpu); + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); +#if 0 +if (vec != 22 /* SCSI */) +printk("++openpic_irq: %d\n", vec); +#endif + return vec; +} +#endif + + + /* + * Signal end of interrupt (EOI) processing + */ + +#ifndef __powerpc__ +void openpic_eoi(void) +#else +void openpic_eoi(u_int cpu) +#endif +{ +#if 0 +printk("++openpic_eoi:\n"); +#endif + check_arg_cpu(cpu); + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); +} + + + /* + * Get/set the current task priority + */ + +#ifndef __powerpc__ +u_int openpic_get_priority(void) +#else +u_int openpic_get_priority(u_int cpu) +#endif +{ + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +#ifndef __powerpc__ +void openpic_set_priority(u_int pri) +#else +void openpic_set_priority(u_int cpu, u_int pri) +#endif +{ + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + + /* + * Get/set the spurious vector + */ + +u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + + + /* + * Initialize one or more CPUs + */ + +void openpic_init_processor(u_int cpumask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask); +} + + +/* -------- Interprocessor Interrupts -------------------------------------- */ + + + /* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ + +void openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_timer(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + + + /* + * Send an IPI to one or more CPUs + */ + +#ifndef __powerpc__ +void openpic_cause_IPI(u_int ipi, u_int cpumask) +#else +void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask) +#endif +{ + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask); +} + + +/* -------- Timer Interrupts ----------------------------------------------- */ + + + /* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ + +void openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + + + /* + * Map a timer interrupt to one or more CPUs + */ + +void openpic_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask); +} + + +/* -------- Interrupt Sources ---------------------------------------------- */ + + + /* + * Enable/disable an interrupt source + */ + +void openpic_enable_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); +} + +void openpic_disable_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); +} + + + /* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ + +void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + check_arg_irq(irq); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_SENSE_POLARITY : 0) | + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + + + /* + * Map an interrupt source to one or more CPUs + */ + +void openpic_mapirq(u_int irq, u_int cpumask) +{ + check_arg_irq(irq); + openpic_write(&OpenPIC->Source[irq].Destination, cpumask); +} + + + /* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ + +void openpic_set_sense(u_int irq, int sense) +{ + check_arg_irq(irq); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + + diff --git a/arch/ppc/kernel/pci-bridge.c b/arch/ppc/kernel/pci-bridge.c new file mode 100644 index 000000000..0e4420340 --- /dev/null +++ b/arch/ppc/kernel/pci-bridge.c @@ -0,0 +1,428 @@ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * 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/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +struct bridge_data { + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; + void *io_base; + int bus_number; + int max_bus; + struct bridge_data *next; + struct device_node *node; +}; + +static struct bridge_data **bridges, *bridge_list; +static int max_bus; + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); + +/* + * Magic constants for enabling cache coherency in the bandit/PSX bridge. + */ +#define APPLE_VENDID 0x106b +#define BANDIT_DEVID 1 +#define BANDIT_REVID 3 + +#define BANDIT_DEVNUM 11 +#define BANDIT_MAGIC 0x50 +#define BANDIT_COHERENT 0x40 + +/* + * For a bandit bridge, turn on cache coherency if necessary. + * N.B. we can't use pcibios_*_config_* here because bridges[] + * is not initialized yet. + */ +static void init_bandit(struct bridge_data *bp) +{ + unsigned int vendev, magic; + int rev; + + /* read the word at offset 0 in config space for device 11 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); + udelay(2); + vendev = in_le32((volatile unsigned int *)bp->cfg_data); + if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) { + printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); + return; + } + + /* read the revision id */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING "Unknown revision %d for bandit at %p\n", + rev, bp->io_base); + + /* read the word at offset 0x50 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); + udelay(2); + magic = in_le32((volatile unsigned int *)bp->cfg_data); + if ((magic & BANDIT_COHERENT) != 0) + return; + magic |= BANDIT_COHERENT; + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, magic); + printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n", + bp->io_base); +} + +unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end) +{ + int bus; + struct bridge_data *bridge; + + bridge_list = 0; + max_bus = 0; + add_bridges(find_devices("bandit"), &mem_start); + add_bridges(find_devices("chaos"), &mem_start); + bridges = (struct bridge_data **) mem_start; + mem_start += (max_bus + 1) * sizeof(struct bridge_data *); + memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *)); + for (bridge = bridge_list; bridge != NULL; bridge = bridge->next) + for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus) + bridges[bus] = bridge; + + return mem_start; +} + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) +{ + int *bus_range; + int len; + struct bridge_data *bp; + + for (; dev != NULL; dev = dev->next) { + if (dev->n_addrs < 1) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + dev->full_name); + continue; + } + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO "PCI bus %d", bus_range[0]); + else + printk(KERN_INFO "PCI buses %d..%d", bus_range[0], + bus_range[1]); + printk(" controlled by %s at %x\n", + dev->name, dev->addrs[0].address); + bp = (struct bridge_data *) *mem_ptr; + *mem_ptr += sizeof(struct bridge_data); + bp->cfg_addr = (volatile unsigned int *) + (dev->addrs[0].address + 0x800000); + bp->cfg_data = (volatile unsigned char *) + (dev->addrs[0].address + 0xc00000); + bp->io_base = (void *) dev->addrs[0].address; + ioremap(dev->addrs[0].address, 0x800000); + bp->bus_number = bus_range[0]; + bp->max_bus = bus_range[1]; + bp->next = bridge_list; + bp->node = dev; + bridge_list = bp; + if (bp->max_bus > max_bus) + max_bus = bp->max_bus; + + if (strcmp(dev->name, "bandit") == 0) + init_bandit(bp); + } +} + +void *pci_io_base(unsigned int bus) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return 0; + return bp->io_base; +} + +int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, + unsigned char *devfn_ptr) +{ + unsigned int *reg; + int len; + + reg = (unsigned int *) get_property(dev, "reg", &len); + if (reg == 0 || len < 5 * sizeof(unsigned int)) { + /* doesn't look like a PCI device */ + *bus_ptr = 0xff; + *devfn_ptr = 0xff; + return -1; + } + *bus_ptr = reg[0] >> 16; + *devfn_ptr = reg[0] >> 8; + return 0; +} + +int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + struct bridge_data *bp; + + *val = 0xff; + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_8(bp->cfg_data + (offset & 3)); + + if (offset == PCI_INTERRUPT_LINE) { + /* + * Open Firmware often doesn't initialize this + * register properly, so we find the node and see + * if it has an AAPL,interrupts property. + */ + struct device_node *node; + unsigned int *reg; + + 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_fn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + *val = node->intrs[0]; + break; + } + } + + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + struct bridge_data *bp; + + *val = 0xffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + struct bridge_data *bp; + + *val = 0xffffffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + *val = in_le32((volatile unsigned int *)bp->cfg_data); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_8(bp->cfg_data + (offset & 3), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x, vendev; + unsigned char h; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_VENDOR_ID, &x) + == PCIBIOS_SUCCESSFUL && x == vendev) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x; + unsigned char h; + + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_CLASS_REVISION, &x) + == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +__initfunc(unsigned long route_pci_interrupts(void)) +{ + return 0; +} diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 612a24bff..49f2f37be 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,630 +1,194 @@ /* - * PCI support - * -- rough emulation of "PCI BIOS" functions - * - * Note: these are very motherboard specific! Some way needs to - * be worked out to handle the differences. + * $Id: pci.c,v 1.12 1997/08/27 05:05:28 cort Exp $ + * Common pmac/prep/chrp pci routines. -- Cort */ -#include <linux/config.h> -#include <linux/types.h> -#include <linux/bios32.h> +#include <linux/kernel.h> #include <linux/pci.h> +/*#include <linux/bios32.h>*/ +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/ptrace.h> #include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> -/* - * PCI interrupt configuration. This is motherboard specific. - */ -/* Which PCI interrupt line does a given device [slot] use? */ -/* Note: This really should be two dimensional based in slot/pin used */ -unsigned char *Motherboard_map; -unsigned char *Motherboard_map_name; - -/* How is the 82378 PIRQ mapping setup? */ -unsigned char *Motherboard_routes; - -/* Tables for known hardware */ - -/* Motorola PowerStack */ -static char Blackhawk_pci_IRQ_map[16] = -{ - 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 */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Blackhawk_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola MVME16xx */ -static char Genesis_pci_IRQ_map[16] = -{ - 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 */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Genesis_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola Series-E */ -static char Comet_pci_IRQ_map[16] = -{ - 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 */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Comet_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* BeBox */ -static char BeBox_pci_IRQ_map[16] = -{ - 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 */ - 16, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ -}; - -static char BeBox_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* IBM Nobis */ -static char Nobis_pci_IRQ_map[16] = -{ - 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 */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ -}; - -static char Nobis_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 13, /* Line 1 */ - 13, /* Line 2 */ - 13, /* Line 3 */ - 13 /* Line 4 */ -}; - +unsigned long io_base; +unsigned long pci_dram_offset; /* - * ibm 830 (and 850?). - * This is actually based on the Carolina motherboard - * -- Cort - */ -static char ibm8xx_pci_IRQ_map[23] = { - 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 - FireCoral */ - 4, /* Slot 12 - Ethernet PCIINTD# */ - 2, /* Slot 13 - PCI Slot #2 */ - 2, /* Slot 14 - S3 Video PCIINTD# */ - 0, /* Slot 15 - onboard SCSI (INDI) [1] */ - 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ - 0, /* Slot 17 - unused */ - 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ -}; -static char ibm8xx_pci_IRQ_routes[] = { - 0, /* Line 0 - unused */ - 13, /* Line 1 */ - 10, /* Line 2 */ - 15, /* Line 3 */ - 15, /* Line 4 */ -}; -/* This just changes the PCI slots & onboard SCSI + S3 to IRQ10, but - * it really needs some logic to set them to unique IRQ's, or even - * add some logic to the drivers to ask an irq.c routine to re-map - * the IRQ if it needs one to itself... + * 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 */ -static char Carolina_PIRQ_routes[] = { - 0xad, /* INTB# = 10, INTA# = 13 */ - 0xff /* INTD# = 15, INTC# = 15 */ -}; -/* We have to turn on LEVEL mode for changed IRQ's */ -/* All PCI IRQ's need to be level mode, so this should be something - * other than hard-coded as well... IRQ's are individually mappable - * to either edge or level. - */ -#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] */ -#define PCI_DEVICE_ID_IBM_CORAL 0x000a - -#undef PCI_DEBUG - -#ifdef PCI_STATS -int PCI_conversions[2]; -#endif - - -unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) -{ - return mem_start; +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); +int (*ptr_pcibios_find_device)(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +int (*ptr_pcibios_find_class)(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + +extern int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +extern int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +extern int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + +extern int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int chrp_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int chrp_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +extern int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +extern int chrp_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + +extern int prep_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int prep_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int prep_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int prep_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int prep_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int prep_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +extern int prep_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +extern int prep_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + + +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); } - -int -pcibios_present (void) +int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) { -#ifdef PCI_DEBUG - printk("PCI [BIOS] present?\n"); -#endif - return (1); + return ptr_pcibios_read_config_word(bus,dev_fn,offset,val); } - -int -pcibios_read_config_dword (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned int *val) +int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) { - unsigned long _val; - unsigned long *ptr; - dev >>= 3; -#ifdef PCI_DEBUG - printk("PCI Read config dword[%d.%d.%x] = ", bus, dev, offset); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); -#ifdef PCI_DEBUG - printk("[%x] ", ptr); -#endif - _val = le32_to_cpu(*ptr); - } -#ifdef PCI_DEBUG - printk("%x\n", _val); -#endif - *val = _val; - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_read_config_dword(bus,dev_fn,offset,val); } - -int -pcibios_read_config_word (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned short *val) +int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) { - unsigned short _val; - unsigned short *ptr; - dev >>= 3; -#ifdef PCI_DEBUG - printk("PCI Read config word[%d.%d.%x] = ", bus, dev, offset); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - *val = (unsigned short)0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); -#ifdef PCI_DEBUG - printk("[%x] ", ptr); -#endif - _val = le16_to_cpu(*ptr); - } -#ifdef PCI_DEBUG - printk("%x\n", _val); -#endif - *val = _val; - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_write_config_byte(bus,dev_fn,offset,val); } - -int -pcibios_read_config_byte (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned char *val) +int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) { - unsigned char _val; - volatile unsigned char *ptr; - dev >>= 3; - /* Note: the configuration registers don't always have this right! */ - if (offset == PCI_INTERRUPT_LINE) - { - if (Motherboard_map[dev] <= 4) - { - *val = Motherboard_routes[Motherboard_map[dev]]; - /*printk("dev %d map %d route %d\n", - dev,Motherboard_map[dev], - Motherboard_routes[Motherboard_map[dev]]);*/ - } else - { /* Pseudo interrupts [for BeBox] */ - *val = Motherboard_map[dev]; - } -#ifdef PCI_DEBUG - printk("PCI Read Interrupt Line[%d.%d] = %d\n", bus, dev, *val); -#endif - return PCIBIOS_SUCCESSFUL; - } -#ifdef PCI_DEBUG - printk("PCI Read config byte[%d.%d.%x] = ", bus, dev, offset); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | offset ^ 1); -#ifdef PCI_DEBUG - printk("[%x] ", ptr); -#endif - _val = *ptr; - } -#ifdef PCI_DEBUG - printk("%x\n", _val); -#endif - *val = _val; - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_write_config_word(bus,dev_fn,offset,val); } - -int -pcibios_write_config_dword (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned int val) +int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) { - unsigned long _val; - unsigned long *ptr; - dev >>= 3; - _val = le32_to_cpu(val); -#ifdef PCI_DEBUG - printk("PCI Write config dword[%d.%d.%x] = %x\n", bus, dev, offset, _val); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); - *ptr = _val; - } - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_write_config_dword(bus,dev_fn,offset,val); } - -int -pcibios_write_config_word (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned short val) +int pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) { - unsigned short _val; - unsigned short *ptr; - dev >>= 3; - _val = le16_to_cpu(val); -#ifdef PCI_DEBUG - printk("PCI Write config word[%d.%d.%x] = %x\n", bus, dev, offset, _val); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); - *ptr = _val; - } - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_find_device(vendor,dev_id,index,bus_ptr,dev_fn_ptr); } - -int -pcibios_write_config_byte (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned char val) +int pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) { - unsigned char _val; - unsigned char *ptr; - dev >>= 3; - _val = val; -#ifdef PCI_DEBUG - printk("PCI Write config byte[%d.%d.%x] = %x\n", bus, dev, offset, _val); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | offset ^ 1); - *ptr = _val; - } - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_find_class(class_code,index,bus_ptr,dev_fn_ptr); } -int -pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *dev) +int pcibios_present(void) { - unsigned long w, desired = (device_id << 16) | vendor; - int devnr; - - if (vendor == 0xffff) { - return PCIBIOS_BAD_VENDOR_ID; - } - - for (devnr = 11; devnr < 16; devnr++) - { - pcibios_read_config_dword(0, devnr<<3, PCI_VENDOR_ID, &w); - if (w == desired) { - if (index == 0) { - *bus = 0; - *dev = devnr<<3; - return PCIBIOS_SUCCESSFUL; - } - --index; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; + return 1; } -int -pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *dev) -{ - int dev_nr, class, indx; - indx = 0; -#ifdef PCI_DEBUG - printk("pcibios_find_class - class: %x, index: %x", class_code, index); -#endif - for (dev_nr = 11; dev_nr < 16; dev_nr++) - { - pcibios_read_config_dword(0, dev_nr<<3, PCI_CLASS_REVISION, &class); - if ((class>>8) == class_code) - { - if (index == indx) - { - *bus = 0; - *dev = dev_nr<<3; -#ifdef PCI_DEBUG - printk(" - device: %x\n", dev_nr); -#endif - return (0); - } - indx++; - } - } -#ifdef PCI_DEBUG - printk(" - not found\n"); -#endif - return PCIBIOS_DEVICE_NOT_FOUND; -} - -const char *pcibios_strerror(int error) -{ - static char buf[32]; - switch (error) - { case PCIBIOS_SUCCESSFUL: - return ("PCI BIOS: no error"); - case PCIBIOS_FUNC_NOT_SUPPORTED: - return ("PCI BIOS: function not supported"); - case PCIBIOS_BAD_VENDOR_ID: - return ("PCI BIOS: bad vendor ID"); - case PCIBIOS_DEVICE_NOT_FOUND: - return ("PCI BIOS: device not found"); - case PCIBIOS_BAD_REGISTER_NUMBER: - return ("PCI BIOS: bad register number"); - case PCIBIOS_SET_FAILED: - return ("PCI BIOS: set failed"); - case PCIBIOS_BUFFER_TOO_SMALL: - return ("PCI BIOS: buffer too small"); - default: - sprintf(buf, "PCI BIOS: invalid error #%d", error); - return(buf); +__initfunc(unsigned long +pcibios_init(unsigned long mem_start,unsigned long mem_end)) +{ + switch (_machine) { + case _MACH_Motorola: + case _MACH_IBM: + ptr_pcibios_read_config_byte = prep_pcibios_read_config_byte; + ptr_pcibios_read_config_word = prep_pcibios_read_config_word; + ptr_pcibios_read_config_dword = prep_pcibios_read_config_dword; + ptr_pcibios_write_config_byte = prep_pcibios_write_config_byte; + ptr_pcibios_write_config_word = prep_pcibios_write_config_word; + ptr_pcibios_write_config_dword = prep_pcibios_write_config_dword; + ptr_pcibios_find_device = prep_pcibios_find_device; + ptr_pcibios_find_class = prep_pcibios_find_class; + break; + case _MACH_Pmac: + ptr_pcibios_read_config_byte = pmac_pcibios_read_config_byte; + ptr_pcibios_read_config_word = pmac_pcibios_read_config_word; + ptr_pcibios_read_config_dword = pmac_pcibios_read_config_dword; + ptr_pcibios_write_config_byte = pmac_pcibios_write_config_byte; + ptr_pcibios_write_config_word = pmac_pcibios_write_config_word; + ptr_pcibios_write_config_dword = pmac_pcibios_write_config_dword; + ptr_pcibios_find_device = pmac_pcibios_find_device; + ptr_pcibios_find_class = pmac_pcibios_find_class; + break; + case _MACH_chrp: + ptr_pcibios_read_config_byte = chrp_pcibios_read_config_byte; + ptr_pcibios_read_config_word = chrp_pcibios_read_config_word; + ptr_pcibios_read_config_dword = chrp_pcibios_read_config_dword; + ptr_pcibios_write_config_byte = chrp_pcibios_write_config_byte; + ptr_pcibios_write_config_word = chrp_pcibios_write_config_word; + ptr_pcibios_write_config_dword = chrp_pcibios_write_config_dword; + ptr_pcibios_find_device = chrp_pcibios_find_device; + ptr_pcibios_find_class = chrp_pcibios_find_class; + break; } -} - -/* - * Note: This routine has to access the PCI configuration space - * for the PCI bridge chip (Intel 82378). - */ -unsigned long pcibios_init(unsigned long mem_start,unsigned long mem_end) -{ return mem_start; } -unsigned long route_pci_interrupts(void) +__initfunc(unsigned long +pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) { - unsigned char *ibc_pirq = (unsigned char *)0x80800860; - unsigned char *ibc_pcicon = (unsigned char *)0x80800840; - extern unsigned long isBeBox[]; - int i; - - if ( _machine == _MACH_Motorola) - { - 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 = "Series E"; - Motherboard_map = Comet_pci_IRQ_map; - Motherboard_routes = Comet_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; - } - } else - { - if ( _machine == _MACH_IBM ) - { - unsigned char pl_id; - unsigned long flags; - unsigned index; - unsigned char fn, bus; - unsigned int addr; - unsigned char dma_mode, ide_mode; - int i; - - Motherboard_map_name = "IBM 8xx (Carolina)"; - Motherboard_map = ibm8xx_pci_IRQ_map; - Motherboard_routes = ibm8xx_pci_IRQ_routes; -ll_printk("before loop\n"); - - for (index = 0; - !pcibios_find_device (PCI_VENDOR_ID_IBM, - PCI_DEVICE_ID_IBM_CORAL, - index, &bus, &fn); ++index) - { - pcibios_read_config_dword(bus, fn, 0x10, &addr); - addr &= ~0x3; - outb(0x26, addr); - dma_mode = inb(addr+4); - outb(0x25, addr); - ide_mode = inb(addr+4); - /*printk("CORAL I/O at 0x%x, DMA mode: %x, IDE mode: %x", - addr, dma_mode, ide_mode);*/ - /* Make CDROM non-DMA */ - ide_mode = (ide_mode & 0x0F) | 0x20; - outb(0x25, addr); - outb(ide_mode, addr+4); - dma_mode = dma_mode & ~0x80; - outb(0x26, addr); - outb(dma_mode, addr+4); - outb(0x26, addr); - dma_mode = inb(addr+4); - outb(0x25, addr); - ide_mode = inb(addr+4); - /*printk("=> DMA mode: %x, IDE mode: %x\n", - dma_mode, ide_mode);*/ - } - - /* Setup the PCI INT mappings for the Carolina */ - /* These are PCI Interrupt Route Control [1|2] Register */ - outb(Carolina_PIRQ_routes[0], 0x0890); - outb(Carolina_PIRQ_routes[1], 0x0891); - - pl_id=inb(0x0852); - /*printk("CPU Planar ID is %#0x\n", pl_id);*/ - - if (pl_id == 0x0C) { - /* INDI */ - Motherboard_map[12] = 1; - } -ll_printk("before edge/level\n"); -#if 0 - /*printk("Changing IRQ mode\n");*/ - pl_id=inb(0x04d0); - /*printk("Low mask is %#0x\n", pl_id);*/ - outb(pl_id|CAROLINA_IRQ_EDGE_MASK_LO, 0x04d0); - - pl_id=inb(0x04d1); - /*printk("Hi mask is %#0x\n", pl_id);*/ - outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); - pl_id=inb(0x04d1); - /*printk("Hi mask now %#0x\n", pl_id);*/ -#endif - } - } - - /* Set up mapping from slots */ - for (i = 1; i <= 4; i++) - { - ibc_pirq[i-1] = Motherboard_routes[i]; - } - /* Enable PCI interrupts */ - *ibc_pcicon |= 0x20; + return mem_start; } diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c new file mode 100644 index 000000000..a48385b3f --- /dev/null +++ b/arch/ppc/kernel/pmac_pci.c @@ -0,0 +1,423 @@ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * 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/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +struct bridge_data { + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; + void *io_base; + int bus_number; + int max_bus; + struct bridge_data *next; + struct device_node *node; +}; + +static struct bridge_data **bridges, *bridge_list; +static int max_bus; + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); + +/* + * Magic constants for enabling cache coherency in the bandit/PSX bridge. + */ +#define APPLE_VENDID 0x106b +#define BANDIT_DEVID 1 +#define BANDIT_REVID 3 + +#define BANDIT_DEVNUM 11 +#define BANDIT_MAGIC 0x50 +#define BANDIT_COHERENT 0x40 + +/* + * For a bandit bridge, turn on cache coherency if necessary. + * N.B. we can't use pcibios_*_config_* here because bridges[] + * is not initialized yet. + */ +static void init_bandit(struct bridge_data *bp) +{ + unsigned int vendev, magic; + int rev; + + /* read the word at offset 0 in config space for device 11 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); + udelay(2); + vendev = in_le32((volatile unsigned int *)bp->cfg_data); + if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) { + printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); + return; + } + + /* read the revision id */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING "Unknown revision %d for bandit at %p\n", + rev, bp->io_base); + + /* read the word at offset 0x50 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); + udelay(2); + magic = in_le32((volatile unsigned int *)bp->cfg_data); + if ((magic & BANDIT_COHERENT) != 0) + return; + magic |= BANDIT_COHERENT; + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, magic); + printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n", + bp->io_base); +} + +unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end) +{ + int bus; + struct bridge_data *bridge; + + bridge_list = 0; + max_bus = 0; + add_bridges(find_devices("bandit"), &mem_start); + add_bridges(find_devices("chaos"), &mem_start); + bridges = (struct bridge_data **) mem_start; + mem_start += (max_bus + 1) * sizeof(struct bridge_data *); + memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *)); + for (bridge = bridge_list; bridge != NULL; bridge = bridge->next) + for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus) + bridges[bus] = bridge; + + return mem_start; +} + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) +{ + int *bus_range; + int len; + struct bridge_data *bp; + + for (; dev != NULL; dev = dev->next) { + if (dev->n_addrs < 1) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + dev->full_name); + continue; + } + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO "PCI bus %d", bus_range[0]); + else + printk(KERN_INFO "PCI buses %d..%d", bus_range[0], + bus_range[1]); + printk(" controlled by %s at %x\n", + dev->name, dev->addrs[0].address); + bp = (struct bridge_data *) *mem_ptr; + *mem_ptr += sizeof(struct bridge_data); + bp->cfg_addr = (volatile unsigned int *) + (dev->addrs[0].address + 0x800000); + bp->cfg_data = (volatile unsigned char *) + (dev->addrs[0].address + 0xc00000); + bp->io_base = (void *) dev->addrs[0].address; + ioremap(dev->addrs[0].address, 0x800000); + bp->bus_number = bus_range[0]; + bp->max_bus = bus_range[1]; + bp->next = bridge_list; + bp->node = dev; + bridge_list = bp; + if (bp->max_bus > max_bus) + max_bus = bp->max_bus; + + if (strcmp(dev->name, "bandit") == 0) + init_bandit(bp); + } +} + +void *pci_io_base(unsigned int bus) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return 0; + return bp->io_base; +} + +int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, + unsigned char *devfn_ptr) +{ + unsigned int *reg; + int len; + + reg = (unsigned int *) get_property(dev, "reg", &len); + if (reg == 0 || len < 5 * sizeof(unsigned int)) { + /* doesn't look like a PCI device */ + *bus_ptr = 0xff; + *devfn_ptr = 0xff; + return -1; + } + *bus_ptr = reg[0] >> 16; + *devfn_ptr = reg[0] >> 8; + return 0; +} + +int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + struct bridge_data *bp; + + *val = 0xff; + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_8(bp->cfg_data + (offset & 3)); + + if (offset == PCI_INTERRUPT_LINE) { + /* + * Open Firmware often doesn't initialize this + * register properly, so we find the node and see + * if it has an AAPL,interrupts property. + */ + struct device_node *node; + unsigned int *reg; + + 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_fn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + *val = node->intrs[0]; + break; + } + } + + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + struct bridge_data *bp; + + *val = 0xffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + struct bridge_data *bp; + + *val = 0xffffffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + *val = in_le32((volatile unsigned int *)bp->cfg_data); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_8(bp->cfg_data + (offset & 3), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x, vendev; + unsigned char h; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_VENDOR_ID, &x) + == PCIBIOS_SUCCESSFUL && x == vendev) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x; + unsigned char h; + + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_CLASS_REVISION, &x) + == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c new file mode 100644 index 000000000..74a8ff92d --- /dev/null +++ b/arch/ppc/kernel/pmac_setup.c @@ -0,0 +1,271 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from "arch/alpha/kernel/setup.c" + * Copyright (C) 1995 Linus Torvalds + * + * 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. + * + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/major.h> +#include <asm/prom.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/io.h> +#include <asm/ide.h> +#include <asm/pci-bridge.h> +#include "time.h" + +/* + * A magic address and value to put into it on machines with the + * "ohare" I/O controller. This makes the IDE CD work on Starmaxes. + * Contributed by Harry Eaton. + */ +#define OMAGICPLACE ((volatile unsigned *) 0xf3000038) +#define OMAGICCONT 0xbeff7a + +extern int root_mountflags; + +extern char command_line[]; +extern char saved_command_line[256]; + +unsigned char drive_info; + +#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ + +extern unsigned long find_available_memory(void); + +void pmac_setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + extern unsigned long *end_of_DRAM; + struct device_node *cpu; + int *fp; + + strcpy(saved_command_line, command_line); + *cmdline_p = command_line; + + *memory_start_p = find_available_memory(); + *memory_end_p = (unsigned long) end_of_DRAM; + + set_prom_callback(); + + *memory_start_p = copy_device_tree(*memory_start_p, *memory_end_p); + + /* Set loops_per_sec to a half-way reasonable value, + for use until calibrate_delay gets called. */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) { + switch (_get_PVR() >> 16) { + case 4: /* 604 */ + case 9: /* 604e */ + case 20: /* 620 */ + loops_per_sec = *fp; + break; + default: /* 601, 603, etc. */ + loops_per_sec = *fp / 2; + } + } else + loops_per_sec = 50000000; + } +} + +char *bootpath; +char bootdevice[256]; +void *boot_host; +int boot_target; +int boot_part; +kdev_t boot_dev; + +unsigned long +powermac_init(unsigned long mem_start, unsigned long mem_end) +{ + struct device_node *chosen_np, *ohare_np; + + mem_start = pmac_find_bridges(mem_start, mem_end); + ohare_np = find_devices("ohare"); + if (ohare_np != NULL) { + printk(KERN_INFO "Twiddling the magic ohare bits\n"); + out_le32(OMAGICPLACE, OMAGICCONT); + } + pmac_nvram_init(); + via_cuda_init(); + pmac_read_rtc_time(); + pmac_find_display(); + bootpath = NULL; + chosen_np = find_devices("chosen"); + if (chosen_np != NULL) + bootpath = (char *) get_property(chosen_np, "bootpath", NULL); + if (bootpath != NULL) { + /* + * There's a bug in the prom. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + */ + call_prom("canon", 3, 1, bootpath, bootdevice, sizeof(bootdevice)); + } + return mem_start; +} + +void +note_scsi_host(struct device_node *node, void *host) +{ + int l; + char *p; + + l = strlen(node->full_name); + if (strncmp(node->full_name, bootdevice, l) == 0 + && (bootdevice[l] == '/' || bootdevice[l] == 0)) { + boot_host = host; + p = strstr(bootpath, "/sd@"); + if (p != NULL) { + p += 4; + boot_target = simple_strtoul(p, NULL, 10); + p = strchr(p, ':'); + if (p != NULL) + boot_part = simple_strtoul(p + 1, NULL, 10); + } + } +} + +void find_boot_device(void) +{ + int dev; + + if (kdev_t_to_nr(ROOT_DEV) != 0) + return; + ROOT_DEV = to_kdev_t(DEFAULT_ROOT_DEVICE); + if (boot_host == NULL) + return; +#ifdef CONFIG_SCSI + dev = sd_find_target(boot_host, boot_target); + if (dev == 0) + return; + boot_dev = to_kdev_t(dev + boot_part); +#endif + /* XXX should cope with booting from IDE also */ +} + +void note_bootable_part(kdev_t dev, int part) +{ + static int found_boot = 0; + + if (!found_boot) { + find_boot_device(); + found_boot = 1; + } + if (dev == boot_dev) { + ROOT_DEV = MKDEV(MAJOR(dev), MINOR(dev) + part); + boot_dev = NODEV; + printk(" (root)"); + } +} + +void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + struct device_node *np; + int i; + static struct device_node *atas; + static int atas_valid; + + *p = 0; + *irq = 0; + if (!atas_valid) { + atas = find_devices("ATA"); + atas_valid = 1; + } + for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next) + ; + if (np == NULL) + return; + if (np->n_addrs == 0) { + printk("ide: no addresses for device %s\n", np->full_name); + return; + } + if (np->n_intrs == 0) { + printk("ide: no intrs for device %s, using 13\n", + np->full_name); + *irq = 13; + } else { + *irq = np->intrs[0]; + } + base = (unsigned long) ioremap(np->addrs[0].address, 0x200); + for (i = 0; i < 8; ++i) + *p++ = base + i * 0x10; + *p = base + 0x160; +} + +int +pmac_get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + char *model; + struct device_node *cpu; + int l, *fp; + + l = 0; + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) + l += sprintf(buffer, "%dMHz ", *fp / 1000000); + } + + switch (pvr>>16) { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + case 9: + model = "604e"; + break; + default: + model = "unknown"; + break; + } + return l + sprintf(buffer+l, "PowerPC %s rev %d.%d\n", model, + (pvr & 0xff00) >> 8, pvr & 0xff); +} diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c new file mode 100644 index 000000000..3c1895088 --- /dev/null +++ b/arch/ppc/kernel/pmac_support.c @@ -0,0 +1,69 @@ +/* + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/reboot.h> +#include <linux/nvram.h> +#include <asm/ptrace.h> +#include <asm/io.h> +#include <asm/cuda.h> +#include <asm/system.h> +#include <asm/prom.h> + +/* + * Read and write the non-volatile RAM on PowerMacs. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; + +void pmac_nvram_init(void) +{ + struct device_node *dp; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + nvram_naddrs = 0; + return; + } + nvram_naddrs = dp->n_addrs; + if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + } else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } +} + +unsigned char nvram_read_byte(int addr) +{ + switch (nvram_naddrs) { + case 1: + return nvram_data[(addr & 0x1fff) << 4]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return 0; +} + +void nvram_write_byte(unsigned char val, int addr) +{ + switch (nvram_naddrs) { + case 1: + nvram_data[(addr & 0x1fff) << 4] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c new file mode 100644 index 000000000..69a17321d --- /dev/null +++ b/arch/ppc/kernel/pmac_time.c @@ -0,0 +1,87 @@ +/* + * Support for periodic interrupts (100 per second) and for getting + * the current time from the RTC on Power Macintoshes. + * + * At present, we use the decrementer register in the 601 CPU + * for our periodic interrupts. This will probably have to be + * changed for other processors. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <asm/cuda.h> +#include <asm/prom.h> +#include <asm/system.h> + +#include "time.h" + + +/* Apparently the RTC stores seconds since 1 Jan 1904 */ +#define RTC_OFFSET 2082844800 + +/* + * Query the OF and get the decr frequency. + * This was taken from the pmac time_init() when merging the prep/pmac + * time functions. + */ +void pmac_calibrate_decr(void) +{ + struct device_node *cpu; + int freq, *fp, divisor; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = find_type_devices("cpu"); + if (cpu == 0) + panic("can't find cpu node in time_init"); + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp == 0) + panic("can't get cpu timebase frequency"); + 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; +} + +unsigned long +pmac_get_rtc_time(void) +{ + struct cuda_request req; + + /* Get the time from the RTC */ + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME); + while (!req.got_reply) + cuda_poll(); + if (req.reply_len != 7) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + return (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; +} + +int pmac_set_rtc_time(unsigned long nowtime) +{ + return 0; +} + +/* + * We can't do this in time_init, because via_cuda_init hasn't + * been called at that stage. + */ +void +pmac_read_rtc_time(void) +{ + xtime.tv_sec = pmac_get_rtc_time(); + xtime.tv_usec = 0; +} diff --git a/arch/ppc/kernel/ppc_asm.tmpl b/arch/ppc/kernel/ppc_asm.tmpl index e31dea266..7036f4a62 100644 --- a/arch/ppc/kernel/ppc_asm.tmpl +++ b/arch/ppc/kernel/ppc_asm.tmpl @@ -187,7 +187,3 @@ n: #define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) #define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) #define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) - -/* Missing instructions */ -#define bdne bc 0,2, - diff --git a/arch/ppc/kernel/ppc_defs.head b/arch/ppc/kernel/ppc_defs.head new file mode 100644 index 000000000..fbe926459 --- /dev/null +++ b/arch/ppc/kernel/ppc_defs.head @@ -0,0 +1,3 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c new file mode 100644 index 000000000..ea2a073eb --- /dev/null +++ b/arch/ppc/kernel/ppc_htab.c @@ -0,0 +1,221 @@ +/* + * $Id: ppc_htab.c,v 1.7 1997/08/24 19:33:32 cort Exp $ + * + * PowerPC hash table management proc entry. Will show information + * about the current hash table and will allow changes to it. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> + +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +static long ppc_htab_read(struct inode * inode, struct file * file, + char * buf, unsigned long nbytes); +static long ppc_htab_write(struct inode * inode, struct file * file, + const char * buffer, unsigned long count); +static long long ppc_htab_lseek(struct inode * inode, struct file * file, + long long offset, int orig); + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern unsigned long _SDR1; + +static struct file_operations ppc_htab_operations = { + ppc_htab_lseek, /* lseek */ + ppc_htab_read, /* read */ + ppc_htab_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +/* + * proc files can do almost nothing.. + */ +struct inode_operations proc_ppc_htab_inode_operations = { + &ppc_htab_operations, /* default proc file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + + +/* + * print some useful info about the hash table. This function + * is _REALLY_ slow (see the nested for loops below) but nothing + * in here should be really timing critical. -- Cort + */ +static long ppc_htab_read(struct inode * inode, struct file * file, + char * buf, unsigned long nbytes) +{ + int n = 0, valid; + unsigned int kptes = 0, overflow = 0, uptes = 0; + PTE *ptr; + struct task_struct *p; + char buffer[128]; + + if (nbytes < 0) + return -EINVAL; + + /* + * compute user/kernel pte's table this info can be + * misleading since there can be valid (v bit set) entries + * in the table but their vsid is used by no process (mm->context) + * due to the way tlb invalidation is handled on the ppc + * -- Cort + */ + for ( ptr = Hash ; ptr < Hash_end ; ptr += sizeof(PTE)) + { + if (ptr->v) + { + /* make sure someone is using this context/vsid */ + for_each_task(p) + { + if ( (ptr->vsid >> 4) == p->mm->context ) + { + valid = 1; + break; + } + } + if ( !valid ) + continue; + /* user not allowed read or write */ + if (ptr->pp == PP_RWXX) + kptes++; + else + uptes++; + if (ptr->h == 1) + overflow++; + } + } + + n += sprintf( buffer, + "Size\t\t: %luKb\n" + "Buckets\t\t: %lu\n" + "Address\t\t: %08lx\n" + "Entries\t\t: %lu\n" + "User ptes\t: %u\n" + "Kernel ptes\t: %u\n" + "Overflows\t: %u\n" + "Percent full\t: %%%lu\n", + (unsigned long)(Hash_size>>10), + (Hash_size/(sizeof(PTE)*8)), + (unsigned long)Hash, + Hash_size/sizeof(PTE), + uptes, + kptes, + overflow, + ((kptes+uptes)*100) / (Hash_size/sizeof(PTE)) + ); + + if (file->f_pos >= strlen(buffer)) + return 0; + if (n > strlen(buffer) - file->f_pos) + n = strlen(buffer) - file->f_pos; + copy_to_user(buf, buffer + file->f_pos, n); + file->f_pos += n; + return n; +} + +/* + * Can't _yet_ adjust the hash table size while running. -- Cort + */ +static long +ppc_htab_write(struct inode * inode, struct file * file, + const char * buffer, unsigned long count) +{ + unsigned long size; + extern void reset_SDR1(void); + + if ( current->uid != 0 ) + return -EACCES; + + /* only know how to set size right now */ + if ( strncmp( buffer, "size ", 5) ) + return -EINVAL; + + size = simple_strtoul( &buffer[5], NULL, 10 ); + + /* only allow to shrink */ + if ( size >= Hash_size>>10 ) + return -EINVAL; + + /* minimum size of htab */ + if ( size < 64 ) + return -EINVAL; + + /* make sure it's a multiple of 64k */ + if ( size % 64 ) + return -EINVAL; + + printk("Hash table resize to %luk\n", size); + /* + * We need to rehash all kernel entries for the new htab size. + * Kernel only since we do a flush_tlb_all(). Since it's kernel + * we only need to bother with vsids 0-15. To avoid problems of + * clobbering un-rehashed values we put the htab at a new spot + * and put everything there. + * -- Cort + */ + Hash_size = size<<10; + Hash_mask = (Hash_size >> 6) - 1; + _SDR1 = __pa(Hash) | (Hash_mask >> 10); + flush_tlb_all(); + + reset_SDR1(); + printk("done\n"); + return count; +} + + +static long long +ppc_htab_lseek(struct inode * inode, struct file * file, + long long offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return(file->f_pos); + case 1: + file->f_pos += offset; + return(file->f_pos); + case 2: + return(-EINVAL); + default: + return(-EINVAL); + } +} + diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c new file mode 100644 index 000000000..fd2ad89ff --- /dev/null +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -0,0 +1,140 @@ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/smp.h> +#include <linux/elfcore.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/bios32.h> + +#include <asm/semaphore.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/bitops.h> +#include <asm/checksum.h> +#include <asm/pgtable.h> +#include <asm/cuda.h> +#include <asm/prom.h> +#include <asm/system.h> +#include <asm/pci-bridge.h> + +void transfer_to_handler(); +void int_return(); +void syscall_trace(); +void do_IRQ(); +void MachineCheckException(); +void AlignmentException(); +void ProgramCheckException(); +void SingleStepException(); +void FloatingPointCheckException(); +void sys_sigreturn(); +extern unsigned lost_interrupts; +extern void do_lost_interrupts(unsigned long); + +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(transfer_to_handler); +EXPORT_SYMBOL(int_return); +EXPORT_SYMBOL(do_IRQ); +EXPORT_SYMBOL(init_task_union); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(sys_sigreturn); +EXPORT_SYMBOL(lost_interrupts); +EXPORT_SYMBOL(do_lost_interrupts); + +EXPORT_SYMBOL(atomic_add); +EXPORT_SYMBOL(atomic_sub); +EXPORT_SYMBOL(atomic_inc); +EXPORT_SYMBOL(atomic_inc_return); +EXPORT_SYMBOL(atomic_dec); +EXPORT_SYMBOL(atomic_dec_return); +EXPORT_SYMBOL(atomic_dec_and_test); + +EXPORT_SYMBOL(set_bit); +EXPORT_SYMBOL(clear_bit); +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_set_bit); +EXPORT_SYMBOL(test_and_clear_bit); +EXPORT_SYMBOL(test_and_change_bit); +#if 0 +EXPORT_SYMBOL(ffz); +EXPORT_SYMBOL(find_first_zero_bit); +EXPORT_SYMBOL(find_next_zero_bit); +#endif + +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strspn); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(memcmp); + +/* EXPORT_SYMBOL(csum_partial); already in net/netsyms.c */ +EXPORT_SYMBOL(csum_partial_copy_generic); +EXPORT_SYMBOL(ip_fast_csum); +EXPORT_SYMBOL(csum_tcpudp_magic); + +EXPORT_SYMBOL(__copy_tofrom_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(strlen_user); + +/* +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); +EXPORT_SYMBOL(outsl);*/ + +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(ioremap); + +EXPORT_SYMBOL(start_thread); + +EXPORT_SYMBOL(__down_interruptible); + +EXPORT_SYMBOL(__cli); +EXPORT_SYMBOL(__sti); +/*EXPORT_SYMBOL(__restore_flags);*/ +EXPORT_SYMBOL(_disable_interrupts); +EXPORT_SYMBOL(_enable_interrupts); +EXPORT_SYMBOL(flush_instruction_cache); +EXPORT_SYMBOL(_get_PVR); +EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(flush_icache_range); +EXPORT_SYMBOL(xchg_u32); + +EXPORT_SYMBOL(cuda_request); +EXPORT_SYMBOL(cuda_send_request); +EXPORT_SYMBOL(adb_register); +EXPORT_SYMBOL(abort); +EXPORT_SYMBOL(call_prom); +EXPORT_SYMBOL(find_devices); +EXPORT_SYMBOL(find_type_devices); +EXPORT_SYMBOL(find_path_device); +EXPORT_SYMBOL(get_property); +EXPORT_SYMBOL(pci_io_base); +EXPORT_SYMBOL(pci_device_loc); +EXPORT_SYMBOL(note_scsi_host); diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c new file mode 100644 index 000000000..ffacd94c9 --- /dev/null +++ b/arch/ppc/kernel/prep_pci.c @@ -0,0 +1,436 @@ +/* + * $Id: prep_pci.c,v 1.7 1997/08/23 22:46:02 cort Exp $ + * PReP pci functions. + * Originally by Gary Thomas + * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) + * + * The motherboard routes/maps will disappear shortly. -- Cort + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/ptrace.h> +#include <asm/processor.h> + +#define MAX_DEVNR 22 + +/* Which PCI interrupt line does a given device [slot] use? */ +/* Note: This really should be two dimensional based in slot/pin used */ +unsigned char *Motherboard_map; +unsigned char *Motherboard_map_name; + +/* How is the 82378 PIRQ mapping setup? */ +unsigned char *Motherboard_routes; + +/* Tables for known hardware */ + +/* Motorola PowerStack */ +static char Blackhawk_pci_IRQ_map[16] = +{ + 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 */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Blackhawk_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola MVME16xx */ +static char Genesis_pci_IRQ_map[16] = +{ + 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 */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Genesis_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola Series-E */ +static char Comet_pci_IRQ_map[16] = +{ + 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 */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Comet_pci_IRQ_routes[] = +{ + 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 + * -- Cort + */ +static char ibm8xx_pci_IRQ_map[23] = { + 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 - FireCoral */ + 4, /* Slot 12 - Ethernet PCIINTD# */ + 2, /* Slot 13 - PCI Slot #2 */ + 2, /* Slot 14 - S3 Video PCIINTD# */ + 0, /* Slot 15 - onboard SCSI (INDI) [1] */ + 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; +static char ibm8xx_pci_IRQ_routes[] = { + 0, /* Line 0 - unused */ + 13, /* Line 1 */ + 10, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* IBM Nobis and 850 */ +static char Nobis_pci_IRQ_map[23] ={ + 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 */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ +}; + +static char Nobis_pci_IRQ_routes[] = { + 0, /* Line 0 - Unused */ + 13, /* Line 1 */ + 13, /* Line 2 */ + 13, /* Line 3 */ + 13 /* Line 4 */ +}; + +/* We have to turn on LEVEL mode for changed IRQ's */ +/* All PCI IRQ's need to be level mode, so this should be something + * other than hard-coded as well... IRQ's are individually mappable + * to either edge or level. + */ +#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] */ + +int +prep_pcibios_read_config_dword (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned int *val) +{ + unsigned long _val; + unsigned long *ptr; + dev >>= 3; + + if ((bus != 0) || (dev > MAX_DEVNR)) + { + *val = 0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + _val = le32_to_cpu(*ptr); + } + *val = _val; + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_read_config_word (unsigned char bus, + unsigned char dev, unsigned char offset, 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 + { + ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + _val = le16_to_cpu(*ptr); + } + *val = _val; + return PCIBIOS_SUCCESSFUL; +} + +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; + /* Note: the configuration registers don't always have this right! */ + if (offset == PCI_INTERRUPT_LINE) + { + *val = Motherboard_routes[Motherboard_map[dev]]; +/*printk("dev %d map %d route %d on board %d\n", + dev,Motherboard_map[dev], + Motherboard_routes[Motherboard_map[dev]], + *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/ + return PCIBIOS_SUCCESSFUL; + } + if ((bus != 0) || (dev > MAX_DEVNR)) + { + *(unsigned long *)val = (unsigned long) 0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)); + _val = *ptr; + } + *val = _val; + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_write_config_dword (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned int val) +{ + unsigned long _val; + unsigned long *ptr; + dev >>= 3; + _val = le32_to_cpu(val); + if ((bus != 0) || (dev > MAX_DEVNR)) + { + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + *ptr = _val; + } + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_write_config_word (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned short val) +{ + unsigned short _val; + unsigned short *ptr; + dev >>= 3; + _val = le16_to_cpu(val); + if ((bus != 0) || (dev > MAX_DEVNR)) + { + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + *ptr = _val; + } + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_write_config_byte (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned char val) +{ + unsigned char _val; + unsigned char *ptr; + dev >>= 3; + _val = val; + if ((bus != 0) || (dev > MAX_DEVNR)) + { + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset^1)); + *ptr = _val; + } + return PCIBIOS_SUCCESSFUL; +} + +int prep_pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, + unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; +/*printk("pcibios_find_device(): vendor %04x devid %04x index %d\n", + vendor,device_id,index);*/ + for (dev = pci_devices; dev; dev = dev->next) { +/*printk(" dev->vendor %04x dev->device %04x\n", + dev->vendor,dev->device);*/ + if (dev->vendor == vendor && dev->device == device_id) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +/* + * Given the class, find the n'th instance of that device + * in the system. + */ +int prep_pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class == class_code) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +__initfunc(unsigned long route_pci_interrupts(void)) +{ + unsigned char *ibc_pirq = (unsigned char *)0x80800860; + unsigned char *ibc_pcicon = (unsigned char *)0x80800840; + int i; + + if ( _machine == _MACH_Motorola) + { + 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 = "Series E"; + Motherboard_map = Comet_pci_IRQ_map; + Motherboard_routes = Comet_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; + } + } else if ( _machine == _MACH_IBM ) + { + unsigned char pl_id; + + if (inb(0x0852) == 0xFF) { + Motherboard_map_name = "IBM 850/860 Portable\n"; + Motherboard_map = Nobis_pci_IRQ_map; + Motherboard_routes = Nobis_pci_IRQ_routes; + } else { + Motherboard_map_name = "IBM 8xx (Carolina)"; + Motherboard_map = ibm8xx_pci_IRQ_map; + Motherboard_routes = ibm8xx_pci_IRQ_routes; + } + /*printk("Changing IRQ mode\n");*/ + pl_id=inb(0x04d0); + /*printk("Low mask is %#0x\n", pl_id);*/ + outb(pl_id|CAROLINA_IRQ_EDGE_MASK_LO, 0x04d0); + + pl_id=inb(0x04d1); + /*printk("Hi mask is %#0x\n", pl_id);*/ + outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); + pl_id=inb(0x04d1); + /*printk("Hi mask now %#0x\n", pl_id);*/ + } else + { + printk("No known machine pci routing!\n"); + return -1; + } + + /* Set up mapping from slots */ + for (i = 1; i <= 4; i++) + { + ibc_pirq[i-1] = Motherboard_routes[i]; + } + /* Enable PCI interrupts */ + *ibc_pcicon |= 0x20; + return 0; +} diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c new file mode 100644 index 000000000..18e013964 --- /dev/null +++ b/arch/ppc/kernel/prep_setup.c @@ -0,0 +1,294 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> + +/* for the mac fs */ +kdev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +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 */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + + +extern char saved_command_line[256]; + +void prep_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; +} + + +int +prep_get_cpuinfo(char *buffer) +{ + extern char *Motherboard_map_name; + extern RESIDUAL res; + int i; + int pvr = _get_PVR(); + int len; + char *model; + + switch (pvr>>16) + { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + default: + model = "unknown"; + break; + } + +#ifdef __SMP__ +#define CD(X) (cpu_data[n].X) +#else +#define CD(X) (X) +#define CPUN 0 +#endif + + len = sprintf(buffer,"processor\t: %d\n" + "cpu\t\t: %s\n" + "revision\t: %d.%d\n" + "upgrade\t\t: %s\n" + "clock\t\t: %dMHz\n" + "bus clock\t: %dMHz\n" + "machine\t\t: %s (sn %s)\n" + "pci map\t\t: %s\n", + CPUN, + model, + MAJOR(pvr), MINOR(pvr), + (inb(IBM_EQUIP_PRESENT) & 2) ? "not upgrade" : "upgrade", + (res.VitalProductData.ProcessorHz > 1024) ? + res.VitalProductData.ProcessorHz>>20 : + res.VitalProductData.ProcessorHz, + (res.VitalProductData.ProcessorBusHz > 1024) ? + res.VitalProductData.ProcessorBusHz>>20 : + res.VitalProductData.ProcessorBusHz, + res.VitalProductData.PrintableModel, + res.VitalProductData.Serial, + Motherboard_map_name + ); + + /* print info about SIMMs */ + len += sprintf(buffer+len,"simms\t\t: "); + for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) + { + if ( res.Memories[i].SIMMSize != 0 ) + len += sprintf(buffer+len,"%d:%dM ",i, + (res.Memories[i].SIMMSize > 1024) ? + res.Memories[i].SIMMSize>>20 : + res.Memories[i].SIMMSize); + } + len += sprintf(buffer+len,"\n"); + + /* TLB */ + len += sprintf(buffer+len,"tlb\t\t:"); + switch(res.VitalProductData.TLBAttrib) + { + case CombinedTLB: + len += sprintf(buffer+len," %d entries\n", + res.VitalProductData.TLBSize); + break; + case SplitTLB: + len += sprintf(buffer+len," (split I/D) %d/%d entries\n", + res.VitalProductData.I_TLBSize, + res.VitalProductData.D_TLBSize); + break; + case NoneTLB: + len += sprintf(buffer+len," not present\n"); + break; + } + + /* L1 */ + len += sprintf(buffer+len,"l1\t\t: "); + switch(res.VitalProductData.CacheAttrib) + { + case CombinedCAC: + len += sprintf(buffer+len,"%dkB LineSize\n", + res.VitalProductData.CacheSize, + res.VitalProductData.CacheLineSize); + break; + case SplitCAC: + len += sprintf(buffer+len,"(split I/D) %dkB/%dkB Linesize %dB/%dB\n", + res.VitalProductData.I_CacheSize, + res.VitalProductData.D_CacheSize, + res.VitalProductData.D_CacheLineSize, + res.VitalProductData.D_CacheLineSize); + break; + case NoneCAC: + len += sprintf(buffer+len,"not present\n"); + break; + } + + /* L2 */ + if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */ + { + len += sprintf(buffer+len,"l2\t\t: %dkB %s\n", + ((inb(IBM_L2_STATUS) >> 5) & 1) ? 512 : 256, + (inb(IBM_SYS_CTL) & 64) ? "enabled" : "disabled"); + } + else + { + len += sprintf(buffer+len,"l2\t\t: not present\n"); + } + + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + CD(loops_per_sec+2500)/500000, + (CD(loops_per_sec+2500)/5000) % 100); + + /* + * Ooh's and aah's info about zero'd pages in idle task + */ + { + extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; + len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " + "current: %u (%uKb) hits: %u/%u (%lu%%)\n", + zerototal, (zerototal*PAGE_SIZE)>>10, + zerocount, (zerocount*PAGE_SIZE)>>10, + zeropage_hits,zeropage_calls, + /* : 1 below is so we don't div by zero */ + (zeropage_hits*100) / + ((zeropage_calls)?zeropage_calls:1)); + } + return len; +} + +__initfunc(void +prep_setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p)) +{ + extern char cmd_line[]; + extern char _etext[], _edata[], _end[]; + extern int panic_timeout; + unsigned char reg; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy( saved_command_line, cmd_line ); + *cmdline_p = cmd_line; + + *memory_start_p = (unsigned long) Hash+Hash_size; + (unsigned long *)*memory_end_p = (unsigned long *)(res.TotalMemory+KERNELBASE); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_sec = 50000000; + + /* reboot on panic */ + panic_timeout = 180; + + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) _etext; + init_task.mm->end_data = (unsigned long) _edata; + init_task.mm->brk = (unsigned long) _end; + + aux_device_present = 0xaa; + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + switch ( _machine ) + { + case _MACH_IBM: + ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ + break; + case _MACH_Motorola: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + break; + } + +#ifdef CONFIG_BLK_DEV_RAM +#if 0 + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif + + printk("Boot arguments: %s\n", cmd_line); + + print_residual_device_info(); + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c new file mode 100644 index 000000000..da0151ede --- /dev/null +++ b/arch/ppc/kernel/prep_time.c @@ -0,0 +1,239 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PreP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * copied and modified from intel version + * + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/nvram.h> + +#include "time.h" + +/* + * The motorola uses the m48t18 rtc (includes DS1643) whose registers + * are at a higher end of nvram (1ff8-1fff) than the ibm mc146818 + * rtc (ds1386) which has regs at addr 0-d). The intel gets + * past this because the bios emulates the mc146818. + * + * Why in the world did they have to use different clocks? + * + * Right now things are hacked to check which machine we're on then + * use the appropriate macro. This is very very ugly and I should + * probably have a function that checks which machine we're on then + * does things correctly transparently or a function pointer which + * is setup at boot time to use the correct addresses. + * -- Cort + */ +/* + * translate from mc146818 to m48t18 addresses + */ +unsigned int clock_transl[] = { 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 */ +}; + +int prep_cmos_clock_read(int addr) +{ + if ( _machine == _MACH_IBM ) + return CMOS_READ(addr); + else if ( _machine == _MACH_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; +} + +void prep_cmos_clock_write(unsigned long val, int addr) +{ + if ( _machine == _MACH_IBM ) + { + CMOS_WRITE(val,addr); + return; + } + else if ( _machine == _MACH_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 + */ +int prep_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 */ + + prep_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + 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); + } + 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); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * 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); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + return 0; +} + +unsigned long prep_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* 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) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(prep_cmos_clock_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); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +#if 0 +void time_init(void) +{ + void (*irq_handler)(int, void *,struct pt_regs *); + + xtime.tv_sec = prep_get_rtc_time(); + xtime.tv_usec = 0; + + prep_calibrate_decr(); + + /* If we have the CPU hardware time counters, use them */ + irq_handler = timer_interrupt; + if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs) +{ + prep_calibrate_decr_handler(irq,dev,regs); + do_timer(regs); + + /* update the hw clock if: + * the time is marked out of sync (TIME_ERROR) + * or ~11 minutes have expired since the last update -- Cort + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. prep_set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ( time_state == TIME_BAD || + xtime.tv_sec > last_rtc_update + 660 ) + /*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 (prep_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 */ + + +#ifdef CONFIG_HEARTBEAT + /* use hard disk LED as a heartbeat instead -- much more useful + for debugging -- Cort */ + switch(kstat.interrupts[0] % 101) + { + /* act like an actual heart beat -- ie thump-thump-pause... */ + case 0: + case 20: + outb(1,IBM_HDD_LED); + break; + case 7: + case 27: + outb(0,IBM_HDD_LED); + break; + case 100: + break; + } +#endif +} +#endif diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index f24872063..5e194fedb 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -7,7 +7,7 @@ * Derived from "arch/i386/kernel/process.c" * Copyright (C) 1995 Linus Torvalds * - * Modified by Cort Dougan (cort@cs.nmt.edu) and + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and * Paul Mackerras (paulus@cs.anu.edu.au) * * This program is free software; you can redistribute it and/or @@ -28,22 +28,22 @@ #include <linux/ptrace.h> #include <linux/malloc.h> #include <linux/user.h> -#include <linux/a.out.h> +#include <linux/elf.h> #include <linux/config.h> +#include <linux/elf.h> #include <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> #include <asm/smp_lock.h> +#include <asm/processor.h> -int dump_fpu(void); +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); void switch_to(struct task_struct *, struct task_struct *); -void print_backtrace(unsigned long *); -void show_regs(struct pt_regs * regs); -void inline zero_paged(void); extern unsigned long _get_SP(void); + #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 #undef IDLE_ZERO 1 @@ -59,7 +59,7 @@ 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 files_struct init_files = INIT_FILES; @@ -69,9 +69,12 @@ struct mm_struct init_mm = INIT_MM; union task_union init_task_union = { INIT_TASK }; int -dump_fpu(void) +dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { - return (1); + if (last_task_used_math == current) + giveup_fpu(); + memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); + return 1; } /* check to make sure the kernel stack is healthy */ @@ -80,7 +83,6 @@ int check_stack(struct task_struct *tsk) unsigned long stack_top = kernel_stack_top(tsk); unsigned long tsk_top = task_top(tsk); int ret = 0; - unsigned long *i; #if 0 /* check tss magic */ @@ -98,7 +100,7 @@ int check_stack(struct task_struct *tsk) if ( (tsk->tss.ksp > stack_top) || (tsk->tss.ksp < tsk_top) ) { printk("stack out of bounds: %s/%d\n" - " tsk_top %08x ksp %08x stack_top %08x\n", + " tsk_top %08lx ksp %08lx stack_top %08lx\n", tsk->comm,tsk->pid, tsk_top, tsk->tss.ksp, stack_top); ret |= 2; @@ -108,7 +110,7 @@ int check_stack(struct task_struct *tsk) if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) { printk("current stack ptr out of bounds: %s/%d\n" - " tsk_top %08x sp %08x stack_top %08x\n", + " tsk_top %08lx sp %08lx stack_top %08lx\n", current->comm,current->pid, tsk_top, _get_SP(), stack_top); ret |= 4; @@ -143,15 +145,12 @@ switch_to(struct task_struct *prev, struct task_struct *new) { struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); - struct pt_regs *regs = (struct pt_regs *)(new->tss.ksp+STACK_FRAME_OVERHEAD); #if CHECK_STACK check_stack(prev); check_stack(new); #endif - /* turn off fpu for task last to run */ - /*prev->tss.regs->msr &= ~MSR_FP;*/ - + #ifdef SHOW_TASK_SWITCHES printk("%s/%d (%x) -> %s/%d (%x) ctx %x\n", prev->comm,prev->pid,prev->tss.regs->nip, @@ -160,289 +159,29 @@ switch_to(struct task_struct *prev, struct task_struct *new) new_tss = &new->tss; old_tss = ¤t->tss; _switch(old_tss, new_tss, new->mm->context); - /* turn off fpu for task last to run */ _enable_interrupts(s); } - -#include <linux/mc146818rtc.h> -asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) -{ -#if 1 - struct task_struct *p; - printk("sys_debug(): r3 %x r4 %x r5 %x r6 %x\n", a,b,c,d); - printk("last %x\n", last_task_used_math); - printk("cur %x regs %x/%x tss %x/%x\n", - current, current->tss.regs,regs,¤t->tss,current->tss); - for_each_task(p) - { - if ((long)p < KERNELBASE) - { - printk("nip %x lr %x r3 %x\n", regs->nip,regs->link,a); - print_mm_info(); - __cli(); - while(1); - } - } - return regs->gpr[3]; -#endif -#if 0 - /* set the time in the cmos clock */ - unsigned long hwtime, nowtime; - struct rtc_time tm; - - hwtime = get_cmos_time(); - to_tm(hwtime, &tm); - printk("hw: H:M:S M/D/Y %02d:%02d:%02d %d/%d/%d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_mon, - tm.tm_mday, tm.tm_year); - return; -#endif -} -/* - * vars for idle task zero'ing out pages - */ -unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ -unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ -unsigned long zerocount = 0; /* # currently pre-zero'd pages */ -unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ -unsigned long pageptr = 0; /* current page being zero'd */ -unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ - -/* - * Returns a pre-zero'd page from the list otherwise returns - * NULL. - */ -unsigned long get_prezerod_page(void) +asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) { - unsigned long page; - unsigned long s; - - if ( zero_list ) - { - /* atomically remove this page from the list */ - asm ( "101:lwarx %1,0,%2\n" /* reserve zero_list */ - " lwz %0,0(%1)\n" /* get next -- new zero_list */ - " stwcx. %0,0,%2\n" /* update zero_list */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_list), "=&r" (page) - : "r" (&zero_list) - : "cc" ); - /* 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 effect anything if it zero's one - * less page -- Cort - */ - atomic_inc((atomic_t *)&zeropage_hits); - atomic_dec((atomic_t *)&zerocount); - /* zero out the pointer to next in the page */ - *(unsigned long *)page = 0; - return page; - } return 0; } -/* - * Experimental stuff to zero out pages in the idle task - * to speed up get_free_pages() -- Cort - * Zero's out pages until we need to resched or - * we've reached the limit of zero'd pages. - */ -void inline zero_paged(void) -{ - extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); - unsigned long tmp; - pte_t ptep; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - - sprintf(current->comm, "zero_paged"); - printk("Started zero_paged\n"); - /* want priority over idle task and powerd */ - current->priority = -98; - current->counter = -98; - __sti(); - - while ( zerocount < 128 ) - { - /* - * Mark a page as reserved so we can mess with it - * If we're interrupted we keep this page and our place in it - * since we validly hold it and it's reserved for us. - */ - pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); - if ( !pageptr ) - { - printk("!pageptr in zero_paged\n"); - goto retry; - } - - if ( need_resched ) - schedule(); - - /* - * Make the page no cache so we don't blow our cache with 0's - */ - dir = pgd_offset( init_task.mm, pageptr ); - if (dir) - { - pmd = pmd_offset(dir, pageptr & PAGE_MASK); - if (pmd && pmd_present(*pmd)) - { - pte = pte_offset(pmd, pageptr & PAGE_MASK); - if (pte && pte_present(*pte)) - { - pte_uncache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); - } - } - } - - /* - * Important here to not take time away from real processes. - */ - for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) - { - if ( need_resched ) - schedule(); - *(unsigned long *)(bytecount + pageptr) = 0; - } - - /* - * If we finished zero-ing out a page add this page to - * the zero_list atomically -- we can't use - * down/up since we can't sleep in idle. - * Disabling interrupts is also a bad idea since we would - * steal time away from real processes. - * We can also have several zero_paged's running - * on different processors so we can't interfere with them. - * 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 */ - asm ( "101:lwarx %0,0,%1\n" /* reserve zero_list */ - " stw %0,0(%2)\n" /* update *pageptr */ -#ifdef __SMP__ - " sync\n" /* let store settle */ -#endif - " mr %0,%2\n" /* update zero_list in reg */ - " stwcx. %2,0,%1\n" /* update zero_list in mem */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_list) - : "r" (&zero_list), "r" (pageptr) - : "cc" ); - /* - * This variable is used in the above loop and nowhere - * else so the worst that could happen is we would - * zero out one more or one less page than we want - * per processor on the machine. This is because - * we could add our page to the list but not have - * zerocount updated yet when another processor - * reads it. -- Cort - */ - atomic_inc((atomic_t *)&zerocount); - atomic_inc((atomic_t *)&zerototal); -retry: - schedule(); - } -} - -void powerd(void) -{ - unsigned long msr, hid0; - - sprintf(current->comm, "powerd"); - __sti(); - while (1) - { - /* want priority over idle task 'swapper' -- Cort */ - current->priority = -99; - current->counter = -99; - asm volatile( - /* clear powersaving modes and set nap mode */ - "mfspr %3,1008 \n\t" - "andc %3,%3,%4 \n\t" - "or %3,%3,%5 \n\t" - "mtspr 1008,%3 \n\t" - /* enter the mode */ - "mfmsr %0 \n\t" - "oris %0,%0,%2 \n\t" - "sync \n\t" - "mtmsr %0 \n\t" - "isync \n\t" - : "=&r" (msr) - : "0" (msr), "i" (MSR_POW>>16), - "r" (hid0), - "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), - "r" (HID0_NAP)); - if ( need_resched ) - schedule(); - /* - * The ibm carolina spec says that the eagle memory - * controller will detect the need for a snoop - * and wake up the processor so we don't need to - * check for cache operations that need to be - * snooped. The ppc book says the run signal - * must be asserted while napping for this though. - * -- Cort - */ - } -} - -asmlinkage int sys_idle(void) -{ - int ret = -EPERM; - if (current->pid != 0) - goto out; - -#ifdef IDLE_ZERO - /* - * want one per cpu since it would be nice to have all - * processors who aren't doing anything - * zero-ing pages since this daemon is lock-free - * -- Cort - */ - kernel_thread(zero_paged, NULL, 0); -#endif /* IDLE_ZERO */ - -#ifdef CONFIG_POWERSAVING - /* no powersaving modes on 601 - one per processor */ - if( (_get_PVR()>>16) != 1 ) - kernel_thread(powerd, NULL, 0); -#endif /* CONFIG_POWERSAVING */ - - /* endless loop with no priority at all */ - current->priority = -100; - current->counter = -100; - for (;;) - { - schedule(); - } - ret = 0; -out: - return ret; -} - void show_regs(struct pt_regs * regs) { int i; - printk("NIP: %08X XER: %08X LR: %08X REGS: %08X TRAP: %04x\n", + printk("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx\n", regs->nip, regs->xer, regs->link, regs,regs->trap); - printk("MSR: %08x EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + printk("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, regs->msr&MSR_IR ? 1 : 0, regs->msr&MSR_DR ? 1 : 0); - printk("TASK = %x[%d] '%s' mm->pgd %08X ", + printk("TASK = %p[%d] '%s' mm->pgd %p ", current, current->pid, current->comm, current->mm->pgd); - printk("Last syscall: %d ", current->tss.last_syscall); - printk("\nlast math %08X\n", last_task_used_math); + printk("Last syscall: %ld ", current->tss.last_syscall); + printk("\nlast math %p\n", last_task_used_math); for (i = 0; i < 32; i++) { long r; @@ -451,9 +190,9 @@ void show_regs(struct pt_regs * regs) printk("GPR%02d: ", i); } - if ( get_user(r, &(regs->gpr[i])) ) + if ( __get_user(r, &(regs->gpr[i])) ) goto out; - printk("%08X ", r); + printk("%08lX ", r); if ((i % 8) == 7) { printk("\n"); @@ -480,14 +219,14 @@ release_thread(struct task_struct *t) } /* - * Copy a thread.. - */ + * Copy a thread.. + */ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { - int i; struct pt_regs * childregs; + /* Copy registers */ childregs = ((struct pt_regs *) ((unsigned long)p + sizeof(union task_union) @@ -497,13 +236,13 @@ 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.ksp = (unsigned long)(childregs) - STACK_FRAME_OVERHEAD; - p->tss.regs = (struct pt_regs *)(childregs); - if (usp >= (unsigned long)regs) - { /* Stack is in kernel space - must adjust */ - childregs->gpr[1] = (long)(childregs+1); - } else - { /* Provided stack is in user space */ + p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->tss.regs = childregs; + if (usp >= (unsigned long) regs) { + /* Stack is in kernel space - must adjust */ + childregs->gpr[1] = (unsigned long)(childregs + 1); + } else { + /* Provided stack is in user space */ childregs->gpr[1] = usp; } @@ -511,24 +250,75 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, /* * copy fpu info - assume lazy fpu switch now always - * this should really be conditional on whether or - * not the process has used the fpu * -- Cort */ if ( last_task_used_math == current ) giveup_fpu(); - + memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); p->tss.fpscr = current->tss.fpscr; childregs->msr &= ~MSR_FP; - + return 0; } +/* + * XXX ld.so expects the auxiliary table to start on + * a 16-byte boundary, so we have to find it and + * move it up. :-( + */ +static inline void shove_aux_table(unsigned long sp) +{ + int argc; + char *p; + unsigned long e; + unsigned long aux_start, offset; + + if (__get_user(argc, (int *)sp)) + return; + sp += sizeof(int) + (argc + 1) * sizeof(char *); + /* skip over the environment pointers */ + do { + if (__get_user(p, (char **)sp)) + return; + sp += sizeof(char *); + } while (p != NULL); + aux_start = sp; + /* skip to the end of the auxiliary table */ + do { + if (__get_user(e, (unsigned long *)sp)) + return; + sp += 2 * sizeof(unsigned long); + } while (e != AT_NULL); + offset = ((aux_start + 15) & ~15) - aux_start; + if (offset != 0) { + do { + sp -= sizeof(unsigned long); + if (__get_user(e, (unsigned long *)sp) + || __put_user(e, (unsigned long *)(sp + offset))) + return; + } while (sp > aux_start); + } +} + +/* + * Set up a thread for executing a new program + */ +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER; + shove_aux_table(sp); +} + + asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { int ret; + lock_kernel(); ret = do_fork(SIGCHLD, regs->gpr[1], regs); unlock_kernel(); @@ -541,26 +331,26 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, { int error; char * filename; - filename = (int) getname((char *) a0); + + filename = getname((char *) a0); error = PTR_ERR(filename); - if(IS_ERR(filename)) + if (IS_ERR(filename)) goto out; if ( last_task_used_math == current ) last_task_used_math = NULL; error = do_execve(filename, (char **) a1, (char **) a2, regs); - putname(filename); - out: unlock_kernel(); return error; } -asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) { unsigned long clone_flags = p1; int res; - + lock_kernel(); res = do_fork(clone_flags, regs->gpr[1], regs); unlock_kernel(); @@ -571,34 +361,23 @@ void print_backtrace(unsigned long *sp) { int cnt = 0; - int i; + unsigned long i; + printk("Call backtrace: "); - while ( !get_user(i, sp) && i) - { - if ( get_user( i, &sp[1] ) ) - return; - printk("%08X ", i); - if ( get_user( (ulong)sp, sp) ) - return; - if (cnt == 6 ) cnt = 7; /* wraparound early -- Cort */ - if (++cnt == 8) - { + while (sp) { + if (__get_user( i, &sp[1] )) + break; + if (cnt++ % 7 == 0) printk("\n"); - } + printk("%08lX ", i); if (cnt > 32) break; + if (__get_user(sp, (unsigned long **)sp)) + break; } printk("\n"); } -inline void start_thread(struct pt_regs * regs, - unsigned long eip, unsigned long esp) -{ - set_fs(USER_DS); - regs->nip = eip; - regs->gpr[1] = esp; - regs->msr = MSR_USER; -} - +#if 0 /* * Low level print for debugging - Cort */ @@ -651,3 +430,4 @@ void ll_puts(const char *s) orig_x = x; orig_y = y; } +#endif /* CONFIG_PREP */ diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c new file mode 100644 index 000000000..c7a566a14 --- /dev/null +++ b/arch/ppc/kernel/prom.c @@ -0,0 +1,524 @@ +/* + * Procedures for interfacing to the Open Firmware PROM on + * Power Macintosh computers. + * + * In particular, we are interested in the device tree + * and in using some of its services (exit, write to stdout). + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ + +#include <stdarg.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/blk.h> +#include <asm/prom.h> +#include <asm/page.h> +#include <asm/processor.h> + +#define getpromprop(node, name, buf, len) \ + ((int)call_prom("getprop", 4, 1, (node), (name), (buf), (len))) + +ihandle prom_stdout; +ihandle prom_chosen; + +char command_line[256]; +int screen_initialized = 0; + +char prom_display_path[128]; + +struct prom_args { + const char *service; + int nargs; + int nret; + void *args[10]; +} prom_args; + +struct pci_address { + unsigned a_hi; + unsigned a_mid; + unsigned a_lo; +}; + +struct pci_reg_property { + struct pci_address addr; + unsigned size_hi; + unsigned size_lo; +}; + +struct pci_range { + struct pci_address addr; + unsigned phys; + unsigned size_hi; + unsigned size_lo; +}; + +void (*prom_entry)(void *); +extern int prom_trashed; + +static int prom_callback(struct prom_args *); +static unsigned long inspect_node(phandle, struct device_node *, unsigned long, + unsigned long, unsigned long); +static void check_display(void); +static int prom_next_node(phandle *); + +extern int pmac_display_supported(const char *); +extern void enter_prom(void *); + +void +prom_exit() +{ + struct prom_args args; + + args.service = "exit"; + args.nargs = 0; + args.nret = 0; + enter_prom(&args); + for (;;) /* should never get here */ + ; +} + +void * +call_prom(const char *service, int nargs, int nret, ...) +{ + va_list list; + int i; + + if (prom_trashed) + panic("prom called after its memory was reclaimed"); + prom_args.service = service; + prom_args.nargs = nargs; + prom_args.nret = nret; + va_start(list, nret); + for (i = 0; i < nargs; ++i) + prom_args.args[i] = va_arg(list, void *); + va_end(list); + for (i = 0; i < nret; ++i) + prom_args.args[i + nargs] = 0; + enter_prom(&prom_args); + return prom_args.args[nargs]; +} + +void +prom_print(const char *msg) +{ + const char *p, *q; + const char *crlf = "\r\n"; + + if (screen_initialized) + return; + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom("write", 3, 1, prom_stdout, p, q - p); + if (*q != 0) { + ++q; + call_prom("write", 3, 1, prom_stdout, crlf, 2); + } + } +} + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ +void +prom_init(char *params, int unused, void (*pp)(void *)) +{ + /* First get a handle for the stdout device */ + if ( ! have_of() ) + return; + prom_entry = pp; + prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); + if (prom_chosen == (void *)-1) + prom_exit(); + call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout, + (void *) sizeof(prom_stdout)); + + /* + * If we were booted via quik, params points to the physical address + * of the command-line parameters. + * If we were booted from an xcoff image (i.e. netbooted or + * booted from floppy), we get the command line from the bootargs + * property of the /chosen node. If an initial ramdisk is present, + * params and unused are used for initrd_start and initrd_size, + * otherwise they contain 0xdeadbeef. + */ + command_line[0] = 0; + if ((unsigned long) params >= 0x4000 + && (unsigned long) params < 0x800000 + && unused == 0) { + strncpy(command_line, params+KERNELBASE, sizeof(command_line)); + } else { +#ifdef CONFIG_BLK_DEV_INITRD + if ((unsigned long) params - KERNELBASE < 0x800000 + && unused != 0 && unused != 0xdeadbeef) { + initrd_start = (unsigned long) params; + initrd_end = initrd_start + unused; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + } +#endif + call_prom("getprop", 4, 1, prom_chosen, "bootargs", + command_line, sizeof(command_line)); + } + command_line[sizeof(command_line) - 1] = 0; + + check_display(); +} + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF may fall over if we do that after + * we've taken over the MMU and done set_prom_callback. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static void +check_display() +{ + phandle node; + ihandle ih; + char type[16], name[64], path[128]; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + getpromprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, "display") != 0) + continue; + name[0] = 0; + getpromprop(node, "name", name, sizeof(name)); + if (pmac_display_supported(name)) + /* we have a supported display */ + return; + } + printk(KERN_INFO "No supported display found\n"); + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + getpromprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, "display") != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + memset(path, 0, sizeof(path)); + if ((int) call_prom("package-to-path", 3, 1, + node, path, sizeof(path) - 1) < 0) { + printk(KERN_WARNING "can't get path for display %p\n", + node); + continue; + } + ih = call_prom("open", 1, 1, path); + if (ih == 0 || ih == (ihandle) -1) { + printk(KERN_WARNING "couldn't open display %s\n", + path); + continue; + } + printk(KERN_INFO "Opened display device %s using " + "Open Firmware\n", path); + strcpy(prom_display_path, path); + break; + } +} + +static int +prom_next_node(phandle *nodep) +{ + phandle node; + + if ((node = *nodep) != 0 + && (*nodep = call_prom("child", 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom("parent", 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + } +} + +/* + * Callback routine for the PROM to call us. + * No services are implemented yet :-) + */ +static int +prom_callback(struct prom_args *argv) +{ + printk("uh oh, prom callback '%s' (%d/%d)\n", argv->service, + argv->nargs, argv->nret); + return -1; +} + +/* + * Register a callback with the Open Firmware PROM so it can ask + * us to map/unmap memory, etc. + */ +void +set_prom_callback() +{ + call_prom("set-callback", 1, 1, prom_callback); +} + +void +abort() +{ +#ifdef CONFIG_XMON + xmon(0); +#endif + prom_exit(); +} + +/* + * Make a copy of the device tree from the PROM. + */ + +static struct device_node *allnodes; +static struct device_node **allnextp; + +#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) + +unsigned long +copy_device_tree(unsigned long mem_start, unsigned long mem_end) +{ + phandle root; + + root = call_prom("peer", 1, 1, (phandle)0); + if (root == (phandle)0) + panic("couldn't get device tree root\n"); + allnextp = &allnodes; + mem_start = inspect_node(root, 0, 0, mem_start, mem_end); + *allnextp = 0; + return mem_start; +} + +static unsigned long +inspect_node(phandle node, struct device_node *dad, unsigned long base_address, + unsigned long mem_start, unsigned long mem_end) +{ + struct reg_property *reg, *rp; + struct pci_reg_property *pci_addrs; + int l, i; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name; + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + *allnextp = np; + allnextp = &np->allnext; + np->parent = dad; + if (dad != 0) { + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = np; + else + dad->next->sibling = np; + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = 0; + for (;;) { + pp = (struct property *) mem_start; + pp->name = (char *) (pp + 1); + if ((int) call_prom("nextprop", 3, 1, node, prev_name, + pp->name) <= 0) + break; + mem_start = ALIGN((unsigned long)pp->name + + strlen(pp->name) + 1); + pp->value = (unsigned char *) mem_start; + pp->length = (int) + call_prom("getprop", 4, 1, node, pp->name, pp->value, + mem_end - mem_start); + if (pp->length < 0) + panic("hey, where did property %s go?", pp->name); + mem_start = ALIGN(mem_start + pp->length); + prev_name = pp->name; + *prev_propp = pp; + prev_propp = &pp->next; + } + *prev_propp = 0; + + np->name = get_property(np, "name", 0); + np->type = get_property(np, "device_type", 0); + + /* get all the device addresses and interrupts */ + reg = (struct reg_property *) mem_start; + pci_addrs = (struct pci_reg_property *) + get_property(np, "assigned-addresses", &l); + i = 0; + if (pci_addrs != 0) { + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + /* XXX assumes PCI addresses mapped 1-1 to physical */ + reg[i].address = pci_addrs[i].addr.a_lo; + reg[i].size = pci_addrs[i].size_lo; + ++i; + } + } else { + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0) { + while ((l -= sizeof(struct reg_property)) >= 0) { + reg[i].address = rp[i].address + base_address; + reg[i].size = rp[i].size; + ++i; + } + } + } + if (i > 0) { + np->addrs = reg; + np->n_addrs = i; + mem_start += i * sizeof(struct reg_property); + } + + np->intrs = (int *) get_property(np, "AAPL,interrupts", &l); + if (np->intrs != 0) + np->n_intrs = l / sizeof(int); + + /* get the node's full name */ + l = (int) call_prom("package-to-path", 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = (char *) mem_start; + np->full_name[l] = 0; + mem_start = ALIGN(mem_start + l + 1); + } + + if (np->type != 0 && strcmp(np->type, "dbdma") == 0 && np->n_addrs > 0) + base_address = np->addrs[0].address; + + child = call_prom("child", 1, 1, node); + while (child != (void *)0) { + mem_start = inspect_node(child, np, base_address, + mem_start, mem_end); + child = call_prom("peer", 1, 1, child); + } + + return mem_start; +} + +/* + * Construct a return a list of the device_nodes with a given name. + */ +struct device_node * +find_devices(const char *name) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->name != 0 && strcasecmp(np->name, name) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Construct a return a list of the device_nodes with a given type. + */ +struct device_node * +find_type_devices(const char *type) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->type != 0 && strcasecmp(np->type, type) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Find the device_node with a given full_name. + */ +struct device_node * +find_path_device(const char *path) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + return np; + return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +unsigned char * +get_property(struct device_node *np, const char *name, int *lenp) +{ + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) + if (strcmp(pp->name, name) == 0) { + if (lenp != 0) + *lenp = pp->length; + return pp->value; + } + return 0; +} + +void +print_properties(struct device_node *np) +{ + struct property *pp; + char *cp; + int i, n; + + for (pp = np->properties; pp != 0; pp = pp->next) { + printk(KERN_INFO "%s", pp->name); + for (i = strlen(pp->name); i < 16; ++i) + printk(" "); + cp = (char *) pp->value; + for (i = pp->length; i > 0; --i, ++cp) + if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) + || (i == 1 && *cp != 0)) + break; + if (i == 0 && pp->length > 1) { + /* looks like a string */ + printk(" %s\n", (char *) pp->value); + } else { + /* dump it in hex */ + n = pp->length; + if (n > 64) + n = 64; + if (pp->length % 4 == 0) { + unsigned int *p = (unsigned int *) pp->value; + + n /= 4; + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 4) == 0) + printk("\n "); + printk(" %08x", *p++); + } + } else { + unsigned char *bp = pp->value; + + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 16) == 0) + printk("\n "); + printk(" %02x", *bp++); + } + } + printk("\n"); + if (pp->length > 64) + printk(" ... (length = %d)\n", + pp->length); + } + } +} diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index a5ec62d20..8a5839d1f 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -171,8 +171,11 @@ repeat: goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (MAP_NR(page) < max_mapnr) - *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + if (MAP_NR(page) < max_mapnr) { + unsigned long phys_addr = page + (addr & ~PAGE_MASK); + *(unsigned long *) phys_addr = data; + flush_icache_range(phys_addr, phys_addr+4); + } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); @@ -364,7 +367,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_PEEKUSR: { unsigned long tmp; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) { + if ((addr & 3) || addr < 0 || addr > (PT_FPSCR << 2)) { ret = -EIO; goto out; } @@ -378,13 +381,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (addr < PT_FPR0) { tmp = get_reg(child, addr); } -#if 1 - else if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { + else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { if (last_task_used_math == child) giveup_fpu(); tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } -#endif else ret = -EIO; if (!ret) @@ -400,7 +401,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) + if ((addr & 3) || addr < 0 || addr >= ((PT_FPR0 + 64) << 2)) goto out; addr = addr >> 2; /* temporary hack. */ @@ -508,7 +509,7 @@ asmlinkage void syscall_trace(void) goto out; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c new file mode 100644 index 000000000..4084f9f62 --- /dev/null +++ b/arch/ppc/kernel/residual.c @@ -0,0 +1,144 @@ +/* + * $Id: residual.c,v 1.2 1997/08/25 06:54:56 cort Exp $ + * + * Code to deal with the PReP residual data. + * + * Written by: Cort Dougan (cort@cs.nmt.edu) + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> +#include <linux/pci.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> +#include <asm/pnp.h> + + +/* + * Spit out some info about residual data + */ +void print_residual_device_info(void) +{ + int i; + union _PnP_TAG_PACKET *pkt; + PPC_DEVICE *dev; +#define did dev->DeviceId + + /* make sure we have residual data first */ + if ( res.ResidualLength == 0 ) + return; + + printk("Residual: %ld devices\n", res.ActualNumDevices); + for ( i = 0; + i < res.ActualNumDevices ; + i++) + { + dev = &res.Devices[i]; + /* + * pci devices + */ + if ( did.BusId & PCIDEVICE ) + { + printk("PCI Device:"); + /* unknown vendor */ + if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) ) + printk(" id %08lx types %d/%d", did.DevId, + did.BaseType, did.SubType); + /* known vendor */ + else + printk(" %s %s", + pci_strvendor(did.DevId>>16), + pci_strdev(did.DevId>>16, + did.DevId&0xffff) + ); + + if ( did.BusId & PNPISADEVICE ) + { + printk(" pnp:"); + /* get pnp info on the device */ + pkt = (union _PnP_TAG_PACKET *) + &res.DevicePnPHeap[dev->AllocatedOffset]; + for (; pkt->S1_Pack.Tag != DF_END_TAG; + pkt++ ) + { + if ( (pkt->S1_Pack.Tag == S4_Packet) || + (pkt->S1_Pack.Tag == S4_Packet_flags) ) + printk(" irq %02x%02x", + pkt->S4_Pack.IRQMask[0], + pkt->S4_Pack.IRQMask[1]); + } + } + printk("\n"); + continue; + } + /* + * isa devices + */ + if ( did.BusId & ISADEVICE ) + { + printk("ISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * eisa devices + */ + if ( did.BusId & EISADEVICE ) + { + printk("EISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * proc bus devices + */ + if ( did.BusId & PROCESSORDEVICE ) + { + printk("ProcBus Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * pcmcia devices + */ + if ( did.BusId & PCMCIADEVICE ) + { + printk("PCMCIA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + printk("Unknown bus access device: busid %lx\n", + did.BusId); + } +} + + + + + + diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index e69de29bb..f3769cad0 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -0,0 +1,268 @@ +/* + * $Id: setup.c,v 1.16 1997/08/27 22:06:54 cort Exp $ + * Common prep/pmac boot and setup code. + */ + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/reboot.h> +#include <linux/openpic.h> + +#include <asm/cuda.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/ide.h> + +char saved_command_line[256]; +unsigned char aux_device_present; + +/* copy of the residual data */ +RESIDUAL res; +int _machine; + +/* + * Perhaps we can put the pmac screen_info[] here + * on pmac as well so we don't need the ifdef's. + * Until we get multiple-console support in here + * that is. -- Cort + */ +#if defined(CONFIG_CHRP) || defined(CONFIG_PREP ) +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + { 0, 0 }, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25, /* orig-video-lines */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; + +/* + * I really need to add multiple-console support... -- Cort + */ +int pmac_display_supported(char *name) +{ + return 0; +} +int sd_find_target(void *a, int b) +{ + return 0; +} +void pmac_find_display(void) +{ +} + +#endif + +/* + * Find out what kind of machine we're on and save any data we need + * from the early boot process (devtree is copied on pmac by prom_init() ) + */ +unsigned long identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + extern unsigned long initrd_start, initrd_end; + extern char cmd_line[256]; +#ifdef CONFIG_PMAC /* cheat for now - perhaps a check for OF could tell us */ + _machine = _MACH_Pmac; +#endif /* CONFIG_PMAC */ +#ifdef CONFIG_PREP + /* make a copy of residual data */ + if ( r3 ) + memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL) ); + if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) + _machine = _MACH_IBM; + else + _machine = _MACH_Motorola; +#endif /* CONFIG_PREP */ +#ifdef CONFIG_CHRP + _machine = _MACH_chrp; +#endif /* CONFIG_CHRP */ + + switch (_machine) + { + case _MACH_Pmac: + io_base = 0; + pci_dram_offset = 0; + break; + case _MACH_IBM: + case _MACH_Motorola: + io_base = 0x80000000; + pci_dram_offset = 0x80000000; +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ + /* take care of cmd line */ + if ( r6 ) + { + + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + break; + case _MACH_chrp: + /* LongTrail */ + io_base = 0xf8000000; + pci_dram_offset = 0; + /* take care of initrd if we have one */ + if ( r4 ) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } + /* take care of cmd line */ + if ( r6 ) { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + break; + default: + printk("Unknown machine type in identify_machine!\n"); + } + return 0; +} + +/* cmd is ignored for now... */ +void machine_restart(char *cmd) +{ + struct cuda_request req; + unsigned long flags; + unsigned long i = 10000; + + switch(_machine) + { + case _MACH_Pmac: + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; + case _MACH_IBM: + case _MACH_Motorola: + _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_chrp: + openpic_init_processor(1<<0); + break; + } +} + +void machine_power_off(void) +{ + struct cuda_request req; + + if ( _machine == _MACH_Pmac ) + { + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); + for (;;) + cuda_poll(); + } + else /* prep or chrp */ + { + machine_restart(NULL); + } +} + +void machine_halt(void) +{ + if ( _machine == _MACH_Pmac ) + { +#if 0 + prom_exit(); /* doesn't work because prom is trashed */ +#else + machine_power_off(); /* for now */ +#endif + } + else /* prep or chrp */ + machine_restart(NULL); + +} + +void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + if ( _machine == _MACH_Pmac ) + pmac_ide_init_hwif_ports(p,base,irq); + else /* prep */ + prep_ide_init_hwif_ports(p,base,irq); + +} + +/* + * Will merge more into here later -- Cort + */ +int get_cpuinfo(char *buffer) +{ + extern int pmac_get_cpuinfo(char *); + extern int chrp_get_cpuinfo(char *); + extern int prep_get_cpuinfo(char *); + + + switch (_machine) + { + case _MACH_Pmac: + return pmac_get_cpuinfo(buffer); + break; + case _MACH_Motorola: + case _MACH_IBM: + return prep_get_cpuinfo(buffer); + break; + case _MACH_chrp: + return chrp_get_cpuinfo(buffer); + break; + } + printk("Unknown machine %d in get_cpuinfo()\n",_machine); + return 0; +} + + +__initfunc(unsigned long +bios32_init(unsigned long memory_start, unsigned long memory_end)) +{ + return memory_start; +} + +__initfunc(void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p)) +{ + extern void pmac_setup_arch(char **, unsigned long *, unsigned long *); + extern void chrp_setup_arch(char **, unsigned long *, unsigned long *); + extern void prep_setup_arch(char **, unsigned long *, unsigned long *); + + switch (_machine) + { + case _MACH_Pmac: + pmac_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; + case _MACH_Motorola: + case _MACH_IBM: + prep_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; + case _MACH_chrp: + return chrp_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; + } + printk("Unknown machine %d in setup_arch()\n",_machine); +} + + + diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 2f602059c..3151d266a 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -24,6 +24,7 @@ #include <linux/wait.h> #include <linux/ptrace.h> #include <linux/unistd.h> +#include <linux/elf.h> #include <asm/uaccess.h> #define _S(nr) (1<<((nr)-1)) @@ -36,6 +37,10 @@ #define PAUSE_AFTER_SIGNAL #undef PAUSE_AFTER_SIGNAL +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); /* @@ -71,32 +76,52 @@ printk("Task: %x[%d] - SIGSUSPEND at %x, Mask: %x\n", current, current->pid, reg } } +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + /* * This sets regs->esp even though we don't actually use sigstacks yet.. */ asmlinkage int sys_sigreturn(struct pt_regs *regs) { - struct sigcontext_struct *sc; - struct pt_regs *int_regs; - int signo, ret; + struct sigcontext_struct *sc, sigctx; + int ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ -#if 1 - if (verify_area(VERIFY_READ, (void *) regs->gpr[1], sizeof(sc)) - || (regs->gpr[1] >= KERNELBASE)) + sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) goto badframe; -#endif - sc = (struct sigcontext_struct *)(regs->gpr[1]+STACK_FRAME_OVERHEAD); - get_user(current->blocked, &sc->oldmask); - current->blocked &= _BLOCKABLE; - get_user(int_regs, &sc->regs); - get_user(signo, &sc->signal); + current->blocked = sigctx.oldmask & _BLOCKABLE; sc++; /* Pop signal 'context' */ #ifdef DEBUG_SIGNALS -printk("Sig return - Regs: %x, sc: %x, sig: %d\n", int_regs, sc, signo); + printk("Sig return - Regs: %p, sc: %p, sig: %d\n", sigctx.regs, sc, + sigctx.signal); #endif - if (sc == (struct sigcontext_struct *)(int_regs)) { - /* Last stacked signal */ - memcpy(regs, int_regs, sizeof(*regs)); + if (sc == (struct sigcontext_struct *)(sigctx.regs)) { + /* Last stacked signal - restore registers */ + if (last_task_used_math == current) + giveup_fpu(); + if (copy_from_user(saved_regs, sigctx.regs, sizeof(saved_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + memcpy(regs, saved_regs, + MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs))); + + if (copy_from_user(current->tss.fpr, + (unsigned long *)sigctx.regs + ELF_NGREG, + ELF_NFPREG * sizeof(double))) + goto badframe; + if (regs->trap == 0x0C00 /* System Call! */ && ((int)regs->result == -ERESTARTNOHAND || (int)regs->result == -ERESTARTSYS || @@ -106,15 +131,17 @@ printk("Sig return - Regs: %x, sc: %x, sig: %d\n", int_regs, sc, signo); regs->result = 0; } ret = regs->result; + } else { /* More signals to go */ - regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD; - get_user(regs->gpr[3], &sc->signal); - get_user(int_regs, (struct pt_regs **) &sc->regs); - regs->gpr[4] = (unsigned long) int_regs; - regs->link = (unsigned long) (int_regs+1); - get_user(regs->nip, &sc->handler); - ret = regs->gpr[3]; + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sigctx.regs; + regs->link = regs->gpr[4] + ELF_NGREG * sizeof(unsigned long) + + ELF_NFPREG * sizeof(double); + regs->nip = sigctx.handler; } return ret; @@ -142,6 +169,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) unsigned long *frame = NULL; unsigned long *trampoline; unsigned long *regs_ptr; + double *fpregs_ptr; unsigned long nip = 0; unsigned long signr; struct sigcontext_struct *sc; @@ -164,7 +192,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -204,7 +232,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; @@ -257,18 +285,33 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) nip = regs->nip; frame = (unsigned long *) regs->gpr[1]; - /* Build trampoline code on stack */ - frame -= 2; + /* + * Build trampoline code on stack, and save gp and fp regs. + * The 56 word hole is because programs using the rs6000/xcoff + * style calling sequence can save up to 19 gp regs and 18 fp regs + * on the stack before decrementing sp. + */ + frame -= 2 + 56; trampoline = frame; - /* verify stack is valid for writing regs struct */ - if (verify_area(VERIFY_WRITE,(void *)frame, sizeof(long)*2+sizeof(*regs)) - || ((unsigned long) frame >= KERNELBASE )) - goto badframe; - put_user(0x38007777UL, trampoline); /* li r0,0x7777 */ - put_user(0x44000002UL, trampoline+1); /* sc */ - frame -= sizeof(*regs) / sizeof(long); + frame -= ELF_NFPREG * sizeof(double) / sizeof(unsigned long); + fpregs_ptr = (double *) frame; + frame -= ELF_NGREG; regs_ptr = frame; - copy_to_user(regs_ptr, regs, sizeof(*regs)); + /* verify stack is valid for writing to */ + if (verify_area(VERIFY_WRITE, frame, + (ELF_NGREG + 2) * sizeof(long) + + ELF_NFPREG * sizeof(double))) + goto badframe; + if (last_task_used_math == current) + giveup_fpu(); + if (__copy_to_user(regs_ptr, regs, + MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs))) + || __copy_to_user(fpregs_ptr, current->tss.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777UL, trampoline) /* li r0,0x7777 */ + || __put_user(0x44000002UL, trampoline+1)) /* sc */ + goto badframe; + signr = 1; sa = current->sig->action; @@ -279,24 +322,29 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) continue; frame -= sizeof(struct sigcontext_struct) / sizeof(long); - if (verify_area(VERIFY_WRITE,(void *)frame, - sizeof(struct sigcontext_struct)/sizeof(long))) + if (verify_area(VERIFY_WRITE, frame, + sizeof(struct sigcontext_struct))) goto badframe; sc = (struct sigcontext_struct *)frame; nip = (unsigned long) sa->sa_handler; if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - put_user(nip, &sc->handler); - put_user(oldmask, &sc->oldmask); /* was current->blocked */ - put_user(regs_ptr, &sc->regs); - put_user(signr, &sc->signal); + if (__put_user(nip, &sc->handler) + || __put_user(oldmask, &sc->oldmask) + || __put_user(regs_ptr, &sc->regs) + || __put_user(signr, &sc->signal)) + goto badframe; current->blocked |= sa->sa_mask; regs->gpr[3] = signr; - regs->gpr[4] = (unsigned long)regs_ptr; + regs->gpr[4] = (unsigned long) regs_ptr; } + + frame -= __SIGNAL_FRAMESIZE / sizeof(unsigned long); + if (put_user(regs->gpr[1], frame)) + goto badframe; regs->link = (unsigned long)trampoline; regs->nip = nip; - regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD; + regs->gpr[1] = (unsigned long) frame; /* The DATA cache must be flushed here to insure coherency */ /* between the DATA & INSTRUCTION caches. Since we just */ @@ -305,8 +353,8 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) /* cache for new data, we have to force the data to go on to */ /* memory and flush the instruction cache to force it to look */ /* there. The following function performs this magic */ - store_cache_range((unsigned long) trampoline, - (unsigned long) (trampoline + 2)); + flush_icache_range((unsigned long) trampoline, + (unsigned long) (trampoline + 2)); return 1; badframe: diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index bfdccdee6..b7bac7a6a 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -1,15 +1,25 @@ /* * linux/arch/ppc/kernel/sys_ppc.c * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/sys_i386.c" * Adapted from the i386 version by Gary Thomas * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au). * * This file contains various random system calls that * have a non-standard calling sequence on the Linux/PPC * platform. + * + * 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/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/mm.h> @@ -20,11 +30,11 @@ #include <linux/shm.h> #include <linux/stat.h> #include <linux/mman.h> +#include <linux/sys.h> #include <linux/ipc.h> #include <asm/uaccess.h> #include <asm/ipc.h> - void check_bugs(void) { @@ -32,32 +42,32 @@ check_bugs(void) asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) { - printk("sys_ioperm()\n"); + printk(KERN_ERR "sys_ioperm()\n"); return -EIO; } int sys_iopl(int a1, int a2, int a3, int a4) { lock_kernel(); - printk( "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return (ENOSYS); + return (-ENOSYS); } int sys_vm86(int a1, int a2, int a3, int a4) { lock_kernel(); - printk( "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return (ENOSYS); + return (-ENOSYS); } int sys_modify_ldt(int a1, int a2, int a3, int a4) { lock_kernel(); - printk( "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return (ENOSYS); + return (-ENOSYS); } /* @@ -65,7 +75,8 @@ int sys_modify_ldt(int a1, int a2, int a3, int a4) * * This is really horribly ugly. */ -asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) +asmlinkage int +sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) { int version, ret; @@ -73,138 +84,113 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; - if (call <= SEMCTL) - switch (call) { - case SEMOP: - ret = sys_semop (first, (struct sembuf *)ptr, second); - goto out; - case SEMGET: - ret = sys_semget (first, second, third); - goto out; - case SEMCTL: { - union semun fourth; - ret = -EINVAL; - if (!ptr) - goto out; - ret = -EFAULT; - if (get_user(fourth.__pad, (void **) ptr)) - goto out; - ret = sys_semctl (first, second, third, fourth); - goto out; - } - default: - ret = -EINVAL; - goto out; + ret = -EINVAL; + switch (call) { + case SEMOP: + ret = sys_semop (first, (struct sembuf *)ptr, second); + break; + case SEMGET: + ret = sys_semget (first, second, third); + break; + case SEMCTL: { + union semun fourth; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(long))) + || (ret = get_user(fourth.__pad, (void **)ptr))) + break; + ret = sys_semctl (first, second, third, fourth); + break; } - if (call <= MSGCTL) - switch (call) { - case MSGSND: - ret = sys_msgsnd (first, (struct msgbuf *) ptr, - second, third); - goto out; - case MSGRCV: - switch (version) { - case 0: { - struct ipc_kludge tmp; - ret = -EINVAL; - if (!ptr) - goto out; - ret = -EFAULT; - if (copy_from_user(&tmp,(struct ipc_kludge *) ptr, - sizeof (tmp))) - goto out; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); - goto out; - } - case 1: default: - ret = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third); - goto out; + case MSGSND: + ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third); + break; + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(tmp))) + || (ret = copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp)))) + break; + ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, + third); + break; } - case MSGGET: - ret = sys_msgget ((key_t) first, second); - goto out; - case MSGCTL: - ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); - goto out; default: - ret = -EINVAL; - goto out; + ret = sys_msgrcv (first, (struct msgbuf *) ptr, + second, fifth, third); + break; } - if (call <= SHMCTL) - switch (call) { - case SHMAT: - switch (version) { - case 0: default: { - ulong raddr; - ret = sys_shmat (first, (char *) ptr, second, &raddr); - if (ret) - goto out; - ret = put_user (raddr, (ulong *) third); - goto out; + break; + case MSGGET: + ret = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); + break; + case SHMAT: + switch (version) { + default: { + ulong raddr; + + if ((ret = verify_area(VERIFY_WRITE, (ulong*) third, + sizeof(ulong)))) + break; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + break; + ret = put_user (raddr, (ulong *) third); + break; } - case 1: /* iBCS2 emulator entry point */ - ret = -EINVAL; - if (get_fs() != get_ds()) - goto out; - ret = sys_shmat (first, (char *) ptr, second, (ulong *) third); - goto out; - } - case SHMDT: - ret = sys_shmdt ((char *)ptr); - goto out; - case SHMGET: - ret = sys_shmget (first, second, third); - goto out; - case SHMCTL: - ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); - goto out; - default: - ret = -EINVAL; - goto out; + case 1: /* iBCS2 emulator entry point */ + if (get_fs() != get_ds()) + break; + ret = sys_shmat (first, (char *) ptr, second, + (ulong *) third); + break; } - else - ret = -EINVAL; -out: - unlock_kernel(); - return ret; -} - + break; + case SHMDT: + ret = sys_shmdt ((char *)ptr); + break; + case SHMGET: + ret = sys_shmget (first, second, third); + break; + case SHMCTL: + ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); + break; + } -#ifndef CONFIG_MODULES -void -scsi_register_module(void) -{ - lock_kernel(); - panic("scsi_register_module"); - unlock_kernel(); -} - -void -scsi_unregister_module(void) -{ - lock_kernel(); - panic("scsi_unregister_module"); unlock_kernel(); + return ret; } -#endif /* * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way unix traditionally does this, though. */ -asmlinkage int sys_pipe(unsigned long * fildes) +asmlinkage int sys_pipe(int *fildes) { int fd[2]; int error; - error = verify_area(VERIFY_WRITE,fildes,8); + error = verify_area(VERIFY_WRITE, fildes, 8); if (error) return error; + lock_kernel(); error = do_pipe(fd); + unlock_kernel(); if (error) return error; - put_user(fd[0],0+fildes); - put_user(fd[1],1+fildes); + if (__put_user(fd[0],0+fildes) + || __put_user(fd[1],1+fildes)) + return -EFAULT; /* should we close the fds? */ return 0; } @@ -213,13 +199,18 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, unsigned long fd, off_t offset) { struct file * file = NULL; + int ret = -EBADF; + + lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; + goto out; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - - return do_mmap(file, addr, len, prot, flags, offset); + ret = do_mmap(file, addr, len, prot, flags, offset); +out: + unlock_kernel(); + return ret; } extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); @@ -233,15 +224,15 @@ extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timev asmlinkage int ppc_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { - int err; if ( (unsigned long)n >= 4096 ) { unsigned long *buffer = (unsigned long *)n; - if ( get_user(n, buffer) || - get_user(inp,buffer+1) || - get_user(outp,buffer+2) || - get_user(exp,buffer+3) || - get_user(tvp,buffer+4) ) + if (verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long)) + || __get_user(n, buffer) + || __get_user(inp, ((fd_set **)(buffer+1))) + || __get_user(outp, ((fd_set **)(buffer+2))) + || __get_user(exp, ((fd_set **)(buffer+3))) + || __get_user(tvp, ((struct timeval **)(buffer+4)))) return -EFAULT; } return sys_select(n, inp, outp, exp, tvp); diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index e69de29bb..fc172d7a7 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -0,0 +1,268 @@ +/* + * $Id: time.c,v 1.10 1997/08/27 22:06:56 cort Exp $ + * Common time routines among all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> +#include <linux/mc146818rtc.h> +#include <linux/time.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/nvram.h> + +#include "time.h" + +/* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */ +int (*set_rtc_time)(unsigned long); + +/* keep track of when we need to update the rtc */ +unsigned long last_rtc_update = 0; + +/* The decrementer counts down by 128 every 128ns on a 601. */ +#define DECREMENTER_COUNT_601 (1000000000 / HZ) +#define COUNT_PERIOD_NUM_601 1 +#define COUNT_PERIOD_DEN_601 1000 + +unsigned decrementer_count; /* count value for 1e6/HZ microseconds */ +unsigned count_period_num; /* 1 decrementer count equals */ +unsigned count_period_den; /* count_period_num / count_period_den us */ + +/* Accessor functions for the decrementer register. */ +inline unsigned long +get_dec(void) +{ + int ret; + + asm volatile("mfspr %0,22" : "=r" (ret) :); + return ret; +} + +inline void +set_dec(int val) +{ + asm volatile("mtspr 22,%0" : : "r" (val)); +} + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * We set it up to overflow again in 1/HZ seconds. + */ +void timer_interrupt(struct pt_regs * regs) +{ + int dval, d; + while ((dval = get_dec()) < 0) { + /* + * Wait for the decrementer to change, then jump + * in and add decrementer_count to its value + * (quickly, before it changes again!) + */ + while ((d = get_dec()) == dval) + ; + set_dec(d + decrementer_count); + do_timer(regs); + /* + * update the rtc when needed + */ + if ( xtime.tv_sec > last_rtc_update + 660 ) + if (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 */ + } +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += (decrementer_count - get_dec()) + * count_period_num / count_period_den; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + unsigned long flags; + int frac_tick; + + last_rtc_update = 0; /* so the rtc gets updated soon */ + + frac_tick = tv->tv_usec % (1000000 / HZ); + save_flags(flags); + cli(); + xtime.tv_sec = tv->tv_sec; + xtime.tv_usec = tv->tv_usec - frac_tick; + set_dec(frac_tick * count_period_den / count_period_num); + restore_flags(flags); +} + + +void +time_init(void) +{ + /* pmac hasn't yet called via_cuda_init() */ + if ( _machine != _MACH_Pmac ) + { + + if ( _machine == _MACH_chrp ) + xtime.tv_sec = chrp_get_rtc_time(); + else /* assume prep */ + xtime.tv_sec = prep_get_rtc_time(); + xtime.tv_usec = 0; + /* + * mark the rtc/on-chip timer as in sync + * so we don't update right away + */ + last_rtc_update = xtime.tv_sec; + } + + 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; + } + + switch (_machine) + { + case _MACH_Pmac: + pmac_calibrate_decr(); + set_rtc_time = pmac_set_rtc_time; + break; + case _MACH_IBM: + case _MACH_Motorola: + prep_calibrate_decr(); + set_rtc_time = prep_set_rtc_time; + break; + case _MACH_chrp: + chrp_calibrate_decr(); + set_rtc_time = chrp_set_rtc_time; + break; + } + set_dec(decrementer_count); +} + +/* + * 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; +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); +} + +void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) +{ + int 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 = %d/%d (%luMHz)\n", + freq, divisor,t2>>20); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + *done_ptr = 1; + } +} + +void chrp_calibrate_decr(void) +{ + int freq, fp, divisor; + + fp = 16666000; /* hardcoded for now */ + 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; +} + + +/* 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. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +inline unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h new file mode 100644 index 000000000..9ce5921dc --- /dev/null +++ b/arch/ppc/kernel/time.h @@ -0,0 +1,74 @@ +/* + * $Id: time.h,v 1.5 1997/08/27 22:06:58 cort Exp $ + * Common time prototypes and such for all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + */ + +#include <linux/mc146818rtc.h> + +/* time.c */ +__inline__ unsigned long get_dec(void); +__inline__ void set_dec(int val); +void prep_calibrate_decr_handler(int, void *,struct pt_regs *); +void prep_calibrate_decr(void); +void pmac_calibrate_decr(void); +void chrp_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, + unsigned int, unsigned int, unsigned int); + +/* 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); +int prep_set_rtc_time(unsigned long nowtime); +int pmac_set_rtc_time(unsigned long nowtime); +int chrp_set_rtc_time(unsigned long nowtime); +void pmac_read_rtc_time(void); + +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +extern void inline to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; +} diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 84ce1c5ca..edfcb4d63 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -1,9 +1,15 @@ /* * linux/arch/ppc/kernel/traps.c * - * Copyright (C) 1995 Gary Thomas - * Adapted for PowerPC by Gary Thomas + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * 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. + * * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) */ /* @@ -24,9 +30,21 @@ #include <linux/config.h> #include <asm/pgtable.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/processor.h> + +extern int fix_alignment(struct pt_regs *); +extern void bad_page_fault(struct pt_regs *, unsigned long); + +#ifdef CONFIG_XMON +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern void xmon(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif /* * Trap & Exception support @@ -43,40 +61,53 @@ _exception(int signr, struct pt_regs *regs) if (!user_mode(regs)) { show_regs(regs); - print_backtrace(regs->gpr[1]); - panic("Exception in kernel pc %x signal %d",regs->nip,signr); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_XMON + xmon(regs); +#endif + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); } force_sig(signr, current); } +void MachineCheckException(struct pt_regs *regs) { if ( !user_mode(regs) ) { +#ifdef CONFIG_XMON + if (xmon_fault_handler) { + xmon_fault_handler(regs); + return; + } +#endif printk("Machine check in kernel mode.\n"); printk("Caused by (from msr): "); - printk("regs %08x ",regs); + printk("regs %p ",regs); switch( regs->msr & 0x0000F000) { case (1<<12) : printk("Machine check signal - probably due to mm fault\n" "with mmu off\n"); - break; + break; case (1<<13) : printk("Transfer error ack signal\n"); - break; + break; case (1<<14) : printk("Data parity signal\n"); - break; + break; case (1<<15) : printk("Address parity signal\n"); - break; + break; default: printk("Unknown values in msr\n"); } show_regs(regs); - print_backtrace(regs->gpr[1]); - panic(""); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_XMON + xmon(regs); +#endif + panic("machine check"); } _exception(SIGSEGV, regs); } @@ -105,33 +136,71 @@ RunModeException(struct pt_regs *regs) _exception(SIGTRAP, regs); } +void ProgramCheckException(struct pt_regs *regs) { - if (current->flags & PF_PTRACED) + if (regs->msr & 0x100000) { + /* IEEE FP exception */ + _exception(SIGFPE, regs); + } else if (regs->msr & 0x20000) { + /* trap exception */ +#ifdef CONFIG_XMON + if (xmon_bpt(regs)) + return; +#endif _exception(SIGTRAP, regs); - else + } else { _exception(SIGILL, regs); + } } +void SingleStepException(struct pt_regs *regs) { regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ +#ifdef CONFIG_XMON + if (xmon_sstep(regs)) + return; +#endif _exception(SIGTRAP, regs); } +void AlignmentException(struct pt_regs *regs) { + int fixed; + + if (last_task_used_math == current) + giveup_fpu(); + fixed = fix_alignment(regs); + if (fixed == 1) { + regs->nip += 4; /* skip over emulated instruction */ + return; + } + if (fixed == -EFAULT) { + /* fixed == -EFAULT means the operand address was bad */ + bad_page_fault(regs, regs->dar); + return; + } _exception(SIGBUS, regs); } +void +PromException(struct pt_regs *regs, int trap) +{ + regs->trap = trap; +#ifdef CONFIG_XMON + xmon(regs); +#endif + printk("Exception %lx in prom at PC: %lx, SR: %lx\n", + regs->trap, regs->nip, regs->msr); + /* probably should turn up the toes here */ +} + +void trace_syscall(struct pt_regs *regs) { - static int count; - printk("Task: %08X(%d), PC: %08X/%08X, Syscall: %3d, Result: %s%d\n", + printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld\n", current, current->pid, regs->nip, regs->link, regs->gpr[0], regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); - if (++count == 20) - { - count = 0; - } } diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile index 68b33f047..8b84ce2ae 100644 --- a/arch/ppc/lib/Makefile +++ b/arch/ppc/lib/Makefile @@ -1,38 +1,11 @@ -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) $(ASFLAGS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< -.S.s: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s -.S.o: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s - $(AS) $(ASFLAGS) -o $*.o $*.s - rm $*.s - -HOST_CC = gcc - -L_TARGET = lib.o -L_OBJS = checksum.o cksum_support.o string.o - -${L_TARGET}: $(L_OBJS) - $(LD) -r -o ${L_TARGET} $(L_OBJS) - - -fastdep: - $(TOPDIR)/scripts/mkdep *.[Sch] > .depend - -dep: - $(CPP) -M *.S *.c > .depend +# +# Makefile for ppc-specific library files.. +# -modules: +.S.o: + $(CC) -D__ASSEMBLY__ -c $< -o $*.o -dummy: +O_TARGET = lib.o +O_OBJS = checksum.o string.o strcase.o -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/lib/checksum.S b/arch/ppc/lib/checksum.S new file mode 100644 index 000000000..6c53d50ff --- /dev/null +++ b/arch/ppc/lib/checksum.S @@ -0,0 +1,192 @@ +/* + * This file contains assembly-language implementations + * of IP-style 1's complement checksum routines. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * 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. + * + * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au). + */ + +#include <linux/sys.h> +#include <asm/errno.h> +#include "../kernel/ppc_asm.tmpl" + +_TEXT() + +/* + * ip_fast_csum(buf, len) -- Optimized for IP header + * len is in words and is always >= 5. + */ +_GLOBAL(ip_fast_csum) + lwz r0,0(r3) + lwzu r5,4(r3) + addi r4,r4,-2 + addc r0,r0,r5 + mtctr r4 +1: lwzu r4,4(r3) + adde r0,r0,r4 + bdnz 1b + addze r0,r0 /* add in final carry */ + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Compute checksum of TCP or UDP pseudo-header: + * csum_tcpudp_magic(saddr, daddr, len, proto, sum) + */ +_GLOBAL(csum_tcpudp_magic) + rlwimi r5,r6,16,0,15 /* put proto in upper half of len */ + addc r0,r3,r4 /* add 4 32-bit words together */ + adde r0,r0,r5 + adde r0,r0,r7 + addze r0,r0 /* add in final carry */ + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * csum_partial(buff, len, sum) + */ +_GLOBAL(csum_partial) + addic r0,r5,0 + subi r3,r3,4 + srwi. r6,r4,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r5,r3,2 /* Align buffer to longword boundary */ + beq+ 1f + lhz r5,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r4,r4,2 + addc r0,r0,r5 + srwi. r6,r4,2 /* # words to do */ + beq 3f +1: mtctr r6 +2: lwzu r5,4(r3) /* the bdnz has zero overhead, so it should */ + adde r0,r0,r5 /* be unnecessary to unroll this loop */ + bdnz 2b + andi. r4,r4,3 +3: cmpi 0,r4,2 + blt+ 4f + lhz r5,4(r3) + addi r3,r3,2 + subi r4,r4,2 + adde r0,r0,r5 +4: cmpi 0,r4,1 + bne+ 5f + lbz r5,4(r3) + slwi r5,r5,8 /* Upper byte of word */ + adde r0,r0,r5 +5: addze r3,r0 /* add in final carry */ + blr + +/* + * Computes the checksum of a memory block at src, length len, + * and adds in "sum" (32-bit), while copying the block to dst. + * If an access exception occurs on src or dst, it stores -EFAULT + * to *src_err or *dst_err respectively, and (for an error on + * src) zeroes the rest of dst. + * + * csum_partial_copy_generic(src, dst, len, sum, src_err, dst_err) + */ +_GLOBAL(csum_partial_copy_generic) + addic r0,r6,0 + subi r3,r3,4 + subi r4,r4,4 + srwi. r6,r5,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r9,r4,2 /* Align dst to longword boundary */ + beq+ 1f +81: lhz r6,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r5,r5,2 +91: sth r6,4(r4) + addi r4,r4,2 + addc r0,r0,r6 + srwi. r6,r5,2 /* # words to do */ + beq 3f +1: mtctr r6 +82: lwzu r6,4(r3) /* the bdnz has zero overhead, so it should */ +92: stwu r6,4(r4) /* be unnecessary to unroll this loop */ + adde r0,r0,r6 + bdnz 82b + andi. r5,r5,3 +3: cmpi 0,r5,2 + blt+ 4f +83: lhz r6,4(r3) + addi r3,r3,2 + subi r5,r5,2 +93: sth r6,4(r4) + addi r4,r4,2 + adde r0,r0,r6 +4: cmpi 0,r5,1 + bne+ 5f +84: lbz r6,4(r3) +94: stb r6,4(r4) + slwi r6,r6,8 /* Upper byte of word */ + adde r0,r0,r6 +5: addze r3,r0 /* add in final carry */ + blr + +.section .fixup,"ax" + +src_error_1: + li r6,0 + subi r5,r5,2 +95: sth r6,4(r4) + addi r4,r4,2 + srwi. r6,r5,2 + beq 3f + mtctr r6 +src_error_2: + li r6,0 +96: stwu r6,4(r4) + bdnz 96b +3: andi. r5,r5,3 + beq src_error +src_error_3: + li r6,0 + mtctr r5 + addi r4,r4,3 +97: stbu r6,1(r4) + bdnz 97b +src_error: + cmpi 0,r7,0 + beq 1f + li r6,-EFAULT + stw r6,0(r7) +1: addze r3,r0 + blr + +dst_error: + cmpi 0,r8,0 + beq 1f + li r6,-EFAULT + stw r6,0(r8) +1: addze r3,r0 + blr + +.section __ex_table,"a" + .long 81b,src_error_1 + .long 91b,dst_error + .long 82b,src_error_2 + .long 92b,dst_error + .long 83b,src_error_3 + .long 93b,dst_error + .long 84b,src_error_3 + .long 94b,dst_error + .long 95b,dst_error + .long 96b,dst_error + .long 97b,dst_error diff --git a/arch/ppc/lib/strcase.c b/arch/ppc/lib/strcase.c new file mode 100644 index 000000000..a9e40bce9 --- /dev/null +++ b/arch/ppc/lib/strcase.c @@ -0,0 +1,12 @@ +#include <linux/ctype.h> + +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} diff --git a/arch/ppc/lib/string.S b/arch/ppc/lib/string.S new file mode 100644 index 000000000..96378dec1 --- /dev/null +++ b/arch/ppc/lib/string.S @@ -0,0 +1,345 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * 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 "../kernel/ppc_asm.tmpl" +#include <asm/errno.h> + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr +2: li r3,0 + blr + + .global memchr +memchr: + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r3,r3,-1 +1: lbzu r0,1(r3) + cmpw 0,r0,r4 + bdnzf 2,1b + beqlr +2: li r3,0 + blr + + .globl __copy_tofrom_user +__copy_tofrom_user: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + li r3,0 /* success return value */ + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) +11: lwzu r8,8(r4) +12: stw r7,4(r6) +13: stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f +14: lwzu r0,4(r4) + addi r5,r5,-4 +15: stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) +16: stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 +17: stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b + .long 11b,99b + .long 12b,99b + .long 13b,99b + .long 14b,99b + .long 15b,99b + .long 4b,99b + .long 16b,99b + .long 6b,99b + .long 17b,99b +.text + + .globl __clear_user +__clear_user: + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f +11: stwu r5,4(r6) + beqlr + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + rlwinm r0,r4,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r5,4(r6) + bdnz 1b +6: andi. r4,r4,3 +7: cmpwi 0,r4,0 + beqlr + mtctr r4 + addi r6,r6,3 +8: stbu r5,1(r6) + bdnz 8b + blr +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 11b,99b + .long 1b,99b + .long 8b,99b +.text + + .globl __strncpy_from_user +__strncpy_from_user: + addi r6,r3,-1 + addi r4,r4,-1 + cmpwi 0,r5,0 + beq 2f + mtctr r5 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + beq 3f +2: addi r6,r6,1 +3: subf r3,r3,r6 + blr +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b +.text + + .globl strlen_user +strlen_user: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + addi r3,r3,1 + blr +99: li r3,0 + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b diff --git a/arch/ppc/mkdiff b/arch/ppc/mkdiff new file mode 100644 index 000000000..304eb9428 --- /dev/null +++ b/arch/ppc/mkdiff @@ -0,0 +1,8 @@ +#!/bin/bash + +N=`basename $PWD` +date=`date +'%y%m%d'` +cd ../ +mkdir -p dist +echo Diff of: $N against $N.ORIG '->' $N-$date-ppc.patch +diff -uNr -X $N/arch/ppc/ignore $N.ORIG $N > dist/$N-$date-ppc.patch diff --git a/arch/ppc/mkdist b/arch/ppc/mkdist new file mode 100644 index 000000000..80408b741 --- /dev/null +++ b/arch/ppc/mkdist @@ -0,0 +1,13 @@ +#!/bin/bash + +V=`egrep ^VERSION Makefile | awk '{print $3}'` +P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'` +S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'` +date=`date +'%y%m%d'` + +echo zImage-$V.$P.$S-$date +echo System.map-$V.$P.$S-$date + +rcp zImage charon:ppc/dist/kernel-images/zImage-$V.$P.$S-$date +rcp System.map charon:ppc/dist/kernel-images/System.map-$V.$P.$S-$date + diff --git a/arch/ppc/mktar b/arch/ppc/mktar new file mode 100644 index 000000000..a50112ff6 --- /dev/null +++ b/arch/ppc/mktar @@ -0,0 +1,9 @@ +#!/bin/bash + +V=`egrep ^VERSION Makefile | awk '{print $3}'` +P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'` +S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'` +date=`date +'%y%m%d'` +cd ../ +mkdir -p dist +tar -vzcf ppclinux-$V.$P.$S-$date.tar.gz -X linux/arch/ppc/ignore linux diff --git a/arch/ppc/mm/Makefile b/arch/ppc/mm/Makefile index 13aeab6ca..2b07c7227 100644 --- a/arch/ppc/mm/Makefile +++ b/arch/ppc/mm/Makefile @@ -7,28 +7,7 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< -.c.s: - $(CC) $(CFLAGS) -S $< +O_TARGET := mm.o +O_OBJS = fault.o init.o extable.o -OBJS = fault.o init.o extable.o - -mm.o: $(OBJS) - $(LD) -r -o mm.o $(OBJS) - -modules: - -dep: - -fastdep: - $(TOPDIR)/scripts/mkdep *.[Sch] > .depend - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/mm/extable.c b/arch/ppc/mm/extable.c new file mode 100644 index 000000000..77e3171bc --- /dev/null +++ b/arch/ppc/mm/extable.c @@ -0,0 +1,65 @@ +/* + * linux/arch/ppc/mm/extable.c + * + * from linux/arch/i386/mm/extable.c + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + const struct exception_table_entry *mid; + for ( mid = first; mid < last; mid++) + { + if ( mid->insn == value ) + return mid->fixup; + } + return 0; +#if 0 + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } +#endif + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#if 1 /*ndef CONFIG_MODULES*/ + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return ret; +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return ret; + } +#endif + + return 0; +} diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index fea722780..67b94d809 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -1,8 +1,12 @@ /* * arch/ppc/mm/fault.c * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * Ported to PPC by Gary Thomas + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * * Modified by Cort Dougan and Paul Mackerras. * * This program is free software; you can redistribute it and/or @@ -26,40 +30,42 @@ #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/mmu.h> #include <asm/mmu_context.h> +#include <asm/system.h> +#include <asm/uaccess.h> -#ifdef CONFIG_PMAC +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *); extern void (*xmon_fault_handler)(void); -#endif - -/* the linux norm for the function name is show_regs() so - make it call dump_regs() on the mac -- Cort */ -#ifdef CONFIG_PMAC -#define show_regs dump_regs +extern int xmon_dabr_match(struct pt_regs *); +int xmon_kernel_faults; #endif extern void die_if_kernel(char *, struct pt_regs *, long); void bad_page_fault(struct pt_regs *, unsigned long); void do_page_fault(struct pt_regs *, unsigned long, unsigned long); -void print_pte(struct _PTE); -void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code) +/* + * The error_code parameter is DSISR for a data fault, SRR1 for + * an instruction fault. + */ +void do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) { - struct task_struct *tsk = current; - extern unsigned _end[]; struct vm_area_struct * vma; struct mm_struct *mm = current->mm; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - - /*printk("do_page_fault() %s/%d addr %x nip %x regs %x error %x\n", - current->comm,current->pid,address,regs->nip,regs,error_code);*/ -#ifdef CONFIG_PMAC + +#ifdef CONFIG_XMON if (xmon_fault_handler && regs->trap == 0x300) { xmon_fault_handler(); return; } + if (error_code & 0x00400000) { + /* DABR match */ + if (xmon_dabr_match(regs)) + return; + } #endif if (in_interrupt()) { static int complained; @@ -68,14 +74,18 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long er printk("page fault in interrupt handler, addr=%lx\n", address); show_regs(regs); +#ifdef CONFIG_XMON + if (xmon_kernel_faults) + xmon(regs); +#endif } } - if (current == NULL) - goto bad_area; - -do_page: + if (current == NULL) { + bad_page_fault(regs, address); + return; + } down(&mm->mmap_sem); - vma = find_vma(tsk->mm, address); + vma = find_vma(mm, address); if (!vma) goto bad_area; if (vma->vm_start <= address) @@ -84,7 +94,7 @@ do_page: goto bad_area; if (expand_stack(vma, address)) goto bad_area; - + good_area: if (error_code & 0xb5700000) /* an error such as lwarx to I/O controller space, @@ -98,67 +108,45 @@ good_area: /* a read */ } else { /* protection fault */ - if ( error_code & 0x08000000 ) + if (error_code & 0x08000000) goto bad_area; if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } handle_mm_fault(current, vma, address, error_code & 0x02000000); - up(&mm->mmap_sem); - /*printk("do_page_fault() return %s/%d addr %x msr %x\n", - current->comm,current->pid,address,regs->msr);*/ - /* not needed since flush_page_to_ram() works */ -#if 0 - flush_page(address); -#endif + up(&mm->mmap_sem); return; - + bad_area: - up(¤t->mm->mmap_sem); + up(&mm->mmap_sem); bad_page_fault(regs, address); } - void bad_page_fault(struct pt_regs *regs, unsigned long address) { - extern unsigned int probingmem; - struct task_struct *tsk = current; unsigned long fixup; - + + if (user_mode(regs)) { + force_sig(SIGSEGV, current); + return; + } /* Are we prepared to handle this fault? */ if ((fixup = search_exception_table(regs->nip)) != 0) { - if ( user_mode(regs) ) - printk("Exception from user mode\n"); -#if 0 - printk(KERN_DEBUG "Exception at %lx (%lx)\n", regs->nip, fixup); -#endif regs->nip = fixup; return; } - if ( user_mode(regs) ) - { - force_sig(SIGSEGV, tsk); - return; - } - -bad_kernel_access: - /* make sure it's not a bootup probe test */ - if ( probingmem ) - { - probingmem = 0; - return; - } /* kernel has accessed a bad area */ show_regs(regs); - print_backtrace( regs->gpr[1] ); -#ifdef CONFIG_PMAC - xmon(regs); + print_backtrace( (unsigned long *)regs->gpr[1] ); +#ifdef CONFIG_XMON + if (xmon_kernel_faults) + xmon(regs); #endif - panic("kernel access of bad area\n pc %x address %X tsk %s/%d", - regs->nip,address,tsk->comm,tsk->pid); + panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", + regs->nip,regs->link,address,current->comm,current->pid); } unsigned long va_to_phys(unsigned long address) @@ -189,6 +177,10 @@ unsigned long va_to_phys(unsigned long address) return (0); } +#if 0 +/* + * Misc debugging functions. Please leave them here. -- Cort + */ void print_pte(struct _PTE p) { printk( @@ -210,7 +202,8 @@ unsigned long htab_phys_to_va(unsigned long address) for ( ptr = Hash ; ptr < Hash_end ; ptr++ ) { if ( ptr->rpn == (address>>12) ) - printk("phys %08X -> va ???\n", + printk("phys %08lX -> va ???\n", address); } } +#endif diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index d46975996..8503401b4 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -31,54 +31,58 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/stddef.h> -#ifdef CONFIG_PMAC #include <asm/prom.h> -#endif #include <asm/io.h> #include <asm/mmu_context.h> #include <asm/pgtable.h> #include <asm/mmu.h> -#ifdef CONFIG_PREP #include <asm/residual.h> +#ifdef CONFIG_BLK_DEV_INITRD +#include <linux/blk.h> /* for initrd_* */ #endif +int prom_trashed; int next_mmu_context; +unsigned long _SDR1; +PTE *Hash, *Hash_end; +unsigned long Hash_size, Hash_mask; +unsigned long *end_of_DRAM; +int mem_init_done; extern pgd_t swapper_pg_dir[]; extern char _start[], _end[]; extern char etext[], _stext[]; -/* References to section boundaries */ extern char __init_begin, __init_end; +extern RESIDUAL res; -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); -extern unsigned long *find_end_of_memory(void); +/* Hardwired MMU segments */ +#if defined(CONFIG_PREP) || defined(CONFIG_PMAC) +#define MMU_SEGMENT_1 0x80000000 +#define MMU_SEGMENT_2 0xc0000000 +#endif /* CONFIG_PREP || CONFIG_PMAC */ +#ifdef CONFIG_CHRP +#define MMU_SEGMENT_1 0xf0000000 /* LongTrail */ +#define MMU_SEGMENT_2 0xc0000000 +#endif /* CONFIG_CHRP */ -#undef MAP_RAM_WITH_SEGREGS 1 -#ifdef CONFIG_PMAC void *find_mem_piece(unsigned, unsigned); static void mapin_ram(void); static void inherit_prom_translations(void); -#endif -#ifdef CONFIG_PREP -inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va); -int inline MMU_hash_page(struct task_struct *,unsigned long,pte *); -#endif - static void hash_init(void); static void *MMU_get_page(void); -void map_page(struct thread_struct *, unsigned long va, +void map_page(struct task_struct *, unsigned long va, unsigned long pa, int flags); +extern void die_if_kernel(char *,struct pt_regs *,long); +extern void show_net_buffers(void); +extern unsigned long *find_end_of_memory(void); -PTE *Hash, *Hash_end; -unsigned long Hash_size, Hash_mask; -unsigned long *end_of_DRAM; -int mem_init_done; +/* + * this tells the prep system to map all of ram with the segregs + * instead of the bats. I'd like to get this to apply to the + * pmac as well then have everything use the bats -- Cort + */ +#undef MAP_RAM_WITH_SEGREGS 1 -#ifdef CONFIG_PREP -#ifdef HASHSTATS -extern unsigned long evicts; -#endif /* HASHSTATS */ /* * these are used to setup the initial page tables * They can waste up to an entire page since the @@ -88,8 +92,6 @@ extern unsigned long evicts; unsigned int probingmem = 0; unsigned int mmu_pages_count = 0; char mmu_pages[(MAX_MMU_PAGES+1)*PAGE_SIZE]; -unsigned long _TotalMemory; -#endif /* CONFIG_PREP */ /* * BAD_PAGE is the page that is used for page faults when linux @@ -120,7 +122,6 @@ pte_t __bad_page(void) return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED)); } -#ifdef CONFIG_PMAC #define MAX_MEM_REGIONS 32 phandle memory_pkg; @@ -128,7 +129,6 @@ struct mem_pieces { int n_regions; struct reg_property regions[MAX_MEM_REGIONS]; }; - struct mem_pieces phys_mem; struct mem_pieces phys_avail; struct mem_pieces prom_mem; @@ -138,7 +138,6 @@ static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); static void print_mem_pieces(struct mem_pieces *); unsigned long avail_start; -int prom_trashed; /* * Read in a property describing some pieces of memory. @@ -272,10 +271,10 @@ find_mem_piece(unsigned size, unsigned align) * Our text, data, bss use something over 1MB, starting at 0. * Open Firmware may be using 1MB at the 4MB point. */ -unsigned long *find_end_of_memory(void) +unsigned long *pmac_find_end_of_memory(void) { unsigned long a, total; - unsigned long h, kstart, ksize; + unsigned long kstart, ksize; extern char _stext[], _end[]; int i; @@ -325,20 +324,6 @@ unsigned long *find_end_of_memory(void) remove_mem_piece(&phys_avail, kstart, ksize, 0); remove_mem_piece(&prom_mem, kstart, ksize, 0); - /* - * Allow 64k of hash table for every 16MB of memory, - * up to a maximum of 2MB. - */ - for (h = 64<<10; h < total / 256 && h < 2<<20; h *= 2) - ; - Hash_size = h; - Hash_mask = (h >> 6) - 1; - - /* Find some memory for the hash table. */ - Hash = find_mem_piece(Hash_size, Hash_size); - printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", - total >> 20, Hash_size >> 10, Hash); - return __va(total); } @@ -353,6 +338,9 @@ unsigned long find_available_memory(void) int i; unsigned long a, free; unsigned long start, end; + + if ( _machine != _MACH_Pmac ) + return 0; free = 0; for (i = 0; i < phys_avail.n_regions - 1; ++i) { @@ -366,7 +354,6 @@ unsigned long find_available_memory(void) avail_start = (unsigned long) __va(a); return avail_start; } -#endif /* CONFIG_PMAC */ void show_mem(void) { @@ -399,14 +386,21 @@ void show_mem(void) "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); for_each_task(p) { - printk("%-8.8s %3d %3d %8d %8d %8d %c%08x %08x", + printk("%-8.8s %3d %3d %8ld %8ld %8ld %c%08lx %08lx ", p->comm,p->pid, p->mm->count,p->mm->context, p->mm->context<<4, p->tss.last_syscall, user_mode(p->tss.regs) ? 'u' : 'k', p->tss.regs->nip, - p); + (ulong)p); if ( p == current ) - printk(" current"); + printk("current"); + if ( p == last_task_used_math ) + { + if ( p == current ) + printk(","); + printk("last math"); + } + printk("\n"); } } @@ -434,76 +428,68 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) void mem_init(unsigned long start_mem, unsigned long end_mem) { unsigned long addr; -#ifdef CONFIG_PMAC int i; - unsigned long lim; -#endif + unsigned long a, lim; int codepages = 0; int datapages = 0; int initpages = 0; end_mem &= PAGE_MASK; high_memory = (void *) end_mem; - num_physpages = max_mapnr = MAP_NR(high_memory); - - /* clear the zero-page */ - memset(empty_zero_page, 0, PAGE_SIZE); - + max_mapnr = MAP_NR(high_memory); + num_physpages = max_mapnr; /* RAM is assumed contiguous */ + /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); - -#ifdef CONFIG_PMAC - remove_mem_piece(&phys_avail, __pa(avail_start), - start_mem - avail_start, 1); - - for (a = KERNELBASE ; a < end_mem; a += PAGE_SIZE) - set_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - - for (i = 0; i < phys_avail.n_regions; ++i) { - a = (unsigned long) __va(phys_avail.regions[i].address); - lim = a + phys_avail.regions[i].size; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) { - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - mem_map[MAP_NR(a)].count = 1; - free_page(a); + + if ( _machine == _MACH_Pmac ) + { + remove_mem_piece(&phys_avail, __pa(avail_start), + start_mem - avail_start, 1); + + for (addr = KERNELBASE ; addr < end_mem; addr += PAGE_SIZE) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + + for (i = 0; i < phys_avail.n_regions; ++i) { + a = (unsigned long) __va(phys_avail.regions[i].address); + lim = a + phys_avail.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } - } - phys_avail.n_regions = 0; - - /* free the prom's memory */ - for (i = 0; i < prom_mem.n_regions; ++i) { - a = (unsigned long) __va(prom_mem.regions[i].address); - lim = a + prom_mem.regions[i].size; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) { - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - mem_map[MAP_NR(a)].count = 1; - free_page(a); + phys_avail.n_regions = 0; + + /* free the prom's memory */ + for (i = 0; i < prom_mem.n_regions; ++i) { + a = (unsigned long) __va(prom_mem.regions[i].address); + lim = a + prom_mem.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } + prom_trashed = 1; } - prom_trashed = 1; -#endif /* CONFIG_PMAC */ - -#ifdef CONFIG_PREP - /* mark mem used by kernel as reserved, mark other unreserved */ - for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) + else /* prep */ { - /* skip hash table gap */ - if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) - continue; - if ( addr < (ulong) /*Hash_end*/ start_mem ) - set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); - else - clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + /* mark mem used by kernel as reserved, mark other unreserved */ + for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) + { + /* skip hash table gap */ + if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) + continue; + if ( addr < (ulong) /*Hash_end*/ start_mem ) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + else + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + } } for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { - if(PageReserved(mem_map + MAP_NR(addr))) { + if (PageReserved(mem_map + MAP_NR(addr))) { if (addr < (ulong) etext) codepages++; - /*else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) - initpages++;*/ + else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + initpages++; else if (addr < (ulong) start_mem) datapages++; continue; @@ -515,9 +501,8 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) #endif /* CONFIG_BLK_DEV_INITRD */ free_page(addr); } - -#endif /* CONFIG_PREP */ - printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), @@ -534,7 +519,6 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) */ void free_initmem(void) { - unsigned long addr; unsigned long a; unsigned long num_freed_pages = 0; @@ -548,16 +532,14 @@ void free_initmem(void) num_freed_pages++; } -#if 0 - addr = (unsigned long)(&__init_begin); - for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - num_freed_pages++; - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - mem_map[MAP_NR(addr)].count = 1; - free_page(addr); + a = (unsigned long)(&__init_begin); + for (; a < (unsigned long)(&__init_end); a += PAGE_SIZE) { + mem_map[MAP_NR(a)].flags &= ~(1 << PG_reserved); + atomic_set(&mem_map[MAP_NR(a)].count, 1); + free_page(a); } -#endif - printk ("Freeing unused kernel memory: %dk freed\n", + + printk ("Freeing unused kernel memory: %ldk freed\n", (num_freed_pages * PAGE_SIZE) >> 10); } @@ -576,29 +558,23 @@ void si_meminfo(struct sysinfo *val) val->totalram++; if (!atomic_read(&mem_map[i].count)) continue; - val->sharedram += atomic_read(&mem_map[i].count)-1; + val->sharedram += atomic_read(&mem_map[i].count) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; return; } -/* Kernel MMU setup & lowest level hardware support */ - -unsigned long _SDR1; /* Hardware SDR1 image */ - -#ifdef CONFIG_PREP - BAT BAT0 = { { - 0x80000000>>17, /* bepi */ + MMU_SEGMENT_1>>17, /* bepi */ BL_256M, /* bl */ 1, /* vs -- supervisor mode valid */ 1, /* vp -- user mode valid */ }, { - 0x80000000>>17, /* brpn */ + MMU_SEGMENT_1>>17, /* brpn */ 1, /* write-through */ 1, /* cache-inhibited */ 0, /* memory coherence */ @@ -609,13 +585,13 @@ BAT BAT0 = BAT BAT1 = { { - 0xC0000000>>17, /* bepi */ + MMU_SEGMENT_2>>17, /* bepi */ BL_256M, /* bl */ 1, /* vs */ 1, /* vp */ }, { - 0xC0000000>>17, /* brpn */ + MMU_SEGMENT_2>>17, /* brpn */ 1, /* w */ 1, /* i (cache disabled) */ 0, /* m */ @@ -635,7 +611,11 @@ BAT BAT2 = 0x00000000>>17, /* brpn */ 0, /* w */ 0, /* i */ +#ifdef __SMP__ 1, /* m */ +#else + 0, /* m */ +#endif 0, /* g */ BPP_RW /* pp */ } @@ -676,13 +656,13 @@ P601_BAT BAT0_601 = P601_BAT BAT1_601 = { { - 0xC0000000>>17, /* bepi */ + MMU_SEGMENT_2>>17, /* bepi */ 1,1,0, /* wim */ 1, 0, /* vs, vp */ BPP_RW, /* pp */ }, { - 0xC0000000>>17, /* brpn */ + MMU_SEGMENT_2>>17, /* brpn */ 1, /* v */ BL_8M, /* bl */ } @@ -724,69 +704,24 @@ P601_BAT BAT3_601 = * this will likely stay seperate from the pmac. * -- Cort */ -unsigned long *find_end_of_memory(void) +unsigned long *prep_find_end_of_memory(void) { - extern RESIDUAL res; - extern unsigned long resptr; - int i, p; - unsigned long h; - - /* copy residual data */ - if ( resptr ) - memcpy( &res, (void *)(resptr+KERNELBASE), sizeof(RESIDUAL) ); - else - /* clearing bss probably clears this but... */ - memset( &res, sizeof(RESIDUAL), 0 ); - _TotalMemory = res.TotalMemory; + int i; - /* this really has nothing to do with the mmu_init() but is - necessary for early setup -- Cort */ - if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) - { - _machine = _MACH_IBM; - } - else - _machine = _MACH_Motorola; - - /* setup the hash table */ - if (_TotalMemory == 0 ) + if (res.TotalMemory == 0 ) { /* * I need a way to probe the amount of memory if the residual * data doesn't contain it. -- Cort */ printk("Ramsize from residual data was 0 -- Probing for value\n"); - _TotalMemory = 0x03000000; - printk("Ramsize default to be %dM\n", _TotalMemory>>20); + res.TotalMemory = 0x03000000; + printk("Ramsize default to be %ldM\n", res.TotalMemory>>20); } - -#if 0 - /* linux has trouble with > 64M ram -- Cort */ - if ( _TotalMemory > 0x04000000 /* 64M */ ) - { - printk("Only using first 64M of ram.\n"); - _TotalMemory = 0x04000000; - } -#endif - - /* setup the bat2 mapping to cover physical ram */ - BAT2.batu.bl = 0x1; /* 256k mapping */ - for ( h = 256*1024 /* 256k */ ; (h <= _TotalMemory) && (h <= 256*1024*1024); - h *= 2 ) - BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; - /* - * Allow 64k of hash table for every 16MB of memory, - * up to a maximum of 2MB. - */ - for (h = 64<<10; h < _TotalMemory / 256 && h < 2<<20; h *= 2) - ; - Hash_size = h; - Hash_mask = (h >> 6) - 1; - - /* align htab on a Hash_size boundry above _end[] */ - Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); - memset(Hash, Hash_size, 0 ); + /* NOTE: everything below here is moving to mapin_ram() */ + + /* * if this is a 601, we can only map sizes of 8M with the BAT's * so we have to map what we can't map with the bats with the segregs @@ -799,11 +734,11 @@ unsigned long *find_end_of_memory(void) if ( _get_PVR() == 1 ) { /* map in rest of ram with seg regs */ - if ( _TotalMemory > 0x01000000 /* 16M */) + if ( res.TotalMemory > 0x01000000 /* 16M */) { for (i = KERNELBASE+0x01000000; - i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) - map_page(&init_task.tss, i, __pa(i), + i < KERNELBASE+res.TotalMemory; i += PAGE_SIZE) + map_page(&init_task, i, __pa(i), _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); } } @@ -815,25 +750,21 @@ unsigned long *find_end_of_memory(void) memset(&BAT2_601, sizeof(BAT2), 0); /* in case we're on a 601 */ memset(&BAT3_601, sizeof(BAT2), 0); /* map all of ram for kernel with segregs */ - for (i = KERNELBASE; i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) + for (i = KERNELBASE; i < KERNELBASE+res.TotalMemory; i += PAGE_SIZE) { if ( i < (unsigned long)etext ) - map_page(&init_task.tss, i, __pa(i), + map_page(&init_task, i, __pa(i), _PAGE_PRESENT/*| _PAGE_RW*/|_PAGE_DIRTY|_PAGE_ACCESSED); else - map_page(&init_task.tss, i, __pa(i), + map_page(&init_task, i, __pa(i), _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); } #endif /* MAP_RAM_WITH_SEGREGS */ - printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", - _TotalMemory >> 20, Hash_size >> 10, Hash); - return ((unsigned long *)_TotalMemory); + return (__va(res.TotalMemory)); } -#endif /* CONFIG_PREP */ -#ifdef CONFIG_PMAC /* * Map in all of physical memory starting at KERNELBASE. */ @@ -847,17 +778,39 @@ static void mapin_ram() int i; unsigned long v, p, s, f; - v = KERNELBASE; - for (i = 0; i < phys_mem.n_regions; ++i) { - p = phys_mem.regions[i].address; - for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { - f = _PAGE_PRESENT | _PAGE_ACCESSED; - if ((char *) v < _stext || (char *) v >= etext) - f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; - map_page(&init_task.tss, v, p, f); - v += PAGE_SIZE; - p += PAGE_SIZE; - } + if ( _machine == _MACH_Pmac ) + { + v = KERNELBASE; + for (i = 0; i < phys_mem.n_regions; ++i) { + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + f = _PAGE_PRESENT | _PAGE_ACCESSED; + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; + else + /* On the powerpc, no user access forces R/W kernel access */ + f |= _PAGE_USER; + map_page(&init_task, v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; + } + } + } + else /* prep */ + { + /* setup the bat2 mapping to cover physical ram */ + BAT2.batu.bl = 0x1; /* 256k mapping */ + for ( f = 256*1024 /* 256k */ ; + (f <= res.TotalMemory) && (f <= 256*1024*1024); + f *= 2 ) + BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; + /* + * let ibm get to the device mem from user mode since + * the X for them needs it right now -- Cort + */ + if ( _machine == _MACH_IBM ) + BAT0.batu.vp = BAT1.batu.vp = 1; + } } @@ -889,7 +842,7 @@ static void inherit_prom_translations() for (tp = prom_translations, i = 0; i < n_translations; ++i, ++tp) { /* ignore stuff mapped down low */ - if (tp->virt < 0x10000000) + if (tp->virt < 0x10000000 && tp->phys < 0x10000000) continue; /* map PPC mmu flags to linux mm flags */ f = (tp->flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU @@ -900,13 +853,12 @@ static void inherit_prom_translations() p = tp->phys; n = tp->size; for (; n != 0; n -= PAGE_SIZE) { - map_page(&init_task.tss, v, p, f); + map_page(&init_task, v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; } } } -#endif /* * Initialize the hash table and patch the instructions in head.S. @@ -914,10 +866,29 @@ static void inherit_prom_translations() static void hash_init(void) { int Hash_bits; + unsigned long h; extern unsigned int hash_page_patch_A[], hash_page_patch_B[], hash_page_patch_C[]; + /* + * Allow 64k of hash table for every 16MB of memory, + * up to a maximum of 2MB. + */ + for (h = 64<<10; h < (ulong)__pa(end_of_DRAM) / 256 && h < 2<<20; h *= 2) + ; + Hash_size = h; + Hash_mask = (h >> 6) - 1; + + /* Find some memory for the hash table. */ + if ( is_prep ) + /* align htab on a Hash_size boundry above _end[] */ + Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); + else /* pmac */ + Hash = find_mem_piece(Hash_size, Hash_size); + + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + __pa(end_of_DRAM) >> 20, Hash_size >> 10, Hash); memset(Hash, 0, Hash_size); Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); @@ -943,8 +914,8 @@ static void hash_init(void) * out from the data cache and invalidated in the instruction * cache, on those machines with split caches. */ - store_cache_range((unsigned long) hash_page_patch_A, - (unsigned long) (hash_page_patch_C + 1)); + flush_icache_range((unsigned long) hash_page_patch_A, + (unsigned long) (hash_page_patch_C + 1)); } @@ -958,71 +929,93 @@ static void hash_init(void) void MMU_init(void) { - end_of_DRAM = find_end_of_memory(); + if ( _machine == _MACH_Pmac ) + end_of_DRAM = pmac_find_end_of_memory(); + else /* prep and chrp */ + end_of_DRAM = prep_find_end_of_memory(); + hash_init(); _SDR1 = __pa(Hash) | (Hash_mask >> 10); -#ifdef CONFIG_PMAC - /* Force initial page tables */ - /* this done by INIT_TSS in processor.h on prep -- Cort */ - init_task.tss.pg_tables = (unsigned long *)swapper_pg_dir; /* Map in all of RAM starting at KERNELBASE */ mapin_ram(); - /* Copy mappings from the prom */ - inherit_prom_translations(); -#endif /* CONFIG_PMAC */ + if ( _machine == _MACH_Pmac ) + /* Copy mappings from the prom */ + inherit_prom_translations(); } static void * MMU_get_page() { - void *p; - - if (mem_init_done) { - p = (void *) __get_free_page(GFP_KERNEL); - if (p == 0) - panic("couldn't get a page in MMU_get_page"); - } else { -#ifdef CONFIG_PREP - mmu_pages_count++; - if ( mmu_pages_count > MAX_MMU_PAGES ) - printk("out of mmu pages!\n"); - p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ - (mmu_pages_count+PAGE_SIZE)); -#endif -#ifdef CONFIG_PMAC - p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); -#endif - } - memset(p, 0, PAGE_SIZE); - return p; + void *p; + + if (mem_init_done) { + p = (void *) __get_free_page(GFP_KERNEL); + if (p == 0) + panic("couldn't get a page in MMU_get_page"); + } else { + if ( is_prep || (_machine == _MACH_chrp) ) + { + mmu_pages_count++; + if ( mmu_pages_count > MAX_MMU_PAGES ) + printk("out of mmu pages!\n"); + p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ + (mmu_pages_count+PAGE_SIZE)); + } + else /* pmac */ + { + p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); + } + } + memset(p, 0, PAGE_SIZE); + return p; } -#ifdef CONFIG_PMAC void * ioremap(unsigned long addr, unsigned long size) { unsigned long p, end = addr + size; + /* + * BAT mappings on prep cover this already so don't waste + * space with it. -- Cort + */ + if ( is_prep ) + if ( ((addr >= 0xc0000000) && (end < (0xc0000000+(256<<20)))) || + ((addr >= 0x80000000) && (end < (0x80000000+(256<<20)))) ) + return (void *)addr; for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) - map_page(&init_task.tss, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); + map_page(&init_task, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); return (void *) addr; } -#endif + +extern void iounmap(unsigned long *addr) +{ + /* + * BAT mappings on prep cover this already so don't waste + * space with it. -- Cort + */ + if ( is_prep ) + if ( (((unsigned long)addr >= 0xc0000000) && ((unsigned long)addr < (0xc0000000+(256<<20)))) || + (((unsigned long)addr >= 0x80000000) && ((unsigned long)addr < (0x80000000+(256<<20)))) ) + return; + /* else unmap it */ +} void -map_page(struct thread_struct *tss, unsigned long va, +map_page(struct task_struct *tsk, unsigned long va, unsigned long pa, int flags) { pmd_t *pd; pte_t *pg; + - if (tss->pg_tables == NULL) { + if (tsk->mm->pgd == NULL) { /* Allocate upper level page map */ - tss->pg_tables = (unsigned long *) MMU_get_page(); + tsk->mm->pgd = (pgd_t *) MMU_get_page(); } /* Use upper 10 bits of VA to index the first level map */ - pd = (pmd_t *) (tss->pg_tables + (va >> PGDIR_SHIFT)); + pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT)); if (pmd_none(*pd)) { /* Need to allocate second-level table */ pg = (pte_t *) MMU_get_page(); @@ -1084,26 +1077,6 @@ flush_tlb_mm(struct mm_struct *mm) } } - -void -flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) -{ - unsigned vsid; - - if ( vmaddr < TASK_SIZE) { - /*vsid = (vma->vm_mm->context << 4) | (vmaddr >> 28);*/ - flush_hash_page(vma->vm_mm->context/*vsid*/, vmaddr); - /* this is needed on prep at the moment -- don't know why - -- Cort*/ - MMU_invalidate_page(vma->vm_mm,vmaddr); - } - else - { - /*printk("flush_tlb_page() vmaddr > TASK_SIZE %08x\n", vmaddr);*/ - } -} - - /* for each page addr in the range, call MMU_invalidate_page() if the range is very large and the hash table is small it might be faster to do a search of the hash table and just invalidate pages that are in the range @@ -1113,15 +1086,10 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - start &= PAGE_MASK; + start &= PAGE_MASK; for (; start < end && start < TASK_SIZE; start += PAGE_SIZE) { - /*flush_hash_page(VSID_FROM_CONTEXT( start>>28, mm->context), - start );*/ flush_hash_page(mm->context, start); - /* this is needed on prep at the moment -- don't know why - -- Cort*/ - MMU_invalidate_page(mm,start); } } @@ -1135,78 +1103,18 @@ void mmu_context_overflow(void) { struct task_struct *tsk; - int nr; - printk(KERN_INFO "mmu_context_overflow\n"); - for (nr = 0; nr < NR_TASKS; ++nr) { - tsk = task[nr]; - if (tsk && tsk->mm) + printk(KERN_DEBUG "mmu_context_overflow\n"); + read_lock(&tasklist_lock); + for_each_task(tsk) { + if (tsk->mm) tsk->mm->context = NO_CONTEXT; } + read_unlock(&tasklist_lock); flush_hash_segments(0x10, 0xffffff); - _tlbia(); next_mmu_context = 0; + /* make sure current always has a context */ + current->mm->context = MUNGE_CONTEXT(++next_mmu_context); + set_context(current->mm->context); } -#ifdef CONFIG_PREP -/* - * it's not a simple matter to get rid of these and switch to the - * ones paul is using. it will take some time and thought -- Cort - */ -inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va) -{ - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *slot; - int flags = 0; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = VSID_FROM_CONTEXT(segment,mm->context); - for (_h = 0; _h < 2; _h++) - { - hash = page_index ^ vsid; - if (_h) - { - hash = ~hash; /* Secondary hash uses ones-complement */ - } - hash &= 0x3FF | (Hash_mask /*<< 10*/); - hash *= 8; /* Eight entries / hash bucket */ - _pte = &Hash[hash]; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) - { /* Found it! */ - _tlbie(va); /* Clear TLB */ - if (_pte->r) flags |= _PAGE_ACCESSED; - if (_pte->c) flags |= _PAGE_DIRTY; - _pte->v = 0; - return /*(flags)*/; - } - } - } - _tlbie(va); - return /*(flags)*/; -} -#endif - -#include <asm/mmu.h> -void print_mm_info(void) -{ - struct _SEGREG s; - long a; - struct _BATU bu; - struct _BATL bl; - unsigned long i; - - for ( i = 0x70000000 ; i <= 0x90000000 ; i+= 0x10000000 ) - { - a = get_SR(i); - memcpy(&s,&a,4); - printk("sr %2d t:%1d ks:%d kp:%d n:%d vsid:%x %x\n", - i>>28, s.t, s.ks, s.kp, s.n, s.vsid, a); - } - - asm("mfspr %0,532; mfspr %1, 533\n" : "=r" (bu), "=r" (bl)); - printk("bat2 bepi: %0x vs: %1x vp: %1x wimg: %x%x%x%x pp: %1x\n", - bu.bepi<<17, bu.vs, bu.vp, bl.w, bl.i, bl.m, bl.g, bl.pp); -} diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig new file mode 100644 index 000000000..5f34efcd2 --- /dev/null +++ b/arch/ppc/pmac_defconfig @@ -0,0 +1,274 @@ +# +# Automatically generated make config: don't edit +# + +# +# Platform support +# +CONFIG_NATIVE=y +CONFIG_PMAC=y +# CONFIG_PREP is not set +CONFIG_MCOMMON=y +# CONFIG_M601 is not set +# CONFIG_M603 is not set +# CONFIG_M604 is not set + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=m +CONFIG_BINFMT_JAVA=m +CONFIG_PMAC_CONSOLE=y +CONFIG_MAC_KEYBOARD=y +CONFIG_MAC_FLOPPY=y +CONFIG_PROC_DEVICETREE=y +CONFIG_XMON=y +CONFIG_ATY_VIDEO=y +CONFIG_IMSTT_VIDEO=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +# 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_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +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=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# 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_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +CONFIG_SCSI_MESH=y +CONFIG_SCSI_MESH_SYNC_RATE=10 +CONFIG_SCSI_MAC53C94=y +CONFIG_SCSI_QLOGIC_PMAC=m + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +CONFIG_NET_ALIAS=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +CONFIG_NET_IPIP=m +# 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_PCTCP is not set +CONFIG_INET_RARP=y +CONFIG_PATH_MTU_DISCOVERY=y +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_MACE=y +CONFIG_DEC_ELCP=m +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m + +# +# CCP compressors for PPP are only built as modules. +# +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +CONFIG_MINIX_FS=m +CONFIG_EXT2_FS=y +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_AUTOFS_FS=y +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +# CONFIG_MOUSE is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +CONFIG_NVRAM=y +# CONFIG_JOYSTICK is not set + +# +# Sound +# +CONFIG_SOUND=m +# CONFIG_PAS is not set +# CONFIG_SB is not set +# CONFIG_ADLIB is not set +# CONFIG_GUS is not set +# CONFIG_MPU401 is not set +# CONFIG_PSS is not set +# CONFIG_GUS16 is not set +# CONFIG_GUSMAX is not set +# CONFIG_MSS is not set +# CONFIG_SSCAPE is not set +# CONFIG_TRIX is not set +# CONFIG_MAD16 is not set +# CONFIG_CS4232 is not set +# CONFIG_MAUI is not set +# CONFIG_YM3812 is not set +CONFIG_LOWLEVEL_SOUND=y +# CONFIG_ACI_MIXER is not set +# CONFIG_AWE32_SYNTH is not set +# CONFIG_AEDSP16 is not set +CONFIG_AWACS=y diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig new file mode 100644 index 000000000..ea39e83ea --- /dev/null +++ b/arch/ppc/prep_defconfig @@ -0,0 +1,250 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_NATIVE=y +# CONFIG_PMAC is not set +CONFIG_PREP=y +CONFIG_MCOMMON=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +CONFIG_VGA_CONSOLE=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# 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_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +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_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# 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_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 +CONFIG_SCSI_NCR53C8XX_SYNC=5 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_XTP is not set +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_PATH_MTU_DISCOVERY=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +CONFIG_EL3=y +# CONFIG_VORTEX is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +CONFIG_PCNET32=y +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +# CONFIG_SERIAL_SHARE_IRQ is not set +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +CONFIG_MOUSE=y +# 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_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds new file mode 100644 index 000000000..1a51a4a7e --- /dev/null +++ b/arch/ppc/vmlinux.lds @@ -0,0 +1,85 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } +/* .init : { *(.init) } =0*/ + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + *(.got1) + } + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + .fixup : { *(.fixup) } + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); +} + diff --git a/arch/sparc/ap1000/aplib.c b/arch/sparc/ap1000/aplib.c index 55a778ed7..8bd67e530 100644 --- a/arch/sparc/ap1000/aplib.c +++ b/arch/sparc/ap1000/aplib.c @@ -455,7 +455,8 @@ static inline int aplib_poll(unsigned counter) while (counter == aplib->rbuf_flag1 + aplib->rbuf_flag2) { tnet_check_completion(); - if (need_resched) break; + if (resched_needed()) + break; if (current->signal & ~current->blocked) break; } return 0; diff --git a/arch/sparc/ap1000/mpp.c b/arch/sparc/ap1000/mpp.c index 84ef7f28e..3465e311b 100644 --- a/arch/sparc/ap1000/mpp.c +++ b/arch/sparc/ap1000/mpp.c @@ -28,7 +28,7 @@ static int last_task = 0; void mpp_schedule(struct cap_request *req) { mpp_current_task = req->data[0]; - need_resched = 1; + resched_force(); mark_bh(TQUEUE_BH); } diff --git a/arch/sparc/ap1000/msc.c b/arch/sparc/ap1000/msc.c index 242df954c..fc8aaf6e5 100644 --- a/arch/sparc/ap1000/msc.c +++ b/arch/sparc/ap1000/msc.c @@ -338,7 +338,7 @@ static inline void qbmful_interrupt(void) #endif MSC_OUT(MSC_INTR, AP_SET_INTR_MASK << MSC_INTR_QBMFUL_SH); intr_mask |= (AP_INTR_REQ << MSC_INTR_QBMFUL_SH); - need_resched = 1; + resched_force(); block_parallel_tasks = 1; mark_bh(TQUEUE_BH); } diff --git a/arch/sparc/ap1000/tnet.c b/arch/sparc/ap1000/tnet.c index 14a29ae7a..293be0d5b 100644 --- a/arch/sparc/ap1000/tnet.c +++ b/arch/sparc/ap1000/tnet.c @@ -613,7 +613,7 @@ void tnet_send_ip(int cid,struct sk_buff *skb) static void reschedule(void) { - need_resched = 1; + resched_force(); mark_bh(TQUEUE_BH); } diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 69ed62422..fa8267db1 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.36 1997/06/17 03:54:47 davem Exp $ +# $Id: config.in,v 1.38 1997/09/04 01:54:33 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -53,9 +53,9 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC - tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA + tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi endmenu @@ -146,6 +146,10 @@ fi source fs/Config.in mainmenu_option next_comment +comment 'Watchdog' + +bool 'Software watchdog' CONFIG_SOFT_WATCHDOG +mainmenu_option next_comment comment 'Kernel hacking' bool 'Kernel profiling support' CONFIG_PROFILE diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 80e114585..434d95d2b 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -49,6 +49,7 @@ SUN_FB_CREATOR=y # CONFIG_SUN_OPENPROMIO=m CONFIG_SUN_MOSTEK_RTC=y +# CONFIG_SAB82532 is not set # CONFIG_SUN_BPP is not set # CONFIG_SUN_VIDEOPIX is not set @@ -105,6 +106,7 @@ CONFIG_IP_MASQUERADE=y CONFIG_IP_ALIAS=m # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set +# CONFIG_XTP is not set # # (it is safe to leave these untouched) @@ -184,7 +186,7 @@ CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m -CONFIG_VFAT_FS=m +# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y @@ -207,6 +209,7 @@ CONFIG_AMIGA_PARTITION=y CONFIG_UFS_FS=y CONFIG_BSD_DISKLABEL=y CONFIG_SMD_DISKLABEL=y +# CONFIG_MAC_PARTITION is not set # # Kernel hacking diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c index 3843a7aa8..b9c6495cf 100644 --- a/arch/sparc/kernel/devices.c +++ b/arch/sparc/kernel/devices.c @@ -53,8 +53,33 @@ device_scan(unsigned long mem_start)) } }; if(cpu_ctr == 0) { + if (sparc_cpu_model == sun4d) { + scan = prom_getchild(prom_root_node); + for (scan = prom_searchsiblings(scan, "cpu-unit"); scan; + scan = prom_searchsiblings(prom_getsibling(scan), "cpu-unit")) { + int node = prom_getchild(scan); + + prom_getstring(node, "device_type", node_str, sizeof(node_str)); + if (strcmp(node_str, "cpu") == 0) { + prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid)); + if (cpu_ctr < NCPUS) { + cpu_nds[cpu_ctr] = node; + linux_cpus[cpu_ctr].prom_node = node; + linux_cpus[cpu_ctr].mid = thismid; + } + prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", + cpu_ctr, (unsigned long) node, + thismid); + cpu_ctr++; + } + } + } + if (cpu_ctr > NCPUS) + cpu_ctr = NCPUS; + } + if(cpu_ctr == 0) { printk("No CPU nodes found, cannot continue.\n"); - /* Probably a sun4d or sun4e, Sun is trying to trick us ;-) */ + /* Probably a sun4e, Sun is trying to trick us ;-) */ halt(); } printk("Found %d CPU prom device tree node(s).\n", cpu_ctr); diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 90965e26e..5742453ff 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.82 1997/05/01 01:40:38 davem Exp $ +/* $Id: head.S,v 1.83 1997/08/28 11:10:39 jj Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -827,22 +827,18 @@ got_prop: cmp %l1, 'c' ! We already know we are not be 1f ! on a plain sun4 because of - nop ! the check for 0x4000 in %o0 - - cmp %l1, 'm' ! at start + ! the check for 0x4000 in %o0 + cmp %l1, 'm' ! at start be 1f - nop - - cmp %l1, 'e' + cmp %l1, 'd' + be 1f + cmp %l1, 'e' be no_sun4e_here ! Could be a sun4e. nop - - b no_sun4u_here ! AIEEE, a V9 sun4u... + b no_sun4u_here ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :)) nop - -1: - set C_LABEL(cputypval), %l1 +1: set C_LABEL(cputypval), %l1 ldub [%l1 + 0x4], %l1 cmp %l1, 'm' ! Test for sun4d, sun4e ? be sun4m_init diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 95eed6287..ff4a3f192 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.99 1997/07/17 02:20:13 davem Exp $ +/* $Id: process.c,v 1.100 1997/08/10 04:49:23 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -105,6 +105,8 @@ out: /* This is being executed in task 0 'user space'. */ int cpu_idle(void *unused) { + extern volatile int smp_commenced; + current->priority = -100; while(1) { /* @@ -118,7 +120,8 @@ int cpu_idle(void *unused) } /* endless idle loop with no priority at all */ current->counter = -100; - schedule(); + if(!smp_commenced || resched_needed()) + schedule(); } } diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 732b3006d..933a5acfa 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -886,7 +886,7 @@ asmlinkage void syscall_trace(void) current->exit_code = SIGTRAP; current->state = TASK_STOPPED; current->tss.flags ^= MAGIC_CONSTANT; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S index d8d160114..8d46d487e 100644 --- a/arch/sparc/kernel/rtrap.S +++ b/arch/sparc/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.46 1997/04/01 02:21:48 davem Exp $ +/* $Id: rtrap.S,v 1.47 1997/08/10 04:49:24 davem Exp $ * rtrap.S: Return from Sparc trap low-level code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -65,12 +65,14 @@ C_LABEL(ret_trap_lockless_ipi): wr %t_psr, 0x0, %psr b ret_trap_kernel - nop + mov 1, %o0 1: + ld [%curptr + AOFF_task_processor], %o1 ld [%twin_tmp1 + %lo(C_LABEL(need_resched))], %g2 + sll %o0, %o1, %o0 - cmp %g2, 0 + andcc %g2, %o0, %g0 be signal_p nop diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index fd5fa634e..fbb5578fa 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.74 1997/05/15 19:57:09 davem Exp $ +/* $Id: signal.c,v 1.75 1997/08/05 19:19:26 davem Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -722,7 +722,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, /* This happens to be SMP safe so no need to * grab master kernel lock even in this case. */ - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -773,7 +773,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, /* notify_parent() is SMP safe */ if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c index beef2df14..17931fd55 100644 --- a/arch/sparc/kernel/smp.c +++ b/arch/sparc/kernel/smp.c @@ -87,7 +87,7 @@ volatile int smp_process_available=0; #define SMP_PRINTK(x) #endif -static volatile int smp_commenced = 0; +volatile int smp_commenced = 0; static char smp_buf[512]; @@ -558,7 +558,7 @@ void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr) /* Reschedule call back. */ void smp_reschedule_irq(void) { - need_resched=1; + resched_force(); } /* Running cross calls. */ @@ -623,7 +623,7 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) if(--current->counter < 0) { current->counter = 0; - need_resched = 1; + resched_force(); } spin_lock(&ticker_lock); diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index ad9aa8953..8c3358a64 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.149 1997/07/20 05:59:34 davem Exp $ +/* $Id: srmmu.c,v 1.151 1997/08/28 11:10:54 jj Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -2103,14 +2103,18 @@ unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem) sparc_iobase_vaddr = 0xfd000000; /* 16MB of IOSPACE on all sun4m's. */ physmem_mapped_contig = 0; /* for init.c:taint_real_pages() */ - /* Find the number of contexts on the srmmu. */ - cpunode = prom_getchild(prom_root_node); - num_contexts = 0; - while((cpunode = prom_getsibling(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; + if (sparc_cpu_model == sun4d) + num_contexts = 65536; /* We now it is Viking */ + else { + /* Find the number of contexts on the srmmu. */ + cpunode = prom_getchild(prom_root_node); + num_contexts = 0; + while((cpunode = prom_getsibling(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; + } } } @@ -2601,7 +2605,7 @@ __initfunc(static void init_swift(void)) } /* turbosparc.S */ -extern void turbosparc_flush_cache_all(); +extern void turbosparc_flush_cache_all(void); extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr); static void poke_turbosparc(void) diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index b8cf06878..20b90a651 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.20 1997/07/11 11:05:29 jj Exp $ +# $Id: Makefile,v 1.22 1997/08/29 15:51:53 jj Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -17,7 +17,7 @@ LD = sparc64-linux-ld NM = sparc64-linux-nm AR = sparc64-linux-ar RANLIB = sparc64-linux-ranlib -ELF2AOUT64 = elf2aout64 +ELFTOAOUT = elftoaout # # Uncomment the first CFLAGS if you are doing kgdb source level @@ -34,13 +34,21 @@ HEAD := arch/sparc64/kernel/head.o arch/sparc64/kernel/init_task.o SUBDIRS := $(SUBDIRS) arch/sparc64/kernel arch/sparc64/lib arch/sparc64/mm \ arch/sparc64/prom +ifneq ($(CONFIG_SOLARIS_EMUL),n) + SUBDIRS += arch/sparc64/solaris +endif + CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES) +ifeq ($(CONFIG_SOLARIS_EMUL),y) + CORE_FILES += arch/sparc64/solaris/solaris.o +endif + LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \ $(TOPDIR)/arch/sparc64/lib/lib.a vmlinux.aout: vmlinux - $(ELF2AOUT64) -o $(TOPDIR)/vmlinux.aout $(TOPDIR)/vmlinux + $(ELFTOAOUT) -o $(TOPDIR)/vmlinux.aout $(TOPDIR)/vmlinux archclean: rm -f $(TOPDIR)/vmlinux.aout diff --git a/arch/sparc64/boot/Makefile b/arch/sparc64/boot/Makefile index ed3fc6cdb..4f2b9ead3 100644 --- a/arch/sparc64/boot/Makefile +++ b/arch/sparc64/boot/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1 1997/07/18 06:26:30 ralf Exp $ +# $Id: Makefile,v 1.3 1997/08/29 11:08:34 davem Exp $ # Makefile for the Sparc64 boot stuff. # # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index fcbac5c1a..c3c438dc6 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.9 1997/07/04 11:33:05 davem Exp $ +# $Id: config.in,v 1.17 1997/09/04 01:54:43 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -43,6 +43,7 @@ else define_bool CONFIG_SUN_CONSOLE y define_bool CONFIG_SUN_AUXIO y define_bool CONFIG_SUN_IO y + define_bool CONFIG_PCI y source drivers/sbus/char/Config.in fi @@ -57,10 +58,11 @@ if [ "$CONFIG_SPARC32_COMPAT" != "n" ]; then bool 'Kernel support for 32-bit (ie. SunOS) a.out binaries' CONFIG_BINFMT_AOUT32 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL fi endmenu @@ -83,6 +85,19 @@ fi tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE + if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then + dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE + dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE + dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE + dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE + dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE + define_bool CONFIG_IDE_CHIPSETS y + define_bool CONFIG_BLK_DEV_NS87415 y + fi +fi + endmenu if [ "$CONFIG_NET" = "y" ]; then @@ -116,6 +131,21 @@ if [ "$CONFIG_SCSI" != "n" ]; then bool 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI tristate 'PTI Qlogic,ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI + + if [ "$CONFIG_PCI" != "n" ]; then + dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI + if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then + bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y + dep_tristate ' Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N + if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then + int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + fi + bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N + bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N + int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15 + fi + fi + endmenu fi endmenu @@ -141,6 +171,9 @@ if [ "$CONFIG_NET" = "y" ]; then tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL 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 + fi # bool 'FDDI driver support' CONFIG_FDDI # if [ "$CONFIG_FDDI" = "y" ]; then # fi diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index c9c92b987..85958cd6e 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -29,6 +29,7 @@ CONFIG_SUN_KEYBOARD=y CONFIG_SUN_CONSOLE=y CONFIG_SUN_AUXIO=y CONFIG_SUN_IO=y +CONFIG_PCI=y # # SBUS Frame Buffer support @@ -49,6 +50,7 @@ SUN_FB_CREATOR=y # CONFIG_SUN_OPENPROMIO=m CONFIG_SUN_MOSTEK_RTC=y +# CONFIG_SAB82532 is not set # CONFIG_SUN_BPP is not set # CONFIG_SUN_VIDEOPIX is not set CONFIG_SUN_OPENPROMFS=m @@ -61,17 +63,26 @@ CONFIG_BINFMT_ELF32=y CONFIG_BINFMT_AOUT32=y CONFIG_BINFMT_JAVA=m CONFIG_BINFMT_MISC=m +CONFIG_SOLARIS_EMUL=m # # Floppy, IDE, and other block devices # -# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=m CONFIG_MD_STRIPED=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_IDE=y +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +CONFIG_BLK_DEV_IDETAPE=m +CONFIG_BLK_DEV_IDEFLOPPY=m +# CONFIG_BLK_DEV_IDESCSI is not set +CONFIG_IDE_CHIPSETS=y +CONFIG_BLK_DEV_NS87415=y # # Networking options @@ -85,12 +96,13 @@ CONFIG_INET=y # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_SYN_COOKIES is not set +# CONFIG_XTP is not set # # (it is safe to leave these untouched) # # CONFIG_INET_PCTCP is not set -# CONFIG_INET_RARP is not set +CONFIG_INET_RARP=m CONFIG_PATH_MTU_DISCOVERY=y CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y @@ -136,6 +148,12 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_SUNESP=y CONFIG_SCSI_QLOGICPTI=m +CONFIG_SCSI_AIC7XXX=y +# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set +# CONFIG_OVERRIDE_CMDS is not set +# CONFIG_AIC7XXX_PAGE_ENABLE is not set +# CONFIG_AIC7XXX_PROC_STATS is not set +CONFIG_AIC7XXX_RESET_DELAY=5 # # Network device support @@ -155,6 +173,7 @@ CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=y CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m +CONFIG_DE4X5=y # # Filesystems @@ -187,6 +206,7 @@ CONFIG_AMIGA_PARTITION=y CONFIG_UFS_FS=m CONFIG_BSD_DISKLABEL=y CONFIG_SMD_DISKLABEL=y +# CONFIG_MAC_PARTITION is not set # # Kernel hacking diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index aecb9fd47..2b07a0e15 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.30 1997/07/24 14:48:04 davem Exp $ +# $Id: Makefile,v 1.34 1997/08/12 04:12:36 ecd Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -29,9 +29,10 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o O_OBJS := process.o setup.o cpu.o idprom.o \ - systbls.o traps.o devices.o auxio.o ioport.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 + unaligned.o sys_sunos32.o sunos_ioctl32.o \ + central.o psycho.o ebus.o OX_OBJS := sparc64_ksyms.o ifdef SMP diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index 00e5f2722..91365af85 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -3,15 +3,21 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ +#include <linux/config.h> #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/smp.h> #include <linux/init.h> +#include <linux/delay.h> +#include <linux/ioport.h> + #include <asm/oplib.h> #include <asm/io.h> #include <asm/auxio.h> #include <asm/sbus.h> +#include <asm/ebus.h> +#include <asm/fhc.h> /* Probe and map in the Auxiliary I/O register */ unsigned char *auxio_register; @@ -31,9 +37,39 @@ __initfunc(void auxio_probe(void)) } if (!sdev) { +#ifdef CONFIG_PCI + struct linux_ebus *ebus; + struct linux_ebus_device *edev = 0; + unsigned long led_auxio; + + for_all_ebusdev(edev, ebus) + if (!strcmp(edev->prom_name, "auxio")) + break; + + if (edev) { + if (check_region(edev->base_address[0], + sizeof(unsigned int))) { + prom_printf("%s: Can't get region %lx, %d\n", + __FUNCTION__, edev->base_address[0], + sizeof(unsigned int)); + prom_halt(); + } + request_region(edev->base_address[0], + sizeof(unsigned int), "LED auxio"); + + led_auxio = edev->base_address[0]; + outl(0x01, led_auxio); + return; + } +#endif + if(central_bus) { + auxio_register = NULL; + return; + } prom_printf("Cannot find auxio node, cannot continue...\n"); prom_halt(); } + prom_getproperty(sdev->prom_node, "reg", (char *) auxregs, sizeof(auxregs)); prom_apply_sbus_ranges(sdev->my_bus, auxregs, 0x1, sdev); /* Map the register both read and write */ diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index 9ab2b7aca..ef41e3f7a 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c @@ -8,6 +8,9 @@ #define elf_check_arch(x) (((x) == EM_SPARC) || ((x) == EM_SPARC32PLUS)) +#define ELF_ET_DYN_BASE 0x60000000 + + #include <asm/processor.h> #include <linux/module.h> #include <linux/config.h> diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c new file mode 100644 index 000000000..817a8ecd3 --- /dev/null +++ b/arch/sparc64/kernel/central.c @@ -0,0 +1,129 @@ +/* $Id: central.c,v 1.4 1997/08/19 14:17:49 jj Exp $ + * central.c: Central FHC driver for Sunfire/Starfire/Wildfire. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> + +#include <asm/page.h> +#include <asm/fhc.h> + +struct linux_central *central_bus = NULL; + +static inline unsigned long long_align(unsigned long addr) +{ + return ((addr + (sizeof(unsigned long) - 1)) & + ~(sizeof(unsigned long) - 1)); +} + +extern void prom_central_ranges_init(int cnode, struct linux_central *central); +extern void prom_fhc_ranges_init(int fnode, struct linux_fhc *fhc); + +unsigned long central_probe(unsigned long memory_start) +{ + struct linux_prom_registers fpregs[6]; + struct linux_fhc *fhc; + char namebuf[128]; + int cnode, fnode, err; + + printk("CENTRAL: "); + cnode = prom_finddevice("/central"); + if(cnode == 0 || cnode == -1) { + printk("no central found.\n"); + return memory_start; + } + prom_printf("CENTRAL: found central PROM node.\n"); + printk("found central PROM node.\n"); + + /* Ok we got one, grab some memory for software state. */ + memory_start = long_align(memory_start); + central_bus = (struct linux_central *) (memory_start); + + prom_printf("CENTRAL: central_bus[%p] ", central_bus); + memory_start += sizeof(struct linux_central); + memory_start = long_align(memory_start); + fhc = (struct linux_fhc *)(memory_start); + memory_start += sizeof(struct linux_fhc); + memory_start = long_align(memory_start); + + prom_printf("fhc[%p] ", fhc); + + /* First init central. */ + central_bus->child = fhc; + central_bus->prom_node = cnode; + + prom_getstring(cnode, "name", namebuf, sizeof(namebuf)); + strcpy(central_bus->prom_name, namebuf); + + prom_printf("init_central_ranges "); + prom_central_ranges_init(cnode, central_bus); + + /* And then central's FHC. */ + fhc->next = NULL; + fhc->parent = central_bus; + fnode = prom_searchsiblings(prom_getchild(cnode), "fhc"); + if(fnode == 0 || fnode == -1) { + prom_printf("Critical error, central board lacks fhc.\n"); + prom_halt(); + } + fhc->prom_node = fnode; + prom_getstring(fnode, "name", namebuf, sizeof(namebuf)); + strcpy(fhc->prom_name, namebuf); + + prom_printf("cnode[%x] fnode[%x] init_fhc_ranges\n", cnode, fnode); + prom_fhc_ranges_init(fnode, fhc); + + /* Finally, map in FHC register set. (From the prtconf dumps + * I have seen on Ex000 boxes only the central ranges need to + * be applied to the fhc internal register set) -DaveM + */ + err = prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)); + if(err == -1) { + prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n"); + prom_halt(); + } + prom_apply_central_ranges(central_bus, &fpregs[0], 6); + prom_printf("CENTRAL: FHC_REGS[(%08x,%08x) (%08x,%08x) " + "(%08x,%08x) (%08x,%08x) (%08x,%08x) (%08x,%08x)]\n", + fpregs[0].which_io, fpregs[0].phys_addr, + fpregs[1].which_io, fpregs[1].phys_addr, + fpregs[2].which_io, fpregs[2].phys_addr, + fpregs[3].which_io, fpregs[3].phys_addr, + fpregs[4].which_io, fpregs[4].phys_addr, + fpregs[5].which_io, fpregs[5].phys_addr); + fhc->fhc_regs.pregs = (struct fhc_internal_regs *) + __va((((unsigned long)fpregs[0].which_io)<<32) | + (((unsigned long)fpregs[0].phys_addr))); + fhc->fhc_regs.ireg = (struct fhc_ign_reg *) + __va((((unsigned long)fpregs[1].which_io)<<32) | + (((unsigned long)fpregs[1].phys_addr))); + fhc->fhc_regs.ffregs = (struct fhc_fanfail_regs *) + __va((((unsigned long)fpregs[2].which_io)<<32) | + (((unsigned long)fpregs[2].phys_addr))); + fhc->fhc_regs.sregs = (struct fhc_system_regs *) + __va((((unsigned long)fpregs[3].which_io)<<32) | + (((unsigned long)fpregs[3].phys_addr))); + fhc->fhc_regs.uregs = (struct fhc_uart_regs *) + __va((((unsigned long)fpregs[4].which_io)<<32) | + (((unsigned long)fpregs[4].phys_addr))); + fhc->fhc_regs.tregs = (struct fhc_tod_regs *) + __va((((unsigned long)fpregs[5].which_io)<<32) | + (((unsigned long)fpregs[5].phys_addr))); + prom_printf("CENTRAL: FHC_REGS[%p %p %p %p %p %p]\n", + fhc->fhc_regs.pregs, fhc->fhc_regs.ireg, + fhc->fhc_regs.ffregs, fhc->fhc_regs.sregs, + fhc->fhc_regs.uregs, fhc->fhc_regs.tregs); + + prom_printf("CENTRAL: reading FHC_ID register... "); + err = fhc->fhc_regs.pregs->fhc_id; + prom_printf("VALUE[%x]\n", err); + printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n", + ((err & FHC_ID_VERS) >> 28), + ((err & FHC_ID_PARTID) >> 12), + ((err & FHC_ID_MANUF) >> 1)); + + return memory_start; +} diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 2c96a83e9..d009f39d8 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -46,8 +46,13 @@ struct cpu_iu_info linux_sparc_chips[] = { #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) -char *sparc_cpu_type[NCPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" }; -char *sparc_fpu_type[NCPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" }; +#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" }; +#else +char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", }; +char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", }; +#endif unsigned int fsr_storage; diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 9327058a8..24ca3ff10 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -17,13 +17,14 @@ struct prom_cpuinfo linux_cpus[NR_CPUS]; int linux_num_cpus = 0; extern void cpu_probe(void); +extern unsigned long central_probe(unsigned long); __initfunc(unsigned long device_scan(unsigned long mem_start)) { char node_str[128]; int nd, prom_node_cpu, thismid; - int cpu_nds[NCPUS]; /* One node for each cpu */ + int cpu_nds[NR_CPUS]; /* One node for each cpu */ int cpu_ctr = 0; prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str)); @@ -43,11 +44,14 @@ device_scan(unsigned long mem_start)) if(strcmp(node_str, "cpu") == 0) { cpu_nds[cpu_ctr] = scan; linux_cpus[cpu_ctr].prom_node = scan; - prom_getproperty(scan, "mid", (char *) &thismid, sizeof(thismid)); + prom_getproperty(scan, "upa-portid", + (char *) &thismid, sizeof(thismid)); linux_cpus[cpu_ctr].mid = thismid; 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); cpu_ctr++; } }; @@ -62,5 +66,5 @@ device_scan(unsigned long mem_start)) linux_num_cpus = cpu_ctr; cpu_probe(); - return mem_start; + return central_probe(mem_start); } diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc64/kernel/dtlb_miss.S index b034ef407..04efb1cec 100644 --- a/arch/sparc64/kernel/dtlb_miss.S +++ b/arch/sparc64/kernel/dtlb_miss.S @@ -1,4 +1,4 @@ -/* $Id: dtlb_miss.S,v 1.12 1997/06/26 12:47:08 jj Exp $ +/* $Id: dtlb_miss.S,v 1.13 1997/08/14 19:27:15 davem Exp $ * dtlb_miss.S: Data TLB miss code, this is included directly * into the trap table. * @@ -62,7 +62,7 @@ /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET /*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET - /*0x4c*/ andcc %g3, 0x80, %g0 ! Slick trick... + /*0x4c*/ andcc %g3, 0x400, %g0 ! Slick trick... /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page? diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S index 2cf372ca9..55e86c887 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.12 1997/05/18 10:04:43 davem Exp $ +/* $Id: dtlb_prot.S,v 1.14 1997/08/03 09:07:00 davem Exp $ * dtlb_prot.S: Data TLB protection code, this is included directly * into the trap table. * @@ -36,17 +36,17 @@ /* ICACHE line 3 */ /*0x40*/ add %g2, 7, %g5 ! Compute mask /*0x44*/ andn %g4, %g5, %g4 ! Mask page - /*0x48*/ or %g4, 0x10, %g4 ! 2ndary Context - /*0x4c*/ stxa %g0, [%g4] ASI_DMMU_DEMAP ! TLB flush page - /*0x50*/ membar #Sync ! Synchronize - /*0x54*/ stxa %g3, [%g1] ASI_PHYS_USE_EC ! Update sw PTE - /*0x58*/ stxa %g3, [%g0] ASI_DTLB_DATA_IN ! TLB load - /*0x5c*/ retry ! Trap return + /*0x48*/ mov TLB_SFSR, %g5 ! read SFSR + /*0x4c*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for + /*0x50*/ and %g5, 0x10, %g5 ! context bit + /*0x54*/ or %g4, %g5, %g4 ! for prot trap +1:/*0x58*/ stxa %g0, [%g4] ASI_DMMU_DEMAP ! TLB flush page + /*0x5c*/ membar #Sync ! Synchronize /* ICACHE line 4 */ - /*0x60*/ nop - /*0x64*/ nop - /*0x68*/ nop + /*0x60*/ stxa %g3, [%g1] ASI_PHYS_USE_EC ! Update sw PTE + /*0x64*/ stxa %g3, [%g0] ASI_DTLB_DATA_IN ! TLB load + /*0x68*/ retry ! Trap return /*0x6c*/ nop /*0x70*/ nop /*0x74*/ nop diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c new file mode 100644 index 000000000..f484cfef8 --- /dev/null +++ b/arch/sparc64/kernel/ebus.c @@ -0,0 +1,326 @@ +/* $Id: ebus.c,v 1.8 1997/09/05 22:59:39 ecd Exp $ + * ebus.c: PCI to EBus bridge device. + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/system.h> +#include <asm/page.h> +#include <asm/pbm.h> +#include <asm/ebus.h> +#include <asm/oplib.h> +#include <asm/bpp.h> + +#undef DEBUG_FILL_EBUS_DEV + +struct linux_ebus *ebus_chain = 0; + +extern void prom_ebus_ranges_init(struct linux_ebus *); +extern unsigned long pci_console_init(unsigned long memory_start); + +#ifdef CONFIG_SUN_OPENPROMIO +extern int openprom_init(void); +#endif +#ifdef CONFIG_SUN_MOSTEK_RTC +extern int rtc_init(void); +#endif +#ifdef CONFIG_SPARCAUDIO +extern int sparcaudio_init(void); +#endif +#ifdef CONFIG_SUN_AUXIO +extern void auxio_probe(void); +#endif + +extern unsigned int psycho_irq_build(unsigned int full_ino); + +static inline unsigned long +ebus_alloc(unsigned long *memory_start, size_t size) +{ + unsigned long mem; + + *memory_start = (*memory_start + 7) & ~(7); + mem = *memory_start; + *memory_start += size; + return mem; +} + +__initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev)) +{ + int regs[PROMREG_MAX]; + int irqs[PROMREG_MAX]; + char lbuf[128]; + int i, len; + + dev->prom_node = node; + prom_getstring(node, "name", lbuf, sizeof(lbuf)); + strcpy(dev->prom_name, lbuf); + + len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs)); + dev->num_addrs = len / sizeof(regs[0]); + + for (i = 0; i < dev->num_addrs; i++) { + if (regs[i] >= dev->parent->num_addrs) { + prom_printf("UGH: property for %s was %d, need < %d\n", + dev->prom_name, len, dev->parent->num_addrs); + panic(__FUNCTION__); + } + dev->base_address[i] = dev->parent->base_address[regs[i]]; + } + + len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs)); + if ((len == -1) || (len == 0)) { + dev->num_irqs = 0; + } else { + dev->num_irqs = len / sizeof(irqs[0]); + for (i = 0; i < dev->num_irqs; i++) + dev->irqs[i] = psycho_irq_build(irqs[i]); + } + +#ifdef DEBUG_FILL_EBUS_DEV + printk("child '%s': address%s\n", dev->prom_name, + dev->num_addrs > 1 ? "es" : ""); + for (i = 0; i < dev->num_addrs; i++) + printk(" %016lx\n", dev->base_address[i]); + if (dev->num_irqs) { + printk(" IRQ%s", dev->num_irqs > 1 ? "s" : ""); + for (i = 0; i < dev->num_irqs; i++) + printk(" %08x", dev->irqs[i]); + printk("\n"); + } +#endif +} + +__initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *dev, + unsigned long memory_start)) +{ + struct linux_prom_registers regs[PROMREG_MAX]; + struct linux_ebus_child *child; + int irqs[PROMINTR_MAX]; + char lbuf[128]; + int i, n, len; + + dev->prom_node = node; + prom_getstring(node, "name", lbuf, sizeof(lbuf)); + strcpy(dev->prom_name, lbuf); + + len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs)); + if (len % sizeof(struct linux_prom_registers)) { + prom_printf("UGH: proplen for %s was %d, need multiple of %d\n", + dev->prom_name, len, + (int)sizeof(struct linux_prom_registers)); + panic(__FUNCTION__); + } + dev->num_addrs = len / sizeof(struct linux_prom_registers); + + for (i = 0; i < dev->num_addrs; i++) { + n = (regs[i].which_io - 0x10) >> 2; + + dev->base_address[i] = dev->parent->self->base_address[n]; + dev->base_address[i] += (unsigned long)regs[i].phys_addr; + } + + len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs)); + if ((len == -1) || (len == 0)) { + dev->num_irqs = 0; + } else { + dev->num_irqs = len / sizeof(irqs[0]); + for (i = 0; i < dev->num_irqs; i++) + dev->irqs[i] = psycho_irq_build(irqs[i]); + } + +#ifdef DEBUG_FILL_EBUS_DEV + printk("'%s': address%s\n", dev->prom_name, + dev->num_addrs > 1 ? "es" : ""); + for (i = 0; i < dev->num_addrs; i++) + printk(" %016lx\n", dev->base_address[i]); + if (dev->num_irqs) { + printk(" IRQ%s", dev->num_irqs > 1 ? "s" : ""); + for (i = 0; i < dev->num_irqs; i++) + printk(" %08x", dev->irqs[i]); + printk("\n"); + } +#endif + if ((node = prom_getchild(node))) { + dev->children = (struct linux_ebus_child *) + ebus_alloc(&memory_start, sizeof(struct linux_ebus_child)); + + child = dev->children; + child->next = 0; + child->parent = dev; + fill_ebus_child(node, child); + + while ((node = prom_getsibling(node))) { + child->next = (struct linux_ebus_child *) + ebus_alloc(&memory_start, sizeof(struct linux_ebus_child)); + + child = child->next; + child->next = 0; + child->parent = dev; + fill_ebus_child(node, child); + } + } + + return memory_start; +} + +__initfunc(unsigned long ebus_init(unsigned long memory_start, + unsigned long memory_end)) +{ + struct linux_prom_pci_registers regs[PROMREG_MAX]; + struct linux_pbm_info *pbm; + struct linux_ebus_device *dev; + struct linux_ebus *ebus; + struct pci_dev *pdev; + struct pcidev_cookie *cookie; + char lbuf[128]; + unsigned long addr, *base; + unsigned short pci_command; + int nd, len, ebusnd; + int reg, rng, nreg; + int num_ebus = 0; + + if (!pcibios_present()) + return memory_start; + + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->vendor == PCI_VENDOR_ID_SUN) && + (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) + break; + } + if (!pdev) { + printk("ebus: No EBus's found.\n"); + return memory_start; + } + + cookie = pdev->sysdata; + ebusnd = cookie->prom_node; + + ebus_chain = ebus = (struct linux_ebus *) + ebus_alloc(&memory_start, sizeof(struct linux_ebus)); + ebus->next = 0; + + while (ebusnd) { + printk("ebus%d:\n", num_ebus); + + prom_getstring(ebusnd, "name", lbuf, sizeof(lbuf)); + ebus->prom_node = ebusnd; + strcpy(ebus->prom_name, lbuf); + + ebus->self = pdev; + ebus->parent = pbm = cookie->pbm; + + /* Enable BUS Master. */ + pcibios_read_config_word(pdev->bus->number, pdev->devfn, + PCI_COMMAND, &pci_command); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pdev->bus->number, pdev->devfn, + PCI_COMMAND, pci_command); + + len = prom_getproperty(ebusnd, "reg", (void *)regs, + sizeof(regs)); + if (len == 0 || len == -1) { + prom_printf("%s: can't find reg property\n", + __FUNCTION__); + prom_halt(); + } + nreg = len / sizeof(struct linux_prom_pci_registers); + + base = &ebus->self->base_address[0]; + for (reg = 0; reg < nreg; reg++) { + if (!(regs[reg].phys_hi & 0x03000000)) + continue; + + for (rng = 0; rng < pbm->num_pbm_ranges; rng++) { + struct linux_prom_pci_ranges *rp = + &pbm->pbm_ranges[rng]; + + if ((rp->child_phys_hi ^ regs[reg].phys_hi) + & 0x03000000) + continue; + + addr = (u64)regs[reg].phys_lo; + addr += (u64)regs[reg].phys_mid << 32UL; + addr += (u64)rp->parent_phys_lo; + addr += (u64)rp->parent_phys_hi << 32UL; + *base++ = (unsigned long)__va(addr); + + break; + } + } + + prom_ebus_ranges_init(ebus); + + nd = prom_getchild(ebusnd); + ebus->devices = (struct linux_ebus_device *) + ebus_alloc(&memory_start, sizeof(struct linux_ebus_device)); + + dev = ebus->devices; + dev->next = 0; + dev->children = 0; + dev->parent = ebus; + memory_start = fill_ebus_device(nd, dev, memory_start); + + while ((nd = prom_getsibling(nd))) { + dev->next = (struct linux_ebus_device *) + ebus_alloc(&memory_start, sizeof(struct linux_ebus_device)); + + dev = dev->next; + dev->next = 0; + dev->children = 0; + dev->parent = ebus; + memory_start = fill_ebus_device(nd, dev, memory_start); + } + + for (pdev = pdev->next; pdev; pdev = pdev->next) { + if ((pdev->vendor == PCI_VENDOR_ID_SUN) && + (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) + break; + } + if (!pdev) + break; + + cookie = pdev->sysdata; + ebusnd = cookie->prom_node; + + ebus->next = (struct linux_ebus *) + ebus_alloc(&memory_start, sizeof(struct linux_ebus)); + ebus = ebus->next; + ebus->next = 0; + ++num_ebus; + } + + memory_start = pci_console_init(memory_start); + +#ifdef CONFIG_SUN_OPENPROMIO + openprom_init(); +#endif +#ifdef CONFIG_SUN_MOSTEK_RTC + rtc_init(); +#endif +#ifdef CONFIG_SPARCAUDIO + sparcaudio_init(); +#endif +#ifdef CONFIG_SUN_BPP + bpp_init(); +#endif +#ifdef CONFIG_SUN_AUXIO + if (sparc_cpu_model == sun4u) + auxio_probe(); +#endif +#ifdef __sparc_v9__ + if (sparc_cpu_model == sun4u) { + extern void sun4u_start_timers(void); + extern void clock_probe(void); + + sun4u_start_timers(); + clock_probe(); + } +#endif + return memory_start; +} diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 425c2d873..a6e2d6da7 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1,9 +1,9 @@ -/* $Id: entry.S,v 1.51 1997/07/24 12:15:04 davem Exp $ +/* $Id: entry.S,v 1.65 1997/08/29 15:51:29 jj 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 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ @@ -17,6 +17,7 @@ #include <asm/page.h> #include <asm/signal.h> #include <asm/pgtable.h> +#include <asm/processor.h> /* #define SYSCALL_TRACING */ @@ -50,41 +51,39 @@ sparc64_dtlb_prot_catch: bgu,a,pn %icc, winfix_trampoline rdpr %tpc, %g3 + sethi %hi(109f), %g7 ba,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 b,pt %xcc, 1f mov 1, %o2 + .align 32 sparc64_dtlb_refbit_catch: srlx %g5, 9, %g4 and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 - cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) be,a,pt %xcc, 2f mov 1, %g4 wr %g0, ASI_DMMU, %asi rdpr %pstate, %g1 wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate + rdpr %tl, %g3 ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 - cmp %g3, 1 bgu,pn %icc, winfix_trampoline rdpr %tpc, %g3 + sethi %hi(109f), %g7 b,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 + clr %o2 1: srlx %l5, PAGE_SHIFT, %o1 add %sp, STACK_BIAS + REGWIN_SZ, %o0 - call do_sparc64_fault sllx %o1, PAGE_SHIFT, %o1 b,pt %xcc, rtrap clr %l6 - nop - nop - nop - nop - + .align 32 sparc64_itlb_refbit_catch: srlx %g5, 9, %g4 and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 @@ -95,17 +94,21 @@ sparc64_itlb_refbit_catch: wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate rdpr %tpc, %g5 + sethi %hi(109f), %g7 b,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 b,pt %xcc, 1b clr %o2 + + .align 32 2: sllx %g4, 63, %g4 ! _PAGE_VALID or %g5, _PAGE_ACCESSED, %g5 or %g5, %g4, %g5 stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE - stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load retry + + .align 32 3: sllx %g4, 63, %g4 ! _PAGE_VALID or %g5, _PAGE_ACCESSED, %g5 or %g5, %g4, %g5 @@ -113,24 +116,33 @@ sparc64_itlb_refbit_catch: stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load retry +#define FPDIS_OFF (((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1)) /* This is trivial with the new code... */ .align 32 .globl do_fpdis do_fpdis: - wr %g0, FPRS_FEF, %fprs - ldx [%g6 + AOFF_task_flags], %g2 - sethi %hi(0x00100000), %g4 ! XXX PF_USEDFPU - andcc %g2, %g4, %g0 - - bne,a,pt %xcc, fpload_fromkstk - sethi %hi((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2 - fzero %f0 - fzero %f2 + ldx [%g6 + AOFF_task_tss + AOFF_thread_flags], %g5 ! Load Group + sethi %hi(TSTATE_PEF), %g4 ! IEU0 + sethi %hi(FPDIS_OFF), %g3 ! IEU1 + wr %g0, FPRS_FEF, %fprs ! LSU Group+4bubbles + andcc %g5, SPARC_FLAG_USEDFPU, %g0 ! IEU1 Group + or %g3, %lo(FPDIS_OFF), %g2 ! IEU0 + sethi %hi(empty_zero_page), %g1 ! IEU0 Group + add %g6, %g2, %g2 ! IEU1 + be,a,pn %icc, 1f ! CTI + clr %g7 ! IEU0 Group + add %g2, 0x100, %g1 ! IEU1 + ldx [%g2 + 0x108], %g7 ! Load +1: andcc %g5, SPARC_FLAG_USEDFPUL, %g0 ! IEU1 Group + bne,pn %icc, 2f ! CTI + fzero %f0 ! FPA + andcc %g5, SPARC_FLAG_USEDFPUU, %g0 ! IEU1 Group + bne,pn %icc, 1f ! CTI + fzero %f2 ! FPA faddd %f0, %f2, %f4 fmuld %f0, %f2, %f6 faddd %f0, %f2, %f8 fmuld %f0, %f2, %f10 - faddd %f0, %f2, %f12 fmuld %f0, %f2, %f14 faddd %f0, %f2, %f16 @@ -139,7 +151,6 @@ do_fpdis: fmuld %f0, %f2, %f22 faddd %f0, %f2, %f24 fmuld %f0, %f2, %f26 - faddd %f0, %f2, %f28 fmuld %f0, %f2, %f30 faddd %f0, %f2, %f32 @@ -148,54 +159,92 @@ do_fpdis: fmuld %f0, %f2, %f38 faddd %f0, %f2, %f40 fmuld %f0, %f2, %f42 - faddd %f0, %f2, %f44 fmuld %f0, %f2, %f46 - ldx [%g6 + AOFF_task_flags], %g2 faddd %f0, %f2, %f48 fmuld %f0, %f2, %f50 - or %g2, %g4, %g2 faddd %f0, %f2, %f52 fmuld %f0, %f2, %f54 - - stx %g2, [%g6 + AOFF_task_flags] faddd %f0, %f2, %f56 - sethi %hi(empty_zero_page), %g3 fmuld %f0, %f2, %f58 - - faddd %f0, %f2, %f60 - ldx [%g3], %fsr ! wheee, empty_zero_page + b,pt %xcc, fpdis_exit2 + faddd %f0, %f2, %f60 +1: mov SECONDARY_CONTEXT, %g3 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + ldxa [%g3] ASI_DMMU, %g5 + stxa %g0, [%g3] ASI_DMMU + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + flush %g2 + wr %g0, ASI_BLK_S, %asi ! grrr, where is ASI_BLK_NUCLEUS 8-( + membar #StoreLoad | #LoadLoad + ldda [%g2 + 0x080] %asi, %f32 + ldda [%g2 + 0x0c0] %asi, %f48 + faddd %f0, %f2, %f12 + fmuld %f0, %f2, %f14 + faddd %f0, %f2, %f16 + fmuld %f0, %f2, %f18 + faddd %f0, %f2, %f20 + fmuld %f0, %f2, %f22 + faddd %f0, %f2, %f24 + fmuld %f0, %f2, %f26 + faddd %f0, %f2, %f28 + fmuld %f0, %f2, %f30 b,pt %xcc, fpdis_exit - wr %g0, 0, %gsr - -fpload_fromkstk: - or %g2, %lo((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2 - add %g6, %g2, %g2 + membar #Sync +2: andcc %g5, SPARC_FLAG_USEDFPUU, %g0 + bne,pt %icc, 3f + fzero %f32 mov SECONDARY_CONTEXT, %g3 + fzero %f34 + ldxa [%g3] ASI_DMMU, %g5 + stxa %g0, [%g3] ASI_DMMU + faddd %f32, %f34, %f36 + fmuld %f32, %f34, %f38 + flush %g2 + wr %g0, ASI_BLK_S, %asi ! grrr, where is ASI_BLK_NUCLEUS 8-( + membar #StoreLoad | #LoadLoad + ldda [%g2 + 0x000] %asi, %f0 + ldda [%g2 + 0x040] %asi, %f16 + faddd %f32, %f34, %f40 + fmuld %f32, %f34, %f42 + faddd %f32, %f34, %f44 + fmuld %f32, %f34, %f46 + faddd %f32, %f34, %f48 + fmuld %f32, %f34, %f50 + faddd %f32, %f34, %f52 + fmuld %f32, %f34, %f54 + faddd %f32, %f34, %f56 + fmuld %f32, %f34, %f58 + faddd %f32, %f34, %f60 + fmuld %f32, %f34, %f62 + b,pt %xcc, fpdis_exit + membar #Sync +3: mov SECONDARY_CONTEXT, %g3 + ldxa [%g3] ASI_DMMU, %g5 stxa %g0, [%g3] ASI_DMMU flush %g2 wr %g0, ASI_BLK_S, %asi ! grrr, where is ASI_BLK_NUCLEUS 8-( membar #StoreLoad | #LoadLoad - ldda [%g2 + 0x000] %asi, %f0 ldda [%g2 + 0x040] %asi, %f16 ldda [%g2 + 0x080] %asi, %f32 ldda [%g2 + 0x0c0] %asi, %f48 - ldx [%g2 + 0x100], %fsr - ldx [%g2 + 0x108], %g2 membar #Sync - wr %g2, 0, %gsr fpdis_exit: + stxa %g5, [%g3] ASI_DMMU + flush %g2 +fpdis_exit2: + wr %g7, 0, %gsr + ldx [%g1], %fsr rdpr %tstate, %g3 - sethi %hi(TSTATE_PEF), %g4 or %g3, %g4, %g3 ! anal... wrpr %g3, %tstate + wr %g0, FPRS_FEF, %fprs ! clean DU/DL bits retry -#ifdef __SMP__ - /* Note check out head.h, this code isn't even used for UP, - * for SMP things will be different. In particular the data - * registers for cross calls will be: + /* The registers for cross calls will be: * * DATA 0: [low 32-bits] Address of function to call, jmp to this * [high 32-bits] MMU Context Argument 0, place in %g5 @@ -205,11 +254,17 @@ fpdis_exit: * With this method we can do most of the cross-call tlb/cache * flushing very quickly. */ + .data + .align 8 + .globl ivec_spurious_cookie +ivec_spurious_cookie: .xword 0 + + .text .align 32 - .globl do_ivec, do_ivec_return + .globl do_ivec do_ivec: - ldxa [%g0] ASI_INTR_RECEIVE, %g1 - andcc %g1, 0x20, %g0 + ldxa [%g0] ASI_INTR_RECEIVE, %g5 + andcc %g5, 0x20, %g0 be,pn %xcc, do_ivec_return mov 0x40, %g2 @@ -223,39 +278,45 @@ do_ivec: sllx %g3, 3, %g3 ldx [%g1 + %g3], %g2 brz,pn %g2, do_ivec_spurious - nop + sethi %hi(0x80000000), %g5 + + or %g2, %g5, %g2 + stx %g2, [%g1 + %g3] /* 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. */ wr %g2, 0x0, %set_softint - do_ivec_return: - /* Acknowledge the UPA */ stxa %g0, [%g0] ASI_INTR_RECEIVE membar #Sync retry do_ivec_xcall: srlx %g3, 32, %g5 add %g2, 0x10, %g2 - sra %g3, 0, %g3 + srl %g3, 0, %g3 ldxa [%g2] ASI_UDB_INTR_R, %g6 add %g2, 0x10, %g2 + ldxa [%g2] ASI_UDB_INTR_R, %g7 + stxa %g0, [%g0] ASI_INTR_RECEIVE jmpl %g3, %g0 - ldxa [%g2] ASI_UDB_INTR_R, %g7 + membar #Sync 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 - rdpr %pstate, %g1 - wrpr %g1, PSTATE_IG | PSTATE_AG, %pstate + rdpr %pstate, %g5 + wrpr %g5, PSTATE_IG | PSTATE_AG, %pstate + sethi %hi(109f), %g7 ba,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 call report_spurious_ivec add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap clr %l6 -#endif /* __SMP__ */ .globl getcc, setcc getcc: @@ -359,8 +420,9 @@ floppy_overrun: floppy_dosoftint: rdpr %pil, %g2 wrpr %g0, 15, %pil + sethi %hi(109f), %g7 b,pt %xcc, etrap_irq - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 mov 11, %o0 mov 0, %o1 @@ -373,10 +435,8 @@ floppy_dosoftint: #endif /* CONFIG_BLK_DEV_FD */ /* XXX Here is stuff we still need to write... -DaveM XXX */ - .globl indirect_syscall, netbsd_syscall, solaris_syscall -indirect_syscall: + .globl netbsd_syscall netbsd_syscall: -solaris_syscall: retl nop @@ -386,8 +446,9 @@ do_mna: cmp %g3, 1 bgu,a,pn %icc, winfix_mna rdpr %tpc, %g3 + sethi %hi(109f), %g7 ba,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 call mem_address_unaligned add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap @@ -573,14 +634,8 @@ sys_fork: sys_vfork: mov SIGCHLD, %o0 clr %o1 sys_clone: mov %o7, %l5 -/*???*/ save %sp, -REGWIN_SZ, %sp - flushw -/*???*/ restore %g0, %g0, %g0 - rdpr %cwp, %o4 add %sp, STACK_BIAS + REGWIN_SZ, %o2 - movrz %o1, %fp, %o1 - stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] call do_fork mov %l5, %o7 #ifdef __SMP__ @@ -611,9 +666,9 @@ linux_sparc_syscall: cmp %g1, NR_SYSCALLS ! IEU1 Group bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI mov %i0, %o0 ! IEU0 - sll %g1, 3, %l4 ! IEU0 Group + sll %g1, 2, %l4 ! IEU0 Group mov %i1, %o1 ! IEU1 - ldx [%l7 + %l4], %l7 ! Load + lduw [%l7 + %l4], %l7 ! Load syscall_is_too_hard: mov %i2, %o2 ! IEU0 Group ldx [%curptr + AOFF_task_flags], %l5 ! Load diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index 4daf30e21..e10480454 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.30 1997/06/30 10:31:37 jj Exp $ +/* $Id: etrap.S,v 1.37 1997/08/21 09:13:18 davem Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -15,118 +15,127 @@ #define FPUREG_SZ ((64 * 4) + (2 * 8)) #define TASK_REGOFF ((((PAGE_SIZE<<1)-FPUREG_SZ)&~(64-1)) - \ TRACEREG_SZ-REGWIN_SZ) +#define FPU_OFF (STACK_BIAS + REGWIN_SZ + TRACEREG_SZ) + +/* + * On entry, %g7 is return address - 0x4. + * %g4 and %g5 will be preserved %l4 and %l5 respectively. + */ .text .align 32 +etrap_priv: or %g1, %g3, %g1 ! IEU0 Group + rd %fprs, %g3 ! Single Group+4bubbles + sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 ! IEU0 Group + andcc %g3, FPRS_FEF, %g0 ! IEU1 + add %g2, REGWIN_SZ + TRACEREG_SZ - FPUREG_SZ, %g3 ! IEU0 Group + be,pt %icc, 1f ! CTI + andcc %g1, TSTATE_PRIV, %g0 ! IEU1 + andn %g3, (64 - 1), %g3 ! IEU0 Group + ba,pt %xcc, 1f ! CTI + sub %g3, REGWIN_SZ + TRACEREG_SZ, %g2 ! IEU0 Group + + .align 32 .globl etrap, etrap_irq, etraptl1 -etrap: rdpr %pil, %g2 -etrap_irq: rdpr %tstate, %g1 - sllx %g2, 20, %g2 - or %g1, %g2, %g1 - andcc %g1, TSTATE_PRIV, %g0 - bne,pn %xcc, etrap_maybe_fpu - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - sethi %hi(TASK_REGOFF), %g2 - - or %g2, %lo(TASK_REGOFF), %g2 - add %g6, %g2, %g2 -etrap_maybe_fpu:rd %fprs, %g3 - brnz,pn %g3, etrap_save_fpu - st %g0, [%g2 + REGWIN_SZ + PT_V9_FPRS] -etrap_after_fpu:rdpr %tpc, %g3 - stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] - rdpr %tnpc, %g1 - - stx %g3, [%g2 + REGWIN_SZ + PT_V9_TPC] - rd %y, %g3 - stx %g1, [%g2 + REGWIN_SZ + PT_V9_TNPC] - st %g3, [%g2 + REGWIN_SZ + PT_V9_Y] - save %g2, -STACK_BIAS, %sp ! The ordering here is - rdpr %pstate, %g1 ! critical, see winfixup - bne,pn %xcc, 2f - rdpr %canrestore, %g3 - - rdpr %wstate, %g2 - wrpr %g0, 7, %cleanwin - wrpr %g0, 0, %canrestore - sll %g2, 3, %g2 - wrpr %g3, 0, %otherwin - wrpr %g2, 0, %wstate - wr %g0, ASI_DMMU, %asi - ldxa [%g0 + PRIMARY_CONTEXT] %asi, %g2 - - stxa %g0, [%g0 + PRIMARY_CONTEXT] %asi - stxa %g2, [%g0 + SECONDARY_CONTEXT] %asi - flush %g6 -2: wrpr %g0, 0x0, %tl - or %g1, 0, %l1 - add %g4, 0, %l4 - or %g5, 0, %l5 - add %g7, 0, %l2 - - or %g6, 0, %l6 - wrpr %l1, (PSTATE_AG|PSTATE_RMO), %pstate - stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] - stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] - stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] - stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] - stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6] - - stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] - stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] - stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] - stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] - stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] - sethi %uhi(PAGE_OFFSET), %g4 - stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] - - stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] - sllx %g4, 32, %g4 - stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] - wrpr %l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate - jmpl %l2 + 0x4, %g0 - mov %l6, %g6 -etrap_save_fpu: and %g3, FPRS_FEF, %g3 - brz,pn %g3, 2f - - nop - be,a,pt %xcc, 3f - add %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 - wr %g0, ASI_BLK_P, %asi - add %g2, ((TRACEREG_SZ+REGWIN_SZ)-FPUREG_SZ), %g2 - andn %g2, (64 - 1), %g2 -1: st %g3, [%g2 - 0x4 /*REGWIN_SZ + PT_V9_FPRS*/] - rd %gsr, %g3 - - stx %fsr, [%g2 + 0x100] - stx %g3, [%g2 + 0x108] - membar #StoreStore | #LoadStore - stda %f0, [%g2 + 0x000] %asi - stda %f16, [%g2 + 0x040] %asi - stda %f32, [%g2 + 0x080] %asi - stda %f48, [%g2 + 0x0c0] %asi - membar #Sync - - sub %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 -2: b,pt %xcc, etrap_after_fpu - wr %g0, 0, %fprs -3: /* Because Ultra lacks ASI_BLK_NUCLEUS a hack has to take place. */ - mov SECONDARY_CONTEXT, %g3 - stxa %g0, [%g3] ASI_DMMU - flush %g2 - wr %g0, ASI_BLK_S, %asi - nop +etrap: rdpr %pil, %g2 ! Single Group +etrap_irq: rdpr %tstate, %g1 ! Single Group + sllx %g2, 20, %g3 ! IEU0 Group + andcc %g1, TSTATE_PRIV, %g0 ! IEU1 + bne,pn %xcc, etrap_priv ! CTI + sethi %hi(TASK_REGOFF), %g2 ! IEU0 Group + or %g1, %g3, %g1 ! IEU1 + or %g2, %lo(TASK_REGOFF), %g2 ! IEU0 Group + add %g6, %g2, %g2 ! IEU0 Group +1: rdpr %tpc, %g3 ! Single Group + stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] ! Store Group + rdpr %tnpc, %g1 ! Single Group + stx %g3, [%g2 + REGWIN_SZ + PT_V9_TPC] ! Store Group + rd %y, %g3 ! Single Group+4bubbles + stx %g1, [%g2 + REGWIN_SZ + PT_V9_TNPC] ! Store Group + st %g3, [%g2 + REGWIN_SZ + PT_V9_Y] ! Store Group + save %g2, -STACK_BIAS, %sp ! The ordering here is ! Single Group + rdpr %pstate, %g1 ! critical, see winfixup ! Single Group+9bubbles + bne,pn %xcc, 2f ! CTI Group + sethi %hi(TSTATE_PEF), %l2 ! IEU0 + mov PRIMARY_CONTEXT, %l4 ! IEU1 + rdpr %canrestore, %g3 ! Single Group+4bubbles + rdpr %wstate, %g2 ! Single Group+4bubbles + wrpr %g0, 7, %cleanwin ! Single Group+4bubbles + wrpr %g0, 0, %canrestore ! Single Group+4bubbles + sll %g2, 3, %g2 ! IEU0 Group + mov SECONDARY_CONTEXT, %l5 ! IEU1 + wrpr %g3, 0, %otherwin ! Single Group+4bubbles + wrpr %g2, 0, %wstate ! Single Group+4bubbles + rdpr %tstate, %l3 ! Single Group + ldxa [%l4] ASI_DMMU, %g2 ! Load Group + stxa %g0, [%l4] ASI_DMMU ! Store Group + stxa %g2, [%l5] ASI_DMMU ! Store Group + flush %g6 ! Single Group+9bubbles + andcc %l3, %l2, %g0 ! IEU1 Group + be,a,pt %icc, 6f ! CTI + st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] ! Store + rd %fprs, %l0 ! Single Group+4bubbles + andcc %l0, FPRS_FEF, %g0 ! IEU1 Group + be,pn %icc, 6f ! CTI + st %l0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] ! Store + ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %l4 ! Load Group + stx %fsr, [%sp + FPU_OFF + 0x100] ! Single Group + or %l4, %l0, %l4 ! IEU0 Group + ba,pt %xcc, 3f ! CTI + st %l4, [%g6 + AOFF_task_tss + AOFF_thread_flags] ! Store +2: rd %fprs, %l0 ! Single Group+4bubbles + andcc %l0, FPRS_FEF, %g0 ! IEU1 Group + be,pn %icc, 6f ! CTI + st %l0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] ! Store + stx %fsr, [%sp + FPU_OFF + 0x100] ! Single Group +3: rd %gsr, %l7 ! Single Group+4bubbles + cmp %l0, FPRS_FEF ! IEU1 Group + be,pn %icc, 6f ! CTI + stx %l7, [%sp + FPU_OFF + 0x108] ! Store + wr %g0, ASI_BLK_P, %asi ! Singe Group+4bubbles + andcc %l0, FPRS_DL, %g0 ! IEU1 Group + be,pn %icc, 4f ! CTI + membar #StoreStore | #LoadStore ! Memory + stda %f0, [%sp + FPU_OFF + 0x000] %asi ! Store Group + stda %f16, [%sp + FPU_OFF + 0x040] %asi ! Store Group + andcc %l0, FPRS_DU, %g0 ! IEU1 + be,pn %icc, 5f ! CTI + nop ! IEU0 Group +4: stda %f32, [%sp + FPU_OFF + 0x080] %asi ! Store Group + stda %f48, [%sp + FPU_OFF + 0x0c0] %asi ! Store Group +5: membar #Sync ! Memory +6: wr %g0, 0x0, %fprs ! Single Group+4bubbles + wrpr %g0, 0x0, %tl ! Single Group+4bubbles + mov %g1, %l1 ! IEU0 Group + mov %g4, %l4 ! IEU1 + mov %g5, %l5 ! IEU0 Group + mov %g7, %l2 ! IEU1 + mov %g6, %l6 ! IEU0 Group + wrpr %l1, (PSTATE_AG|PSTATE_RMO), %pstate ! Single Group+4bubbles + stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] ! Store Group + stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] ! Store Group + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] ! Store Group + stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] ! Store Group + stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] ! Store Group + stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6] ! Store Group + stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] ! Store Group + stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] ! Store Group + stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] ! Store Group + stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] ! Store Group + stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] ! Store Group + stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] ! Store Group + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 + stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] ! Store Group + stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] ! Store Group + sllx %g4, 32, %g4 ! IEU0 + stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] ! Store Group + wrpr %l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate ! Single Group+4bubbles + jmpl %l2 + 0x4, %g0 ! CTI Group + mov %l6, %g6 ! IEU0 - b,pt %xcc, 1b - mov FPRS_FEF, %g3 - nop -etraptl1: rdpr %tstate, %g1 - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - ba,pt %xcc, etrap_maybe_fpu - andcc %g1, TSTATE_PRIV, %g0 - nop +etraptl1: rdpr %tstate, %g1 ! Single Group+4bubbles + ba,pt %xcc, etrap_priv ! CTI Group + clr %g3 ! IEU0 #undef TASK_REGOFF #undef FPUREG_SZ diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 0ed975aff..43f950b25 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.43 1997/07/07 03:05:25 davem Exp $ +/* $Id: head.S,v 1.46 1997/08/08 08:33:30 jj Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -21,6 +21,7 @@ #include <asm/processor.h> #include <asm/lsu.h> #include <asm/head.h> +#include <asm/ttable.h> /* This section from from _start to sparc64_boot_end should fit into * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space @@ -89,6 +90,28 @@ sparc64_boot: */ wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate +#ifdef __SMP__ + /* Ugly but necessary... */ + sethi %hi(KERNBASE), %g7 + sethi %hi(sparc64_cpu_startup), %g5 + or %g5, %lo(sparc64_cpu_startup), %g5 + sub %g5, %g7, %g5 + sethi %hi(sparc64_cpu_startup_end), %g6 + or %g6, %lo(sparc64_cpu_startup_end), %g6 + sub %g6, %g7, %g6 + sethi %hi(smp_trampoline), %g3 + or %g3, %lo(smp_trampoline), %g3 + sub %g3, %g7, %g3 +1: ldx [%g5], %g1 + stx %g1, [%g3] + membar #StoreStore + flush %g3 + add %g5, 8, %g5 + cmp %g5, %g6 + blu,pt %xcc, 1b + add %g3, 8, %g3 +#endif + create_mappings: /* %g5 holds the tlb data */ sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 @@ -340,7 +363,7 @@ setup_tba: mov 0x40, %g2 /* INTR data 0 register */ /* Ok, we're done setting up all the state our trap mechanims needs, - * now get back into normal globals and let the PROM know what it up. + * now get back into normal globals and let the PROM know what is up. */ wrpr %g0, %g0, %wstate wrpr %o1, PSTATE_IE, %pstate @@ -374,6 +397,7 @@ bootup_kernel_stack: ! 0x0000000000408000 #include "ttable.S" +#include "systbls.S" #include "etrap.S" #include "rtrap.S" #include "winfixup.S" diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 81eb45e42..af88ca4b6 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.14 1997/07/17 06:21:12 davem Exp $ +/* $Id: ioctl32.c,v 1.18 1997/09/06 02:25:13 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -23,6 +23,7 @@ #include <linux/vt.h> #include <linux/fs.h> #include <linux/fd.h> +#include <linux/if_ppp.h> #include <asm/types.h> #include <asm/uaccess.h> @@ -102,6 +103,7 @@ static inline int dev_ifconf(unsigned int fd, u32 arg) if (copy_from_user(&ifc32, (struct ifconf32 *)A(arg), sizeof(struct ifconf32))) return -EFAULT; + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * sizeof (struct ifreq); ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); if (!ifc.ifc_buf) return -ENOMEM; @@ -145,7 +147,8 @@ static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg) unsigned long old_fs; int err; - if (cmd == SIOCSIFMAP) { + switch (cmd) { + case SIOCSIFMAP: if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(ifr.ifr_name)) || __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) || __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_end)) || @@ -154,9 +157,20 @@ static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg) __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.dma)) || __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.port))) return -EFAULT; - } else { + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + break; + default: if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(struct ifreq32))) return -EFAULT; + break; } old_fs = get_fs(); set_fs (KERNEL_DS); @@ -177,6 +191,25 @@ static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg) if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(struct ifreq32))) return -EFAULT; break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + { + u32 data; + int len; + + __get_user(data, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_data)); + if(cmd == SIOCGPPPVER) + len = strlen(PPP_VERSION) + 1; + else if(cmd == SIOCGPPPCSTATS) + len = sizeof(struct ppp_comp_stats); + else + len = sizeof(struct ppp_stats); + + if (copy_to_user((char *)A(data), ifr.ifr_data, len)) + return -EFAULT; + break; + } case SIOCGIFMAP: if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(ifr.ifr_name)) || __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) || @@ -481,6 +514,74 @@ static inline int fbiogscursor(unsigned int fd, unsigned int cmd, u32 arg) return ret; } +static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) +{ + unsigned long old_fs = get_fs(); + unsigned long kval; + unsigned int *uvp; + int error; + + set_fs(KERNEL_DS); + error = sys_ioctl(fd, cmd, (long)&kval); + set_fs(old_fs); + + if(error == 0) { + uvp = (unsigned int *)A(arg); + if(put_user(kval, uvp)) + error = -EFAULT; + } + return error; +} + +struct ppp_option_data32 { + __kernel_caddr_t32 ptr; + __u32 length; + int transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +static int ppp_ioctl(unsigned int fd, unsigned int cmd, u32 arg) +{ + unsigned long old_fs = get_fs(); + struct ppp_option_data32 data32; + struct ppp_option_data data; + int err; + + switch (cmd) { + case PPPIOCSCOMPRESS32: + if (copy_from_user(&data32, (struct ppp_option_data32 *)A(arg), sizeof(struct ppp_option_data32))) + return -EFAULT; + data.ptr = kmalloc (data32.length, GFP_KERNEL); + if (!data.ptr) + return -ENOMEM; + if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) { + err = -EFAULT; + goto out; + } + data.length = data32.length; + data.transmit = data32.transmit; + break; + default: + printk("ppp_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + return -EINVAL; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&data); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case PPPIOCSCOMPRESS32: + default: + break; + } +out: + kfree(data.ptr); + return err; +} + asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { struct file * filp; @@ -527,6 +628,9 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case SIOCSIFDSTADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: error = dev_ifsioc(fd, cmd, arg); goto out; @@ -557,6 +661,16 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = fbiogscursor(fd, cmd, arg); goto out; + case HDIO_GET_KEEPSETTINGS: + case HDIO_GET_UNMASKINTR: + case HDIO_GET_DMA: + case HDIO_GET_32BIT: + case HDIO_GET_MULTCOUNT: + case HDIO_GET_NOWERR: + case HDIO_GET_NICE: + error = hdio_ioctl_trans(fd, cmd, arg); + goto out; + /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... */ @@ -619,6 +733,23 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case FIBMAP: case FIGETBSZ: + /* 0x03 -- HD/IDE ioctl's used by hdparm and friends. + * Some need translations, these do not. + */ + case HDIO_GET_IDENTITY: + case HDIO_SET_DMA: + case HDIO_SET_KEEPSETTINGS: + case HDIO_SET_UNMASKINTR: + case HDIO_SET_NOWERR: + case HDIO_SET_32BIT: + case HDIO_SET_MULTCOUNT: + case HDIO_DRIVE_CMD: + case HDIO_SET_PIO_MODE: + case HDIO_SCAN_HWIF: + case HDIO_SET_NICE: + case BLKROSET: + case BLKROGET: + /* 0x02 -- Floppy ioctls */ case FDSETEMSGTRESH: case FDFLUSH: @@ -729,11 +860,40 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case SIOCSARP: case SIOCGARP: case SIOCDARP: + case OLD_SIOCSARP: + case OLD_SIOCGARP: + case OLD_SIOCDARP: + case SIOCSRARP: + case SIOCGRARP: + case SIOCDRARP: case SIOCADDDLCI: case SIOCDELDLCI: + + /* PPP stuff */ + case PPPIOCGFLAGS: + case PPPIOCSFLAGS: + case PPPIOCGASYNCMAP: + case PPPIOCSASYNCMAP: + case PPPIOCGUNIT: + case PPPIOCGRASYNCMAP: + case PPPIOCSRASYNCMAP: + case PPPIOCGMRU: + case PPPIOCSMRU: + case PPPIOCSMAXCID: + case PPPIOCGXASYNCMAP: + case PPPIOCSXASYNCMAP: + case PPPIOCXFERUNIT: + case PPPIOCGNPMODE: + case PPPIOCSNPMODE: + case PPPIOCGDEBUG: + case PPPIOCSDEBUG: + case PPPIOCGIDLE: error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; - break; + + case PPPIOCSCOMPRESS32: + error = ppp_ioctl (fd, cmd, (unsigned long)arg); + goto out; default: printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", diff --git a/arch/sparc64/kernel/ioport.c b/arch/sparc64/kernel/ioport.c index 7d1580b39..7ae12df11 100644 --- a/arch/sparc64/kernel/ioport.c +++ b/arch/sparc64/kernel/ioport.c @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.11 1997/07/22 06:14:04 davem Exp $ +/* $Id: ioport.c,v 1.13 1997/08/18 01:20:22 davem Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -45,7 +45,7 @@ void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, u32 bus_type, int rdonly) { unsigned long vaddr, base_address; - unsigned long addr = ((unsigned long) address) + (((unsigned long) bus_type) << 32); + unsigned long addr = ((unsigned long)address) + (((unsigned long)bus_type)<<32); unsigned long offset = (addr & (~PAGE_MASK)); if (virtual) { @@ -64,7 +64,12 @@ void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, /* Tell Linux resource manager about the mapping */ request_region ((vaddr | offset), len, name); } else { - return __va(addr); + unsigned long vaddr = (unsigned long) __va(addr); + + if(!check_region(vaddr, len)) + request_region(vaddr, len, name); + + return (void *) vaddr; } base_address = vaddr; @@ -83,11 +88,11 @@ void sparc_free_io (void *virtual, int len) unsigned long vaddr = (unsigned long) virtual & PAGE_MASK; unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK; + release_region(vaddr, plen); + if (((unsigned long)virtual) >= PAGE_OFFSET + 0x10000000000UL) return; - release_region(vaddr, plen); - for (; plen != 0;) { plen -= PAGE_SIZE; unmapioaddr(vaddr + plen); diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index f76c27c57..6f4c9dfdf 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.19 1997/07/24 12:15:04 davem Exp $ +/* $Id: irq.c,v 1.39 1997/08/31 03:11:18 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -28,8 +28,18 @@ #include <asm/hardirq.h> #include <asm/softirq.h> +#ifdef CONFIG_PCI +#include <linux/pci.h> +#include <asm/pbm.h> +#endif + /* Internal flag, should not be visible elsewhere at all. */ -#define SA_SYSIO_MASKED 0x100 +#define SA_IMAP_MASKED 0x100 + +#ifdef __SMP__ +void distribute_irqs(void); +static int irqs_have_been_distributed = 0; +#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 @@ -37,10 +47,39 @@ * make things even more swift we store the complete mask here. */ -#define NUM_IVECS 2048 /* XXX may need more on sunfire/wildfire */ +#define NUM_HARD_IVECS 2048 +#define NUM_IVECS (NUM_HARD_IVECS + 64) /* For SMP IRQ distribution alg. */ unsigned long ivector_to_mask[NUM_IVECS]; +struct ino_bucket { + struct ino_bucket *next; + unsigned int ino; + unsigned int *imap; + unsigned int *iclr; +}; + +#define INO_HASHSZ (NUM_HARD_IVECS >> 2) +#define NUM_INO_STATIC 4 +static struct ino_bucket *ino_hash[INO_HASHSZ] = { NULL, }; +static struct ino_bucket static_ino_buckets[NUM_INO_STATIC]; +static int static_ino_bucket_count = 0; + +static inline struct ino_bucket *__ino_lookup(unsigned int hash, unsigned int ino) +{ + struct ino_bucket *ret = ino_hash[hash]; + + for(ret = ino_hash[hash]; ret && ret->ino != ino; ret = ret->next) + ; + + return ret; +} + +static inline struct ino_bucket *ino_lookup(unsigned int ino) +{ + return __ino_lookup((ino & (INO_HASHSZ - 1)), ino); +} + /* This is based upon code in the 32-bit Sparc kernel written mostly by * David Redman (djhr@tadpole.co.uk). */ @@ -73,15 +112,22 @@ int get_irq_list(char *buf) } len += sprintf(buf + len, "\n"); } +#if 0 +#ifdef CONFIG_PCI + len += sprintf(buf + len, "ISTAT: PCI[%016lx] OBIO[%016lx]\n", + psycho_root->psycho_regs->pci_istate, + psycho_root->psycho_regs->obio_istate); +#endif +#endif return len; } -/* INO number to Sparc PIL level. */ -unsigned char ino_to_pil[] = { - 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 0 */ - 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 1 */ - 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 2 */ - 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 3 */ +/* SBUS SYSIO INO number to Sparc PIL level. */ +unsigned char sysio_ino_to_pil[] = { + 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 0 */ + 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 1 */ + 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 2 */ + 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 3 */ 3, /* Onboard SCSI */ 5, /* Onboard Ethernet */ /*XXX*/ 8, /* Onboard BPP */ @@ -112,7 +158,7 @@ unsigned char ino_to_pil[] = { */ #define offset(x) ((unsigned long)(&(((struct sysio_regs *)0)->x))) #define bogon ((unsigned long) -1) -static unsigned long irq_offsets[] = { +static unsigned long sysio_irq_offsets[] = { /* SBUS Slot 0 --> 3, level 1 --> 7 */ offset(imap_slot0),offset(imap_slot0),offset(imap_slot0),offset(imap_slot0), offset(imap_slot0),offset(imap_slot0),offset(imap_slot0),offset(imap_slot0), @@ -134,29 +180,29 @@ offset(imap_pmgmt), #undef bogon -#define NUM_IRQ_ENTRIES (sizeof(irq_offsets) / sizeof(irq_offsets[0])) +#define NUM_SYSIO_OFFSETS (sizeof(sysio_irq_offsets) / sizeof(sysio_irq_offsets[0])) -/* Convert an "interrupts" property IRQ level to an SBUS/SYSIO - * Interrupt Mapping register pointer, or NULL if none exists. +/* XXX Old compatability cruft, get rid of me when all drivers have been + * XXX converted to dcookie registry calls... -DaveM */ -static unsigned int *irq_to_imap(unsigned int irq) +static unsigned int *sysio_irq_to_imap(unsigned int irq) { unsigned long offset; struct sysio_regs *sregs; if((irq == 14) || - (irq >= NUM_IRQ_ENTRIES) || - ((offset = irq_offsets[irq]) == ((unsigned long)-1))) + (irq >= NUM_SYSIO_OFFSETS) || + ((offset = sysio_irq_offsets[irq]) == ((unsigned long)-1))) return NULL; sregs = SBus_chain->iommu->sysio_regs; offset += ((unsigned long) sregs); - return ((unsigned int *)offset) + 1; + return ((unsigned int *)offset); } /* Convert Interrupt Mapping register pointer to assosciated - * Interrupt Clear register pointer. + * Interrupt Clear register pointer, SYSIO specific version. */ -static unsigned int *imap_to_iclr(unsigned int *imap) +static unsigned int *sysio_imap_to_iclr(unsigned int *imap) { unsigned long diff; @@ -166,32 +212,68 @@ static unsigned int *imap_to_iclr(unsigned int *imap) #undef offset -/* For non-SBUS IRQ's we do nothing, else we must enable them in the - * appropriate SYSIO interrupt map registers. +#ifdef CONFIG_PCI +/* PCI PSYCHO INO number to Sparc PIL level. */ +unsigned char psycho_ino_to_pil[] = { + 7, 5, 5, 2, /* PCI A slot 0 Int A, B, C, D */ + 7, 5, 5, 2, /* PCI A slot 1 Int A, B, C, D */ + 0, 0, 0, 0, + 0, 0, 0, 0, + 6, 4, 3, 1, /* PCI B slot 0 Int A, B, C, D */ + 6, 4, 3, 1, /* PCI B slot 1 Int A, B, C, D */ + 6, 4, 3, 1, /* PCI B slot 2 Int A, B, C, D */ + 6, 4, 3, 1, /* PCI B slot 3 Int A, B, C, D */ + 3, /* SCSI */ + 5, /* Ethernet */ + 8, /* Parallel Port */ + 13, /* Audio Record */ + 14, /* Audio Playback */ + 15, /* PowerFail */ + 12, /* Keyboard/Mouse/Serial */ + 11, /* Floppy */ + 2, /* Spare Hardware */ + 12, /* Keyboard */ + 4, /* Mouse */ + 12, /* Serial */ + 10, /* Timer 0 */ + 11, /* Timer 1 */ + 15, /* Uncorrectable ECC */ + 15, /* Correctable ECC */ + 15, /* PCI Bus A Error */ + 15, /* PCI Bus B Error */ + 1, /* Power Management */ +}; + +/* INO number to IMAP register offset for PSYCHO external IRQ's. */ -void enable_irq(unsigned int irq) +#define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x))) + +#define psycho_imap_offset(ino) \ + ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \ + (psycho_offset(imap_a_slot0) + (((ino) & 0x3c) << 1))) + +#define psycho_iclr_offset(ino) \ + ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \ + (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f) << 3))) + +#endif + +/* Now these are always passed a true fully specified sun4u INO. */ +void enable_irq(unsigned int ino) { + struct ino_bucket *bucket; unsigned long tid; unsigned int *imap; - /* If this is for the tick interrupt, just ignore, note - * that this is the one and only locally generated interrupt - * source, all others come from external sources (essentially - * any UPA device which is an interruptor). (actually, on - * second thought Ultra can generate local interrupts for - * async memory errors and we may setup handlers for those - * at some point as well) - * - * XXX See commentary below in request_irq() this assumption - * XXX is broken and needs to be fixed. - */ - if(irq == 14) +#ifdef CONFIG_PCI + if(PCI_IRQ_P(ino)) + ino &= (PCI_IRQ_IGN | PCI_IRQ_INO); +#endif + bucket = ino_lookup(ino); + if(!bucket) return; - /* Check for bogons. */ - imap = irq_to_imap(irq); - if(imap == NULL) - goto do_the_stb_watoosi; + imap = bucket->imap; /* 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)); @@ -202,28 +284,27 @@ void enable_irq(unsigned int irq) * Register, the hardware just mirrors that value here. * However for Graphics and UPA Slave devices the full * SYSIO_IMAP_INR field can be set by the programmer here. - * (XXX we will have to handle those for FFB etc. XXX) + * + * Things like FFB can now be handled via the dcookie mechanism. */ *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); - return; - -do_the_stb_watoosi: - printk("Cannot enable irq(%d), doing the \"STB Watoosi\" instead.", irq); - panic("Trying to enable bogon IRQ"); } -void disable_irq(unsigned int irq) +/* This now gets passed true ino's as well. */ +void disable_irq(unsigned int ino) { + struct ino_bucket *bucket; unsigned int *imap; - /* XXX Grrr, I know this is broken... */ - if(irq == 14) +#ifdef CONFIG_PCI + if(PCI_IRQ_P(ino)) + ino &= (PCI_IRQ_IGN | PCI_IRQ_INO); +#endif + bucket = ino_lookup(ino); + if(!bucket) return; - /* Check for bogons. */ - imap = irq_to_imap(irq); - if(imap == NULL) - goto do_the_stb_watoosi; + imap = bucket->imap; /* NOTE: We do not want to futz with the IRQ clear registers * and move the state to IDLE, the SCSI code does call @@ -231,34 +312,254 @@ void disable_irq(unsigned int irq) * SCSI adapter driver code. Thus we'd lose interrupts. */ *imap &= ~(SYSIO_IMAP_VALID); - return; +} + +static void get_irq_translations(int *cpu_irq, int *ivindex_fixup, + unsigned int **imap, unsigned int **iclr, + void *busp, unsigned long flags, + unsigned int irq) +{ + if(*cpu_irq != -1 && *imap != NULL && *iclr != NULL) + return; + + if(*cpu_irq != -1 || *imap != NULL || *iclr != NULL || busp == NULL) { + printk("get_irq_translations: Partial specification, this is bad.\n"); + printk("get_irq_translations: cpu_irq[%d] imap[%p] iclr[%p] busp[%p]\n", + *cpu_irq, *imap, *iclr, busp); + panic("Bad IRQ translations..."); + } + + if(SA_BUS(flags) == SA_SBUS) { + struct linux_sbus *sbusp = busp; + struct sysio_regs *sregs = sbusp->iommu->sysio_regs; + unsigned long offset; -do_the_stb_watoosi: - printk("Cannot disable irq(%d), doing the \"STB Watoosi\" instead.", irq); - panic("Trying to enable bogon IRQ"); + *cpu_irq = sysio_ino_to_pil[irq]; + if(*cpu_irq == 0) { + printk("get_irq_translations: Bad SYSIO INO[%x]\n", irq); + panic("Bad SYSIO IRQ translations..."); + } + offset = sysio_irq_offsets[irq]; + if(offset == ((unsigned long)-1)) { + printk("get_irq_translations: Bad SYSIO INO[%x] cpu[%d]\n", + irq, *cpu_irq); + panic("BAD SYSIO IRQ offset..."); + } + offset += ((unsigned long)sregs); + *imap = ((unsigned int *)offset); + + /* SYSIO inconsistancy. For external SLOTS, we have to select + * the right ICLR register based upon the lower SBUS irq level + * bits. + */ + if(irq >= 0x20) { + *iclr = sysio_imap_to_iclr(*imap); + } else { + unsigned long iclraddr; + int sbus_slot = (irq & 0x18)>>3; + int sbus_level = irq & 0x7; + + switch(sbus_slot) { + case 0: + *iclr = &sregs->iclr_slot0; + break; + case 1: + *iclr = &sregs->iclr_slot1; + break; + case 2: + *iclr = &sregs->iclr_slot2; + break; + case 3: + *iclr = &sregs->iclr_slot3; + break; + }; + + iclraddr = (unsigned long) *iclr; + iclraddr += ((sbus_level - 1) * 8); + *iclr = (unsigned int *) iclraddr; + +#if 0 /* DEBUGGING */ + printk("SYSIO_FIXUP: slot[%x] level[%x] iclr[%p] ", + sbus_slot, sbus_level, *iclr); +#endif + + /* Also, make sure this is accounted for in ivindex + * computations done by the caller. + */ + *ivindex_fixup = sbus_level; + } + return; + } +#ifdef CONFIG_PCI + if(SA_BUS(flags) == SA_PCI) { + struct pci_bus *pbusp = busp; + struct linux_pbm_info *pbm = pbusp->sysdata; + struct psycho_regs *pregs = pbm->parent->psycho_regs; + unsigned long offset; + + *cpu_irq = psycho_ino_to_pil[irq & 0x3f]; + if(*cpu_irq == 0) { + printk("get_irq_translations: Bad PSYCHO INO[%x]\n", irq); + panic("Bad PSYCHO IRQ translations..."); + } + offset = psycho_imap_offset(irq); + if(offset == ((unsigned long)-1)) { + printk("get_irq_translations: Bad PSYCHO INO[%x] cpu[%d]\n", + irq, *cpu_irq); + panic("Bad PSYCHO IRQ offset..."); + } + offset += ((unsigned long)pregs); + *imap = ((unsigned int *)offset) + 1; + *iclr = (unsigned int *) + (((unsigned long)pregs) + psycho_imap_offset(irq)); + return; + } +#endif +#if 0 /* XXX More to do before we can use this. -DaveM */ + if(SA_BUS(flags) == SA_FHC) { + struct fhc_bus *fbusp = busp; + struct fhc_regs *fregs = fbusp->regs; + unsigned long offset; + + *cpu_irq = fhc_ino_to_pil[irq]; + if(*cpu_irq == 0) { + printk("get_irq_translations: Bad FHC INO[%x]\n", irq); + panic("Bad FHC IRQ translations..."); + } + offset = fhc_irq_offset[*cpu_irq]; + if(offset == ((unsigned long)-1)) { + printk("get_irq_translations: Bad FHC INO[%x] cpu[%d]\n", + irq, *cpu_irq); + panic("Bad FHC IRQ offset..."); + } + offset += ((unsigned long)pregs); + *imap = (((unsigned int *)offset)+1); + *iclr = fhc_imap_to_iclr(*imap); + return; + } +#endif + printk("get_irq_translations: IRQ register for unknown bus type.\n"); + printk("get_irq_translations: BUS[%lx] IRQ[%x]\n", + SA_BUS(flags), irq); + panic("Bad IRQ bus type..."); +} + +#ifdef CONFIG_PCI +static void pci_irq_frobnicate(int *cpu_irq, int *ivindex_fixup, + unsigned int **imap, unsigned int **iclr, + unsigned int irq) +{ + struct linux_psycho *psycho = psycho_root; + struct psycho_regs *pregs = psycho->psycho_regs; + unsigned long addr, imoff; + + addr = (unsigned long) &pregs->imap_a_slot0; + imoff = (irq & PCI_IRQ_IMAP_OFF) >> PCI_IRQ_IMAP_OFF_SHFT; + addr = addr + imoff; + + *imap = ((unsigned int *)addr) + 1; + + addr = (unsigned long) pregs; + addr += psycho_iclr_offset(irq & (PCI_IRQ_INO)); + *iclr = ((unsigned int *)addr) + 1; + + *cpu_irq = psycho_ino_to_pil[irq & (PCI_IRQ_INO)]; + if(*cpu_irq == 0) { + printk("get_irq_translations: BAD PSYCHO INO[%x]\n", irq); + panic("Bad PSYCHO IRQ frobnication..."); + } + + /* IVINDEX fixup only needed for PCI slot irq lines. */ + if(!(irq & 0x20)) + *ivindex_fixup = irq & 0x03; +} +#endif + +/* Once added, they are never removed. */ +static struct ino_bucket *add_ino_hash(unsigned int ivindex, + unsigned int *imap, unsigned int *iclr, + unsigned long flags) +{ + struct ino_bucket *new = NULL, **hashp; + unsigned int hash = (ivindex & (INO_HASHSZ - 1)); + + new = __ino_lookup(hash, ivindex); + if(new) + return new; + if(flags & SA_STATIC_ALLOC) { + if(static_ino_bucket_count < NUM_INO_STATIC) + new = &static_ino_buckets[static_ino_bucket_count++]; + else + printk("Request for ino bucket SA_STATIC_ALLOC failed " + "using kmalloc\n"); + } + if(new == NULL) + new = kmalloc(sizeof(struct ino_bucket), GFP_KERNEL); + if(new) { + hashp = &ino_hash[hash]; + new->imap = imap; + new->iclr = iclr; + new->ino = ivindex; + new->next = *hashp; + *hashp = new; + } + return new; } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char *name, void *dev_cookie) + unsigned long irqflags, const char *name, void *dev_id) { struct irqaction *action, *tmp = NULL; + struct devid_cookie *dcookie = NULL; + struct ino_bucket *bucket = NULL; unsigned long flags; - unsigned int cpu_irq, *imap, *iclr; + unsigned int *imap, *iclr; + void *bus_id = NULL; + int ivindex, ivindex_fixup, cpu_irq = -1; - /* XXX This really is not the way to do it, the "right way" - * XXX is to have drivers set SA_SBUS or something like that - * XXX in irqflags and we base our decision here on whether - * XXX that flag bit is set or not. - */ - if(irq == 14) - cpu_irq = irq; - else - cpu_irq = ino_to_pil[irq]; - if(!handler) return -EINVAL; - imap = irq_to_imap(irq); + imap = iclr = NULL; + + ivindex_fixup = 0; +#ifdef CONFIG_PCI + if(PCI_IRQ_P(irq)) { + pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); + } else +#endif + if(irqflags & SA_DCOOKIE) { + if(!dev_id) { + printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n"); + panic("Bogus irq registry."); + } + dcookie = dev_id; + dev_id = dcookie->real_dev_id; + cpu_irq = dcookie->pil; + imap = dcookie->imap; + iclr = dcookie->iclr; + bus_id = dcookie->bus_cookie; + get_irq_translations(&cpu_irq, &ivindex_fixup, &imap, + &iclr, bus_id, irqflags, irq); + } else { + /* XXX NOTE: This code is maintained for compatability until I can + * XXX verify that all drivers sparc64 will use are updated + * XXX to use the new IRQ registry dcookie interface. -DaveM + */ + if(irq == 14) + cpu_irq = irq; + else + cpu_irq = sysio_ino_to_pil[irq]; + imap = sysio_irq_to_imap(irq); + if(!imap) { + printk("request_irq: BAD, null imap for old style " + "irq registry IRQ[%x].\n", irq); + panic("Bad IRQ registery..."); + } + iclr = sysio_imap_to_iclr(imap); + } + ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); + ivindex += ivindex_fixup; action = *(cpu_irq + irq_action); if(action) { @@ -297,52 +598,67 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) return -ENOMEM; } - if(imap) { - int ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); + bucket = add_ino_hash(ivindex, imap, iclr, irqflags); + if(!bucket) { + kfree(action); + restore_flags(flags); + return -ENOMEM; + } - ivector_to_mask[ivindex] = (1<<cpu_irq); - iclr = imap_to_iclr(imap); - action->mask = (unsigned long) iclr; - irqflags |= SA_SYSIO_MASKED; - } else { - action->mask = 0; + ivector_to_mask[ivindex] = (1 << cpu_irq); + + if(dcookie) { + dcookie->ret_ino = ivindex; + dcookie->ret_pil = cpu_irq; } + action->mask = (unsigned long) bucket; action->handler = handler; - action->flags = irqflags; + action->flags = irqflags | SA_IMAP_MASKED; action->name = name; action->next = NULL; - action->dev_id = dev_cookie; + action->dev_id = dev_id; if(tmp) tmp->next = action; else *(cpu_irq + irq_action) = action; - enable_irq(irq); + enable_irq(ivindex); restore_flags(flags); +#ifdef __SMP__ + if(irqs_have_been_distributed) + distribute_irqs(); +#endif return 0; } -void free_irq(unsigned int irq, void *dev_cookie) +void free_irq(unsigned int irq, void *dev_id) { struct irqaction *action; struct irqaction *tmp = NULL; unsigned long flags; unsigned int cpu_irq; + int ivindex = -1; - if(irq == 14) + if(irq == 14) { cpu_irq = irq; - else - cpu_irq = ino_to_pil[irq]; + } else { +#ifdef CONFIG_PCI + if(PCI_IRQ_P(irq)) + cpu_irq = psycho_ino_to_pil[irq & PCI_IRQ_INO]; + else +#endif + cpu_irq = sysio_ino_to_pil[irq]; + } action = *(cpu_irq + irq_action); if(!action->handler) { printk("Freeing free IRQ %d\n", irq); return; } - if(dev_cookie) { + if(dev_id) { for( ; action; action = action->next) { - if(action->dev_id == dev_cookie) + if(action->dev_id == dev_id) break; tmp = action; } @@ -351,7 +667,7 @@ void free_irq(unsigned int irq, void *dev_cookie) return; } } else if(action->flags & SA_SHIRQ) { - printk("Trying to free shared IRQ %d with NULL device cookie\n", irq); + printk("Trying to free shared IRQ %d with NULL device ID\n", irq); return; } @@ -367,29 +683,37 @@ void free_irq(unsigned int irq, void *dev_cookie) else *(cpu_irq + irq_action) = action->next; - if(action->flags & SA_SYSIO_MASKED) { - unsigned int *imap = irq_to_imap(irq); - if(imap != NULL) - ivector_to_mask[*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)] = 0; + if(action->flags & SA_IMAP_MASKED) { + struct ino_bucket *bucket = (struct ino_bucket *)action->mask; + unsigned int *imap = bucket->imap; + + if(imap != NULL) { + ivindex = bucket->ino; + ivector_to_mask[ivindex] = 0; + } else printk("free_irq: WHeee, SYSIO_MASKED yet no imap reg.\n"); } kfree(action); - if(!*(cpu_irq + irq_action)) - disable_irq(irq); + if(ivindex != -1) + disable_irq(ivindex); restore_flags(flags); } -/* Per-processor IRQ locking depth, both SMP and non-SMP code use this. */ -unsigned int local_irq_count[NR_CPUS]; +/* Only uniprocessor needs this IRQ locking depth, on SMP it lives in the per-cpu + * structure for cache reasons. + */ +#ifndef __SMP__ +unsigned int local_irq_count; +#endif #ifndef __SMP__ int __sparc64_bh_counter = 0; -#define irq_enter(cpu, irq) (local_irq_count[cpu]++) -#define irq_exit(cpu, irq) (local_irq_count[cpu]--) +#define irq_enter(cpu, irq) (local_irq_count++) +#define irq_exit(cpu, irq) (local_irq_count--) #else @@ -407,18 +731,31 @@ spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED; /* Global IRQ locking depth. */ atomic_t global_irq_count = ATOMIC_INIT(0); -static inline void wait_on_irq(int cpu) +static unsigned long previous_irqholder; + +#undef INIT_STUCK +#define INIT_STUCK 100000000 + +#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; } + +static inline void wait_on_irq(int cpu, unsigned long where) { - int local_count = local_irq_count[cpu]; + int stuck = INIT_STUCK; + int local_count = local_irq_count; while(local_count != atomic_read(&global_irq_count)) { atomic_sub(local_count, &global_irq_count); spin_unlock(&global_irq_lock); for(;;) { + STUCK; + membar("#StoreLoad | #LoadLoad"); if (atomic_read(&global_irq_count)) continue; - if (*((unsigned char *)&global_irq_lock)) + if (*((volatile unsigned char *)&global_irq_lock)) continue; + membar("#LoadLoad | #LoadStore"); if (spin_trylock(&global_irq_lock)) break; } @@ -426,25 +763,41 @@ static inline void wait_on_irq(int cpu) } } -static inline void get_irqlock(int cpu) +#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) { + int stuck = INIT_STUCK; + if (!spin_trylock(&global_irq_lock)) { + membar("#StoreLoad | #LoadLoad"); if ((unsigned char) cpu == global_irq_holder) return; do { - barrier(); + do { + STUCK; + membar("#LoadLoad"); + } while(*((volatile unsigned char *)&global_irq_lock)); } while (!spin_trylock(&global_irq_lock)); } - wait_on_irq(cpu); + wait_on_irq(cpu, where); global_irq_holder = cpu; + previous_irqholder = where; } void __global_cli(void) { int cpu = smp_processor_id(); + unsigned long where; + __asm__ __volatile__("mov %%i7, %0" : "=r" (where)); __cli(); - get_irqlock(cpu); + get_irqlock(cpu, where); } void __global_sti(void) @@ -453,11 +806,6 @@ void __global_sti(void) __sti(); } -unsigned long __global_save_flags(void) -{ - return global_irq_holder == (unsigned char) smp_processor_id(); -} - void __global_restore_flags(unsigned long flags) { if (flags & 1) { @@ -472,15 +820,24 @@ void __global_restore_flags(unsigned long flags) } } +#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;} + void irq_enter(int cpu, int irq) { + int stuck = INIT_STUCK; + hardirq_enter(cpu); - barrier(); - while (*((unsigned char *)&global_irq_lock)) { + while (*((volatile unsigned char *)&global_irq_lock)) { if ((unsigned char) cpu == global_irq_holder) printk("irq_enter: Frosted Lucky Charms, " "they're magically delicious!\n"); - barrier(); + STUCK; + membar("#LoadLoad"); } } @@ -492,8 +849,7 @@ void irq_exit(int cpu, int irq) void synchronize_irq(void) { - int cpu = smp_processor_id(); - int local_count = local_irq_count[cpu]; + int local_count = local_irq_count; unsigned long flags; if (local_count != atomic_read(&global_irq_count)) { @@ -506,9 +862,13 @@ void synchronize_irq(void) void report_spurious_ivec(struct pt_regs *regs) { - printk("IVEC: Spurious interrupt vector received at (%016lx)\n", - regs->tpc); - return; + extern unsigned long ivec_spurious_cookie; + static int times = 0; + + printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n", + ivec_spurious_cookie, regs->tpc); + if(times++ > 1) + prom_halt(); } void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs) @@ -547,13 +907,28 @@ void handler_irq(int irq, struct pt_regs *regs) irq_enter(cpu, irq); action = *(irq + irq_action); kstat.interrupts[irq]++; - do { - if(!action || !action->handler) - unexpected_irq(irq, 0, regs); - action->handler(irq, action->dev_id, regs); - if(action->flags & SA_SYSIO_MASKED) - *((unsigned int *)action->mask) = SYSIO_ICLR_IDLE; - } while((action = action->next) != NULL); + if(!action) { + unexpected_irq(irq, 0, regs); + } else { + do { + struct ino_bucket *bucket = NULL; + unsigned int ino = 0; + + if(action->flags & SA_IMAP_MASKED) { + bucket = (struct ino_bucket *)action->mask; + + ino = bucket->ino; + if(!(ivector_to_mask[ino] & 0x80000000)) + continue; + } + + action->handler(irq, action->dev_id, regs); + if(bucket) { + ivector_to_mask[ino] &= ~(0x80000000); + *(bucket->iclr) = SYSIO_ICLR_IDLE; + } + } while((action = action->next) != NULL); + } irq_exit(cpu, irq); } @@ -567,7 +942,7 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) irq_enter(cpu, irq); floppy_interrupt(irq, dev_cookie, regs); - if(action->flags & SA_SYSIO_MASKED) + if(action->flags & SA_IMAP_MASKED) *((unsigned int *)action->mask) = SYSIO_ICLR_IDLE; irq_exit(cpu, irq); } @@ -595,7 +970,7 @@ static void install_fast_irq(unsigned int cpu_irq, insns[0] = SPARC_BRANCH(((unsigned long) handler), ((unsigned long)&insns[0])); insns[1] = SPARC_NOP; - __asm__ __volatile__("flush %0" : : "r" (ttent)); + __asm__ __volatile__("membar #StoreStore; flush %0" : : "r" (ttent)); } int request_fast_irq(unsigned int irq, @@ -605,6 +980,7 @@ int request_fast_irq(unsigned int irq, struct irqaction *action; unsigned long flags; unsigned int cpu_irq, *imap, *iclr; + int ivindex = -1; /* XXX This really is not the way to do it, the "right way" * XXX is to have drivers set SA_SBUS or something like that @@ -616,11 +992,11 @@ int request_fast_irq(unsigned int irq, */ if(irq == 14) return -EINVAL; - cpu_irq = ino_to_pil[irq]; + cpu_irq = sysio_ino_to_pil[irq]; if(!handler) return -EINVAL; - imap = irq_to_imap(irq); + imap = sysio_irq_to_imap(irq); action = *(cpu_irq + irq_action); if(action) { if(action->flags & SA_SHIRQ) @@ -648,12 +1024,12 @@ int request_fast_irq(unsigned int irq, install_fast_irq(cpu_irq, handler); if(imap) { - int ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); - + ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); ivector_to_mask[ivindex] = (1 << cpu_irq); - iclr = imap_to_iclr(imap); + iclr = sysio_imap_to_iclr(imap); action->mask = (unsigned long) iclr; - irqflags |= SA_SYSIO_MASKED; + irqflags |= SA_IMAP_MASKED; + add_ino_hash(ivindex, imap, iclr, irqflags); } else action->mask = 0; @@ -665,7 +1041,9 @@ int request_fast_irq(unsigned int irq, *(cpu_irq + irq_action) = action; - enable_irq(irq); + if(ivindex != -1) + enable_irq(ivindex); + restore_flags(flags); return 0; } @@ -675,31 +1053,27 @@ int request_fast_irq(unsigned int irq, */ unsigned long probe_irq_on(void) { - return 0; + return 0; } int probe_irq_off(unsigned long mask) { - return 0; + return 0; } struct sun5_timer *linux_timers = NULL; -/* This is called from sbus_init() to get the jiffies timer going. - * We need to call this after there exists a valid SBus_chain so - * that the IMAP/ICLR registers can be accessed. - * - * XXX That is because the whole startup sequence is broken. I will - * XXX fix it all up very soon. -DaveM - */ +/* This is gets the master level10 timer going. */ void init_timers(void (*cfunc)(int, void *, struct pt_regs *)) { struct linux_prom64_registers pregs[3]; + struct devid_cookie dcookie; + unsigned int *imap, *iclr; u32 pirqs[2]; int node, err; node = prom_finddevice("/counter-timer"); - if(node == 0) { + if(node == 0 || node == -1) { prom_printf("init_timers: Cannot find counter-timer PROM node.\n"); prom_halt(); } @@ -715,13 +1089,22 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *)) prom_halt(); } linux_timers = (struct sun5_timer *) __va(pregs[0].phys_addr); + iclr = (((unsigned int *)__va(pregs[1].phys_addr))+1); + imap = (((unsigned int *)__va(pregs[2].phys_addr))+1); /* Shut it up first. */ linux_timers->limit0 = 0; /* Register IRQ handler. */ - err = request_irq(pirqs[0] & 0x3f, /* XXX Fix this for big Enterprise XXX */ - cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC), "timer", NULL); + dcookie.real_dev_id = NULL; + dcookie.imap = imap; + dcookie.iclr = iclr; + dcookie.pil = 10; + dcookie.bus_cookie = NULL; + + err = request_irq(pirqs[0], cfunc, + (SA_DCOOKIE | SA_INTERRUPT | SA_STATIC_ALLOC), + "timer", &dcookie); if(err) { prom_printf("Serious problem, cannot register timer interrupt\n"); @@ -825,6 +1208,52 @@ void enable_prom_timer(void) prom_timers->count0 = 0; } +#ifdef __SMP__ +/* Called from smp_commence, when we know how many cpus are in the system + * and can have device IRQ's directed at them. + */ +void distribute_irqs(void) +{ + unsigned long flags; + int cpu, level; + + printk("SMP: redistributing interrupts...\n"); + save_and_cli(flags); + cpu = 0; + for(level = 0; level < NR_IRQS; level++) { + struct irqaction *p = irq_action[level]; + + while(p) { + if(p->flags & SA_IMAP_MASKED) { + struct ino_bucket *bucket = (struct ino_bucket *)p->mask; + unsigned int *imap = bucket->imap; + unsigned int val; + unsigned long tid = linux_cpus[cpu].mid << 9; + + val = *imap; + *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); + + printk("SMP: Redirecting IGN[%x] INO[%x] " + "to cpu %d [%s]\n", + (val & SYSIO_IMAP_IGN) >> 6, + (val & SYSIO_IMAP_INO), cpu, + p->name); + + cpu += 1; + while(!(cpu_present_map & (1UL << cpu))) { + cpu += 1; + if(cpu >= smp_num_cpus) + cpu = 0; + } + } + p = p->next; + } + } + restore_flags(flags); + irqs_have_been_distributed = 1; +} +#endif + __initfunc(void init_IRQ(void)) { int i; diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index afd5af8d0..35d4d606d 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.31 1997/07/24 12:15:05 davem Exp $ +/* $Id: process.c,v 1.42 1997/08/19 14:17:55 jj Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -27,18 +27,21 @@ #include <linux/a.out.h> #include <linux/config.h> #include <linux/reboot.h> +#include <linux/delay.h> #include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/delay.h> #include <asm/processor.h> #include <asm/pstate.h> #include <asm/elf.h> #include <asm/fpumacro.h> +#define PGTCACHE_HIGH_WATER 50 +#define PGTCACHE_LOW_WATER 25 + #ifndef __SMP__ /* @@ -53,6 +56,16 @@ asmlinkage int sys_idle(void) current->priority = -100; current->counter = -100; for (;;) { + if(pgtable_cache_size > PGTCACHE_LOW_WATER) { + do { + if(pgd_quicklist) + free_page((unsigned long) get_pgd_fast()); + if(pmd_quicklist) + free_page((unsigned long) get_pmd_fast()); + if(pte_quicklist) + free_page((unsigned long) get_pte_fast()); + } while(pgtable_cache_size > PGTCACHE_HIGH_WATER); + } run_task_queue(&tq_scheduler); schedule(); } @@ -68,13 +81,26 @@ asmlinkage int cpu_idle(void) { current->priority = -100; while(1) { + if(pgtable_cache_size > PGTCACHE_LOW_WATER) { + do { + if(pgd_quicklist) + free_page((unsigned long) get_pgd_fast()); + if(pmd_quicklist) + free_page((unsigned long) get_pmd_fast()); + if(pte_quicklist) + free_page((unsigned long) get_pte_fast()); + } while(pgtable_cache_size > PGTCACHE_HIGH_WATER); + } if(tq_scheduler) { lock_kernel(); run_task_queue(&tq_scheduler); unlock_kernel(); } + barrier(); current->counter = -100; - schedule(); + if(resched_needed()) + schedule(); + barrier(); } } @@ -251,8 +277,20 @@ void show_stackframe32(struct sparc_stackf32 *sf) } while ((size -= sizeof(unsigned))); } -void show_regs(struct pt_regs * regs) +#ifdef __SMP__ +static spinlock_t regdump_lock = SPIN_LOCK_UNLOCKED; +#endif + +void __show_regs(struct pt_regs * regs) { +#ifdef __SMP__ + unsigned long flags; + + spin_lock_irqsave(®dump_lock, flags); + printk("CPU[%d]: local_irq_count[%ld] global_irq_count[%d]\n", + smp_processor_id(), local_irq_count, + atomic_read(&global_irq_count)); +#endif printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x\n", regs->tstate, regs->tpc, regs->tnpc, regs->y); printk("g0: %016lx g1: %016lx g2: %016lx g3: %016lx\n", @@ -268,6 +306,21 @@ void show_regs(struct pt_regs * regs) regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], regs->u_regs[15]); show_regwindow(regs); +#ifdef __SMP__ + spin_unlock_irqrestore(®dump_lock, flags); +#endif +} + +void show_regs(struct pt_regs *regs) +{ + __show_regs(regs); +#ifdef __SMP__ + { + extern void smp_report_regs(void); + + smp_report_regs(); + } +#endif } void show_regs32(struct pt_regs32 *regs) @@ -332,17 +385,22 @@ void flush_thread(void) /* No new signal delivery by default. */ current->tss.new_signal = 0; - current->flags &= ~PF_USEDFPU; + current->tss.flags &= ~(SPARC_FLAG_USEDFPU | SPARC_FLAG_USEDFPUL | + SPARC_FLAG_USEDFPUU); /* Now, this task is no longer a kernel thread. */ current->tss.current_ds = USER_DS; if(current->tss.flags & SPARC_FLAG_KTHREAD) { + extern spinlock_t scheduler_lock; + current->tss.flags &= ~SPARC_FLAG_KTHREAD; /* exec_mmap() set context to NO_CONTEXT, here is * where we grab a new one. */ + spin_lock(&scheduler_lock); get_mmu_context(current); + spin_unlock(&scheduler_lock); } current->tss.ctx = current->mm->context & 0x1fff; spitfire_set_secondary_context (current->tss.ctx); @@ -437,10 +495,14 @@ void fault_in_user_windows(struct pt_regs *regs) struct reg_window *rwin = &tp->reg_window[window]; if(copy_to_user((char *)sp, rwin, winsize)) - do_exit(SIGILL); + goto barf; } while(window--); } current->tss.w_saved = 0; + return; +barf: + lock_kernel(); + do_exit(SIGILL); } /* Copy a Sparc thread. The fork() return value conventions @@ -483,7 +545,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8; #endif p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window)); - p->tss.cwp = regs->u_regs[UREG_G0]; + p->tss.cwp = (regs->tstate + 1) & TSTATE_CWP; if(regs->tstate & TSTATE_PRIV) { p->tss.kregs->u_regs[UREG_FP] = p->tss.ksp; p->tss.flags |= SPARC_FLAG_KTHREAD; @@ -581,6 +643,7 @@ asmlinkage int sparc_execve(struct pt_regs *regs) if(!error) { fprs_write(0); regs->fprs = 0; + regs->tstate &= ~TSTATE_PEF; } out: unlock_kernel(); diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c new file mode 100644 index 000000000..8aa1c342b --- /dev/null +++ b/arch/sparc64/kernel/psycho.c @@ -0,0 +1,1417 @@ +/* $Id: psycho.c,v 1.22 1997/08/31 03:51:40 davem Exp $ + * psycho.c: Ultra/AX U2P PCI controller support. + * + * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <asm/ebus.h> +#include <asm/sbus.h> /* for sanity check... */ + +#ifndef CONFIG_PCI + +int pcibios_present(void) +{ + return 0; +} + +asmlinkage int sys_pciconfig_read(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf) +{ + return 0; +} + +asmlinkage int sys_pciconfig_write(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf) +{ + return 0; +} + +#else + +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/bios32.h> +#include <linux/pci.h> + +#include <asm/io.h> +#include <asm/oplib.h> +#include <asm/pbm.h> +#include <asm/uaccess.h> + +struct linux_psycho *psycho_root = NULL; + +/* This is used to make the scan_bus in the generic PCI code be + * a nop, as we need to control the actual bus probing sequence. + * After that we leave it on of course. + */ +static int pci_probe_enable = 0; + +static inline unsigned long long_align(unsigned long addr) +{ + return ((addr + (sizeof(unsigned long) - 1)) & + ~(sizeof(unsigned long) - 1)); +} + +static unsigned long psycho_iommu_init(struct linux_psycho *psycho, + unsigned long memory_start) +{ + unsigned long tsbbase = PAGE_ALIGN(memory_start); + unsigned long control, i; + unsigned long *iopte; + + memory_start = (tsbbase + ((32 * 1024) * 8)); + iopte = (unsigned long *)tsbbase; + + for(i = 0; i < (65536 / 2); i++) { + *iopte = (IOPTE_VALID | IOPTE_64K | + IOPTE_CACHE | IOPTE_WRITE); + *iopte |= (i << 16); + iopte++; + } + + psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase); + + control = psycho->psycho_regs->iommu_control; + control &= ~(IOMMU_CTRL_TSBSZ); + control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); + psycho->psycho_regs->iommu_control = control; + + return memory_start; +} + +extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm); + +unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) +{ + struct linux_prom64_registers pr_regs[3]; + char namebuf[128]; + u32 portid; + int node; + + printk("PSYCHO: Probing for controllers.\n"); + + memory_start = long_align(memory_start); + node = prom_getchild(prom_root_node); + while((node = prom_searchsiblings(node, "pci")) != 0) { + struct linux_psycho *psycho = (struct linux_psycho *)memory_start; + struct linux_psycho *search; + struct linux_pbm_info *pbm = NULL; + u32 busrange[2]; + int err, is_pbm_a; + + portid = prom_getintdefault(node, "upa-portid", 0xff); + for(search = psycho_root; search; search = search->next) { + if(search->upa_portid == portid) { + psycho = search; + + /* This represents _this_ instance, so it's + * which ever one does _not_ have the prom node + * info filled in yet. + */ + is_pbm_a = (psycho->pbm_A.prom_node == 0); + goto other_pbm; + } + } + + memory_start = long_align(memory_start + sizeof(struct linux_psycho)); + + memset(psycho, 0, sizeof(*psycho)); + + psycho->next = psycho_root; + psycho_root = psycho; + + psycho->upa_portid = portid; + + /* Map in PSYCHO register set and report the presence of this PSYCHO. */ + err = prom_getproperty(node, "reg", + (char *)&pr_regs[0], sizeof(pr_regs)); + if(err == 0 || err == -1) { + prom_printf("PSYCHO: Error, cannot get U2P registers " + "from PROM.\n"); + prom_halt(); + } + + /* Third REG in property is base of entire PSYCHO register space. */ + psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), + NULL, sizeof(struct psycho_regs), + "PSYCHO Registers", + (pr_regs[2].phys_addr >> 32), 0); + if(psycho->psycho_regs == NULL) { + prom_printf("PSYCHO: Error, cannot map PSYCHO " + "main registers.\n"); + prom_halt(); + } + + printk("PSYCHO: Found controller, main regs at %p\n", + psycho->psycho_regs); +#if 0 + printk("PSYCHO: Interrupt retry [%016lx]\n", + psycho->psycho_regs->irq_retry); +#endif + psycho->psycho_regs->irq_retry = 0xff; + + /* Now map in PCI config space for entire PSYCHO. */ + psycho->pci_config_space = + sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000), + NULL, 0x01000000, + "PCI Config Space", + (pr_regs[2].phys_addr >> 32), 0); + if(psycho->pci_config_space == NULL) { + prom_printf("PSYCHO: Error, cannot map PCI config space.\n"); + prom_halt(); + } + + /* Report some more info. */ + printk("PSYCHO: PCI config space at %p\n", psycho->pci_config_space); + + memory_start = psycho_iommu_init(psycho, memory_start); + + 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; + + other_pbm: + if(is_pbm_a) + pbm = &psycho->pbm_A; + else + pbm = &psycho->pbm_B; + + pbm->parent = psycho; + pbm->IO_assignments = NULL; + pbm->MEM_assignments = NULL; + pbm->prom_node = node; + + prom_getstring(node, "name", namebuf, sizeof(namebuf)); + strcpy(pbm->prom_name, namebuf); + + /* Now the ranges. */ + prom_pbm_ranges_init(node, pbm); + + /* Finally grab the pci bus root array for this pbm after + * having found the bus range existing under it. + */ + err = prom_getproperty(node, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if(err == 0 || err == -1) { + prom_printf("PSYCHO: Error, cannot get PCI bus range.\n"); + prom_halt(); + } + pbm->pci_first_busno = busrange[0]; + pbm->pci_last_busno = busrange[1]; + memset(&pbm->pci_bus, 0, sizeof(struct pci_bus)); + + node = prom_getsibling(node); + if(!node) + break; + } + + /* Last minute sanity check. */ + if(psycho_root == NULL && SBus_chain == NULL) { + prom_printf("Fatal error, neither SBUS nor PCI bus found.\n"); + prom_halt(); + } + + return memory_start; +} + +int pcibios_present(void) +{ + return psycho_root != NULL; +} + +int pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, + unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->vendor == vendor && dev->device == device_id) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class == class_code) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm, + unsigned long start, + int io) +{ + struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments); + + while(vp) { + if(vp->end > start) + break; + vp = vp->next; + } + return vp; +} + +static inline void pci_add_vma(struct linux_pbm_info *pbm, struct pci_vma *new, int io) +{ + struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments); + + if(!vp) { + new->next = NULL; + if(io) + pbm->IO_assignments = new; + else + pbm->MEM_assignments = new; + } else { + struct pci_vma *prev = NULL; + + while(vp && (vp->end < new->end)) { + prev = vp; + vp = vp->next; + } + new->next = vp; + if(!prev) { + if(io) + pbm->IO_assignments = new; + else + pbm->MEM_assignments = new; + } else { + prev->next = new; + } + + /* Check for programming errors. */ + if(vp && + ((vp->start >= new->start && vp->start < new->end) || + ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) { + prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n", + io ? "IO" : "MEM"); + prom_printf("pci_add_vma: vp[%016lx:%016lx] " + "new[%016lx:%016lx]\n", + vp->start, vp->end, + new->start, new->end); + } + } +} + +static unsigned long *pci_alloc_arena = NULL; + +static inline void pci_init_alloc_init(unsigned long *mstart) +{ + pci_alloc_arena = mstart; +} + +static inline void pci_init_alloc_fini(void) +{ + pci_alloc_arena = NULL; +} + +static void *pci_init_alloc(int size) +{ + unsigned long start = long_align(*pci_alloc_arena); + void *mp = (void *)start; + + if(!pci_alloc_arena) { + prom_printf("pci_init_alloc: pci_vma arena not init'd\n"); + prom_halt(); + } + start += size; + *pci_alloc_arena = start; + return mp; +} + +static inline struct pci_vma *pci_vma_alloc(void) +{ + return pci_init_alloc(sizeof(struct pci_vma)); +} + +static inline struct pcidev_cookie *pci_devcookie_alloc(void) +{ + return pci_init_alloc(sizeof(struct pcidev_cookie)); +} + +static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) +{ + struct pci_bus *pbus = &pbm->pci_bus; + + /* PSYCHO PBM's include child PCI bridges in bus-range property, + * but we don't scan each of those ourselves, Linux generic PCI + * probing code will find child bridges and link them into this + * pbm's root PCI device hierarchy. + */ + pbus->number = pbm->pci_first_busno; + pbus->sysdata = pbm; + pbus->subordinate = pci_scan_bus(pbus, mstart); +} + +static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + int node) +{ + struct linux_prom_pci_registers pregs[PROMREG_MAX]; + int err; + + while(node) { + int child; + + child = prom_getchild(node); + if(child != 0 && child != -1) { + int res; + + res = pdev_to_pnode_sibtraverse(pbm, pdev, child); + if(res != 0 && res != -1) + return res; + } + err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs)); + if(err != 0 && err != -1) { + u32 devfn = (pregs[0].phys_hi >> 8) & 0xff; + + if(devfn == pdev->devfn) + return node; /* Match */ + } + + node = prom_getsibling(node); + } + return 0; +} + +static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev) +{ + struct pcidev_cookie *pcp; + int node = prom_getchild(pbm->prom_node); + + node = pdev_to_pnode_sibtraverse(pbm, pdev, node); + if(node == 0) + node = -1; + pcp = pci_devcookie_alloc(); + pcp->pbm = pbm; + pcp->prom_node = node; + pdev->sysdata = pcp; +} + +static void fill_in_pbm_cookies(struct linux_pbm_info *pbm) +{ + struct pci_bus *pbtmp, *pbus = &pbm->pci_bus; + struct pci_dev *pdev; + + for(pbtmp = pbus->children; pbtmp; pbtmp = pbtmp->children) + pbtmp->sysdata = pbm; + + for( ; pbus; pbus = pbus->children) + for(pdev = pbus->devices; pdev; pdev = pdev->sibling) + pdev_cookie_fillin(pbm, pdev); +} + +/* #define RECORD_ASSIGNMENTS_DEBUG */ + +/* Walk PROM device tree under PBM, looking for 'assigned-address' + * properties, and recording them in pci_vma's linked in via + * PBM->assignments. + */ +static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs) +{ + struct linux_prom_ebus_ranges erng[PROMREG_MAX]; + int err, iter; + + err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng)); + if(err == 0 || err == -1) { + prom_printf("EBUS: fatal error, no range property.\n"); + prom_halt(); + } + err = (err / sizeof(struct linux_prom_ebus_ranges)); + for(iter = 0; iter < err; iter++) { + struct linux_prom_ebus_ranges *ep = &erng[iter]; + struct linux_prom_pci_registers *ap = &aregs[iter]; + + ap->phys_hi = ep->parent_phys_hi; + ap->phys_mid = ep->parent_phys_mid; + ap->phys_lo = ep->parent_phys_lo; + } + return err; +} + +static void assignment_process(struct linux_pbm_info *pbm, int node) +{ + struct linux_prom_pci_registers aregs[PROMREG_MAX]; + char pname[256]; + int err, iter, numa; + + err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname)); + if(strncmp(pname, "ebus", 4) == 0) { + numa = gimme_ebus_assignments(node, &aregs[0]); + } else { + err = prom_getproperty(node, "assigned-addresses", + (char *)&aregs[0], sizeof(aregs)); + + /* No assignments, nothing to do. */ + if(err == 0 || err == -1) + return; + + numa = (err / sizeof(struct linux_prom_pci_ranges)); + } + + for(iter = 0; iter < numa; iter++) { + struct linux_prom_pci_registers *ap = &aregs[iter]; + struct pci_vma *vp; + int space, breg, io; + + space = (ap->phys_hi >> 24) & 3; + if(space != 1 && space != 2) + continue; + io = (space == 1); + + breg = (ap->phys_hi & 0xff); + if(breg == PCI_ROM_ADDRESS) + continue; + + vp = pci_vma_alloc(); + + /* XXX Means we don't support > 32-bit range of + * XXX PCI MEM space, PSYCHO/PBM does not support it + * XXX either due to it's layout so... + */ + vp->start = ap->phys_lo; + vp->end = vp->start + ap->size_lo; + vp->base_reg = breg; + + /* Sanity */ + if(io && (vp->end & ~(0xffff))) { + prom_printf("assignment_process: Out of range PCI I/O " + "[%08lx:%08lx]\n", vp->start, vp->end); + prom_halt(); + } + + pci_add_vma(pbm, vp, io); + } +} + +static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node) +{ + while(node) { + int child = prom_getchild(node); + if(child) + assignment_walk_siblings(pbm, child); + + assignment_process(pbm, node); + + node = prom_getsibling(node); + } +} + +static void record_assignments(struct linux_pbm_info *pbm) +{ + assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node)); +} + +/* #define FIXUP_REGS_DEBUG */ + +static void fixup_regs(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + struct linux_prom_pci_registers *assigned, + int numaa) +{ + int preg, rng; + int IO_seen = 0; + int MEM_seen = 0; + + for(preg = 0; preg < nregs; preg++) { + struct linux_prom_pci_registers *ap = NULL; + int bustype = (pregs[preg].phys_hi >> 24) & 0x3; + int bsreg, brindex; + u64 pci_addr; + + if(bustype == 0) { + /* Config space cookie, nothing to do. */ + if(preg != 0) + prom_printf("fixup_doit: strange, config space not 0\n"); + continue; + } else if(bustype == 3) { + /* XXX add support for this... */ + prom_printf("fixup_doit: Warning, ignoring 64-bit PCI " + "memory space, tell DaveM.\n"); + continue; + } + bsreg = (pregs[preg].phys_hi & 0xff); + + /* We can safely ignore these. */ + if(bsreg == PCI_ROM_ADDRESS) + continue; + + /* Sanity */ + if((bsreg < PCI_BASE_ADDRESS_0) || + (bsreg > (PCI_BASE_ADDRESS_5 + 4)) || + (bsreg & 3)) { + prom_printf("fixup_doit: Warning, ignoring bogus basereg [%x]\n", + bsreg); + continue; + } + + brindex = (bsreg - PCI_BASE_ADDRESS_0) >> 2; + if(numaa) { + int r; + + for(r = 0; r < numaa; r++) { + int abreg; + + abreg = (assigned[r].phys_hi & 0xff); + if(abreg == bsreg) { + ap = &assigned[r]; + break; + } + } + } + + /* Now construct UPA physical address. */ + pci_addr = (((u64)pregs[preg].phys_mid) << 32UL); + pci_addr |= (((u64)pregs[preg].phys_lo)); + + if(ap) { + pci_addr += ((u64)ap->phys_lo); + pci_addr += (((u64)ap->phys_mid) << 32UL); + } + + /* Final step, apply PBM range. */ + for(rng = 0; rng < pbm->num_pbm_ranges; rng++) { + struct linux_prom_pci_ranges *rp = &pbm->pbm_ranges[rng]; + int space = (rp->child_phys_hi >> 24) & 3; + + if(space == bustype) { + pci_addr += ((u64)rp->parent_phys_lo); + pci_addr += (((u64)rp->parent_phys_hi) << 32UL); + break; + } + } + if(rng == pbm->num_pbm_ranges) { + /* AIEEE */ + prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n"); + } + pdev->base_address[brindex] = (unsigned long)__va(pci_addr); + + /* Preserve I/O space bit. */ + if(bustype == 0x1) { + pdev->base_address[brindex] |= 1; + IO_seen = 1; + } else { + MEM_seen = 1; + } + } + + /* Now handle assignments PROM did not take care of. */ + if(nregs) { + int breg; + + for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) { + unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2); + unsigned int base = (unsigned int)pdev->base_address[ridx]; + struct pci_vma *vp; + u64 pci_addr; + int io; + + if(pdev->base_address[ridx] > PAGE_OFFSET) + continue; + + io = (base & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO; + base &= ~((io ? + PCI_BASE_ADDRESS_IO_MASK : + PCI_BASE_ADDRESS_MEM_MASK)); + vp = pci_find_vma(pbm, base, io); + if(!vp || vp->start > base) { + unsigned int size, new_base; + + pcibios_read_config_dword(pdev->bus->number, + pdev->devfn, + breg, &rtmp); + pcibios_write_config_dword(pdev->bus->number, + pdev->devfn, + breg, 0xffffffff); + pcibios_read_config_dword(pdev->bus->number, + pdev->devfn, + breg, &size); + if(io) + size &= ~1; + size = (~(size) + 1); + if(!size) + continue; + + new_base = 0; + for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) { + if(!vp || new_base + size <= vp->start) + break; + new_base = (vp->end + (size - 1)) & ~(size-1); + } + if(vp && (new_base + size > vp->start)) { + prom_printf("PCI: Impossible full %s space.\n", + (io ? "IO" : "MEM")); + prom_halt(); + } + vp = pci_vma_alloc(); + vp->start = new_base; + vp->end = vp->start + size; + vp->base_reg = breg; + + /* Sanity */ + if(io && vp->end & ~(0xffff)) { + prom_printf("PCI: Out of range PCI I/O " + "[%08lx:%08lx] during fixup\n", + vp->start, vp->end); + prom_halt(); + } + pci_add_vma(pbm, vp, io); + + rtmp = new_base; + if(io) + rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK); + else + rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK); + pcibios_write_config_dword(pdev->bus->number, + pdev->devfn, + breg, rtmp); + + /* Apply PBM ranges and update pci_dev. */ + pci_addr = new_base; + for(rng = 0; rng < pbm->num_pbm_ranges; rng++) { + struct linux_prom_pci_ranges *rp; + int rspace; + + rp = &pbm->pbm_ranges[rng]; + rspace = (rp->child_phys_hi >> 24) & 3; + if(io && rspace != 1) + continue; + else if(!io && rspace != 2) + continue; + pci_addr += ((u64)rp->parent_phys_lo); + pci_addr += (((u64)rp->parent_phys_hi)<<32UL); + break; + } + if(rng == pbm->num_pbm_ranges) { + /* AIEEE */ + prom_printf("fixup_doit: YIEEE, cannot find " + "PBM ranges\n"); + } + pdev->base_address[ridx] = (unsigned long)__va(pci_addr); + + /* Preserve I/O space bit. */ + if(io) { + pdev->base_address[ridx] |= 1; + IO_seen = 1; + } else { + MEM_seen = 1; + } + } + } + } + if(IO_seen || MEM_seen) { + unsigned int l; + + pcibios_read_config_dword(pdev->bus->number, + pdev->devfn, + PCI_COMMAND, &l); +#ifdef FIXUP_REGS_DEBUG + prom_printf("["); +#endif + if(IO_seen) { +#ifdef FIXUP_REGS_DEBUG + prom_printf("IO "); +#endif + l |= PCI_COMMAND_IO; + } + if(MEM_seen) { +#ifdef FIXUP_REGS_DEBUG + prom_printf("MEM"); +#endif + l |= PCI_COMMAND_MEMORY; + } +#ifdef FIXUP_REGS_DEBUG + prom_printf("]"); +#endif + pcibios_write_config_dword(pdev->bus->number, + pdev->devfn, + PCI_COMMAND, l); + } + +#ifdef FIXUP_REGS_DEBUG + prom_printf("REG_FIXUP[%s]: ", pci_strdev(pdev->vendor, pdev->device)); + for(preg = 0; preg < 6; preg++) { + if(pdev->base_address[preg] != 0) + prom_printf("%d[%016lx] ", preg, pdev->base_address[preg]); + } + prom_printf("\n"); +#endif +} + +#define imap_offset(__member) \ + ((unsigned long)(&(((struct psycho_regs *)0)->__member))) + +static unsigned long psycho_pcislot_imap_offset(unsigned long ino) +{ + unsigned int bus, slot; + + bus = (ino & 0x10) >> 4; + slot = (ino & 0x0c) >> 2; + + if(bus == 0) { + /* Perform a sanity check, we might as well. + * PBM A only has 2 PCI slots. + */ + if(slot > 1) { + prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot); + prom_halt(); + } + if(slot == 0) + return imap_offset(imap_a_slot0); + else + return imap_offset(imap_a_slot1); + } else { + switch(slot) { + case 0: + return imap_offset(imap_b_slot0); + case 1: + return imap_offset(imap_b_slot1); + 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 0; /* Make gcc happy */ + }; + } +} + +/* Exported for EBUS probing layer. */ +unsigned int psycho_irq_build(unsigned int full_ino) +{ + unsigned long imap_off, ign, ino; + + ign = (full_ino & PSYCHO_IMAP_IGN) >> 6; + ino = (full_ino & PSYCHO_IMAP_INO); + + /* Compute IMAP register offset, generic IRQ layer figures out + * the ICLR register address as this is simple given the 32-bit + * irq number and IMAP register address. + */ + if((ino & 0x20) == 0) + imap_off = psycho_pcislot_imap_offset(ino); + else { + switch(ino) { + case 0x20: + /* Onboard SCSI. */ + imap_off = imap_offset(imap_scsi); + break; + + case 0x21: + /* Onboard Ethernet (ie. CheerIO/HME) */ + imap_off = imap_offset(imap_eth); + break; + + case 0x22: + /* Onboard Parallel Port */ + imap_off = imap_offset(imap_bpp); + break; + + case 0x23: + /* Audio Record */ + imap_off = imap_offset(imap_au_rec); + break; + + case 0x24: + /* Audio Play */ + imap_off = imap_offset(imap_au_play); + break; + + case 0x25: + /* Power Fail */ + imap_off = imap_offset(imap_pfail); + break; + + case 0x26: + /* Onboard KBD/MOUSE/SERIAL */ + imap_off = imap_offset(imap_kms); + break; + + case 0x27: + /* Floppy (ie. fdthree) */ + imap_off = imap_offset(imap_flpy); + break; + + case 0x28: + /* Spare HW INT */ + imap_off = imap_offset(imap_shw); + break; + + case 0x29: + /* Onboard Keyboard (only) */ + imap_off = imap_offset(imap_kbd); + break; + + case 0x2a: + /* Onboard Mouse (only) */ + imap_off = imap_offset(imap_ms); + break; + + case 0x2b: + /* Onboard Serial (only) */ + imap_off = imap_offset(imap_ser); + break; + + case 0x32: + /* Power Management */ + imap_off = imap_offset(imap_pmgmt); + break; + + default: + /* We don't expect anything else. The other possible + * values are not found in PCI device nodes, and are + * so hardware specific that they should use DCOOKIE's + * anyways. + */ + prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino); + prom_halt(); + }; + } + imap_off -= imap_offset(imap_a_slot0); + + return pci_irq_encode(imap_off, 0 /* XXX */, ign, ino); +} + +/* #define FIXUP_IRQ_DEBUG */ + +static void fixup_irq(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + int node) +{ + unsigned int prom_irq, portid = pbm->parent->upa_portid; + unsigned char pci_irq_line = pdev->irq; + int err; + +#ifdef FIXUP_IRQ_DEBUG + printk("fixup_irq[%s:%s]: ", + pci_strvendor(pdev->vendor), + pci_strdev(pdev->vendor, pdev->device)); +#endif + err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq)); + if(err == 0 || err == -1) { + prom_printf("fixup_irq: No interrupts property for dev[%s:%s]\n", + pci_strvendor(pdev->vendor), + pci_strdev(pdev->vendor, pdev->device)); + prom_halt(); + } + + /* See if fully specified already (ie. for onboard devices like hme) */ + if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) { + pdev->irq = psycho_irq_build(prom_irq); +#ifdef FIXUP_IRQ_DEBUG + printk("fully specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); +#endif + } else { + unsigned int bus, slot, line; + + bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0; + line = (pci_irq_line) & 3; + + /* Slot determination is only slightly complex. Handle + * the easy case first. + */ + if(pdev->bus->number == pbm->pci_first_busno) { + if(pbm == &pbm->parent->pbm_A) + slot = (pdev->devfn >> 3) - 1; + else + slot = ((pdev->devfn >> 3) >> 1) - 1; + } else { + /* Underneath a bridge, use slot number of parent + * bridge. + */ + slot = (pdev->bus->self->devfn >> 3) - 1; + + /* Use low slot number bits of child as IRQ line. */ + line = ((pdev->devfn >> 3) & 3); + } + slot = (slot << 2); + + pdev->irq = psycho_irq_build((((portid << 6) & PSYCHO_IMAP_IGN) | + (bus | slot | line))); +#ifdef FIXUP_IRQ_DEBUG + do { + unsigned char iline, ipin; + + (void)pcibios_read_config_byte(pdev->bus->number, + pdev->devfn, + PCI_INTERRUPT_PIN, + &ipin); + (void)pcibios_read_config_byte(pdev->bus->number, + pdev->devfn, + PCI_INTERRUPT_LINE, + &iline); + printk("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] " + "iline[%x] ipin[%x] prom_irq[%x]", + portid, bus>>4, slot>>2, line, pdev->irq, + iline, ipin, prom_irq); + } while(0); +#endif + } +#ifdef FIXUP_IRQ_DEBUG + printk("\n"); +#endif +} + +static void fixup_doit(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + int node) +{ + struct linux_prom_pci_registers assigned[PROMREG_MAX]; + int numaa, err; + + /* Get assigned addresses, if any. */ + err = prom_getproperty(node, "assigned-addresses", + (char *)&assigned[0], sizeof(assigned)); + if(err == 0 || err == -1) + numaa = 0; + else + numaa = (err / sizeof(struct linux_prom_pci_registers)); + + /* First, scan and fixup base registers. */ + fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa); + + /* Next, fixup interrupt numbers. */ + fixup_irq(pdev, pbm, node); +} + +static void fixup_pci_dev(struct pci_dev *pdev, + struct pci_bus *pbus, + struct linux_pbm_info *pbm) +{ + struct linux_prom_pci_registers pregs[PROMREG_MAX]; + struct pcidev_cookie *pcp = pdev->sysdata; + int node, nregs, err; + + /* If this is a PCI bridge, we must program it. */ + if(pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) { + unsigned short cmd; + + /* First, enable bus mastering. */ + pcibios_read_config_word(pdev->bus->number, + pdev->devfn, + PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pdev->bus->number, + pdev->devfn, + PCI_COMMAND, cmd); + + /* Now, set cache line size to 64-bytes. */ + pcibios_write_config_byte(pdev->bus->number, + pdev->devfn, + PCI_CACHE_LINE_SIZE, 64); + } + + /* Ignore if this is one of the PBM's, EBUS, or a + * sub-bridge underneath the PBM. We only need to fixup + * true devices. + */ + if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) || + (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) || + (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) || + (pcp == NULL)) + return; + + node = pcp->prom_node; + + err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs)); + if(err == 0 || err == -1) { + prom_printf("Cannot find REG for pci_dev\n"); + prom_halt(); + } + + nregs = (err / sizeof(pregs[0])); + + fixup_doit(pdev, pbm, &pregs[0], nregs, node); +} + +static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm) +{ + struct pci_dev *pdev; + + for(pdev = pbus->devices; pdev; pdev = pdev->sibling) + fixup_pci_dev(pdev, pbus, pbm); + + for(pbus = pbus->children; pbus; pbus = pbus->children) + fixup_pci_bus(pbus, pbm); +} + +static void fixup_addr_irq(struct linux_pbm_info *pbm) +{ + struct pci_bus *pbus = &pbm->pci_bus; + + /* Work through top level devices (not bridges, those and their + * devices are handled specially in the next loop). + */ + fixup_pci_bus(pbus, pbm); +} + +/* Walk all PCI devices probes, fixing up base registers and IRQ registers. + * We use OBP for most of this work. + */ +static void psycho_final_fixup(struct linux_psycho *psycho) +{ + /* Second, fixup base address registers and IRQ lines... */ + fixup_addr_irq(&psycho->pbm_A); + fixup_addr_irq(&psycho->pbm_B); + +#if 0 + prom_halt(); +#endif +} + +unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end) +{ + struct linux_psycho *psycho = psycho_root; + + pci_probe_enable = 1; + + /* XXX Really this should be per-PSYCHO, but the config space + * XXX reads and writes give us no way to know which PSYCHO + * XXX in which the config space reads should occur. + * XXX + * XXX Further thought says that we cannot change this generic + * XXX interface, else we'd break xfree86 and other parts of the + * XXX kernel (but whats more important is breaking userland for + * XXX the ix86/Alpha/etc. people). So we should define our own + * XXX internal extension initially, we can compile our own user + * XXX apps that need to get at PCI configuration space. + */ + + /* Probe busses under PBM A. */ + pbm_probe(&psycho->pbm_A, &memory_start); + + /* Probe busses under PBM B. */ + pbm_probe(&psycho->pbm_B, &memory_start); + + pci_init_alloc_init(&memory_start); + + /* Walk all PCI devices found. For each device, and + * PCI bridge which is not one of the PSYCHO PBM's, fill in the + * sysdata with a pointer to the PBM (for pci_bus's) or + * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's). + */ + fill_in_pbm_cookies(&psycho->pbm_A); + fill_in_pbm_cookies(&psycho->pbm_B); + + /* See what OBP has taken care of already. */ + record_assignments(&psycho->pbm_A); + record_assignments(&psycho->pbm_B); + + /* Now, fix it all up. */ + psycho_final_fixup(psycho); + + pci_init_alloc_fini(); + + return ebus_init(memory_start, memory_end); +} + +/* "PCI: The emerging standard..." 8-( */ +volatile int pci_poke_in_progress = 0; +volatile int pci_poke_faulted = 0; + +/* XXX Current PCI support code is broken, it assumes one master PCI config + * XXX space exists, on Ultra we can have many of them, especially with + * XXX 'dual-pci' boards on Sunfire/Starfire/Wildfire. + */ +static char *pci_mkaddr(unsigned char bus, unsigned char device_fn, + unsigned char where) +{ + unsigned long ret = (unsigned long) psycho_root->pci_config_space; + + ret |= (1 << 24); + ret |= ((bus & 0xff) << 16); + ret |= ((device_fn & 0xff) << 8); + ret |= (where & 0xfc); + return (unsigned char *)ret; +} + +static inline int out_of_range(unsigned char bus, unsigned char device_fn) +{ + return ((bus == 0 && PCI_SLOT(device_fn) > 4) || + (bus == 1 && PCI_SLOT(device_fn) > 6) || + (pci_probe_enable == 0)); +} + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned char *addr = pci_mkaddr(bus, device_fn, where); + unsigned int word, trapped; + + *value = 0xff; + + if(out_of_range(bus, device_fn)) + return PCIBIOS_SUCCESSFUL; + + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "lduwa [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (word) + : "r" (addr), "i" (ASI_PL)); + pci_poke_in_progress = 0; + trapped = pci_poke_faulted; + pci_poke_faulted = 0; + if(!trapped) { + switch(where & 3) { + case 0: + *value = word & 0xff; + break; + case 1: + *value = (word >> 8) & 0xff; + break; + case 2: + *value = (word >> 16) & 0xff; + break; + case 3: + *value = (word >> 24) & 0xff; + break; + }; + } + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where); + unsigned int word, trapped; + + *value = 0xffff; + + if(out_of_range(bus, device_fn)) + return PCIBIOS_SUCCESSFUL; + + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "lduwa [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (word) + : "r" (addr), "i" (ASI_PL)); + pci_poke_in_progress = 0; + trapped = pci_poke_faulted; + pci_poke_faulted = 0; + if(!trapped) { + switch(where & 3) { + case 0: + *value = word & 0xffff; + break; + case 2: + *value = (word >> 16) & 0xffff; + break; + default: + printk("pcibios_read_config_word: misaligned " + "reg [%x]\n", where); + break; + }; + } + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where); + unsigned int word, trapped; + + *value = 0xffffffff; + + if(out_of_range(bus, device_fn)) + return PCIBIOS_SUCCESSFUL; + + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "lduwa [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (word) + : "r" (addr), "i" (ASI_PL)); + pci_poke_in_progress = 0; + trapped = pci_poke_faulted; + pci_poke_faulted = 0; + if(!trapped) + *value = word; + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned char *addr = pci_mkaddr(bus, device_fn, where); + + if(out_of_range(bus, device_fn)) + return PCIBIOS_SUCCESSFUL; + + pci_poke_in_progress = 1; + + /* Endianness doesn't matter but we have to get the memory + * barriers in there so... + */ + __asm__ __volatile__("membar #Sync\n\t" + "stba %0, [%1] %2\n\t" + "membar #Sync\n\t" + : /* no outputs */ + : "r" (value), "r" (addr), "i" (ASI_PL)); + pci_poke_in_progress = 0; + + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where); + + if(out_of_range(bus, device_fn)) + return PCIBIOS_SUCCESSFUL; + + pci_poke_in_progress = 1; + __asm__ __volatile__("membar #Sync\n\t" + "stha %0, [%1] %2\n\t" + "membar #Sync\n\t" + : /* no outputs */ + : "r" (value), "r" (addr), "i" (ASI_PL)); + pci_poke_in_progress = 0; + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where); + + if(out_of_range(bus, device_fn)) + return PCIBIOS_SUCCESSFUL; + + pci_poke_in_progress = 1; + __asm__ __volatile__("membar #Sync\n\t" + "stwa %0, [%1] %2\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (value), "r" (addr), "i" (ASI_PL)); + pci_poke_in_progress = 0; + return PCIBIOS_SUCCESSFUL; +} + +asmlinkage int sys_pciconfig_read(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf) +{ + unsigned char ubyte; + unsigned short ushort; + unsigned int uint; + int err = 0; + + lock_kernel(); + switch(len) { + case 1: + pcibios_read_config_byte(bus, dfn, off, &ubyte); + put_user(ubyte, buf); + break; + case 2: + pcibios_read_config_word(bus, dfn, off, &ushort); + put_user(ushort, buf); + break; + case 4: + pcibios_read_config_dword(bus, dfn, off, &uint); + put_user(uint, buf); + break; + + default: + err = -EINVAL; + break; + }; + unlock_kernel(); + + return err; +} + +asmlinkage int sys_pciconfig_write(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf) +{ + unsigned char ubyte; + unsigned short ushort; + unsigned int uint; + int err = 0; + + lock_kernel(); + switch(len) { + case 1: + err = get_user(ubyte, (unsigned char *)buf); + if(err) + break; + pcibios_write_config_byte(bus, dfn, off, ubyte); + break; + + case 2: + err = get_user(ushort, (unsigned short *)buf); + if(err) + break; + pcibios_write_config_byte(bus, dfn, off, ushort); + break; + + case 4: + err = get_user(uint, (unsigned int *)buf); + if(err) + break; + pcibios_write_config_byte(bus, dfn, off, uint); + break; + + default: + err = -EINVAL; + break; + + }; + unlock_kernel(); + + return err; +} + +#endif diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index ac91df894..3df35ef14 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -609,10 +609,6 @@ asmlinkage void do_ptrace(struct pt_regs *regs) unsigned long tmp; int res; -#if 0 - /* XXX Find out what is really going on. */ - flush_cache_all(); -#endif /* Non-word alignment _not_ allowed on Sparc. */ if (current->tss.flags & SPARC_FLAG_32BIT) { unsigned int x; @@ -1055,7 +1051,7 @@ asmlinkage void syscall_trace(void) current->exit_code = SIGTRAP; current->state = TASK_STOPPED; current->tss.flags ^= MAGIC_CONSTANT; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do @@ -1067,9 +1063,9 @@ asmlinkage void syscall_trace(void) current->pid, current->exit_code); #endif if (current->exit_code) { - /* spin_lock_irq(¤t->sigmask_lock); */ + spin_lock_irq(¤t->sigmask_lock); current->signal |= (1 << (current->exit_code - 1)); - /* spin_unlock_irq(¤t->sigmask_lock); */ + spin_unlock_irq(¤t->sigmask_lock); } current->exit_code = 0; diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 9f087a969..a3ac093f3 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.28 1997/06/30 10:31:39 jj Exp $ +/* $Id: rtrap.S,v 1.33 1997/08/21 09:13:22 davem Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -36,13 +36,29 @@ rtrap: sethi %hi(bh_active), %l2 andn %l1, %l4, %l1 be,pt %icc, to_user andn %l7, PSTATE_IE, %l7 -rt_continue: ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 - ld [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 - ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1 - brnz,pn %l2, rt_fpu_restore - ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2 -rt_after_fpu: ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 + ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 + andcc %l2, FPRS_FEF, %g0 + be,pt %icc, rt_continue + and %l2, FPRS_DL, %l6 + wr %g0, FPRS_FEF, %fprs + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x108], %g5 + membar #StoreLoad | #LoadLoad + brz,pn %l6, 1f + wr %g0, ASI_BLK_P, %asi + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x000] %asi, %f0 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x040] %asi, %f16 +1: andcc %l2, FPRS_DU, %g0 + be,pn %icc, 1f + wr %g5, 0, %gsr + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x080] %asi, %f32 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48 +1: membar #Sync + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr +rt_continue: ld [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 + ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1 + ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2 + ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 mov %g6, %l6 ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4 ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5 @@ -69,10 +85,12 @@ rt_after_fpu: ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 wrpr %l1, %g0, %tstate wrpr %l2, %g0, %tpc + wrpr %o2, %g0, %tnpc mov PRIMARY_CONTEXT, %l7 brnz,pn %l3, kern_rtt - wrpr %o2, %g0, %tnpc + mov SECONDARY_CONTEXT, %l5 stxa %l0, [%l7] ASI_DMMU + stxa %l0, [%l5] ASI_DMMU flush %l6 rdpr %wstate, %l1 @@ -88,10 +106,14 @@ rt_after_fpu: ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 retry kern_rtt: restore retry -to_user: sethi %hi(need_resched), %l0 - ld [%l0 + %lo(need_resched)], %l0 +to_user: lduw [%g6 + AOFF_task_processor], %o0 + mov 1, %o1 + sethi %hi(need_resched), %l0 + ldx [%l0 + %lo(need_resched)], %l0 + sllx %o1, %o0, %o1 wrpr %l7, PSTATE_IE, %pstate - brz,pt %l0, check_signal + andcc %o1, %l0, %g0 + be,pt %xcc, check_signal ldx [%g6 + AOFF_task_signal], %l0 call schedule @@ -99,36 +121,123 @@ to_user: sethi %hi(need_resched), %l0 ldx [%g6 + AOFF_task_signal], %l0 nop check_signal: ldx [%g6 + AOFF_task_blocked], %o0 + ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 andncc %l0, %o0, %g0 be,pt %xcc, check_user_wins ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - mov %l5, %o2 mov %l6, %o3 call do_signal add %sp, STACK_BIAS + REGWIN_SZ, %o1 ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 clr %l6 -check_user_wins: - brz,pt %o2, rt_continue - nop +check_user_wins:brz,pt %o2, 1f + sethi %hi(TSTATE_PEF), %o3 call fault_in_user_windows add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ba,a,pt %xcc, rt_continue -rt_fpu_restore: wr %g0, FPRS_FEF, %fprs - add %sp, PTREGS_OFF + TRACEREG_SZ, %g4 - wr %g0, ASI_BLK_P, %asi + sethi %hi(TSTATE_PEF), %o3 +1: andcc %l2, FPRS_FEF, %g0 + be,a,pt %icc, rt_continue + andn %l1, %o3, %l1 ! If fprs.FEF is not set, disable tstate.PEF + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x108], %o3 + ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %l2 + wr %g0, FPRS_FEF, %fprs + wr %o3, 0, %gsr + andcc %l2, SPARC_FLAG_USEDFPUL, %g0 + bne,pn %icc, 2f + andcc %l2, SPARC_FLAG_USEDFPUU, %g0 + fzero %f0 + bne,pn %icc, 1f + fzero %f2 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + faddd %f0, %f2, %f12 + fmuld %f0, %f2, %f14 + faddd %f0, %f2, %f16 + fmuld %f0, %f2, %f18 + faddd %f0, %f2, %f20 + fmuld %f0, %f2, %f22 + faddd %f0, %f2, %f24 + fmuld %f0, %f2, %f26 + faddd %f0, %f2, %f28 + fmuld %f0, %f2, %f30 + faddd %f0, %f2, %f32 + fmuld %f0, %f2, %f34 + faddd %f0, %f2, %f36 + fmuld %f0, %f2, %f38 + faddd %f0, %f2, %f40 + fmuld %f0, %f2, %f42 + faddd %f0, %f2, %f44 + fmuld %f0, %f2, %f46 + faddd %f0, %f2, %f48 + fmuld %f0, %f2, %f50 + faddd %f0, %f2, %f52 + fmuld %f0, %f2, %f54 + faddd %f0, %f2, %f56 + fmuld %f0, %f2, %f58 + faddd %f0, %f2, %f60 + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr + ba,pt %xcc, rt_continue + wr %g0, FPRS_FEF, %fprs +1: wr %g0, ASI_BLK_P, %asi membar #StoreLoad | #LoadLoad - ldda [%g4 + 0x000] %asi, %f0 - ldda [%g4 + 0x040] %asi, %f16 - ldda [%g4 + 0x080] %asi, %f32 - ldda [%g4 + 0x0c0] %asi, %f48 - ldx [%g4 + 0x100], %fsr - ldx [%g4 + 0x108], %g3 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x080] %asi, %f32 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + faddd %f0, %f2, %f12 + fmuld %f0, %f2, %f14 + faddd %f0, %f2, %f16 + fmuld %f0, %f2, %f18 + faddd %f0, %f2, %f20 + fmuld %f0, %f2, %f22 + faddd %f0, %f2, %f24 + fmuld %f0, %f2, %f26 + faddd %f0, %f2, %f28 + fmuld %f0, %f2, %f30 + membar #Sync + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr + ba,pt %xcc, rt_continue + wr %g0, FPRS_FEF, %fprs +2: membar #StoreLoad | #LoadLoad + andcc %l2, SPARC_FLAG_USEDFPUU, %g0 + bne,pt %icc, 3f + wr %g0, ASI_BLK_P, %asi + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x000] %asi, %f0 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x040] %asi, %f16 + fzero %f32 + fzero %f34 + faddd %f32, %f34, %f36 + fmuld %f32, %f34, %f38 + faddd %f32, %f34, %f40 + fmuld %f32, %f34, %f42 + faddd %f32, %f34, %f44 + fmuld %f32, %f34, %f46 + faddd %f32, %f34, %f48 + fmuld %f32, %f34, %f50 + faddd %f32, %f34, %f52 + fmuld %f32, %f34, %f54 + faddd %f32, %f34, %f56 + fmuld %f32, %f34, %f58 + faddd %f32, %f34, %f60 + fmuld %f32, %f34, %f62 + membar #Sync + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr + ba,pt %xcc, rt_continue + wr %g0, FPRS_FEF, %fprs +3: ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x000] %asi, %f0 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x040] %asi, %f16 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x080] %asi, %f32 + ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48 membar #Sync + ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr + ba,pt %xcc, rt_continue + wr %g0, FPRS_FEF, %fprs - b,pt %xcc, rt_after_fpu - wr %g3, 0, %gsr #undef PTREGS_OFF diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 7eb47e976..147c6e55d 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.11 1997/07/24 12:15:05 davem Exp $ +/* $Id: setup.c,v 1.12 1997/08/28 02:23:19 ecd Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -233,8 +233,6 @@ extern unsigned ramdisk_size; extern int root_mountflags; -extern void register_console(void (*proc)(const char *)); - char saved_command_line[256]; char reboot_command[256]; diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index cfc55fc2e..87241f8e3 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.20 1997/07/14 03:10:28 davem Exp $ +/* $Id: signal.c,v 1.24 1997/09/02 20:53:03 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -54,12 +54,12 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs) if(tp->w_saved || (((unsigned long)ucp) & (sizeof(unsigned long)-1)) || (!__access_ok((unsigned long)ucp, sizeof(*ucp)))) - do_exit(SIGSEGV); + goto do_sigsegv; grp = &ucp->uc_mcontext.mc_gregs; __get_user(pc, &((*grp)[MC_PC])); __get_user(npc, &((*grp)[MC_NPC])); if((pc | npc) & 3) - do_exit(SIGSEGV); + goto do_sigsegv; if(regs->u_regs[UREG_I1]) { __get_user(current->blocked, &ucp->uc_sigmask); current->blocked &= _BLOCKABLE; @@ -67,7 +67,7 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs) regs->tpc = pc; regs->tnpc = npc; __get_user(regs->y, &((*grp)[MC_Y])); - __get_user(tstate, &((*grp)[MC_Y])); + __get_user(tstate, &((*grp)[MC_TSTATE])); regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); @@ -94,12 +94,23 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs) __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); if(fenab) { unsigned long *fpregs = (unsigned long *)(regs+1); - copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), - (sizeof(unsigned long) * 32)); + unsigned long fprs; + __get_user(fprs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fprs)); + if (fprs & FPRS_DL) + copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), + (sizeof(unsigned int) * 32)); + if (fprs & FPRS_DU) + copy_from_user(fpregs+16, ((unsigned long *)&(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs))+16, + (sizeof(unsigned int) * 32)); __get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); __get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); - regs->fprs = FPRS_FEF; + regs->fprs = fprs; + regs->tstate |= TSTATE_PEF; } + return; +do_sigsegv: + lock_kernel(); + do_exit(SIGSEGV); } asmlinkage void sparc64_get_context(struct pt_regs *regs) @@ -109,11 +120,11 @@ asmlinkage void sparc64_get_context(struct pt_regs *regs) mc_gregset_t *grp; mcontext_t *mcp; unsigned long fp, i7; - unsigned char fenab = (current->flags & PF_USEDFPU); + unsigned char fenab = (current->tss.flags & SPARC_FLAG_USEDFPU); synchronize_user_stack(); if(tp->w_saved || clear_user(ucp, sizeof(*ucp))) - do_exit(SIGSEGV); + goto do_sigsegv; mcp = &ucp->uc_mcontext; grp = &mcp->mc_gregs; @@ -150,12 +161,30 @@ asmlinkage void sparc64_get_context(struct pt_regs *regs) __put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab)); if(fenab) { unsigned long *fpregs = (unsigned long *)(regs+1); - copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs, - (sizeof(unsigned long) * 32)); + unsigned long fprs; + + fprs = (regs->fprs & FPRS_FEF) | + (current->tss.flags & (SPARC_FLAG_USEDFPUL | SPARC_FLAG_USEDFPUU)); + if (fprs & FPRS_DL) + copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs, + (sizeof(unsigned int) * 32)); + else + clear_user(&(mcp->mc_fpregs.mcfpu_fregs), + (sizeof(unsigned int) * 32)); + if (fprs & FPRS_DU) + copy_to_user(((unsigned long *)&(mcp->mc_fpregs.mcfpu_fregs))+16, fpregs+16, + (sizeof(unsigned int) * 32)); + else + clear_user(((unsigned long *)&(mcp->mc_fpregs.mcfpu_fregs))+16, + (sizeof(unsigned int) * 32)); __put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr)); __put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr)); - __put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs)); + __put_user(fprs, &(mcp->mc_fpregs.mcfpu_fprs)); } + return; +do_sigsegv: + lock_kernel(); + do_exit(SIGSEGV); } /* @@ -233,11 +262,19 @@ static inline void restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { unsigned long *fpregs = (unsigned long *)(regs+1); - copy_from_user(fpregs, &fpu->si_float_regs[0], - (sizeof(unsigned int) * 64)); + unsigned long fprs; + + __get_user(fprs, &fpu->si_fprs); + if (fprs & FPRS_DL) + copy_from_user(fpregs, &fpu->si_float_regs[0], + (sizeof(unsigned int) * 32)); + if (fprs & FPRS_DU) + copy_from_user(fpregs+16, &fpu->si_float_regs[32], + (sizeof(unsigned int) * 32)); __get_user(fpregs[32], &fpu->si_fsr); __get_user(fpregs[33], &fpu->si_gsr); - regs->fprs = FPRS_FEF; + regs->fprs = fprs; + regs->tstate |= TSTATE_PEF; } void do_sigreturn(struct pt_regs *regs) @@ -304,11 +341,26 @@ static inline void save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { unsigned long *fpregs = (unsigned long *)(regs+1); - copy_to_user(&fpu->si_float_regs[0], fpregs, - (sizeof(unsigned int) * 64)); + unsigned long fprs; + + fprs = (regs->fprs & FPRS_FEF) | + (current->tss.flags & (SPARC_FLAG_USEDFPUL | SPARC_FLAG_USEDFPUU)); + if (fprs & FPRS_DL) + copy_to_user(&fpu->si_float_regs[0], fpregs, + (sizeof(unsigned int) * 32)); + else + clear_user(&fpu->si_float_regs[0], + (sizeof(unsigned int) * 32)); + if (fprs & FPRS_DU) + copy_to_user(&fpu->si_float_regs[32], fpregs+16, + (sizeof(unsigned int) * 32)); + else + clear_user(&fpu->si_float_regs[32], + (sizeof(unsigned int) * 32)); __put_user(fpregs[32], &fpu->si_fsr); __put_user(fpregs[33], &fpu->si_gsr); - regs->fprs = 0; + __put_user(fprs, &fpu->si_fprs); + regs->tstate &= ~TSTATE_PEF; } static inline void @@ -321,7 +373,7 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!(current->flags & PF_USEDFPU)) + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) sigframe_size -= sizeof(__siginfo_fpu_t); sf = (struct new_signal_frame *) @@ -339,7 +391,7 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, /* 2. Save the current process state */ copy_to_user(&sf->info.si_regs, regs, sizeof (*regs)); - if (current->flags & PF_USEDFPU) { + if (current->tss.flags & SPARC_FLAG_USEDFPU) { save_fpu_state(regs, &sf->fpu_state); __put_user((u64)&sf->fpu_state, &sf->fpu_save); } else { @@ -452,7 +504,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -498,7 +550,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, current->exit_code = signr; if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 5135c2ae5..16cec2a72 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.26 1997/07/14 03:10:31 davem Exp $ +/* $Id: signal32.c,v 1.30 1997/08/29 15:51:33 jj Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -117,9 +117,17 @@ asmlinkage void _sigpause32_common(unsigned int set, struct pt_regs *regs) static inline void restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { unsigned long *fpregs = (unsigned long *)(regs + 1); - copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64)); + unsigned long fprs; + + __get_user(fprs, &fpu->si_fprs); + if (fprs & FPRS_DL) + copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32)); + if (fprs & FPRS_DU) + copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32)); __get_user(fpregs[32], &fpu->si_fsr); __get_user(fpregs[33], &fpu->si_gsr); + regs->fprs = fprs; + regs->tstate |= TSTATE_PEF; } void do_new_sigreturn32(struct pt_regs *regs) @@ -169,8 +177,10 @@ void do_new_sigreturn32(struct pt_regs *regs) regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); +#if 0 if (psr & PSR_EF) - regs->fprs = FPRS_FEF; + regs->tstate |= TSTATE_PEF; +#endif __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -273,7 +283,7 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, __put_user(pc, &sc->sigc_pc); __put_user(npc, &sc->sigc_npc); psr = tstate_to_psr (regs->tstate); - if(current->flags & PF_USEDFPU) + if(current->tss.flags & SPARC_FLAG_USEDFPU) psr |= PSR_EF; __put_user(psr, &sc->sigc_psr); __put_user(regs->u_regs[UREG_G1], &sc->sigc_g1); @@ -318,10 +328,22 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, static inline void save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { unsigned long *fpregs = (unsigned long *)(regs+1); - copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64)); + unsigned long fprs; + + fprs = (regs->fprs & FPRS_FEF) | + (current->tss.flags & (SPARC_FLAG_USEDFPUL | SPARC_FLAG_USEDFPUU)); + if (fprs & FPRS_DL) + copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 32)); + else + clear_user(&fpu->si_float_regs[0], (sizeof(unsigned int) * 32)); + if (fprs & FPRS_DU) + copy_to_user(&fpu->si_float_regs[32], fpregs+16, (sizeof(unsigned int) * 32)); + else + clear_user(&fpu->si_float_regs[32], (sizeof(unsigned int) * 32)); __put_user(fpregs[32], &fpu->si_fsr); __put_user(fpregs[33], &fpu->si_gsr); - regs->fprs = 0; + __put_user(fprs, &fpu->si_fprs); + regs->tstate &= ~TSTATE_PEF; } static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, @@ -335,7 +357,7 @@ static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!(current->flags & PF_USEDFPU)) + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) sigframe_size -= sizeof(__siginfo_fpu_t); regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; @@ -362,7 +384,7 @@ static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, __put_user(regs->tnpc, &sf->info.si_regs.npc); __put_user(regs->y, &sf->info.si_regs.y); psr = tstate_to_psr (regs->tstate); - if(current->flags & PF_USEDFPU) + if(current->tss.flags & SPARC_FLAG_USEDFPU) psr |= PSR_EF; __put_user(psr, &sf->info.si_regs.psr); for (i = 0; i < 16; i++) @@ -469,7 +491,7 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, __put_user(regs->tpc, &((*gr) [SVR4_PC])); __put_user(regs->tnpc, &((*gr) [SVR4_NPC])); psr = tstate_to_psr (regs->tstate); - if(current->flags & PF_USEDFPU) + if(current->tss.flags & SPARC_FLAG_USEDFPU) psr |= PSR_EF; __put_user(psr, &((*gr) [SVR4_PSR])); __put_user(regs->y, &((*gr) [SVR4_Y])); @@ -488,7 +510,7 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, /* Save the currently window file: */ /* 1. Link sfp->uc->gwins to our windows */ - __put_user(gw, &mc->gwin); + __put_user((u32)(long)gw, &mc->gwin); /* 2. Number of windows to restore at setcontext (): */ __put_user(current->tss.w_saved, &gw->count); @@ -506,9 +528,9 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, */ #if 0 for(window = 0; window < current->tss.w_saved; window++) { - __put_user((int *) &(gw->win [window]), &gw->winptr [window]); + __put_user((int *) &(gw->win [window]), (int **)gw->winptr +window ); copy_to_user(&gw->win [window], ¤t->tss.reg_window [window], sizeof (svr4_rwindow_t)); - __put_user(0, gw->winptr [window]); + __put_user(0, (int *)gw->winptr + window); } #endif @@ -546,7 +568,7 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, } asmlinkage int -svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs) +svr4_getcontext(svr4_ucontext_t *uc, struct pt_regs *regs) { svr4_gregset_t *gr; svr4_mcontext_t *mc; @@ -555,6 +577,7 @@ svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs) synchronize_user_stack(); if (current->tss.w_saved){ printk ("Uh oh, w_saved is not zero (%ld)\n", current->tss.w_saved); + lock_kernel(); do_exit (SIGSEGV); } if(clear_user(uc, sizeof (*uc))) @@ -571,7 +594,7 @@ svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs) __put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]); __put_user(regs->tnpc, &uc->mcontext.greg [SVR4_NPC]); __put_user((tstate_to_psr(regs->tstate) | - ((current->flags & PF_USEDFPU) ? PSR_EF : 0)), + ((current->tss.flags & SPARC_FLAG_USEDFPU) ? PSR_EF : 0)), &uc->mcontext.greg [SVR4_PSR]); __put_user(regs->y, &uc->mcontext.greg [SVR4_Y]); @@ -594,7 +617,7 @@ svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs) /* Set the context for a svr4 application, this is Solaris way to sigreturn */ -asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs) +asmlinkage int svr4_setcontext(svr4_ucontext_t *c, struct pt_regs *regs) { struct thread_struct *tp = ¤t->tss; svr4_gregset_t *gr; @@ -639,9 +662,10 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs) __get_user(psr, &((*gr) [SVR4_PSR])); regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); +#if 0 if(psr & PSR_EF) - regs->fprs = FPRS_FEF; - + regs->tstate |= TSTATE_PEF; +#endif /* Restore g[1..7] and o[0..7] registers */ for (i = 0; i < 7; i++) __get_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i); @@ -718,7 +742,7 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -764,7 +788,7 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, current->exit_code = signr; if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 77ccf40a0..8dd471be6 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -10,11 +10,12 @@ #include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/kernel_stat.h> +#include <linux/delay.h> +#include <asm/head.h> #include <asm/ptrace.h> #include <asm/atomic.h> -#include <asm/delay.h> #include <asm/irq.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -33,9 +34,9 @@ volatile int smp_processors_ready = 0; unsigned long cpu_present_map = 0; int smp_num_cpus = 1; int smp_threads_ready = 0; -volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; -struct cpuinfo_sparc cpu_data[NR_CPUS]; +struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); + static unsigned char boot_cpu_id = 0; static int smp_activated = 0; @@ -44,8 +45,6 @@ volatile int cpu_logical_map[NR_CPUS]; struct klock_info klock_info = { KLOCK_CLEAR, 0 }; -static volatile int smp_commenced = 0; - void smp_setup(char *str, int *ints) { /* XXX implement me XXX */ @@ -68,45 +67,64 @@ char *smp_info(void) void smp_store_cpu_info(int id) { - cpu_data[id].udelay_val = loops_per_sec; + cpu_data[id].udelay_val = loops_per_sec; + cpu_data[id].irq_count = 0; + cpu_data[id].last_tlbversion_seen = tlb_context_cache & CTX_VERSION_MASK; + cpu_data[id].pgcache_size = 0; + cpu_data[id].pgd_cache = NULL; + cpu_data[id].pmd_cache = NULL; + cpu_data[id].pte_cache = NULL; } +extern void distribute_irqs(void); + void smp_commence(void) { - flush_cache_all(); - flush_tlb_all(); - smp_commenced = 1; - flush_cache_all(); - flush_tlb_all(); + distribute_irqs(); } static void smp_setup_percpu_timer(void); static volatile unsigned long callin_flag = 0; +extern void inherit_locked_prom_mappings(int save_p); +extern void cpu_probe(void); + void smp_callin(void) { int cpuid = hard_smp_processor_id(); - flush_cache_all(); - flush_tlb_all(); + inherit_locked_prom_mappings(0); + + __flush_cache_all(); + __flush_tlb_all(); + + cpu_probe(); + + /* Master did this already, now is the time for us to do it. */ + __asm__ __volatile__(" + sethi %%hi(0x80000000), %%g1 + sllx %%g1, 32, %%g1 + rd %%tick, %%g2 + add %%g2, 6, %%g2 + andn %%g2, %%g1, %%g2 + wrpr %%g2, 0, %%tick +" : /* no outputs */ + : /* no inputs */ + : "g1", "g2"); smp_setup_percpu_timer(); + __sti(); + calibrate_delay(); smp_store_cpu_info(cpuid); callin_flag = 1; __asm__ __volatile__("membar #Sync\n\t" "flush %%g6" : : : "memory"); - while(!task[cpuid]) - barrier(); - current = task[cpuid]; - - while(!smp_commenced) - barrier(); - - __sti(); + while(!smp_processors_ready) + membar("#LoadLoad"); } extern int cpu_idle(void *unused); @@ -130,18 +148,22 @@ void cpu_panic(void) panic("SMP bolixed\n"); } +static void smp_tickoffset_init(void); + extern struct prom_cpuinfo linux_cpus[NR_CPUS]; -extern unsigned long sparc64_cpu_startup; + +extern unsigned long smp_trampoline; void smp_boot_cpus(void) { int cpucount = 0, i; printk("Entering UltraSMPenguin Mode...\n"); + smp_tickoffset_init(); __sti(); cpu_present_map = 0; for(i = 0; i < linux_num_cpus; i++) - cpu_present_map |= (1 << i); + cpu_present_map |= (1UL << i); for(i = 0; i < NR_CPUS; i++) { cpu_number_map[i] = -1; cpu_logical_map[i] = -1; @@ -160,23 +182,24 @@ void smp_boot_cpus(void) if(i == boot_cpu_id) continue; - if(cpu_present_map & (1 << i)) { + if(cpu_present_map & (1UL << i)) { + unsigned long entry = (unsigned long)(&smp_trampoline); struct task_struct *p; int timeout; + entry -= KERNBASE; kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; + callin_flag = 0; prom_startcpu(linux_cpus[i].prom_node, - ((unsigned long)&sparc64_cpu_startup), - ((unsigned long)p)); + entry, ((unsigned long)p)); for(timeout = 0; timeout < 5000000; timeout++) { - if(cpu_callin_map[i]) + if(callin_flag) break; udelay(100); } - if(cpu_callin_map[i]) { - /* XXX fix this */ + if(callin_flag) { cpu_number_map[i] = i; cpu_logical_map[i] = i; } else { @@ -184,19 +207,19 @@ void smp_boot_cpus(void) printk("Processor %d is stuck.\n", i); } } - if(!(cpu_callin_map[i])) { - cpu_present_map &= ~(1 << i); + if(!callin_flag) { + cpu_present_map &= ~(1UL << i); cpu_number_map[i] = -1; } } if(cpucount == 0) { printk("Error: only one processor found.\n"); - cpu_present_map = (1 << smp_processor_id()); + cpu_present_map = (1UL << smp_processor_id()); } else { unsigned long bogosum = 0; for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1 << i)) + if(cpu_present_map & (1UL << i)) bogosum += cpu_data[i].udelay_val; } printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n", @@ -207,27 +230,39 @@ void smp_boot_cpus(void) smp_num_cpus = cpucount + 1; } smp_processors_ready = 1; + membar("#StoreStore | #StoreLoad"); } -/* XXX deprecated interface... */ +/* 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) { - printk("smp_message_pass() called, this is bad, spinning.\n"); - __sti(); - while(1) - barrier(); } +/* #define XCALL_DEBUG */ + static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, int cpu) { - u64 result, target = (cpu_number_map[cpu] << 14) | 0x70; - + u64 result, target = (((unsigned long)linux_cpus[cpu].mid) << 14) | 0x70; + int stuck; + +#ifdef XCALL_DEBUG + printk("CPU[%d]: xcall(data[%016lx:%016lx:%016lx],tgt[%016lx])\n", + smp_processor_id(), data0, data1, data2, target); +#endif +again: __asm__ __volatile__(" wrpr %0, %1, %%pstate wr %%g0, %2, %%asi stxa %3, [0x40] %%asi stxa %4, [0x50] %%asi stxa %5, [0x60] %%asi + membar #Sync stxa %%g0, [%6] %%asi membar #Sync" : /* No outputs */ @@ -235,27 +270,46 @@ static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, in "r" (data0), "r" (data1), "r" (data2), "r" (target)); /* NOTE: PSTATE_IE is still clear. */ + stuck = 100000; do { __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (result) : "i" (ASI_INTR_DISPATCH_STAT)); + if(result == 0) { + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + return; + } + stuck -= 1; + if(stuck == 0) + break; } while(result & 0x1); __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); - if(result & 0x2) - panic("Penguin NACK's master!"); + if(stuck == 0) { +#ifdef XCALL_DEBUG + printk("CPU[%d]: mondo stuckage result[%016lx]\n", + smp_processor_id(), result); +#endif + } else { +#ifdef XCALL_DEBUG + printk("CPU[%d]: Penguin %d NACK's master.\n", smp_processor_id(), cpu); +#endif + udelay(2); + goto again; + } } void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) { if(smp_processors_ready) { - unsigned long mask = (cpu_present_map & ~(1<<smp_processor_id())); + unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id())); u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); int i, ncpus = smp_num_cpus; __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); for(i = 0; i < ncpus; i++) { - if(mask & (1 << i)) + if(mask & (1UL << i)) xcall_deliver(data0, data1, data2, pstate, i); } /* NOTE: Caller runs local copy on master. */ @@ -266,7 +320,14 @@ extern unsigned long xcall_flush_tlb_page; extern unsigned long xcall_flush_tlb_mm; extern unsigned long xcall_flush_tlb_range; 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; + +void smp_report_regs(void) +{ + smp_cross_call(&xcall_report_regs, 0, 0, 0); +} void smp_flush_cache_all(void) { @@ -280,11 +341,33 @@ void smp_flush_tlb_all(void) __flush_tlb_all(); } +static void smp_cross_call_avoidance(struct mm_struct *mm) +{ + spin_lock(&scheduler_lock); + get_new_mmu_context(mm, &tlb_context_cache); + mm->cpu_vm_mask = (1UL << smp_processor_id()); + if(current->tss.current_ds) { + u32 ctx = mm->context & 0x1fff; + + current->tss.ctx = ctx; + spitfire_set_secondary_context(ctx); + __asm__ __volatile__("flush %g6"); + } + spin_unlock(&scheduler_lock); +} + void smp_flush_tlb_mm(struct mm_struct *mm) { u32 ctx = mm->context & 0x1fff; - if(mm->cpu_vm_mask != (1UL << smp_processor_id())) - smp_cross_call(&xcall_flush_tlb_mm, ctx, 0, 0); + + if(mm == current->mm && mm->count == 1) { + if(mm->cpu_vm_mask == (1UL << smp_processor_id())) + goto local_flush_and_out; + return smp_cross_call_avoidance(mm); + } + smp_cross_call(&xcall_flush_tlb_mm, ctx, 0, 0); + +local_flush_and_out: __flush_tlb_mm(ctx); } @@ -292,22 +375,101 @@ void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { u32 ctx = mm->context & 0x1fff; - if(mm->cpu_vm_mask != (1UL << smp_processor_id())) - smp_cross_call(&xcall_flush_tlb_range, ctx, start, end); + + if(mm == current->mm && mm->count == 1) { + if(mm->cpu_vm_mask == (1UL << smp_processor_id())) + goto local_flush_and_out; + return smp_cross_call_avoidance(mm); + } + smp_cross_call(&xcall_flush_tlb_range, ctx, start, end); + +local_flush_and_out: __flush_tlb_range(ctx, start, end); } -void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +void smp_flush_tlb_page(struct mm_struct *mm, unsigned long page) { - struct mm_struct *mm = vma->vm_mm; u32 ctx = mm->context & 0x1fff; - if(mm->cpu_vm_mask != (1UL << smp_processor_id())) - smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0); + if(mm == current->mm && 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(mm != current->mm && mm->count == 1) { + /* 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) + return; /* It's dead, nothing to do. */ + if(mm->cpu_vm_mask == (1UL << smp_processor_id())) + goto local_flush_and_out; + } +#endif + smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0); + +local_flush_and_out: __flush_tlb_page(ctx, page); } -static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; +/* CPU capture. */ +#define CAPTURE_DEBUG +extern unsigned long xcall_capture; + +static atomic_t smp_capture_depth = ATOMIC_INIT(0); +static atomic_t smp_capture_registry = ATOMIC_INIT(0); +static unsigned long penguins_are_doing_time = 0; + +void smp_capture(void) +{ + int result = atomic_add_return(1, &smp_capture_depth); + + membar("#StoreStore | #LoadStore"); + if(result == 1) { + int ncpus = smp_num_cpus; + +#ifdef CAPTURE_DEBUG + printk("CPU[%d]: Sending penguins to jail...", smp_processor_id()); +#endif + penguins_are_doing_time = 1; + membar("#StoreStore | #LoadStore"); + atomic_inc(&smp_capture_registry); + smp_cross_call(&xcall_capture, 0, 0, 0); + while(atomic_read(&smp_capture_registry) != ncpus) + membar("#LoadLoad"); +#ifdef CAPTURE_DEBUG + printk("done\n"); +#endif + } +} + +void smp_release(void) +{ + if(atomic_dec_and_test(&smp_capture_depth)) { +#ifdef CAPTURE_DEBUG + printk("CPU[%d]: Giving pardon to imprisoned penguins\n", + smp_processor_id()); +#endif + penguins_are_doing_time = 0; + membar("#StoreStore | #StoreLoad"); + atomic_dec(&smp_capture_registry); + } +} + +/* Imprisoned penguins run with %pil == 15, but PSTATE_IE set, so they + * can service tlb flush xcalls... + */ +void smp_penguin_jailcell(void) +{ + flushw_user(); + atomic_inc(&smp_capture_registry); + membar("#StoreLoad | #StoreStore"); + while(penguins_are_doing_time) + membar("#LoadLoad"); + atomic_dec(&smp_capture_registry); +} static inline void sparc64_do_profile(unsigned long pc) { @@ -317,59 +479,100 @@ static inline void sparc64_do_profile(unsigned long pc) pc -= (unsigned long) &_stext; pc >>= prof_shift; - spin_lock(&ticker_lock); - if(pc < prof_len) - prof_buffer[pc]++; - else - prof_buffer[prof_len - 1]++; - spin_unlock(&ticker_lock); + if(pc >= prof_len) + pc = prof_len - 1; + atomic_inc((atomic_t *)&prof_buffer[pc]); } } -unsigned int prof_multiplier[NR_CPUS]; -unsigned int prof_counter[NR_CPUS]; +static unsigned long real_tick_offset, current_tick_offset; + +#define prof_multiplier(__cpu) cpu_data[(__cpu)].multiplier +#define prof_counter(__cpu) cpu_data[(__cpu)].counter extern void update_one_process(struct task_struct *p, unsigned long ticks, unsigned long user, unsigned long system); void smp_percpu_timer_interrupt(struct pt_regs *regs) { + unsigned long compare, tick; int cpu = smp_processor_id(); int user = user_mode(regs); - /* XXX clear_profile_irq(cpu); */ - if(!user) - sparc64_do_profile(regs->tpc); - if(!--prof_counter[cpu]) { - if(current->pid) { - update_one_process(current, 1, user, !user); - if(--current->counter < 0) { - current->counter = 0; - need_resched = 1; - } - - spin_lock(&ticker_lock); - if(user) { - if(current->priority < DEF_PRIORITY) - kstat.cpu_nice++; - else - kstat.cpu_user++; - } else { - kstat.cpu_system++; + clear_softint((1UL << 0)); + do { + if(!user) + sparc64_do_profile(regs->tpc); + if(!--prof_counter(cpu)) { + if(current->pid) { + unsigned int *inc_me; + + update_one_process(current, 1, user, !user); + if(--current->counter < 0) { + current->counter = 0; + resched_force(); + } + + if(user) { + if(current->priority < DEF_PRIORITY) + inc_me = &kstat.cpu_nice; + else + inc_me = &kstat.cpu_user; + } else { + inc_me = &kstat.cpu_system; + } + atomic_inc((atomic_t *)inc_me); } - spin_unlock(&ticker_lock); + prof_counter(cpu) = prof_multiplier(cpu); } - prof_counter[cpu] = prof_multiplier[cpu]; - } + __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" + "add %0, %2, %0\n\t" + "wr %0, 0x0, %%tick_cmpr\n\t" + "rd %%tick, %1" + : "=&r" (compare), "=r" (tick) + : "r" (current_tick_offset)); + } while (tick >= compare); } static void smp_setup_percpu_timer(void) { - /* XXX implement me */ + int cpu = smp_processor_id(); + + prof_counter(cpu) = prof_multiplier(cpu) = 1; + + __asm__ __volatile__("rd %%tick, %%g1\n\t" + "add %%g1, %0, %%g1\n\t" + "wr %%g1, 0x0, %%tick_cmpr" + : /* no outputs */ + : "r" (current_tick_offset) + : "g1"); +} + +static void smp_tickoffset_init(void) +{ + int node; + + node = linux_cpus[0].prom_node; + real_tick_offset = prom_getint(node, "clock-frequency"); + real_tick_offset = real_tick_offset / HZ; + current_tick_offset = real_tick_offset; } int setup_profiling_timer(unsigned int multiplier) { - /* XXX implement me */ + unsigned long flags; + int i; + + if((!multiplier) || (real_tick_offset / multiplier) < 1000) + return -EINVAL; + + save_and_cli(flags); + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1UL << i)) + prof_multiplier(i) = multiplier; + } + current_tick_offset = (real_tick_offset / multiplier); + restore_flags(flags); + return 0; } diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 990202bac..c1ceacc3f 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.11 1997/07/14 23:58:20 davem Exp $ +/* $Id: sparc64_ksyms.c,v 1.21 1997/09/03 12:29:07 jj Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/string.h> +#include <linux/in6.h> #include <asm/oplib.h> #include <asm/delay.h> @@ -28,11 +29,13 @@ #include <asm/ptrace.h> #include <asm/user.h> #include <asm/uaccess.h> +#include <asm/checksum.h> #ifdef CONFIG_SBUS #include <asm/sbus.h> #include <asm/dma.h> #endif #include <asm/a.out.h> +#include <asm/svr4.h> struct poll { int fd; @@ -45,18 +48,36 @@ extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, void _sigpause_common (unsigned int set, struct pt_regs *); extern void *__bzero_1page(void *); extern void *__bzero(void *, size_t); +extern void *__bzero_noasi(void *, size_t); extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern int __strncmp(const char *, const char *, __kernel_size_t); -extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *); extern char saved_command_line[]; - +extern char *getname32(u32 name); +extern void linux_sparc_syscall(void); +extern void rtrap(void); +extern void show_regs(struct pt_regs *); +extern void solaris_syscall(void); +extern void syscall_trace(void); +extern u32 sunos_sys_table[], sys_call_table32[]; +extern void tl0_solaris(void); +extern void sys_sigsuspend(void); +extern int sys_getppid(void); +extern int svr4_getcontext(svr4_ucontext_t *uc, struct pt_regs *regs); +extern int svr4_setcontext(svr4_ucontext_t *uc, struct pt_regs *regs); +extern int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); +extern int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg); + extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); extern void dump_thread(struct pt_regs *, struct user *); +#ifdef __SMP__ +extern spinlock_t scheduler_lock; +#endif + /* 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: @@ -70,7 +91,17 @@ __attribute__((section("__ksymtab"))) = \ /* used by various drivers */ #ifdef __SMP__ +EXPORT_SYMBOL(scheduler_lock); +EXPORT_SYMBOL(global_bh_lock); EXPORT_SYMBOL(klock_info); +EXPORT_SYMBOL(global_irq_holder); +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL_PRIVATE(global_cli); +EXPORT_SYMBOL_PRIVATE(global_sti); +EXPORT_SYMBOL_PRIVATE(global_restore_flags); +#else +EXPORT_SYMBOL(local_irq_count); #endif EXPORT_SYMBOL_PRIVATE(_lock_kernel); EXPORT_SYMBOL_PRIVATE(_unlock_kernel); @@ -81,12 +112,13 @@ EXPORT_SYMBOL(mstk48t02_regs); EXPORT_SYMBOL(request_fast_irq); EXPORT_SYMBOL(sparc_alloc_io); EXPORT_SYMBOL(sparc_free_io); -EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(__sparc64_bh_counter); EXPORT_SYMBOL(sparc_ultra_unmapioaddr); EXPORT_SYMBOL(mmu_get_scsi_sgl); EXPORT_SYMBOL(mmu_get_scsi_one); EXPORT_SYMBOL(sparc_dvma_malloc); +EXPORT_SYMBOL(mmu_release_scsi_one); +EXPORT_SYMBOL(mmu_release_scsi_sgl); #if CONFIG_SBUS EXPORT_SYMBOL(SBus_chain); EXPORT_SYMBOL(dma_chain); @@ -139,6 +171,25 @@ EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strspn); +#ifdef CONFIG_SOLARIS_EMUL_MODULE +EXPORT_SYMBOL(getname32); +EXPORT_SYMBOL(linux_sparc_syscall); +EXPORT_SYMBOL(rtrap); +EXPORT_SYMBOL(show_regs); +EXPORT_SYMBOL(solaris_syscall); +EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(sunos_sys_table); +EXPORT_SYMBOL(sys_call_table32); +EXPORT_SYMBOL(tl0_solaris); +EXPORT_SYMBOL(sys_sigsuspend); +EXPORT_SYMBOL(sys_getppid); +EXPORT_SYMBOL(svr4_getcontext); +EXPORT_SYMBOL(svr4_setcontext); +EXPORT_SYMBOL(linux_cpus); +EXPORT_SYMBOL(sys_ioctl); +EXPORT_SYMBOL(sys32_ioctl); +#endif + /* Special internal versions of library functions. */ EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memset); @@ -150,12 +201,13 @@ EXPORT_SYMBOL(__memcmp); EXPORT_SYMBOL(__strncmp); EXPORT_SYMBOL(__memmove); -EXPORT_SYMBOL(__csum_partial_copy_sparc_generic); +EXPORT_SYMBOL(csum_partial_copy_sparc64); /* Moving data to/from userspace. */ EXPORT_SYMBOL(__copy_to_user); EXPORT_SYMBOL(__copy_from_user); EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(__bzero_noasi); /* No version information on this, heavily used in inline asm, * and will always be 'void __ret_efault(void)'. diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S index ca0faec5d..37c541755 100644 --- a/arch/sparc64/kernel/sys32.S +++ b/arch/sparc64/kernel/sys32.S @@ -1,4 +1,4 @@ -/* $Id: sys32.S,v 1.2 1997/07/25 01:50:47 ralf Exp $ +/* $Id: sys32.S,v 1.3 1997/08/22 20:11:47 davem Exp $ * sys32.S: I-cache tricks for 32-bit compatability layer simple * conversions. * @@ -73,7 +73,7 @@ sys32_mremap: .globl sys32_readlink, sys32_unlink, sys32_rmdir, sys32_symlink .globl sys32_link, sys32_rename, sys32_truncate, sys32_ftruncate .globl sys32_chroot, sys32_chmod, sys32_chown, sys32_creat - .globl sys32_mkdir, sys32_mknod, sys32_utimes, sys32_ustat + .globl sys32_mkdir, sys32_mknod, sys32_ustat sys32_read: srl %o1, 0, %o1 mov %o7, %g1 @@ -202,12 +202,6 @@ sys32_mknod: srl %o2, 16, %o2 call sys_mknod mov %g1, %o7 -sys32_utimes: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_utimes - mov %g1, %o7 sys32_ustat: srl %o1, 0, %o1 mov %o7, %g1 @@ -285,24 +279,9 @@ sys32_getsockopt: call sys_setsockopt mov %g1, %o7 - .align 32 - .globl sys32_gettimeofday, sys32_settimeofday -sys32_gettimeofday: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_gettimeofday - mov %g1, %o7 -sys32_settimeofday: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_settimeofday - mov %g1, %o7 - .globl sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog - .globl sys32_personality, sys32_waitpid, sys32_getitimer - .globl sys32_setitimer, sys32_sched_setscheduler + .globl sys32_personality, sys32_waitpid + .globl sys32_sched_setscheduler .globl sys32_sched_setparam, sys32_sched_getparam, sys32_signal .globl sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname .globl sys32_sethostname, sys32_gethostname, sys32_setdomainname @@ -338,17 +317,6 @@ sys32_waitpid: mov %o7, %g1 call sys_waitpid mov %g1, %o7 -sys32_getitimer: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_getitimer - mov %g1, %o7 -sys32_setitimer: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_setitimer - mov %g1, %o7 sys32_sched_setscheduler: srl %o2, 0, %o2 mov %o7, %g1 diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index c827df7a1..0ec6de167 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.2 1997/07/05 09:52:34 davem Exp $ +/* $Id: sys_sparc.c,v 1.5 1997/09/03 12:29:05 jj Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -191,9 +191,12 @@ out: asmlinkage unsigned long c_sys_nis_syscall (struct pt_regs *regs) { + static int count=0; lock_kernel(); - printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]); - show_regs (regs); + 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); + } unlock_kernel(); return -ENOSYS; } @@ -220,46 +223,36 @@ asmlinkage int sparc_sigaction (int signum, const struct sigaction *action, struct sigaction *oldaction) { struct sigaction new_sa, *p; - int err = -EINVAL; - lock_kernel(); if(signum < 0) { current->tss.new_signal = 1; signum = -signum; } - if (signum<1 || signum>32) - goto out; + return -EINVAL; p = signum - 1 + current->sig->action; if (action) { - err = -EINVAL; if (signum==SIGKILL || signum==SIGSTOP) - goto out; - err = -EFAULT; + return -EINVAL; if(copy_from_user(&new_sa, action, sizeof(struct sigaction))) - goto out; + return -EFAULT; if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) { - err = verify_area(VERIFY_READ, new_sa.sa_handler, 1); + int err = verify_area(VERIFY_READ, new_sa.sa_handler, 1); if (err) - goto out; + return err; } } - if (oldaction) { - err = -EFAULT; if (copy_to_user(oldaction, p, sizeof(struct sigaction))) - goto out; + return -EFAULT; } - if (action) { + spin_lock_irq(¤t->sig->siglock); *p = new_sa; check_pending(signum); + spin_unlock_irq(¤t->sig->siglock); } - - err = 0; -out: - unlock_kernel(); - return err; + return 0; } /* only AP+ systems have sys_aplib */ @@ -267,3 +260,15 @@ asmlinkage int sys_aplib(void) { return -ENOSYS; } + +asmlinkage int solaris_syscall(struct pt_regs *regs) +{ + lock_kernel(); + regs->tpc = regs->tnpc; + regs->tnpc += 4; + printk ("For Solaris binary emulation you need solaris module loaded\n"); + show_regs (regs); + send_sig(SIGSEGV, current, 1); + unlock_kernel(); + return 0; +} diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index b6ca9448c..e2c199f60 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.44 1997/07/20 09:18:47 davem Exp $ +/* $Id: sys_sparc32.c,v 1.55 1997/09/04 01:54:51 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -44,6 +44,7 @@ #include <asm/ipc.h> #include <asm/uaccess.h> #include <asm/fpumacro.h> +#include <asm/semaphore.h> /* As gcc will warn about casting u32 to some ptr, we have to cast it to * unsigned long first, and that's what is A() for. @@ -53,6 +54,33 @@ */ #define A(x) ((unsigned long)x) +extern char * getname_quicklist; +extern int getname_quickcount; +extern struct semaphore getname_quicklock; +extern int kerneld_msqid; + +/* Tuning: increase locality by reusing same pages again... + * if getname_quicklist becomes too long on low memory machines, either a limit + * should be added or after a number of cycles some pages should + * be released again ... + */ +static inline char * get_page(void) +{ + char * res; + down(&getname_quicklock); + res = getname_quicklist; + if (res) { + getname_quicklist = *(char**)res; + getname_quickcount--; + } + else + res = (char*)__get_free_page(GFP_KERNEL); + up(&getname_quicklock); + 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.. @@ -74,44 +102,67 @@ static inline int do_getname32(u32 filename, char *page) return retval; } -/* This is a single page for faster getname. - * If the page is available when entering getname, use it. - * If the page is not available, call __get_free_page instead. - * This works even though do_getname can block (think about it). - * -- Michael Chastain, based on idea of Linus Torvalds, 1 Dec 1996. - * We don't use the common getname/putname from namei.c, so that - * this still works well, as every routine which calls getname32 - * will then call getname, then putname and then putname32. - */ -static unsigned long name_page_cache32 = 0; +char * getname32(u32 filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = get_page(); + if (tmp) { + int retval = do_getname32(filename, tmp); -void putname32(char * name) + result = tmp; + if (retval < 0) { + putname32(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + +/* 32-bit timeval and related flotsam. */ + +struct timeval32 { - if (name_page_cache32 == 0) - name_page_cache32 = (unsigned long) name; - else - free_page((unsigned long) name); + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, tv32, sizeof(*tv32)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); } -int getname32(u32 filename, char **result) +static inline long put_tv32(struct timeval32 *o, struct timeval *i) { - unsigned long page; - int retval; + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} - page = name_page_cache32; - name_page_cache32 = 0; - if (!page) { - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - } +static inline long get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i32, sizeof(*i32)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); +} - retval = do_getname32(filename, (char *) page); - if (retval < 0) - putname32( (char *) page ); - else - *result = (char *) page; - return retval; +static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, i32, sizeof(*i32)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); } extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); @@ -266,14 +317,25 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u switch (call) { case MSGSND: { - struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_KERNEL); + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_KERNEL); if (!p) err = -ENOMEM; else { - if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second)) - err = -EFAULT; - else { + err = 0; + if (first == kerneld_msqid) { + *(int *)p->mtext = 0; + if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) || + __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4)) + err = -EFAULT; + else + second += 4; + } else { + if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second)) + err = -EFAULT; + } + if (!err) { unsigned long old_fs = get_fs(); set_fs (KERNEL_DS); err = sys_msgsnd (first, p, second, third); @@ -287,6 +349,7 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u { struct msgbuf *p; unsigned long old_fs; + long msgtyp = fifth; if (!version) { struct ipc_kludge tmp; @@ -297,20 +360,35 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u if(copy_from_user(&tmp,(struct ipc_kludge *)A(ptr), sizeof (tmp))) goto out; ptr = tmp.msgp; - fifth = tmp.msgtyp; + msgtyp = tmp.msgtyp; } - p = kmalloc (second + sizeof (struct msgbuf), GFP_KERNEL); + + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_KERNEL); if (!p) { err = -EFAULT; goto out; } + old_fs = get_fs(); set_fs (KERNEL_DS); - err = sys_msgrcv (first, p, second, fifth, third); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); set_fs (old_fs); - if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || - __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, second)) - err = -EFAULT; + + if (err < 0) + goto out; + + if (first == kerneld_msqid) { + if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) || + __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8)) + err = -EFAULT; + else + err -= 4; + } else { + if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) || + __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err)) + err = -EFAULT; + } kfree (p); goto out; } @@ -472,8 +550,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u err = -EINVAL; goto out; } - else - err = -EINVAL; + + err = -EINVAL; + out: unlock_kernel(); return err; @@ -565,8 +644,9 @@ asmlinkage int sys32_quotactl(int cmd, u32 special, int id, u32 addr) return sys_quotactl(cmd, (const char *)A(special), id, (caddr_t)A(addr)); } - err = getname32 (special, &spec); - if (err) return err; + spec = getname32 (special); + err = PTR_ERR(spec); + if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)A(addr)); @@ -608,8 +688,9 @@ asmlinkage int sys32_statfs(u32 path, u32 buf) unsigned long old_fs = get_fs(); char *pth; - ret = getname32 (path, &pth); - if (!ret) { + pth = getname32 (path); + ret = PTR_ERR(pth); + if (!IS_ERR(pth)) { set_fs (KERNEL_DS); ret = sys_statfs((const char *)pth, &s); set_fs (old_fs); @@ -651,8 +732,9 @@ asmlinkage int sys32_utime(u32 filename, u32 times) if (get_user (t.actime, &(((struct utimbuf32 *)A(times))->actime)) || __get_user (t.modtime, &(((struct utimbuf32 *)A(times))->modtime))) return -EFAULT; - ret = getname32 (filename, &filenam); - if (!ret) { + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_utime(filenam, &t); @@ -1010,7 +1092,7 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) lock_kernel (); p = (char *)__get_free_page (GFP_KERNEL); if (!p) - goto out; + goto out_nofree; q = (u32 *)p; Inp = (u32 *)A(inp); @@ -1019,7 +1101,13 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) ret = -EFAULT; - nn = (n + (8 * sizeof(unsigned long)) - 1) / (8 * sizeof (unsigned long)); + nn = (n + (8 * sizeof(long)) - 1) / (8 * sizeof(long)); + if (inp && verify_area(VERIFY_WRITE, Inp, nn*sizeof(long))) + goto out; + if (outp && verify_area(VERIFY_WRITE, Outp, nn*sizeof(long))) + goto out; + if (exp && verify_area(VERIFY_WRITE, Exp, nn*sizeof(long))) + goto out; for (i = 0; i < nn; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { if(inp && (__get_user (q[1], Inp) || __get_user (q[0], Inp+1))) goto out; @@ -1033,7 +1121,7 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) ktvp = NULL; if(tvp) { - if(copy_from_user(&kern_tv, (struct timeval *)A(tvp), sizeof(*ktvp))) + if (get_tv32(&kern_tv, (struct timeval32 *)A(tvp))) goto out; ktvp = &kern_tv; } @@ -1048,8 +1136,12 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) ktvp); set_fs (old_fs); - if(tvp && !(current->personality & STICKY_TIMEOUTS)) - copy_to_user((struct timeval *)A(tvp), &kern_tv, sizeof(*ktvp)); + if(tvp && !(current->personality & STICKY_TIMEOUTS)) { + if (put_tv32((struct timeval32 *)A(tvp), &kern_tv)) { + ret = -EFAULT; + goto out; + } + } q = (u32 *)p; Inp = (u32 *)A(inp); @@ -1079,6 +1171,7 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) } out: free_page ((unsigned long)p); +out_nofree: unlock_kernel(); return ret; } @@ -1111,8 +1204,9 @@ asmlinkage int sys32_newstat(u32 filename, u32 statbuf) char *filenam; unsigned long old_fs = get_fs(); - ret = getname32 (filename, &filenam); - if (!ret) { + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); @@ -1132,8 +1226,9 @@ asmlinkage int sys32_newlstat(u32 filename, u32 statbuf) char *filenam; unsigned long old_fs = get_fs(); - ret = getname32 (filename, &filenam); - if (!ret) { + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); @@ -1239,12 +1334,12 @@ static void *do_smb_super_data_conv(void *raw_data) struct smb_mount_data *s = (struct smb_mount_data *)raw_data; struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; - s->dir_mode = s32->dir_mode; - s->file_mode = s32->file_mode; - s->gid = s32->gid; - s->uid = s32->uid; - memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr))); + s->version = s32->version; s->mounted_uid = s32->mounted_uid; + s->uid = s32->uid; + s->gid = s32->gid; + s->file_mode = s32->file_mode; + s->dir_mode = s32->dir_mode; return raw_data; } @@ -1344,8 +1439,8 @@ asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, } struct rusage32 { - struct timeval ru_utime; - struct timeval ru_stime; + struct timeval32 ru_utime; + struct timeval32 ru_stime; s32 ru_maxrss; s32 ru_ixrss; s32 ru_idrss; @@ -1703,7 +1798,7 @@ struct timex32 { s32 constant; s32 precision; s32 tolerance; - struct timeval time; + struct timeval32 time; s32 tick; s32 ppsfreq; s32 jitter; @@ -2110,54 +2205,45 @@ asmlinkage int sparc32_sigaction (int signum, u32 action, u32 oldaction) { struct sigaction32 new_sa, old_sa; struct sigaction *p; - int err = -EINVAL; - lock_kernel(); if(signum < 0) { current->tss.new_signal = 1; signum = -signum; } - if (signum<1 || signum>32) - goto out; + return -EINVAL; p = signum - 1 + current->sig->action; if (action) { - err = -EINVAL; if (signum==SIGKILL || signum==SIGSTOP) - goto out; - err = -EFAULT; + return -EINVAL; if(copy_from_user(&new_sa, A(action), sizeof(struct sigaction32))) - goto out; + return -EFAULT; if (((__sighandler_t)A(new_sa.sa_handler)) != SIG_DFL && ((__sighandler_t)A(new_sa.sa_handler)) != SIG_IGN) { - err = verify_area(VERIFY_READ, (__sighandler_t)A(new_sa.sa_handler), 1); + int err = verify_area(VERIFY_READ, + (__sighandler_t)A(new_sa.sa_handler), 1); if (err) - goto out; + return err; } } - if (oldaction) { - err = -EFAULT; old_sa.sa_handler = (unsigned)(u64)(p->sa_handler); old_sa.sa_mask = (sigset_t32)(p->sa_mask); old_sa.sa_flags = (unsigned)(p->sa_flags); old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer); if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32))) - goto out; + return -EFAULT; } - if (action) { + spin_lock_irq(¤t->sig->siglock); p->sa_handler = (__sighandler_t)A(new_sa.sa_handler); p->sa_mask = (sigset_t)(new_sa.sa_mask); p->sa_flags = new_sa.sa_flags; p->sa_restorer = (void (*)(void))A(new_sa.sa_restorer); check_pending(signum); + spin_unlock_irq(¤t->sig->siglock); } - - err = 0; -out: - unlock_kernel(); - return err; + return 0; } /* @@ -2298,7 +2384,7 @@ asmlinkage int sparc32_execve(struct pt_regs *regs) base = 1; lock_kernel(); - filename = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0]); + filename = getname((char *)A((u32)regs->u_regs[base + UREG_I0])); error = PTR_ERR(filename); if(IS_ERR(filename)) goto out; @@ -2310,6 +2396,7 @@ asmlinkage int sparc32_execve(struct pt_regs *regs) if(!error) { fprs_write(0); regs->fprs = 0; + regs->tstate &= ~TSTATE_PEF; } out: unlock_kernel(); @@ -2369,8 +2456,12 @@ asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_si case QM_MODULES: case QM_REFS: case QM_DEPS: - if (name_user && (ret = getname32 (name_user, &usernam))) - return ret; + if (name_user) { + usernam = getname32 (name_user); + ret = PTR_ERR(usernam); + if (IS_ERR(usernam)) + return ret; + } buff = kmalloc (bufsiz, GFP_KERNEL); if (!buff) { if (name_user) putname32 (usernam); @@ -2431,8 +2522,12 @@ qmsym_toshort: if (name_user) putname32 (usernam); return ret; case QM_INFO: - if (name_user && (ret = getname32 (name_user, &usernam))) - return ret; + if (name_user) { + usernam = getname32 (name_user); + ret = PTR_ERR(usernam); + if (IS_ERR(usernam)) + return ret; + } set_fs (KERNEL_DS); ret = sys_query_module (usernam, which, (char *)&mi, sizeof (mi), &val); set_fs (old_fs); @@ -2720,9 +2815,7 @@ static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res return 0; } -extern asmlinkage int sys_nfsservctl(int cmd, - struct nfsctl_arg *arg, - union nfsctl_res *resp); +extern asmlinkage int sys_nfsservctl(int cmd, void *arg, void *resp); int asmlinkage sys32_nfsservctl(int cmd, u32 u_argp, u32 u_resp) { @@ -2793,3 +2886,106 @@ done: kfree(kres); return err; } + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ + +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage int sys32_gettimeofday(u32 tv, u32 tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32((struct timeval32 *)A(tv), &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user((void*)A(tz), &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage int sys32_settimeofday(u32 tv, u32 tz) +{ + struct timeval ktv; + struct timezone ktz; + + if (tv) { + if (get_tv32(&ktv, (struct timeval32 *)A(tv))) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, (void*)A(tz), sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + +extern int do_getitimer(int which, struct itimerval *value); + +asmlinkage int sys32_getitimer(int which, u32 it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32((struct itimerval32 *)A(it), &kit)) + error = -EFAULT; + + return error; +} + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +asmlinkage int sys32_setitimer(int which, u32 in, u32 out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, (struct itimerval32 *)A(in))) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32((struct itimerval32 *)A(out), &kout)) + return -EFAULT; + + return 0; + +} + +asmlinkage int sys_utimes(char *, struct timeval *); + +asmlinkage int sys32_utimes(u32 filename, u32 tvs) +{ + char *kfilename; + struct timeval ktvs[2]; + unsigned long old_fs; + int ret; + + kfilename = getname32(filename); + ret = PTR_ERR(kfilename); + if (!IS_ERR(kfilename)) { + if (tvs) { + if (get_tv32(&ktvs[0], (struct timeval32 *)A(tvs)) || + get_tv32(&ktvs[1], 1+(struct timeval32 *)A(tvs))) + return -EFAULT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_utimes(kfilename, &ktvs[0]); + set_fs(old_fs); + + putname32(kfilename); + } + return ret; +} diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index eda0ff326..9b00175a3 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.21 1997/07/05 07:09:17 davem Exp $ +/* $Id: systbls.S,v 1.24 1997/08/22 20:12:06 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -10,213 +10,214 @@ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu) */ - .data - .align 8 + .text + .align 1024 /* First, the 32-bit Linux native syscall table. */ .globl sys_call_table32 sys_call_table32: -/*0*/ .xword sys_setup, sys_exit, sys_fork, sys32_read, sys32_write -/*5*/ .xword sys32_open, sys_close, sys32_wait4, sys32_creat, sys32_link -/*10*/ .xword sys32_unlink, sunos_execv, sys32_chdir, sys_nis_syscall, sys32_mknod -/*15*/ .xword sys32_chmod, sys32_chown, sparc32_brk, sys_nis_syscall, sys32_lseek -/*20*/ .xword sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid -/*25*/ .xword sys32_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause -/*30*/ .xword sys32_utime, sys_stty, sys_gtty, sys32_access, sys_nice - .xword sys_ftime, sys_sync, sys_kill, sys32_newstat, sys_nis_syscall -/*40*/ .xword sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_profil - .xword sys_nis_syscall, sys_setgid, sys_getgid, sys32_signal, sys_geteuid -/*50*/ .xword sys_getegid, sys32_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl - .xword sys32_reboot, sys_nis_syscall, sys32_symlink, sys32_readlink, sys32_execve -/*60*/ .xword sys_umask, sys32_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize - .xword sys32_msync, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*70*/ .xword sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys32_munmap, sys32_mprotect - .xword sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys32_getgroups -/*80*/ .xword sys32_setgroups, sys_getpgrp, sys_nis_syscall, sys32_setitimer, sys_nis_syscall - .xword sys32_swapon, sys32_getitimer, sys_nis_syscall, sys32_sethostname, sys_nis_syscall -/*90*/ .xword sys_dup2, sys_nis_syscall, sys32_fcntl, sys32_select, sys_nis_syscall - .xword sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*100*/ .xword sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys32_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_nis_syscall -/*120*/ .xword sys32_readv, sys32_writev, sys32_settimeofday, sys_fchown, sys_fchmod - .xword sys_nis_syscall, sys32_setreuid, sys_setregid, sys32_rename, sys32_truncate -/*130*/ .xword sys32_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys32_mkdir, sys32_rmdir, sys_nis_syscall, sys_nis_syscall -/*140*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit - .xword sys32_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*150*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys32_umount -/*160*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_setdomainname, sys_nis_syscall - .xword sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall -/*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents - .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module - .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .xword sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock - .xword sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask -/*200*/ .xword sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir - .xword sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall -/*210*/ .xword sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo - .xword sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex -/*220*/ .xword sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid - .xword sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid -/*230*/ .xword sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall - .xword sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall -/*240*/ .xword sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep -/*250*/ .xword sys32_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl - .xword sys_aplib, sys_nis_syscall +/*0*/ .word sys_setup, sys_exit, sys_fork, sys32_read, sys32_write +/*5*/ .word sys32_open, sys_close, sys32_wait4, sys32_creat, sys32_link +/*10*/ .word sys32_unlink, sunos_execv, sys32_chdir, sys_nis_syscall, sys32_mknod +/*15*/ .word sys32_chmod, sys32_chown, sparc32_brk, sys_nis_syscall, sys32_lseek +/*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid +/*25*/ .word sys32_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause +/*30*/ .word sys32_utime, sys_stty, sys_gtty, sys32_access, sys_nice + .word sys_ftime, sys_sync, sys_kill, sys32_newstat, sys_nis_syscall +/*40*/ .word sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_profil + .word sys_nis_syscall, sys_setgid, sys_getgid, sys32_signal, sys_geteuid +/*50*/ .word sys_getegid, sys32_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl + .word sys32_reboot, sys_nis_syscall, sys32_symlink, sys32_readlink, sys32_execve +/*60*/ .word sys_umask, sys32_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize + .word sys32_msync, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*70*/ .word sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys32_munmap, sys32_mprotect + .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys32_getgroups +/*80*/ .word sys32_setgroups, sys_getpgrp, sys_nis_syscall, sys32_setitimer, sys_nis_syscall + .word sys32_swapon, sys32_getitimer, sys_nis_syscall, sys32_sethostname, sys_nis_syscall +/*90*/ .word sys_dup2, sys_nis_syscall, sys32_fcntl, sys32_select, sys_nis_syscall + .word sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*100*/ .word sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*110*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys32_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_nis_syscall +/*120*/ .word sys32_readv, sys32_writev, sys32_settimeofday, sys_fchown, sys_fchmod + .word sys_nis_syscall, sys32_setreuid, sys_setregid, sys32_rename, sys32_truncate +/*130*/ .word sys32_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys32_mkdir, sys32_rmdir, sys_nis_syscall, sys_nis_syscall +/*140*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit + .word sys32_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*150*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys32_umount +/*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_setdomainname, sys_nis_syscall + .word sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall +/*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents + .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module + .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname +/*190*/ .word sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock + .word sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask +/*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir + .word sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall +/*210*/ .word sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo + .word sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex +/*220*/ .word sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid + .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid +/*230*/ .word sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall + .word sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall +/*240*/ .word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep +/*250*/ .word sys32_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl + .word sys_aplib /* Now the 64-bit native Linux syscall table. */ + .align 1024 .globl sys_call_table64, sys_call_table sys_call_table64: sys_call_table: -/*0*/ .xword sys_setup, sys_exit, sys_fork, sys_read, sys_write -/*5*/ .xword sys_open, sys_close, sys_wait4, sys_creat, sys_link -/*10*/ .xword sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod -/*15*/ .xword sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek -/*20*/ .xword sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid -/*25*/ .xword sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause -/*30*/ .xword sys_utime, sys_stty, sys_gtty, sys_access, sys_nice - .xword sys_ftime, sys_sync, sys_kill, sys_newstat, sys_nis_syscall -/*40*/ .xword sys_newlstat, sys_dup, sys_pipe, sys_times, sys_profil - .xword sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid -/*50*/ .xword sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys_ioctl - .xword sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve -/*60*/ .xword sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize - .xword sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*70*/ .xword sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect - .xword sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups -/*80*/ .xword sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall - .xword sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall -/*90*/ .xword sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall - .xword sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept -/*100*/ .xword sys_getpriority, sys_send, sys_recv, sys_nis_syscall, sys_bind - .xword sys_setsockopt, sys_listen, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_recvmsg, sys_sendmsg - .xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_nis_syscall -/*120*/ .xword sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod - .xword sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate -/*130*/ .xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_sendto, sys_shutdown - .xword sys_socketpair, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall -/*140*/ .xword sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit - .xword sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*150*/ .xword sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount -/*160*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall - .xword sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall -/*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents - .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module - .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname -/*190*/ .xword sys_init_module, sys_personality, sys_prof, sys_break, sys_lock - .xword sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask -/*200*/ .xword sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall - .xword sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall -/*210*/ .xword sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo - .xword sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex -/*220*/ .xword sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid - .xword sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid -/*230*/ .xword sys_llseek, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall - .xword sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall -/*240*/ .xword sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep -/*250*/ .xword sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl - .xword sys_aplib, sys_nis_syscall +/*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write +/*5*/ .word sys_open, sys_close, sys_wait4, sys_creat, sys_link +/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod +/*15*/ .word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek +/*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid +/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause +/*30*/ .word sys_utime, sys_stty, sys_gtty, sys_access, sys_nice + .word sys_ftime, sys_sync, sys_kill, sys_newstat, sys_nis_syscall +/*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_profil + .word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid +/*50*/ .word sys_getegid, sys_acct, sys_nis_syscall, 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 + .word sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect + .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups +/*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall + .word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall +/*90*/ .word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall + .word sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept +/*100*/ .word sys_getpriority, sys_send, sys_recv, sys_nis_syscall, sys_bind + .word sys_setsockopt, sys_listen, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*110*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_recvmsg, sys_sendmsg + .word sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_nis_syscall +/*120*/ .word sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod + .word sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate +/*130*/ .word sys_ftruncate, sys_flock, sys_nis_syscall, sys_sendto, sys_shutdown + .word sys_socketpair, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall +/*140*/ .word sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit + .word sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*150*/ .word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount +/*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall + .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 + .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module + .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname +/*190*/ .word sys_init_module, sys_personality, sys_prof, sys_break, sys_lock + .word sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask +/*200*/ .word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall +/*210*/ .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo + .word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex +/*220*/ .word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid + .word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid +/*230*/ .word sys_llseek, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall + .word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall +/*240*/ .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep +/*250*/ .word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl + .word sys_aplib /* Now the 32-bit SunOS syscall table. */ - .align 8 + .align 1024 .globl sunos_sys_table sunos_sys_table: -/*0*/ .xword sunos_indir, sys_exit, sys_fork - .xword sunos_read, sunos_write, sunos_open - .xword sys_close, sunos_wait4, sys32_creat - .xword sys32_link, sys32_unlink, sunos_execv - .xword sys32_chdir, sunos_nosys, sys32_mknod - .xword sys32_chmod, sys32_chown, sunos_brk - .xword sunos_nosys, sys32_lseek, sunos_getpid - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_getuid, sunos_nosys, sys_ptrace - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sys32_access, sunos_nosys, sunos_nosys - .xword sys_sync, sys_kill, sys32_newstat - .xword sunos_nosys, sys32_newlstat, sys_dup - .xword sys_pipe, sunos_nosys, sys_profil - .xword sunos_nosys, sunos_nosys, sunos_getgid - .xword sunos_nosys, sunos_nosys -/*50*/ .xword sunos_nosys, sys32_acct, sunos_nosys - .xword sunos_mctl, sunos_ioctl, sys32_reboot - .xword sunos_nosys, sys32_symlink, sys32_readlink - .xword sys32_execve, sys_umask, sys32_chroot - .xword sys32_newfstat, sunos_nosys, sys_getpagesize - .xword sys32_msync, sys_vfork, sunos_nosys - .xword sunos_nosys, sunos_sbrk, sunos_sstk - .xword sunos_mmap, sunos_vadvise, sys32_munmap - .xword sys32_mprotect, sunos_madvise, sys_vhangup - .xword sunos_nosys, sunos_mincore, sys32_getgroups - .xword sys32_setgroups, sys_getpgrp, sunos_setpgrp - .xword sys32_setitimer, sunos_nosys, sys32_swapon - .xword sys32_getitimer, sys32_gethostname, sys32_sethostname - .xword sunos_getdtablesize, sys_dup2, sunos_nop - .xword sys32_fcntl, sunos_select, sunos_nop - .xword sys_fsync, sys_setpriority, sys_socket - .xword sys32_connect, sunos_accept -/*100*/ .xword sys_getpriority, sunos_send, sunos_recv - .xword sunos_nosys, sys32_bind, sunos_setsockopt - .xword sys_listen, sunos_nosys, sunos_sigaction - .xword sunos_sigblock, sunos_sigsetmask, sys_sigpause - .xword sys32_sigstack, sys32_recvmsg, sys32_sendmsg - .xword sunos_nosys, sys_gettimeofday, sys32_getrusage - .xword sunos_getsockopt, sunos_nosys, sunos_readv - .xword sunos_writev, sys_settimeofday, sys_fchown - .xword sys_fchmod, sys32_recvfrom, sys32_setreuid - .xword sys_setregid, sys32_rename, sys32_truncate - .xword sys32_ftruncate, sys_flock, sunos_nosys - .xword sys32_sendto, sys_shutdown, sys_socketpair - .xword sys32_mkdir, sys32_rmdir, sys32_utimes - .xword sys_sigreturn, sunos_nosys, sys32_getpeername - .xword sunos_gethostid, sunos_nosys, sys32_getrlimit - .xword sys32_setrlimit, sunos_killpg, sunos_nosys - .xword sunos_nosys, sunos_nosys -/*150*/ .xword sys32_getsockname, sunos_nosys, sunos_nosys - .xword sys32_poll, sunos_nosys, sunos_nosys - .xword sunos_getdirentries, sys32_statfs, sys32_fstatfs - .xword sys32_umount, sunos_nosys, sunos_nosys - .xword sunos_getdomainname, sys32_setdomainname - .xword sunos_nosys, sys32_quotactl, sunos_nosys - .xword sunos_mount, sys32_ustat, sunos_semsys - .xword sunos_nosys, sunos_shmsys, sunos_audit - .xword sunos_nosys, sunos_getdents, sys_setsid - .xword sys_fchdir, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sys32_sigpending, sunos_nosys - .xword sys_setpgid, sunos_pathconf, sunos_fpathconf - .xword sunos_sysconf, sunos_uname, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys -/*200*/ .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys -/*250*/ .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sunos_nosys, sys_aplib +/*0*/ .word sunos_indir, sys_exit, sys_fork + .word sunos_read, sunos_write, sunos_open + .word sys_close, sunos_wait4, sys32_creat + .word sys32_link, sys32_unlink, sunos_execv + .word sys32_chdir, sunos_nosys, sys32_mknod + .word sys32_chmod, sys32_chown, sunos_brk + .word sunos_nosys, sys32_lseek, sunos_getpid + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_getuid, sunos_nosys, sys_ptrace + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sys32_access, sunos_nosys, sunos_nosys + .word sys_sync, sys_kill, sys32_newstat + .word sunos_nosys, sys32_newlstat, sys_dup + .word sys_pipe, sunos_nosys, sys_profil + .word sunos_nosys, sunos_nosys, sunos_getgid + .word sunos_nosys, sunos_nosys +/*50*/ .word sunos_nosys, sys32_acct, sunos_nosys + .word sunos_mctl, sunos_ioctl, sys32_reboot + .word sunos_nosys, sys32_symlink, sys32_readlink + .word sys32_execve, sys_umask, sys32_chroot + .word sys32_newfstat, sunos_nosys, sys_getpagesize + .word sys32_msync, sys_vfork, sunos_nosys + .word sunos_nosys, sunos_sbrk, sunos_sstk + .word sunos_mmap, sunos_vadvise, sys32_munmap + .word sys32_mprotect, sunos_madvise, sys_vhangup + .word sunos_nosys, sunos_mincore, sys32_getgroups + .word sys32_setgroups, sys_getpgrp, sunos_setpgrp + .word sys32_setitimer, sunos_nosys, sys32_swapon + .word sys32_getitimer, sys32_gethostname, sys32_sethostname + .word sunos_getdtablesize, sys_dup2, sunos_nop + .word sys32_fcntl, sunos_select, sunos_nop + .word sys_fsync, sys_setpriority, sys_socket + .word sys32_connect, sunos_accept +/*100*/ .word sys_getpriority, sunos_send, sunos_recv + .word sunos_nosys, sys32_bind, sunos_setsockopt + .word sys_listen, sunos_nosys, sunos_sigaction + .word sunos_sigblock, sunos_sigsetmask, sys_sigpause + .word sys32_sigstack, sys32_recvmsg, sys32_sendmsg + .word sunos_nosys, sys32_gettimeofday, sys32_getrusage + .word sunos_getsockopt, sunos_nosys, sunos_readv + .word sunos_writev, sys32_settimeofday, sys_fchown + .word sys_fchmod, sys32_recvfrom, sys32_setreuid + .word sys_setregid, sys32_rename, sys32_truncate + .word sys32_ftruncate, sys_flock, sunos_nosys + .word sys32_sendto, sys_shutdown, sys_socketpair + .word sys32_mkdir, sys32_rmdir, sys32_utimes + .word sys_sigreturn, sunos_nosys, sys32_getpeername + .word sunos_gethostid, sunos_nosys, sys32_getrlimit + .word sys32_setrlimit, sunos_killpg, sunos_nosys + .word sunos_nosys, sunos_nosys +/*150*/ .word sys32_getsockname, sunos_nosys, sunos_nosys + .word sys32_poll, sunos_nosys, sunos_nosys + .word sunos_getdirentries, sys32_statfs, sys32_fstatfs + .word sys32_umount, sunos_nosys, sunos_nosys + .word sunos_getdomainname, sys32_setdomainname + .word sunos_nosys, sys32_quotactl, sunos_nosys + .word sunos_mount, sys32_ustat, sunos_semsys + .word sunos_nosys, sunos_shmsys, sunos_audit + .word sunos_nosys, sunos_getdents, sys_setsid + .word sys_fchdir, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sys32_sigpending, sunos_nosys + .word sys_setpgid, sunos_pathconf, sunos_fpathconf + .word sunos_sysconf, sunos_uname, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys +/*200*/ .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys +/*250*/ .word sunos_nosys, sunos_nosys, sunos_nosys + .word sunos_nosys, sunos_nosys, sys_aplib diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index df6a1c05e..8b0152231 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.5 1997/07/23 11:32:06 davem Exp $ +/* $Id: time.c,v 1.12 1997/08/22 20:12:13 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -8,6 +8,7 @@ * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -17,12 +18,17 @@ #include <linux/interrupt.h> #include <linux/timex.h> #include <linux/init.h> +#include <linux/ioport.h> #include <asm/oplib.h> #include <asm/mostek.h> #include <asm/timer.h> #include <asm/irq.h> #include <asm/io.h> +#include <asm/sbus.h> +#include <asm/fhc.h> +#include <asm/pbm.h> +#include <asm/ebus.h> struct mostek48t02 *mstk48t02_regs = 0; struct mostek48t08 *mstk48t08_regs = 0; @@ -155,33 +161,55 @@ static int has_low_battery(void) /* Probe for the real time clock chip. */ -__initfunc(static void clock_probe(void)) +__initfunc(static void set_system_time(void)) { - struct linux_prom_registers clk_reg[2]; - char model[128]; - int node, sbusnd, err; - - /* XXX HACK HACK HACK, delete me soon */ - struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX]; - int XXX_sbus_nranges; + unsigned int year, mon, day, hour, min, sec; + struct mostek48t02 *mregs; - node = prom_getchild(prom_root_node); - sbusnd = prom_searchsiblings(node, "sbus"); - node = prom_getchild(sbusnd); + do_get_fast_time = do_gettimeofday; - if(node == 0 || node == -1) { - prom_printf("clock_probe: Serious problem can't find sbus PROM node.\n"); + mregs = mstk48t02_regs; + if(!mregs) { + prom_printf("Something wrong, clock regs not mapped yet.\n"); prom_halt(); + } + + mregs->creg |= MSTK_CREG_READ; + sec = MSTK_REG_SEC(mregs); + min = MSTK_REG_MIN(mregs); + hour = MSTK_REG_HOUR(mregs); + day = MSTK_REG_DOM(mregs); + mon = MSTK_REG_MONTH(mregs); + year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); + xtime.tv_sec = mktime(year, mon, day, hour, min, sec); + xtime.tv_usec = 0; + mregs->creg &= ~MSTK_CREG_READ; +} + +__initfunc(void clock_probe(void)) +{ + struct linux_prom_registers clk_reg[2]; + char model[128]; + int node, busnd = -1, err; + + if(central_bus != NULL) { + busnd = central_bus->child->prom_node; + } +#ifdef CONFIG_PCI + else if (ebus_chain != NULL) { + busnd = ebus_chain->prom_node; + } +#endif + else { + busnd = SBus_chain->prom_node; } - /* XXX FIX ME */ - err = prom_getproperty(sbusnd, "ranges", (char *) XXX_sbus_ranges, - sizeof(XXX_sbus_ranges)); - if(err == -1) { - prom_printf("clock_probe: Cannot get XXX sbus ranges\n"); + if(busnd == -1) { + prom_printf("clock_probe: problem, cannot find bus to search.\n"); prom_halt(); } - XXX_sbus_nranges = (err / sizeof(struct linux_prom_ranges)); + + node = prom_getchild(busnd); while(1) { prom_getstring(node, "model", model, sizeof(model)); @@ -199,12 +227,48 @@ __initfunc(static void clock_probe(void)) err = prom_getproperty(node, "reg", (char *)clk_reg, sizeof(clk_reg)); if(err == -1) { - prom_printf("clock_probe: Cannot make Mostek\n"); + prom_printf("clock_probe: Cannot get Mostek reg property\n"); prom_halt(); } - /* XXX fix me badly */ - prom_adjust_regs(clk_reg, 1, XXX_sbus_ranges, XXX_sbus_nranges); + if(central_bus) { + prom_apply_fhc_ranges(central_bus->child, clk_reg, 1); + prom_apply_central_ranges(central_bus, clk_reg, 1); + } +#ifdef CONFIG_PCI + else if (ebus_chain) { + struct linux_ebus_device *edev; + + for_each_ebusdev(edev, ebus_chain) + if (edev->prom_node == node) + break; + if (!edev) { + prom_printf("%s: Mostek not probed by EBUS\n", + __FUNCTION__); + prom_halt(); + } + + if (check_region(edev->base_address[0], + sizeof(struct mostek48t59))) { + prom_printf("%s: Can't get region %lx, %d\n", + __FUNCTION__, edev->base_address[0], + sizeof(struct mostek48t59)); + prom_halt(); + } + request_region(edev->base_address[0], + sizeof(struct mostek48t59), "clock"); + + mstk48t59_regs = (struct mostek48t59 *) + edev->base_address[0]; + mstk48t02_regs = &mstk48t59_regs->regs; + break; + } +#endif + else { + prom_adjust_regs(clk_reg, 1, + SBus_chain->sbus_ranges, + SBus_chain->num_sbus_ranges); + } if(model[5] == '0' && model[6] == '2') { mstk48t02_regs = (struct mostek48t02 *) @@ -234,6 +298,8 @@ __initfunc(static void clock_probe(void)) /* Kick start the clock if it is completely stopped. */ if (mstk48t02_regs->sec & MSTK_STOP) kick_start_clock(); + + set_system_time(); } #ifndef BCD_TO_BIN @@ -246,29 +312,10 @@ __initfunc(static void clock_probe(void)) __initfunc(void time_init(void)) { - unsigned int year, mon, day, hour, min, sec; - struct mostek48t02 *mregs; - - do_get_fast_time = do_gettimeofday; - - clock_probe(); - - mregs = mstk48t02_regs; - if(!mregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - - mregs->creg |= MSTK_CREG_READ; - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_usec = 0; - mregs->creg &= ~MSTK_CREG_READ; + /* clock_probe() is now done at end of sbus_init on sparc64 + * so that both sbus and fhc bus information is probed and + * available. + */ } extern void init_timers(void (*func)(int, void *, struct pt_regs *)); @@ -301,22 +348,29 @@ void do_gettimeofday(struct timeval *tv) /* Load doubles must be used on xtime so that what we get * is guarenteed to be atomic, this is why we can run this * with interrupts on full blast. Don't touch this... -DaveM + * + * Note with time_t changes to the timeval type, I must now use + * nucleus atomic quad 128-bit loads. */ __asm__ __volatile__(" sethi %hi(linux_timers), %o1 sethi %hi(xtime), %g2 ldx [%o1 + %lo(linux_timers)], %g3 -1: ldd [%g2 + %lo(xtime)], %o4 + or %g2, %lo(xtime), %g2 +1: ldda [%g2] 0x24, %o4 + membar #LoadLoad | #MemIssue ldx [%g3], %o1 - ldd [%g2 + %lo(xtime)], %o2 + membar #LoadLoad | #MemIssue + ldda [%g2] 0x24, %o2 + membar #LoadLoad xor %o4, %o2, %o2 xor %o5, %o3, %o3 orcc %o2, %o3, %g0 - bne,pn %icc, 1b + bne,pn %xcc, 1b cmp %o1, 0 bge,pt %icc, 1f sethi %hi(tick), %o3 - ld [%o3 + %lo(tick)], %o3 + ldx [%o3 + %lo(tick)], %o3 sethi %hi(0x1fffff), %o2 or %o2, %lo(0x1fffff), %o2 add %o5, %o3, %o5 @@ -325,12 +379,12 @@ void do_gettimeofday(struct timeval *tv) sethi %hi(1000000), %o2 or %o2, %lo(1000000), %o2 cmp %o5, %o2 - bl,a,pn %icc, 1f - st %o4, [%o0 + 0x0] + bl,a,pn %xcc, 1f + stx %o4, [%o0 + 0x0] add %o4, 0x1, %o4 sub %o5, %o2, %o5 - st %o4, [%o0 + 0x0] -1: st %o5, [%o0 + 0x4]"); + stx %o4, [%o0 + 0x0] +1: stx %o5, [%o0 + 0x8]"); } void do_settimeofday(struct timeval *tv) diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S new file mode 100644 index 000000000..cefcb6ba3 --- /dev/null +++ b/arch/sparc64/kernel/trampoline.S @@ -0,0 +1,207 @@ +/* $Id: trampoline.S,v 1.2 1997/08/30 04:53:05 ralf Exp $ + * trampoline.S: Jump start slave processors on sparc64. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <asm/head.h> +#include <asm/asi.h> +#include <asm/lsu.h> +#include <asm/pstate.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/spitfire.h> +#include <asm/asm_offsets.h> + + .data + .align 8 + .globl smp_trampoline +smp_trampoline: .skip 0x300 + + .text + .align 8 + .globl sparc64_cpu_startup, sparc64_cpu_startup_end +sparc64_cpu_startup: + flushw + mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1 + stxa %g1, [%g0] ASI_LSU_CONTROL + membar #Sync + wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate + wrpr %g0, 15, %pil + + sethi %uhi(PAGE_OFFSET), %g4 + sllx %g4, 32, %g4 + + /* XXX Buggy PROM... */ + srl %o0, 0, %g6 + add %g6, %g4, %g6 + + sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 + sllx %g5, 32, %g5 + or %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5 + + sethi %uhi(_PAGE_PADDR), %g3 + or %g3, %ulo(_PAGE_PADDR), %g3 + sllx %g3, 32, %g3 + sethi %hi(_PAGE_PADDR), %g7 + or %g7, %lo(_PAGE_PADDR), %g7 + or %g3, %g7, %g3 + + clr %l0 + set 0x1fff, %l2 + rd %pc, %l3 + andn %l3, %l2, %g2 +1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 + cmp %g1, %g2 + be,a,pn %xcc, 2f + ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 + cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + +2: nop + nop + nop + and %g1, %g3, %g1 + sub %g1, %g2, %g1 + or %g5, %g1, %g5 + clr %l0 + sethi %hi(KERNBASE), %g3 + sethi %hi(KERNBASE<<1), %g7 + mov TLB_TAG_ACCESS, %l7 +1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_IMMU + stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS +2: cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop + nop + nop + clr %l0 +1: ldxa [%l0] ASI_DTLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_DMMU + stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS +2: cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop + nop + nop + sethi %hi(KERNBASE), %g3 + mov (63 << 3), %g7 + stxa %g3, [%l7] ASI_DMMU + stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS + membar #Sync + stxa %g3, [%l7] ASI_IMMU + stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS + membar #Sync + flush %g3 + membar #Sync + b,pt %xcc, 1f + nop +1: set bounce, %g2 + jmpl %g2 + %g0, %g0 + nop + +bounce: + mov PRIMARY_CONTEXT, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync + mov SECONDARY_CONTEXT, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync + + mov TLB_TAG_ACCESS, %g2 + stxa %g3, [%g2] ASI_IMMU + stxa %g3, [%g2] ASI_DMMU + + mov (63 << 3), %g7 + ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 + andn %g1, (_PAGE_G), %g1 + stxa %g1, [%g7] ASI_ITLB_DATA_ACCESS + membar #Sync + + ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g1 + andn %g1, (_PAGE_G), %g1 + stxa %g1, [%g7] ASI_DTLB_DATA_ACCESS + membar #Sync + + flush %g3 + membar #Sync + + mov 1, %g5 + sllx %g5, (PAGE_SHIFT + 1), %g5 + sub %g5, (REGWIN_SZ + STACK_BIAS), %g5 + add %g6, %g5, %sp + mov 0, %fp + + wrpr %g0, 0, %wstate + wrpr %g0, 0, %tl + + /* Setup the trap globals, then we can resurface. */ + rdpr %pstate, %o1 + mov %g6, %o2 + wrpr %o1, (PSTATE_AG | PSTATE_IE), %pstate + sethi %hi(sparc64_ttable_tl0), %g5 + wrpr %g5, %tba + mov %o2, %g6 + + wrpr %o1, (PSTATE_MG | PSTATE_IE), %pstate + sethi %hi(0x1ff8), %g2 + or %g2, %lo(0x1ff8), %g2 + ldx [%o2 + AOFF_task_mm], %g6 + ldx [%g6 + AOFF_mm_pgd], %g6 + clr %g7 + + wrpr %o1, (PSTATE_IG | PSTATE_IE), %pstate + sethi %hi(ivector_to_mask), %g5 + or %g5, %lo(ivector_to_mask), %g1 + mov 0x40, %g2 + + wrpr %g0, 0, %wstate + wrpr %o1, PSTATE_IE, %pstate + + mov TSB_REG, %o4 + mov 1, %o5 + stxa %o5, [%o4] ASI_DMMU + stxa %o5, [%o4] ASI_IMMU + membar #Sync + + or %o1, PSTATE_IE, %o1 + wrpr %o1, 0, %pstate + + call smp_callin + nop + call cpu_idle + mov 0, %o0 + call cpu_panic + nop +1: b,a,pt %xcc, 1b + + .align 8 +sparc64_cpu_startup_end: diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index ac3e79958..1ffd43730 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.29 1997/07/05 09:52:38 davem Exp $ +/* $Id: traps.c,v 1.31 1997/08/11 14:35:33 davem Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -25,6 +25,7 @@ #include <asm/unistd.h> #include <asm/uaccess.h> #include <asm/fpumacro.h> +#include <asm/lsu.h> /* #define SYSCALL_TRACING */ /* #define VERBOSE_SYSCALL_TRACING */ @@ -194,8 +195,55 @@ void data_access_exception (struct pt_regs *regs) send_sig(SIGSEGV, current, 1); } +#ifdef CONFIG_PCI +/* This is really pathetic... */ +/* #define DEBUG_PCI_POKES */ +extern volatile int pci_poke_in_progress; +extern volatile int pci_poke_faulted; +#endif + void do_dae(struct pt_regs *regs) { +#ifdef CONFIG_PCI +#ifdef DEBUG_PCI_POKES + prom_printf(" (POKE "); +#endif + if(pci_poke_in_progress) { + unsigned long va; +#ifdef DEBUG_PCI_POKES + prom_printf("tpc[%016lx] tnpc[%016lx] ", + regs->tpc, regs->tnpc); +#endif + pci_poke_faulted = 1; + regs->tnpc = regs->tpc + 4; + + +#ifdef DEBUG_PCI_POKES + prom_printf("PCI) "); + /* prom_halt(); */ +#endif + /* Re-enable I/D caches, Ultra turned them off. */ + for(va = 0; va < (PAGE_SIZE << 1); va += 32) { + spitfire_put_icache_tag(va, 0x0); + spitfire_put_dcache_tag(va, 0x0); + } + __asm__ __volatile__("flush %%g6\n\t" + "membar #Sync\n\t" + "stxa %0, [%%g0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC | + LSU_CONTROL_IM | LSU_CONTROL_DM), + "i" (ASI_LSU_CONTROL) + : "memory"); + return; + } +#ifdef DEBUG_PCI_POKES + prom_printf("USER) "); + prom_printf("tpc[%016lx] tnpc[%016lx]\n"); + prom_halt(); +#endif +#endif send_sig(SIGSEGV, current, 1); } @@ -215,11 +263,9 @@ void do_fpe_common(struct pt_regs *regs) regs->tpc = regs->tnpc; regs->tnpc += 4; } else { - lock_kernel(); current->tss.sig_address = regs->tpc; current->tss.sig_desc = SUBSIG_FPERROR; send_sig(SIGFPE, current, 1); - unlock_kernel(); } } @@ -288,6 +334,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) } printk("Instruction DUMP:"); instruction_dump ((unsigned int *) regs->tpc); + lock_kernel(); /* Or else! */ if(regs->tstate & TSTATE_PRIV) do_exit(SIGKILL); do_exit(SIGSEGV); @@ -298,13 +345,11 @@ void do_illegal_instruction(struct pt_regs *regs) unsigned long pc = regs->tpc; unsigned long tstate = regs->tstate; - lock_kernel(); if(tstate & TSTATE_PRIV) die_if_kernel("Kernel illegal instruction", regs); current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_ILLINST; send_sig(SIGILL, current, 1); - unlock_kernel(); } void mem_address_unaligned(struct pt_regs *regs) @@ -333,19 +378,16 @@ void do_privact(struct pt_regs *regs) current->tss.sig_address = regs->tpc; current->tss.sig_desc = SUBSIG_PRIVINST; send_sig(SIGILL, current, 1); - unlock_kernel(); } void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long tstate) { - lock_kernel(); if(tstate & TSTATE_PRIV) die_if_kernel("Penguin instruction from Penguin mode??!?!", regs); current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_PRIVINST; send_sig(SIGILL, current, 1); - unlock_kernel(); } /* XXX User may want to be allowed to do this. XXX */ @@ -353,7 +395,6 @@ void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long n void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long tstate) { - lock_kernel(); if(regs->tstate & TSTATE_PRIV) { printk("KERNEL MNA at pc %016lx npc %016lx called by %016lx\n", pc, npc, regs->u_regs[UREG_RETPC]); @@ -363,15 +404,12 @@ void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned lon current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_PRIVINST; send_sig(SIGBUS, current, 1); - unlock_kernel(); } void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long psr) { - lock_kernel(); send_sig(SIGILL, current, 1); - unlock_kernel(); } /* Trap level 1 stuff or other traps we should never see... */ diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 73bda96d9..bf00cb231 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.18 1997/07/05 09:52:41 davem Exp $ +/* $Id: ttable.S,v 1.20 1997/08/29 15:51:39 jj Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -44,7 +44,8 @@ tl0_irq5: TRAP_IRQ(handler_irq, 5) TRAP_IRQ(handler_irq, 6) tl0_irq7: TRAP_IRQ(handler_irq, 7) TRAP_IRQ(handler_irq, 8) tl0_irq9: TRAP_IRQ(handler_irq, 9) TRAP_IRQ(handler_irq, 10) tl0_irq11: TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12) -tl0_irq13: TRAP_IRQ(handler_irq, 13) TRAP_IRQ(handler_irq, 14) +tl0_irq13: TRAP_IRQ(handler_irq, 13) +tl0_itick: TRAP_TICK tl0_irq15: TRAP_IRQ(handler_irq, 15) tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55) tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b) @@ -99,6 +100,7 @@ tl0_bkpt: BREAKPOINT_TRAP tl0_resv102: BTRAP(0x102) tl0_flushw: FLUSH_WINDOW_TRAP tl0_resv104: BTRAP(0x104) BTRAP(0x105) BTRAP(0x106) BTRAP(0x107) + .globl tl0_solaris tl0_solaris: SOLARIS_SYSCALL_TRAP tl0_netbsd: NETBSD_SYSCALL_TRAP tl0_resv10a: BTRAP(0x10a) BTRAP(0x10b) BTRAP(0x10c) BTRAP(0x10d) BTRAP(0x10e) diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index f66889195..3aea13953 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.1 1997/07/18 06:26:45 ralf Exp $ +/* $Id: unaligned.c,v 1.4 1997/08/19 15:25:11 jj Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -205,19 +205,19 @@ __asm__ __volatile__ ( \ "stx %%g7, [%0 + 8]\n" \ "0:\n\n\t" \ ".section __ex_table\n\t" \ - ".xword 4b, " #errh "\n\t" \ - ".xword 5b, " #errh "\n\t" \ - ".xword 6b, " #errh "\n\t" \ - ".xword 7b, " #errh "\n\t" \ - ".xword 8b, " #errh "\n\t" \ - ".xword 9b, " #errh "\n\t" \ - ".xword 10b, " #errh "\n\t" \ - ".xword 11b, " #errh "\n\t" \ - ".xword 12b, " #errh "\n\t" \ - ".xword 13b, " #errh "\n\t" \ - ".xword 14b, " #errh "\n\t" \ - ".xword 15b, " #errh "\n\t" \ - ".xword 16b, " #errh "\n\n\t" \ + ".word 4b, " #errh "\n\t" \ + ".word 5b, " #errh "\n\t" \ + ".word 6b, " #errh "\n\t" \ + ".word 7b, " #errh "\n\t" \ + ".word 8b, " #errh "\n\t" \ + ".word 9b, " #errh "\n\t" \ + ".word 10b, " #errh "\n\t" \ + ".word 11b, " #errh "\n\t" \ + ".word 12b, " #errh "\n\t" \ + ".word 13b, " #errh "\n\t" \ + ".word 14b, " #errh "\n\t" \ + ".word 15b, " #errh "\n\t" \ + ".word 16b, " #errh "\n\n\t" \ ".previous\n\t" \ : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), "r" (asi) \ : "l1", "l2", "g7", "g1", "cc"); \ @@ -259,20 +259,20 @@ __asm__ __volatile__ ( \ "17:\t" "stba %%l1, [%0 + 1] %%asi\n" \ "0:\n\n\t" \ ".section __ex_table\n\t" \ - ".xword 4b, " #errh "\n\t" \ - ".xword 5b, " #errh "\n\t" \ - ".xword 6b, " #errh "\n\t" \ - ".xword 7b, " #errh "\n\t" \ - ".xword 8b, " #errh "\n\t" \ - ".xword 9b, " #errh "\n\t" \ - ".xword 10b, " #errh "\n\t" \ - ".xword 11b, " #errh "\n\t" \ - ".xword 12b, " #errh "\n\t" \ - ".xword 13b, " #errh "\n\t" \ - ".xword 14b, " #errh "\n\t" \ - ".xword 15b, " #errh "\n\t" \ - ".xword 16b, " #errh "\n\t" \ - ".xword 17b, " #errh "\n\n\t" \ + ".word 4b, " #errh "\n\t" \ + ".word 5b, " #errh "\n\t" \ + ".word 6b, " #errh "\n\t" \ + ".word 7b, " #errh "\n\t" \ + ".word 8b, " #errh "\n\t" \ + ".word 9b, " #errh "\n\t" \ + ".word 10b, " #errh "\n\t" \ + ".word 11b, " #errh "\n\t" \ + ".word 12b, " #errh "\n\t" \ + ".word 13b, " #errh "\n\t" \ + ".word 14b, " #errh "\n\t" \ + ".word 15b, " #errh "\n\t" \ + ".word 16b, " #errh "\n\t" \ + ".word 17b, " #errh "\n\n\t" \ ".previous\n\t" \ : : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi) \ : "l1", "l2", "g7", "g1", "cc"); \ diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc64/kernel/winfixup.S index f2c714eae..0ebf92767 100644 --- a/arch/sparc64/kernel/winfixup.S +++ b/arch/sparc64/kernel/winfixup.S @@ -1,4 +1,4 @@ -/* $Id: winfixup.S,v 1.16 1997/07/13 20:02:42 davem Exp $ +/* $Id: winfixup.S,v 1.19 1997/08/08 08:33:37 jj Exp $ * * winfixup.S: Handle cases where user stack pointer is found to be bogus. * @@ -143,8 +143,9 @@ spill_fixup: retry window_scheisse_from_user_common: wrpr %g1, %cwp + sethi %hi(109f), %g7 ba,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 window_scheisse_merge: srlx %l5, PAGE_SHIFT, %o1 @@ -244,8 +245,9 @@ spill_fixup_mna: retry window_mna_from_user_common: wrpr %g1, %cwp + sethi %hi(109f), %g7 ba,pt %xcc, etrap - rd %pc, %g7 +109: or %g7, %lo(109b), %g7 window_mna_merge: call mem_address_unaligned add %sp, STACK_BIAS + REGWIN_SZ, %o0 diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index 3da21c606..9f8729ee5 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile @@ -1,12 +1,12 @@ -# $Id: Makefile,v 1.13 1997/07/16 10:12:03 jj Exp $ +# $Id: Makefile,v 1.15 1997/08/19 03:11:50 davem Exp $ # Makefile for Sparc library files.. # -CFLAGS := $(CFLAGS) -ansi +CFLAGS := $(CFLAGS) -OBJS = blockops.o locks.o strlen.o strncmp.o \ +OBJS = PeeCeeI.o blockops.o locks.o strlen.o strncmp.o \ memscan.o strncpy_from_user.o strlen_user.o memcmp.o checksum.o \ - VIScopy.o VISbzero.o VISmemset.o VIScsum.o + VIScopy.o VISbzero.o VISmemset.o VIScsum.o VIScsumcopy.o lib.a: $(OBJS) $(AR) rcs lib.a $(OBJS) diff --git a/arch/sparc64/lib/PeeCeeI.c b/arch/sparc64/lib/PeeCeeI.c new file mode 100644 index 000000000..6677f581a --- /dev/null +++ b/arch/sparc64/lib/PeeCeeI.c @@ -0,0 +1,225 @@ +/* $Id: PeeCeeI.c,v 1.3 1997/08/28 23:59:52 davem Exp $ + * PeeCeeI.c: The emerging standard... + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> + +#ifdef CONFIG_PCI + +#include <asm/io.h> + +void outsb(unsigned long addr, const void *src, unsigned long count) +{ + const u8 *p = src; + + while(count--) + outb(*p++, addr); +} + +void outsw(unsigned long addr, const void *src, unsigned long count) +{ + if(count) { + const u16 *ps = src; + const u32 *pi; + + if(((u64)src) & 0x2) { + outw(*ps++, addr); + count--; + } + pi = (const u32 *)ps; + while(count >= 2) { + u32 w; + + w = *pi++; + outw(w >> 16, addr); + outw(w, addr); + count -= 2; + } + ps = (const u16 *)pi; + if(count) + outw(*ps, addr); + } +} + +void outsl(unsigned long addr, const void *src, unsigned long count) +{ + if(count) { + if((((u64)src) & 0x3) == 0) { + const u32 *p = src; + while(count--) + outl(*p++, addr); + } else { + const u8 *pb; + const u16 *ps = src; + u32 l = 0, l2; + const u32 *pi; + + switch(((u64)src) & 0x3) { + case 0x2: + count -= 1; + l = *ps++; + pi = (const u32 *)ps; + while(count--) { + l2 = *pi++; + outl(((l <<16) | (l2 >> 16)), addr); + l = l2; + } + ps = (const u16 *)pi; + outl(((l << 16) | (*ps >> 16)), addr); + break; + + case 0x1: + count -= 1; + pb = src; + l = (*pb++ << 16); + ps = (const u16 *)pb; + l |= *ps++; + pi = (const u32 *)ps; + while(count--) { + l2 = *pi++; + outl(((l << 8) | (l2 >> 24)), addr); + l = l2; + } + pb = (const u8 *)pi; + outl(((l << 8) | (*pb >> 24)), addr); + break; + + case 0x3: + count -= 1; + pb = src; + l = (*pb++ >> 24); + pi = (const u32 *)pb; + while(count--) { + l2 = *pi++; + outl(((l << 24) | (l2 >> 8)), addr); + l = l2; + } + ps = (const u16 *)pi; + l2 = (*ps++ << 16); + pb = (const u8 *)ps; + l2 |= (*pb << 8); + outl(((l << 24) | (l2 >> 8)), addr); + break; + } + } + } +} + +void insb(unsigned long addr, void *dst, unsigned long count) +{ + if(count) { + u32 *pi; + u8 *pb = dst; + + while((((unsigned long)pb) & 0x3) && count--) + *pb++ = inb(addr); + pi = (u32 *)pb; + while(count >= 4) { + u32 w; + + w = (inb(addr) << 24); + w |= (inb(addr) << 16); + w |= (inb(addr) << 8); + w |= inb(addr); + *pi++ = w; + count -= 4; + } + pb = (u8 *)pi; + while(count--) + *pb++ = inb(addr); + } +} + +void insw(unsigned long addr, void *dst, unsigned long count) +{ + if(count) { + u16 *ps = dst; + u32 *pi; + + if(((unsigned long)ps) & 0x2) { + *ps++ = inw(addr); + count--; + } + pi = (u32 *)ps; + while(count >= 2) { + u32 w; + + w = (inw(addr) << 16); + w |= inw(addr); + *pi++ = w; + count -= 2; + } + ps = (u16 *)pi; + if(count) + *ps = inw(addr); + } +} + +void insl(unsigned long addr, void *dst, unsigned long count) +{ + if(count) { + if((((unsigned long)dst) & 0x3) == 0) { + u32 *pi = dst; + while(count--) + *pi++ = inl(addr); + } else { + u32 l = 0, l2, *pi; + u16 *ps; + u8 *pb; + + switch(((unsigned long)dst) & 3) { + case 0x2: + ps = dst; + count -= 1; + l = inl(addr); + *ps++ = (l >> 16); + pi = (u32 *)ps; + while(count--) { + l2 = inl(addr); + *pi++ = (l << 16) | (l2 >> 16); + l = l2; + } + ps = (u16 *)pi; + *ps = (l << 16); + break; + + case 0x1: + pb = dst; + count -= 1; + *pb++ = (l >> 24); + ps = (u16 *)pb; + *ps++ = (l >> 8); + pi = (u32 *)ps; + while(count--) { + l2 = inl(addr); + *pi++ = ((l << 24) | (l2 >> 8)); + l = l2; + } + pb = (u8 *)pi; + *pb = (l >> 8); + break; + + case 0x3: + pb = (u8 *)dst; + count -= 1; + l = inl(addr); + *pb++ = l >> 24; + pi = (u32 *)pb; + while(count--) { + l2 = inl(addr); + *pi++ = ((l >> 24) | (l2 << 8)); + l = l2; + } + ps = (u16 *)pi; + *ps++ = l >> 8; + pb = (u8 *)ps; + *pb = l; + break; + } + } + } +} + +#endif /* CONFIG_PCI */ diff --git a/arch/sparc64/lib/VISbzero.S b/arch/sparc64/lib/VISbzero.S index 3c86861fd..ede87843b 100644 --- a/arch/sparc64/lib/VISbzero.S +++ b/arch/sparc64/lib/VISbzero.S @@ -1,4 +1,4 @@ -/* $Id: VISbzero.S,v 1.1 1997/07/18 06:26:48 ralf Exp $ +/* $Id: VISbzero.S,v 1.8 1997/08/22 15:54:50 jj Exp $ * VISbzero.S: High speed clear operations utilizing the UltraSparc * Visual Instruction Set. * @@ -16,8 +16,8 @@ 99: ba VISbzerofixup_ret##z; \ a, b, %o0; \ .section __ex_table; \ - .align 8; \ - .xword 98b, 99b; \ + .align 4; \ + .word 98b, 99b; \ .text; \ .align 4; #define EXC(x,y,a,b,c...) \ @@ -28,15 +28,15 @@ ba VISbzerofixup_ret0; \ a, b, %o0; \ .section __ex_table; \ - .align 8; \ - .xword 98b, 99b; \ + .align 4; \ + .word 98b, 99b; \ .text; \ .align 4; #define EXO1(x,y) \ 98: x,y; \ .section __ex_table; \ - .align 8; \ - .xword 98b, VISbzerofixup_reto1; \ + .align 4; \ + .word 98b, VISbzerofixup_reto1; \ .text; \ .align 4; #define EX(x,y,a,b) EXN(x,y,a,b,0) @@ -44,8 +44,8 @@ #define EX2(x,y,a,b) EXN(x,y,a,b,2) #define EXT(start,end,handler) \ .section __ex_table; \ - .align 8; \ - .xword start, 0, end, handler; \ + .align 4; \ + .word start, 0, end, handler; \ .text; \ .align 4 #else @@ -147,7 +147,7 @@ bzero: #else wr %g0, ASI_BLK_P, %asi #endif - membar #StoreStore | #LoadStore + membar #StoreLoad | #StoreStore | #LoadStore fzero %f0 andcc %o3, 0xc0, %o2 and %o1, 0x3f, %o1 @@ -180,16 +180,28 @@ bzero: #ifdef __KERNEL__ wr %g0, 0, %fprs wr %g7, 0x0, %asi +#else +#ifndef REGS_64BIT + wr %g0, FPRS_FEF, %fprs #endif - membar #Sync +#endif + membar #StoreLoad | #StoreStore 9: andcc %o1, 0xf8, %o2 be,pn %xcc, 13f andcc %o1, 7, %o1 +#ifdef __KERNEL__ +14: sethi %hi(13f), %o4 + srl %o2, 1, %o3 + sub %o4, %o3, %o4 + jmpl %o4 + %lo(13f), %g0 + add %o0, %o2, %o0 +#else 14: rd %pc, %o4 srl %o2, 1, %o3 sub %o4, %o3, %o4 jmpl %o4 + (13f - 14b), %g0 add %o0, %o2, %o0 +#endif 12: ZERO_BLOCKS(%o0, 0xc8, %g0) ZERO_BLOCKS(%o0, 0x88, %g0) ZERO_BLOCKS(%o0, 0x48, %g0) diff --git a/arch/sparc64/lib/VIScopy.S b/arch/sparc64/lib/VIScopy.S index 1429f1658..40b781e73 100644 --- a/arch/sparc64/lib/VIScopy.S +++ b/arch/sparc64/lib/VIScopy.S @@ -1,4 +1,4 @@ -/* $Id: VIScopy.S,v 1.1 1997/07/18 06:26:48 ralf Exp $ +/* $Id: VIScopy.S,v 1.14 1997/08/22 15:54:53 jj Exp $ * VIScopy.S: High speed copy operations utilizing the UltraSparc * Visual Instruction Set. * @@ -42,8 +42,8 @@ 99: ba VIScopyfixup_ret; \ a, b, %o0; \ .section __ex_table; \ - .align 8; \ - .xword 98b, 99b; \ + .align 4; \ + .word 98b, 99b; \ .text; \ .align 4; #define EX2(x,y,c,d,e,a,b) \ @@ -54,37 +54,48 @@ ba VIScopyfixup_ret; \ a, b, %o0; \ .section __ex_table; \ - .align 8; \ - .xword 98b, 99b; \ + .align 4; \ + .word 98b, 99b; \ .text; \ .align 4; #define EXO2(x,y) \ 98: x,y; \ .section __ex_table; \ - .align 8; \ - .xword 98b, VIScopyfixup_reto2; \ + .align 4; \ + .word 98b, VIScopyfixup_reto2; \ .text; \ .align 4; #define EXVISN(x,y,n) \ 98: x,y; \ .section __ex_table; \ - .align 8; \ - .xword 98b, VIScopyfixup_vis##n; \ + .align 4; \ + .word 98b, VIScopyfixup_vis##n; \ .text; \ .align 4; #define EXT(start,end,handler) \ .section __ex_table; \ - .align 8; \ - .xword start, 0, end, handler; \ + .align 4; \ + .word start, 0, end, handler; \ .text; \ .align 4; #else -#define FPU_CLEAN_RETL \ - retl; \ +#ifdef REGS_64BIT +#define FPU_CLEAN_RETL \ + retl; \ mov %g6, %o0; -#define FPU_RETL \ - retl; \ +#define FPU_RETL \ + retl; \ mov %g6, %o0; +#else +#define FPU_CLEAN_RETL \ + wr %g0, FPRS_FEF, %fprs; \ + retl; \ + mov %g6, %o0; +#define FPU_RETL \ + wr %g0, FPRS_FEF, %fprs; \ + retl; \ + mov %g6, %o0; +#endif #define NORMAL_RETL \ retl; \ mov %g6, %o0; @@ -113,8 +124,8 @@ #define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \ EXVIS(LDBLK [%src] ASIBLK, %fdest); \ - add %src, 0x40, %src; \ ASI_SETDST_BLK \ + add %src, 0x40, %src; \ add %dest, 0x40, %dest; \ subcc %len, 0x40, %len; \ be,pn %xcc, jmptgt; \ @@ -303,7 +314,7 @@ copy_page: wr %g0, FPRS_FEF, %fprs ! FPU Group sethi %hi(8192), %o2 ! IEU0 Group mov ASI_BLK_P, asi_src ! IEU1 b,pt %xcc, dest_is_64byte_aligned ! CTI - mov ASI_BLK_COMMIT_P, asi_dest ! IEU0 Group + mov ASI_BLK_P, asi_dest ! IEU0 Group .align 32 .globl __copy_from_user @@ -446,6 +457,13 @@ dest_is_64byte_aligned: EXVIS1(LDBLK [%o1 + 0x40] ASIBLK, %f16) ! LSU Group sub %g7, 0x80, %g7 ! IEU0 EXVIS(LDBLK [%o1 + 0x80] ASIBLK, %f32) ! LSU Group +#ifdef __KERNEL__ +vispc: sll %g2, 9, %g2 ! IEU0 Group + sethi %hi(vis00), %g5 ! IEU1 + or %g5, %lo(vis00), %g5 ! IEU0 Group + jmpl %g5 + %g2, %g0 ! CTI Group brk forced + addcc %o1, 0xc0, %o1 ! IEU1 Group +#else ! Clk1 Group 8-( ! Clk2 Group 8-( ! Clk3 Group 8-( @@ -455,6 +473,7 @@ vispc: rd %pc, %g5 ! PDU Group 8-( sll %g2, 9, %g2 ! IEU0 jmpl %g5 + %g2, %g0 ! CTI Group brk forced addcc %o1, 0xc0, %o1 ! IEU1 Group +#endif .align 512 /* OK, here comes the fun part... */ vis00:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) LOOP_CHUNK1(o1, o0, g7, vis01) FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) LOOP_CHUNK2(o1, o0, g7, vis02) @@ -721,20 +740,21 @@ __memcpy_16plus: 3: andcc %o2, 0x70, %g7 ! IEU1 Group 41: be,pn %xcc, 80f ! CTI andcc %o2, 8, %g0 ! IEU1 Group - ! Clk1 8-( - ! Clk2 8-( - ! Clk3 8-( - ! Clk4 8-( -79: rd %pc, %o5 ! PDU Group #ifdef __KERNEL__ +79: sethi %hi(80f), %o5 ! IEU0 sll %g7, 1, %g5 ! IEU0 Group add %o1, %g7, %o1 ! IEU1 srl %g7, 1, %g2 ! IEU0 Group sub %o5, %g5, %o5 ! IEU1 sub %o5, %g2, %o5 ! IEU0 Group - jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced + jmpl %o5 + %lo(80f), %g0 ! CTI Group brk forced add %o0, %g7, %o0 ! IEU0 Group #else + ! Clk1 8-( + ! Clk2 8-( + ! Clk3 8-( + ! Clk4 8-( +79: rd %pc, %o5 ! PDU Group sll %g7, 1, %g5 ! IEU0 Group add %o1, %g7, %o1 ! IEU1 sub %o5, %g5, %o5 ! IEU0 Group @@ -814,19 +834,20 @@ normal_retl: andcc %o2, 0x70, %g7 ! IEU1 be,pn %xcc, 84f ! CTI andcc %o2, 8, %g0 ! IEU1 Group +#ifdef __KERNEL__ +83: srl %g7, 1, %g5 ! IEU0 + sethi %hi(84f), %o5 ! IEU0 Group + add %g7, %g5, %g5 ! IEU1 + add %o1, %g7, %o1 ! IEU0 Group + sub %o5, %g5, %o5 ! IEU1 + jmpl %o5 + %lo(84f), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#else ! Clk1 8-( ! Clk2 8-( ! Clk3 8-( ! Clk4 8-( 83: rd %pc, %o5 ! PDU Group -#ifdef __KERNEL__ - srl %g7, 1, %g5 ! IEU0 Group - add %g7, %g5, %g5 ! IEU0 Group - add %o1, %g7, %o1 ! IEU1 - sub %o5, %g5, %o5 ! IEU0 Group - jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced - add %o0, %g7, %o0 ! IEU0 Group -#else add %o1, %g7, %o1 ! IEU0 Group sub %o5, %g7, %o5 ! IEU1 jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced diff --git a/arch/sparc64/lib/VIScsum.S b/arch/sparc64/lib/VIScsum.S index 1ccb98759..81b020c49 100644 --- a/arch/sparc64/lib/VIScsum.S +++ b/arch/sparc64/lib/VIScsum.S @@ -1,4 +1,4 @@ -/* $Id: VIScsum.S,v 1.1 1997/07/18 06:26:49 ralf Exp $ +/* $Id: VIScsum.S,v 1.2 1997/08/08 08:34:05 jj Exp $ * VIScsum.S: High bandwidth IP checksumming utilizing the UltraSparc * Visual Instruction Set. * @@ -345,11 +345,19 @@ csum_partial: 20: andcc %o1, 0xf0, %g1 /* IEU1 Group */ be,pn %icc, 23f /* CTI */ and %o1, 0xf, %o3 /* IEU0 */ -22: rd %pc, %g7 /* LSU Group */ +#ifdef __KERNEL__ +22: sll %g1, 1, %o4 /* IEU0 Group */ + sethi %hi(23f), %g7 /* IEU1 */ + sub %g7, %o4, %g7 /* IEU0 Group */ + jmpl %g7 + %lo(23f), %g0 /* CTI Group brk forced */ + add %o0, %g1, %o0 /* IEU0 */ +#else +22: rd %pc, %g7 /* LSU Group+4bubbles */ sll %g1, 1, %o4 /* IEU0 Group */ sub %g7, %o4, %g7 /* IEU0 Group (regdep) */ jmpl %g7 + (23f - 22b), %g0 /* CTI Group brk forced */ - add %o0, %g1, %o0 /* IEU0 */ + add %o0, %g1, %o0 /* IEU0 */ +#endif CSUM_LASTCHUNK(0xe0) CSUM_LASTCHUNK(0xd0) CSUM_LASTCHUNK(0xc0) diff --git a/arch/sparc64/lib/VIScsumcopy.S b/arch/sparc64/lib/VIScsumcopy.S new file mode 100644 index 000000000..efd2bfcd5 --- /dev/null +++ b/arch/sparc64/lib/VIScsumcopy.S @@ -0,0 +1,880 @@ +/* $Id: VIScsumcopy.S,v 1.2 1997/08/19 15:25:22 jj Exp $ + * VIScsumcopy.S: High bandwidth IP checksumming with simultaneous + * copying utilizing the UltraSparc Visual Instruction Set. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Based on older sparc32/sparc64 checksum.S, which is: + * + * Copyright(C) 1995 Linus Torvalds + * Copyright(C) 1995 Miguel de Icaza + * Copyright(C) 1996,1997 David S. Miller + * derived from: + * Linux/Alpha checksum c-code + * Linux/ix86 inline checksum assembly + * RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code) + * David Mosberger-Tang for optimized reference c-code + * BSD4.4 portable checksum routine + */ + +#ifdef __sparc_v9__ +#define STACKOFF 0x7ff+128 +#else +#define STACKOFF 64 +#endif + +#ifdef __KERNEL__ +#include <asm/head.h> +#include <asm/asi.h> +#include <asm/page.h> +#else +#define ASI_P 0x80 +#define ASI_BLK_P 0xf0 +#define FRPS_FEF 0x04 +#define FPRS_DU 0x02 +#define FPRS_DL 0x01 +#endif +#define ASI_BLK_XOR (ASI_BLK_P ^ ASI_P) + +#define src o0 +#define dst o1 +#define len o2 +#define sum o3 +#define x1 g1 +#define x2 g2 +#define x3 g3 +#define x4 g4 +#define x5 g5 +#define x6 g7 +#define x7 o4 +#define x8 o5 + +/* Dobrou noc, SunSoft engineers. Spete sladce. + * This has a couple of tricks in and those + * tricks are UltraLinux trade secrets :)) + * Once AGAIN, the SunSoft engineers are caught + * asleep at the keyboard :)). + * The main loop does about 20 superscalar cycles + * per 64bytes checksummed/copied. + */ + +#define LDBLK(O0) \ + ldda [%src] %asi, %O0 /* Load Group */ + +#define STBLK \ + stda %f48, [%dst] ASI_BLK_P /* Store */ + +#define ST(fx,off) \ + std %fx, [%dst + off] /* Store */ + +#define SYNC \ + membar #Sync + + +#define DO_THE_TRICK(f0,f2,f4,f6,f8,f10,f12,f14,F0,F2,F4,F6,F8,F10,F12,F14,DUMMY1,A0,A2,A4,A6,A8,A10,A12,A14,B14,DYMMY2,LOAD,STORE1,STORE2,STORE3,STORE4,STORE5,STORE6,STORE7,STORE8,DUMMY3,BRANCH...) \ + LOAD /* Load Group */; \ + faligndata %A14, %F0, %A14 /* FPA Group */; \ + inc %x5 /* IEU0 */; \ + STORE1 /* Store (optional) */; \ + faligndata %F0, %F2, %A0 /* FPA Group */; \ + srl %x5, 1, %x5 /* IEU0 */; \ + add %sum, %x4, %sum /* IEU1 */; \ + fpadd32 %F0, %f0, %F0 /* FPA Group */; \ + inc %x6 /* IEU0 */; \ + STORE2 /* Store (optional) */; \ + faligndata %F2, %F4, %A2 /* FPA Group */; \ + srl %x6, 1, %x6 /* IEU0 */; \ + add %sum, %x5, %sum /* IEU1 */; \ + fpadd32 %F2, %f2, %F2 /* FPA Group */; \ + add %src, 64, %src /* IEU0 */; \ + add %dst, 64, %dst /* IEU1 */; \ + fcmpgt32 %f0, %F0, %x1 /* FPM Group */; \ + inc %x7 /* IEU0 */; \ + STORE3 /* Store (optional) */; \ + faligndata %F4, %F6, %A4 /* FPA */; \ + srl %x7, 1, %x7 /* IEU0 Group */; \ + add %sum, %x6, %sum /* IEU1 */; \ + fpadd32 %F4, %f4, %F4 /* FPA */; \ + fcmpgt32 %f2, %F2, %x2 /* FPM Group */; \ + inc %x8 /* IEU0 */; \ + STORE4 /* Store (optional) */; \ + faligndata %F6, %F8, %A6 /* FPA */; \ + srl %x8, 1, %x8 /* IEU0 Group */; \ + add %sum, %x7, %sum /* IEU1 */; \ + fpadd32 %F6, %f6, %F6 /* FPA */; \ + fcmpgt32 %f4, %F4, %x3 /* FPM Group */; \ + inc %x1 /* IEU0 */; \ + STORE5 /* Store (optional) */; \ + faligndata %F8, %F10, %A8 /* FPA */; \ + srl %x1, 1, %x1 /* IEU0 Group */; \ + add %sum, %x8, %sum /* IEU1 */; \ + fpadd32 %F8, %f8, %F8 /* FPA */; \ + fcmpgt32 %f6, %F6, %x4 /* FPM Group */; \ + inc %x2 /* IEU0 */; \ + STORE6 /* Store (optional) */; \ + faligndata %F10, %F12, %A10 /* FPA */; \ + srl %x2, 1, %x2 /* IEU0 Group */; \ + add %sum, %x1, %sum /* IEU1 */; \ + fpadd32 %F10, %f10, %F10 /* FPA */; \ + fcmpgt32 %f8, %F8, %x5 /* FPM Group */; \ + inc %x3 /* IEU0 */; \ + STORE7 /* Store (optional) */; \ + faligndata %F12, %F14, %A12 /* FPA */; \ + srl %x3, 1, %x3 /* IEU0 Group */; \ + add %sum, %x2, %sum /* IEU1 */; \ + fpadd32 %F12, %f12, %F12 /* FPA */; \ + fcmpgt32 %f10, %F10, %x6 /* FPM Group */; \ + inc %x4 /* IEU0 */; \ + STORE8 /* Store (optional) */; \ + fmovd %F14, %B14 /* FPA */; \ + srl %x4, 1, %x4 /* IEU0 Group */; \ + add %sum, %x3, %sum /* IEU1 */; \ + fpadd32 %F14, %f14, %F14 /* FPA */; \ + fcmpgt32 %f12, %F12, %x7 /* FPM Group */; \ + subcc %len, 64, %len /* IEU1 */; \ + BRANCH /* CTI */; \ + fcmpgt32 %f14, %F14, %x8 /* FPM Group */; \ + +#define END_THE_TRICK(f0,f2,f4,f6,f8,f10,f12,f14,FA,FB,S0,S1,S2,S3,T0,T1,U0,fz) \ + inc %x5 /* IEU0 Group */; \ + fpadd32 %f2, %f0, %S0 /* FPA */; \ + srl %x5, 1, %x5 /* IEU0 Group */; \ + add %sum, %x4, %sum /* IEU1 */; \ + fpadd32 %f6, %f4, %S1 /* FPA */; \ + inc %x6 /* IEU0 Group */; \ + add %sum, %x5, %sum /* IEU1 */; \ + fcmpgt32 %f0, %S0, %x1 /* FPM Group */; \ + srl %x6, 1, %x6 /* IEU0 */; \ + inc %x7 /* IEU1 */; \ + fpadd32 %f10, %f8, %S2 /* FPA */; \ + fcmpgt32 %f4, %S1, %x2 /* FPM Group */; \ + srl %x7, 1, %x7 /* IEU0 */; \ + add %sum, %x6, %sum /* IEU1 */; \ + fpadd32 %f14, %f12, %S3 /* FPA */; \ + inc %x8 /* IEU0 Group */; \ + add %sum, %x7, %sum /* IEU1 */; \ + fzero %fz /* FPA */; \ + fcmpgt32 %f8, %S2, %x3 /* FPM Group */; \ + srl %x8, 1, %x8 /* IEU0 */; \ + inc %x1 /* IEU1 */; \ + fpadd32 %S0, %S1, %T0 /* FPA */; \ + fcmpgt32 %f12, %S3, %x4 /* FPM Group */; \ + srl %x1, 1, %x1 /* IEU0 */; \ + add %sum, %x8, %sum /* IEU1 */; \ + fpadd32 %S2, %S3, %T1 /* FPA */; \ + inc %x2 /* IEU0 Group */; \ + add %sum, %x1, %sum /* IEU1 */; \ + fcmpgt32 %S0, %T0, %x5 /* FPM Group */; \ + srl %x2, 1, %x2 /* IEU0 */; \ + inc %x3 /* IEU1 */; \ + fcmpgt32 %S2, %T1, %x6 /* FPM Group */; \ + srl %x3, 1, %x3 /* IEU0 */; \ + add %sum, %x2, %sum /* IEU1 */; \ + inc %x4 /* IEU0 Group */; \ + add %sum, %x3, %sum /* IEU1 */; \ + fcmpgt32 %fz, %f2, %x7 /* FPM Group */; \ + srl %x4, 1, %x4 /* IEU0 */; \ + inc %x5 /* IEU1 */; \ + fpadd32 %T0, %T1, %U0 /* FPA */; \ + fcmpgt32 %fz, %f6, %x8 /* FPM Group */; \ + srl %x5, 1, %x5 /* IEU0 */; \ + add %sum, %x4, %sum /* IEU1 */; \ + inc %x6 /* IEU0 Group */; \ + add %sum, %x5, %sum /* IEU1 */; \ + fcmpgt32 %fz, %f10, %x1 /* FPM Group */; \ + srl %x6, 1, %x6 /* IEU0 */; \ + inc %x7 /* IEU1 */; \ + fcmpgt32 %fz, %f14, %x2 /* FPM Group */; \ + ba,pt %xcc, ett /* CTI */; \ + fmovd %FA, %FB /* FPA */; \ + +#define END_THE_TRICK1(f0,f2,f4,f6,f8,f10,f12,f14,FA,FB) \ + END_THE_TRICK(f0,f2,f4,f6,f8,f10,f12,f14,FA,FB,f48,f50,f52,f54,f56,f58,f60,f62) + +#define END_THE_TRICK2(S0,S1,S2,S3,T0,T1,U0,U1,V0,fz) \ + fpadd32 %U0, %U1, %V0 /* FPA Group */; \ + srl %x7, 1, %x7 /* IEU0 */; \ + add %sum, %x6, %sum /* IEU1 */; \ + std %V0, [%sp + STACKOFF] /* Store Group */; \ + inc %x8 /* IEU0 */; \ + sub %sum, %x7, %sum /* IEU1 */; \ + fcmpgt32 %fz, %S1, %x3 /* FPM Group */; \ + srl %x8, 1, %x8 /* IEU0 */; \ + inc %x1 /* IEU1 */; \ + fcmpgt32 %fz, %S3, %x4 /* FPM Group */; \ + srl %x1, 1, %x1 /* IEU0 */; \ + sub %sum, %x8, %sum /* IEU1 */; \ + ldx [%sp + STACKOFF], %x8 /* Load Group */; \ + inc %x2 /* IEU0 */; \ + sub %sum, %x1, %sum /* IEU1 */; \ + fcmpgt32 %fz, %T1, %x5 /* FPM Group */; \ + srl %x2, 1, %x2 /* IEU0 */; \ + inc %x3 /* IEU1 */; \ + fcmpgt32 %T0, %U0, %x6 /* FPM Group */; \ + srl %x3, 1, %x3 /* IEU0 */; \ + sub %sum, %x2, %sum /* IEU1 */; \ + inc %x4 /* IEU0 Group */; \ + sub %sum, %x3, %sum /* IEU1 */; \ + fcmpgt32 %fz, %U1, %x7 /* FPM Group */; \ + srl %x4, 1, %x4 /* IEU0 */; \ + inc %x5 /* IEU1 */; \ + fcmpgt32 %U0, %V0, %x1 /* FPM Group */; \ + srl %x5, 1, %x5 /* IEU0 */; \ + sub %sum, %x4, %sum /* IEU1 */; \ + fcmpgt32 %fz, %V0, %x2 /* FPM Group */; \ + inc %x6 /* IEU0 */; \ + sub %sum, %x5, %sum /* IEU1 */; \ + srl %x6, 1, %x6 /* IEU0 Group */; \ + inc %x7 /* IEU1 */; \ + srl %x7, 1, %x7 /* IEU0 Group */; \ + add %sum, %x6, %sum /* IEU1 */; \ + inc %x1 /* IEU0 Group */; \ + sub %sum, %x7, %sum /* IEU1 */; \ + srl %x1, 1, %x1 /* IEU0 Group */; \ + inc %x2 /* IEU1 */; \ + srl %x2, 1, %x2 /* IEU0 Group */; \ + add %sum, %x1, %sum /* IEU1 */; \ + sub %sum, %x2, %sum /* IEU0 Group */; \ + addcc %sum, %x8, %sum /* IEU Group */; \ + bcs,a,pn %xcc, 33f /* CTI */; \ + add %sum, 1, %sum /* IEU0 */; \ +33: /* That's it */; + + .text + .globl csum_partial_copy_vis + .align 32 +/* %asi should be either ASI_P or ASI_S for csum_partial_copy resp. csum_partial_copy_from_user */ +/* This assumes that !((%src^%dst)&3) && !((%src|%dst)&1) && %len >= 256 */ +csum_partial_copy_vis: + andcc %dst, 7, %g0 /* IEU1 Group */ + be,pt %icc, 4f /* CTI */ + and %dst, 0x38, %g3 /* IEU0 */ + mov 1, %g5 /* IEU0 Group */ + andcc %dst, 2, %g0 /* IEU1 */ + be,pt %icc, 1f /* CTI */ + and %dst, 4, %g7 /* IEU0 Group */ + lduha [%src] %asi, %g2 /* Load */ + sub %len, 2, %len /* IEU0 Group */ + add %dst, 2, %dst /* IEU1 */ + andcc %dst, 4, %g7 /* IEU1 Group */ + sll %g5, 16, %g5 /* IEU0 */ + sth %g2, [%dst - 2] /* Store Group */ + sll %g2, 16, %g2 /* IEU0 */ + add %src, 2, %src /* IEU1 */ + addcc %g2, %sum, %sum /* IEU1 Group */ + bcs,a,pn %icc, 1f /* CTI */ + add %sum, %g5, %sum /* IEU0 */ +1: lduwa [%src] %asi, %g2 /* Load */ + brz,a,pn %g7, 4f /* CTI+IEU1 Group */ + and %dst, 0x38, %g3 /* IEU0 */ + add %dst, 4, %dst /* IEU0 Group */ + sub %len, 4, %len /* IEU1 */ + addcc %g2, %sum, %sum /* IEU1 Group */ + bcs,a,pn %icc, 1f /* CTI */ + add %sum, 1, %sum /* IEU0 */ +1: and %dst, 0x38, %g3 /* IEU0 Group */ + stw %g2, [%dst - 4] /* Store */ + add %src, 4, %src /* IEU1 */ +4: +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs /* LSU Group */ +#endif + mov %src, %g7 /* IEU1 Group */ + fzero %f48 /* FPA */ + alignaddr %src, %g0, %src /* Single Group */ + subcc %g7, %src, %g7 /* IEU1 Group */ + be,pt %xcc, 1f /* CTI */ + mov 0x40, %g1 /* IEU0 */ + lduwa [%src] %asi, %g2 /* Load Group */ + subcc %sum, %g2, %sum /* IEU1 Group+load stall */ + bcs,a,pn %icc, 1f /* CTI */ + sub %sum, 1, %sum /* IEU0 */ +1: srl %sum, 0, %sum /* IEU0 Group */ + clr %g5 /* IEU1 */ + brz,pn %g3, 3f /* CTI+IEU1 Group */ + sub %g1, %g3, %g1 /* IEU0 */ + ldda [%src] %asi, %f0 /* Load */ + clr %g3 /* IEU0 Group */ + andcc %dst, 8, %g0 /* IEU1 */ + be,pn %icc, 1f /* CTI */ + ldda [%src + 8] %asi, %f2 /* Load Group */ + add %src, 8, %src /* IEU0 */ + sub %len, 8, %len /* IEU1 */ + fpadd32 %f0, %f48, %f50 /* FPA */ + addcc %dst, 8, %dst /* IEU1 Group */ + faligndata %f0, %f2, %f16 /* FPA */ + fcmpgt32 %f48, %f50, %g3 /* FPM Group */ + fmovd %f2, %f0 /* FPA Group */ + ldda [%src + 8] %asi, %f2 /* Load */ + std %f16, [%dst - 8] /* Store */ + fmovd %f50, %f48 /* FPA */ +1: andcc %g1, 0x10, %g0 /* IEU1 Group */ + be,pn %icc, 1f /* CTI */ + and %g1, 0x20, %g1 /* IEU0 */ + fpadd32 %f0, %f48, %f50 /* FPA */ + ldda [%src + 16] %asi, %f4 /* Load Group */ + add %src, 16, %src /* IEU0 */ + add %dst, 16, %dst /* IEU1 */ + faligndata %f0, %f2, %f16 /* FPA */ + fcmpgt32 %f48, %f50, %g5 /* FPM Group */ + sub %len, 16, %len /* IEU0 */ + inc %g3 /* IEU1 */ + std %f16, [%dst - 16] /* Store Group */ + fpadd32 %f2, %f50, %f48 /* FPA */ + srl %g3, 1, %o5 /* IEU0 */ + faligndata %f2, %f4, %f18 /* FPA Group */ + std %f18, [%dst - 8] /* Store */ + fcmpgt32 %f50, %f48, %g3 /* FPM Group */ + add %o5, %sum, %sum /* IEU0 */ + ldda [%src + 8] %asi, %f2 /* Load */ + fmovd %f4, %f0 /* FPA */ +1: brz,a,pn %g1, 4f /* CTI+IEU1 Group */ + rd %asi, %g2 /* LSU Group + 4 bubbles */ + inc %g5 /* IEU0 */ + fpadd32 %f0, %f48, %f50 /* FPA */ + ldda [%src + 16] %asi, %f4 /* Load Group */ + srl %g5, 1, %g5 /* IEU0 */ + add %dst, 32, %dst /* IEU1 */ + faligndata %f0, %f2, %f16 /* FPA */ + fcmpgt32 %f48, %f50, %o5 /* FPM Group */ + inc %g3 /* IEU0 */ + ldda [%src + 24] %asi, %f6 /* Load */ + srl %g3, 1, %g3 /* IEU0 Group */ + add %g5, %sum, %sum /* IEU1 */ + ldda [%src + 32] %asi, %f8 /* Load */ + fpadd32 %f2, %f50, %f48 /* FPA */ + faligndata %f2, %f4, %f18 /* FPA Group */ + sub %len, 32, %len /* IEU0 */ + std %f16, [%dst - 32] /* Store */ + fcmpgt32 %f50, %f48, %o4 /* FPM Group */ + inc %o5 /* IEU0 */ + add %g3, %sum, %sum /* IEU1 */ + fpadd32 %f4, %f48, %f50 /* FPA */ + faligndata %f4, %f6, %f20 /* FPA Group */ + srl %o5, 1, %o5 /* IEU0 */ + fcmpgt32 %f48, %f50, %g5 /* FPM Group */ + add %o5, %sum, %sum /* IEU0 */ + std %f18, [%dst - 24] /* Store */ + fpadd32 %f6, %f50, %f48 /* FPA */ + inc %o4 /* IEU0 Group */ + std %f20, [%dst - 16] /* Store */ + add %src, 32, %src /* IEU1 */ + faligndata %f6, %f8, %f22 /* FPA */ + fcmpgt32 %f50, %f48, %g3 /* FPM Group */ + srl %o4, 1, %o4 /* IEU0 */ + std %f22, [%dst - 8] /* Store */ + add %o4, %sum, %sum /* IEU0 Group */ +3: rd %asi, %g2 /* LSU Group + 4 bubbles */ +#ifdef __KERNEL__ +4: sethi %hi(vis0s), %g7 /* IEU0 Group */ +#else +4: rd %pc, %g7 /* LSU Group + 4 bubbles */ +#endif + inc %g5 /* IEU0 Group */ + and %src, 0x38, %o4 /* IEU1 */ + membar #StoreLoad /* LSU Group */ + srl %g5, 1, %g5 /* IEU0 */ + inc %g3 /* IEU1 */ + sll %o4, 8, %o4 /* IEU0 Group */ + sub %len, 0xc0, %len /* IEU1 */ + addcc %g5, %sum, %sum /* IEU1 Group */ + srl %g3, 1, %g3 /* IEU0 */ + add %g7, %o4, %g7 /* IEU0 Group */ + add %g3, %sum, %sum /* IEU1 */ +#ifdef __KERNEL__ + jmpl %g7 + %lo(vis0s), %g0 /* CTI+IEU1 Group */ +#else + jmpl %g7 + (vis0s - 4b), %g0 /* CTI+IEU1 Group */ +#endif + fzero %f32 /* FPA */ + + .align 2048 +vis0s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + add %src, 128, %src /* IEU0 Group */ + ldda [%src-128] %asi, %f0 /* Load Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f62 /* FPA Group */ + faligndata %f0, %f2, %f48 /* FPA Group */ + fcmpgt32 %f32, %f2, %x1 /* FPM Group */ + fpadd32 %f0, %f62, %f0 /* FPA */ + fcmpgt32 %f32, %f4, %x2 /* FPM Group */ + faligndata %f2, %f4, %f50 /* FPA */ + fcmpgt32 %f62, %f0, %x3 /* FPM Group */ + faligndata %f4, %f6, %f52 /* FPA */ + fcmpgt32 %f32, %f6, %x4 /* FPM Group */ + inc %x1 /* IEU0 */ + faligndata %f6, %f8, %f54 /* FPA */ + fcmpgt32 %f32, %f8, %x5 /* FPM Group */ + srl %x1, 1, %x1 /* IEU0 */ + inc %x2 /* IEU1 */ + faligndata %f8, %f10, %f56 /* FPA */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + srl %x2, 1, %x2 /* IEU0 */ + add %sum, %x1, %sum /* IEU1 */ + faligndata %f10, %f12, %f58 /* FPA */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + inc %x3 /* IEU0 */ + add %sum, %x2, %sum /* IEU1 */ + faligndata %f12, %f14, %f60 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + srl %x3, 1, %x3 /* IEU0 */ + inc %x4 /* IEU1 */ + fmovd %f14, %f62 /* FPA */ + srl %x4, 1, %x4 /* IEU0 Group */ + add %sum, %x3, %sum /* IEU1 */ +vis0: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f48,f50,f52,f54,f56,f58,f60,f62,f62, + ,LDBLK(f32), STBLK,,,,,,,, + ,bcs,pn %icc, vis0e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f48,f50,f52,f54,f56,f58,f60,f62,f62, + ,LDBLK(f0), STBLK,,,,,,,, + ,bcs,pn %icc, vis0e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f48,f50,f52,f54,f56,f58,f60,f62,f62, + ,LDBLK(f16), STBLK,,,,,,,, + ,bcc,pt %icc, vis0) +vis0e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f48,f50,f52,f54,f56,f58,f60,f62,f32, + ,SYNC, STBLK,ST(f48,64),ST(f50,8),ST(f52,16),ST(f54,24),ST(f56,32),ST(f58,40),ST(f60,48), + ,add %dst, 56, %dst; add %len, 192 - 8*8, %len; ba,pt %icc, e2) +vis0e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f48,f50,f52,f54,f56,f58,f60,f62,f0, + ,SYNC, STBLK,ST(f48,64),ST(f50,8),ST(f52,16),ST(f54,24),ST(f56,32),ST(f58,40),ST(f60,48), + ,add %dst, 56, %dst; add %len, 192 - 8*8, %len; ba,pt %icc, e3) +vis0e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f48,f50,f52,f54,f56,f58,f60,f62,f16, + ,SYNC, STBLK,ST(f48,64),ST(f50,8),ST(f52,16),ST(f54,24),ST(f56,32),ST(f58,40),ST(f60,48), + ,add %dst, 56, %dst; add %len, 192 - 8*8, %len; ba,pt %icc, e1) + .align 2048 +vis1s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + add %src, 128 - 8, %src /* IEU0 Group */ + ldda [%src-128] %asi, %f0 /* Load Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + fcmpgt32 %f32, %f2, %x2 /* FPM Group */ + faligndata %f2, %f4, %f48 /* FPA */ + fcmpgt32 %f32, %f4, %x3 /* FPM Group */ + faligndata %f4, %f6, %f50 /* FPA */ + fcmpgt32 %f32, %f6, %x4 /* FPM Group */ + faligndata %f6, %f8, %f52 /* FPA */ + fcmpgt32 %f32, %f8, %x5 /* FPM Group */ + inc %x2 /* IEU1 */ + faligndata %f8, %f10, %f54 /* FPA */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + srl %x2, 1, %x2 /* IEU0 */ + faligndata %f10, %f12, %f56 /* FPA */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + inc %x3 /* IEU0 */ + add %sum, %x2, %sum /* IEU1 */ + faligndata %f12, %f14, %f58 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + srl %x3, 1, %x3 /* IEU0 */ + inc %x4 /* IEU1 */ + fmovd %f14, %f60 /* FPA */ + srl %x4, 1, %x4 /* IEU0 Group */ + add %sum, %x3, %sum /* IEU1 */ +vis1: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f62,f48,f50,f52,f54,f56,f58,f60,f60, + ,LDBLK(f32), ,STBLK,,,,,,, + ,bcs,pn %icc, vis1e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f62,f48,f50,f52,f54,f56,f58,f60,f60, + ,LDBLK(f0), ,STBLK,,,,,,, + ,bcs,pn %icc, vis1e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f62,f48,f50,f52,f54,f56,f58,f60,f60, + ,LDBLK(f16), ,STBLK,,,,,,, + ,bcc,pt %icc, vis1) +vis1e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f62,f48,f50,f52,f54,f56,f58,f60,f32, + ,SYNC, ,STBLK,ST(f48,0),ST(f50,8),ST(f52,16),ST(f54,24),ST(f56,32),ST(f58,40), + ,add %dst, 48, %dst; add %len, 192 - 7*8, %len; ba,pt %icc, e2) +vis1e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f62,f48,f50,f52,f54,f56,f58,f60,f0, + ,SYNC, ,STBLK,ST(f48,0),ST(f50,8),ST(f52,16),ST(f54,24),ST(f56,32),ST(f58,40), + ,add %dst, 48, %dst; add %len, 192 - 7*8, %len; ba,pt %icc, e3) +vis1e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f62,f48,f50,f52,f54,f56,f58,f60,f16, + ,SYNC, ,STBLK,ST(f48,0),ST(f50,8),ST(f52,16),ST(f54,24),ST(f56,32),ST(f58,40), + ,add %dst, 48, %dst; add %len, 192 - 7*8, %len; ba,pt %icc, e1) + .align 2048 +vis2s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + add %src, 128 - 16, %src /* IEU0 Group */ + ldda [%src-128] %asi, %f0 /* Load Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + sub %dst, 64, %dst /* IEU0 */ + fzero %f2 /* FPA Group */ + fcmpgt32 %f32, %f4, %x3 /* FPM Group */ + faligndata %f4, %f6, %f48 /* FPA */ + fcmpgt32 %f32, %f6, %x4 /* FPM Group */ + faligndata %f6, %f8, %f50 /* FPA */ + fcmpgt32 %f32, %f8, %x5 /* FPM Group */ + faligndata %f8, %f10, %f52 /* FPA */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + faligndata %f10, %f12, %f54 /* FPA */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + inc %x3 /* IEU0 */ + faligndata %f12, %f14, %f56 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + srl %x3, 1, %x3 /* IEU0 */ + inc %x4 /* IEU1 */ + fmovd %f14, %f58 /* FPA */ + srl %x4, 1, %x4 /* IEU0 Group */ + add %sum, %x3, %sum /* IEU1 */ +vis2: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f60,f62,f48,f50,f52,f54,f56,f58,f58, + ,LDBLK(f32), ,,STBLK,,,,,, + ,bcs,pn %icc, vis2e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f60,f62,f48,f50,f52,f54,f56,f58,f58, + ,LDBLK(f0), ,,STBLK,,,,,, + ,bcs,pn %icc, vis2e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f60,f62,f48,f50,f52,f54,f56,f58,f58, + ,LDBLK(f16), ,,STBLK,,,,,, + ,bcc,pt %icc, vis2) +vis2e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f60,f62,f48,f50,f52,f54,f56,f58,f32, + ,SYNC, ,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80),ST(f54,88),ST(f56,96), + ,add %dst, 104, %dst; add %len, 192 - 6*8, %len; ba,pt %icc, e2) +vis2e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f60,f62,f48,f50,f52,f54,f56,f58,f0, + ,SYNC, ,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80),ST(f54,88),ST(f56,96), + ,add %dst, 104, %dst; add %len, 192 - 6*8, %len; ba,pt %icc, e3) +vis2e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f60,f62,f48,f50,f52,f54,f56,f58,f16, + ,SYNC, ,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80),ST(f54,88),ST(f56,96), + ,add %dst, 104, %dst; add %len, 192 - 6*8, %len; ba,pt %icc, e1) + .align 2048 +vis3s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + add %src, 128 - 24, %src /* IEU0 Group */ + ldda [%src-128] %asi, %f0 /* Load Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + sub %dst, 64, %dst /* IEU0 */ + fzero %f2 /* FPA Group */ + fzero %f4 /* FPA Group */ + fcmpgt32 %f32, %f6, %x4 /* FPM Group */ + faligndata %f6, %f8, %f48 /* FPA */ + fcmpgt32 %f32, %f8, %x5 /* FPM Group */ + faligndata %f8, %f10, %f50 /* FPA */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + faligndata %f10, %f12, %f52 /* FPA */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + faligndata %f12, %f14, %f54 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + fmovd %f14, %f56 /* FPA */ + inc %x4 /* IEU0 */ + srl %x4, 1, %x4 /* IEU0 Group */ +vis3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f58,f60,f62,f48,f50,f52,f54,f56,f56, + ,LDBLK(f32), ,,,STBLK,,,,, + ,bcs,pn %icc, vis3e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f58,f60,f62,f48,f50,f52,f54,f56,f56, + ,LDBLK(f0), ,,,STBLK,,,,, + ,bcs,pn %icc, vis3e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f58,f60,f62,f48,f50,f52,f54,f56,f56, + ,LDBLK(f16), ,,,STBLK,,,,, + ,bcc,pt %icc, vis3) +vis3e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f58,f60,f62,f48,f50,f52,f54,f56,f32, + ,SYNC, ,,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80),ST(f54,88), + ,add %dst, 96, %dst; add %len, 192 - 5*8, %len; ba,pt %icc, e2) +vis3e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f58,f60,f62,f48,f50,f52,f54,f56,f0, + ,SYNC, ,,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80),ST(f54,88), + ,add %dst, 96, %dst; add %len, 192 - 5*8, %len; ba,pt %icc, e3) +vis3e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f58,f60,f62,f48,f50,f52,f54,f56,f16, + ,SYNC, ,,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80),ST(f54,88), + ,add %dst, 96, %dst; add %len, 192 - 5*8, %len; ba,pt %icc, e1) + .align 2048 +vis4s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + add %src, 128 - 32, %src /* IEU0 Group */ + ldda [%src-128] %asi, %f0 /* Load Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + sub %dst, 64, %dst /* IEU0 */ + fzero %f2 /* FPA Group */ + fzero %f4 /* FPA Group */ + fzero %f6 /* FPA Group */ + clr %x4 /* IEU0 */ + fcmpgt32 %f32, %f8, %x5 /* FPM Group */ + faligndata %f8, %f10, %f48 /* FPA */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + faligndata %f10, %f12, %f50 /* FPA */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + faligndata %f12, %f14, %f52 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + fmovd %f14, %f54 /* FPA */ +vis4: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f56,f58,f60,f62,f48,f50,f52,f54,f54, + ,LDBLK(f32), ,,,,STBLK,,,, + ,bcs,pn %icc, vis4e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f56,f58,f60,f62,f48,f50,f52,f54,f54, + ,LDBLK(f0), ,,,,STBLK,,,, + ,bcs,pn %icc, vis4e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f56,f58,f60,f62,f48,f50,f52,f54,f54, + ,LDBLK(f16), ,,,,STBLK,,,, + ,bcc,pt %icc, vis4) +vis4e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f56,f58,f60,f62,f48,f50,f52,f54,f32, + ,SYNC, ,,,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80), + ,add %dst, 88, %dst; add %len, 192 - 4*8, %len; ba,pt %icc, e2) +vis4e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f56,f58,f60,f62,f48,f50,f52,f54,f0, + ,SYNC, ,,,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80), + ,add %dst, 88, %dst; add %len, 192 - 4*8, %len; ba,pt %icc, e3) +vis4e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f56,f58,f60,f62,f48,f50,f52,f54,f16, + ,SYNC, ,,,,STBLK,ST(f48,64),ST(f50,72),ST(f52,80), + ,add %dst, 88, %dst; add %len, 192 - 4*8, %len; ba,pt %icc, e1) + .align 2048 +vis5s: add %src, 128 - 40, %src /* IEU0 Group */ + ldda [%src-88] %asi, %f10 /* Load Group */ + ldda [%src-80] %asi, %f12 /* Load Group */ + ldda [%src-72] %asi, %f14 /* Load Group */ + wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + fmuld %f32, %f32, %f2 /* FPM */ + clr %x4 /* IEU0 */ + faddd %f32, %f32, %f4 /* FPA Group */ + fmuld %f32, %f32, %f6 /* FPM */ + clr %x5 /* IEU0 */ + faddd %f32, %f32, %f8 /* FPA Group */ + fcmpgt32 %f32, %f10, %x6 /* FPM Group */ + sub %dst, 64, %dst /* IEU0 */ + faligndata %f10, %f12, %f48 /* FPA */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + faligndata %f12, %f14, %f50 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + fmovd %f14, %f52 /* FPA */ +vis5: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f54,f56,f58,f60,f62,f48,f50,f52,f52, + ,LDBLK(f32), ,,,,,STBLK,,, + ,bcs,pn %icc, vis5e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f54,f56,f58,f60,f62,f48,f50,f52,f52, + ,LDBLK(f0), ,,,,,STBLK,,, + ,bcs,pn %icc, vis5e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f54,f56,f58,f60,f62,f48,f50,f52,f52, + ,LDBLK(f16), ,,,,,STBLK,,, + ,bcc,pt %icc, vis5) +vis5e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f54,f56,f58,f60,f62,f48,f50,f52,f32, + ,SYNC, ,,,,,STBLK,ST(f48,64),ST(f50,72), + ,add %dst, 80, %dst; add %len, 192 - 3*8, %len; ba,pt %icc, e2) +vis5e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f54,f56,f58,f60,f62,f48,f50,f52,f0, + ,SYNC, ,,,,,STBLK,ST(f48,64),ST(f50,72), + ,add %dst, 80, %dst; add %len, 192 - 3*8, %len; ba,pt %icc, e3) +vis5e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f54,f56,f58,f60,f62,f48,f50,f52,f16, + ,SYNC, ,,,,,STBLK,ST(f48,64),ST(f50,72), + ,add %dst, 80, %dst; add %len, 192 - 3*8, %len; ba,pt %icc, e1) + .align 2048 +vis6s: add %src, 128 - 48, %src /* IEU0 Group */ + ldda [%src-80] %asi, %f12 /* Load Group */ + ldda [%src-72] %asi, %f14 /* Load Group */ + wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + fmuld %f32, %f32, %f2 /* FPM */ + clr %x4 /* IEU0 */ + faddd %f32, %f32, %f4 /* FPA Group */ + fmuld %f32, %f32, %f6 /* FPM */ + clr %x5 /* IEU0 */ + faddd %f32, %f32, %f8 /* FPA Group */ + fmuld %f32, %f32, %f10 /* FPM */ + clr %x6 /* IEU0 */ + fcmpgt32 %f32, %f12, %x7 /* FPM Group */ + sub %dst, 64, %dst /* IEU0 */ + faligndata %f12, %f14, %f48 /* FPA */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + fmovd %f14, %f50 /* FPA */ +vis6: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f52,f54,f56,f58,f60,f62,f48,f50,f50, + ,LDBLK(f32), ,,,,,,STBLK,, + ,bcs,pn %icc, vis6e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f52,f54,f56,f58,f60,f62,f48,f50,f50, + ,LDBLK(f0), ,,,,,,STBLK,, + ,bcs,pn %icc, vis6e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f52,f54,f56,f58,f60,f62,f48,f50,f50, + ,LDBLK(f16), ,,,,,,STBLK,, + ,bcc,pt %icc, vis6) +vis6e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f52,f54,f56,f58,f60,f62,f48,f50,f32, + ,SYNC, ,,,,,,STBLK,ST(f48,64), + ,add %dst, 72, %dst; add %len, 192 - 2*8, %len; ba,pt %icc, e2) +vis6e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f52,f54,f56,f58,f60,f62,f48,f50,f0, + ,SYNC, ,,,,,,STBLK,ST(f48,64), + ,add %dst, 72, %dst; add %len, 192 - 2*8, %len; ba,pt %icc, e3) +vis6e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f52,f54,f56,f58,f60,f62,f48,f50,f16, + ,SYNC, ,,,,,,STBLK,ST(f48,64), + ,add %dst, 72, %dst; add %len, 192 - 2*8, %len; ba,pt %icc, e1) + .align 2048 +vis7s: add %src, 128 - 56, %src /* IEU0 Group */ + ldda [%src-72] %asi, %f14 /* Load Group */ + wr %g2, ASI_BLK_XOR, %asi /* LSU Group */ + ldda [%src-64] %asi, %f16 /* Load Group */ + fmovd %f48, %f0 /* FPA Group */ + fmuld %f32, %f32, %f2 /* FPM */ + clr %x4 /* IEU0 */ + faddd %f32, %f32, %f4 /* FPA Group */ + fmuld %f32, %f32, %f6 /* FPM */ + clr %x5 /* IEU0 */ + faddd %f32, %f32, %f8 /* FPA Group */ + fmuld %f32, %f32, %f10 /* FPM */ + clr %x6 /* IEU0 */ + faddd %f32, %f32, %f12 /* FPA Group */ + clr %x7 /* IEU0 */ + fcmpgt32 %f32, %f14, %x8 /* FPM Group */ + sub %dst, 64, %dst /* IEU0 */ + fmovd %f14, %f48 /* FPA */ +vis7: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f50,f52,f54,f56,f58,f60,f62,f48,f48, + ,LDBLK(f32), ,,,,,,,STBLK, + ,bcs,pn %icc, vis7e1) + DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f50,f52,f54,f56,f58,f60,f62,f48,f48, + ,LDBLK(f0), ,,,,,,,STBLK, + ,bcs,pn %icc, vis7e2) + DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f50,f52,f54,f56,f58,f60,f62,f48,f48, + ,LDBLK(f16), ,,,,,,,STBLK, + ,bcc,pt %icc, vis7) +vis7e3: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30, + ,f50,f52,f54,f56,f58,f60,f62,f48,f32, + ,SYNC, ,,,,,,,STBLK, + ,add %dst, 64, %dst; add %len, 192 - 1*8, %len; ba,pt %icc, e2) +vis7e1: DO_THE_TRICK( f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46, + ,f50,f52,f54,f56,f58,f60,f62,f48,f0, + ,SYNC, ,,,,,,,STBLK, + ,add %dst, 64, %dst; add %len, 192 - 1*8, %len; ba,pt %icc, e3) +vis7e2: DO_THE_TRICK( f32,f34,f36,f38,f40,f42,f44,f46,f0,f2,f4,f6,f8,f10,f12,f14, + ,f50,f52,f54,f56,f58,f60,f62,f48,f16, + ,SYNC, ,,,,,,,STBLK, + ,add %dst, 64, %dst; add %len, 192 - 1*8, %len; ba,pt %icc, e1) +e1: END_THE_TRICK1( f0,f2,f4,f6,f8,f10,f12,f14,f16,f6) +e2: END_THE_TRICK1( f16,f18,f20,f22,f24,f26,f28,f30,f32,f6) +e3: END_THE_TRICK1( f32,f34,f36,f38,f40,f42,f44,f46,f0,f6) +ett: rd %gsr, %x3 /* LSU Group+4bubbles */ + andcc %x3, 7, %x3 /* IEU1 Group */ + add %dst, 8, %dst /* IEU0 Group */ + bne,pn %icc, 1f /* CTI */ + fzero %f10 /* FPA */ + brz,a,pn %len, 2f /* CTI+IEU1 Group */ + std %f6, [%dst - 8] /* Store */ +1: rd %asi, %x4 /* LSU Group+4bubbles */ + sub %src, 64, %src /* IEU0 Group */ + cmp %len, 8 /* IEU1 */ + blu,pn %icc, 3f /* CTI */ + wr %x4, ASI_BLK_XOR, %asi /* LSU Group+4bubbles */ +1: ldda [%src] %asi, %f2 /* Load Group */ + fpadd32 %f10, %f2, %f12 /* FPA Group+load stall */ + add %src, 8, %src /* IEU0 */ + add %dst, 8, %dst /* IEU1 */ + faligndata %f6, %f2, %f14 /* FPA Group */ + fcmpgt32 %f10, %f12, %x5 /* FPM Group */ + std %f14, [%dst - 16] /* Store */ + fmovd %f2, %f6 /* FPA */ + fmovd %f12, %f10 /* FPA Group */ + sub %len, 8, %len /* IEU1 */ + fzero %f16 /* FPA Group - FPU nop */ + fzero %f18 /* FPA Group - FPU nop */ + inc %x5 /* IEU0 */ + srl %x5, 1, %x5 /* IEU0 Group (regdep) */ + cmp %len, 8 /* IEU1 */ + bgeu,pt %icc, 1b /* CTI */ + add %x5, %sum, %sum /* IEU0 Group */ +3: brz,a,pt %x3, 2f /* CTI+IEU1 */ + std %f6, [%dst - 8] /* Store Group */ + st %f7, [%dst - 8] /* Store Group */ + sub %dst, 4, %dst /* IEU0 */ + add %len, 4, %len /* IEU1 */ +2: +#ifdef __KERNEL__ + sub %sp, 8, %sp /* IEU0 Group */ +#endif + END_THE_TRICK2( f48,f50,f52,f54,f56,f58,f60,f10,f12,f62) + membar #Sync /* LSU Group */ +#ifdef __KERNEL__ + wr %g0, 0, %fprs /* LSU Group */ + add %sp, 8, %sp /* IEU0 Group */ +#endif +23: brnz,pn %len, 26f /* CTI+IEU1 Group */ +24: sllx %sum, 32, %g1 /* IEU0 */ +25: addcc %sum, %g1, %src /* IEU1 Group */ + srlx %src, 32, %src /* IEU0 Group (regdep) */ + bcs,a,pn %xcc, 1f /* CTI */ + add %src, 1, %src /* IEU1 */ +#ifndef __KERNEL__ +1: retl /* CTI Group brk forced */ + srl %src, 0, %src /* IEU0 */ +#else +1: sethi %uhi(PAGE_OFFSET), %g4 /* IEU0 Group */ + retl /* CTI Group brk forced */ + sllx %g4, 32, %g4 /* IEU0 */ +#endif +26: andcc %len, 8, %g0 /* IEU1 Group */ + be,pn %icc, 1f /* CTI */ + lduwa [%src] %asi, %g3 /* Load */ + lduwa [%src+4] %asi, %g2 /* Load Group */ + add %src, 8, %src /* IEU0 */ + add %dst, 8, %dst /* IEU1 */ + sllx %g3, 32, %g5 /* IEU0 Group */ + stw %g3, [%dst - 8] /* Store */ + or %g5, %g2, %g5 /* IEU0 Group */ + stw %g2, [%dst - 4] /* Store */ + addcc %g5, %sum, %sum /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %sum, 1, %sum /* IEU0 */ +1: andcc %len, 4, %g0 /* IEU1 Group */ + be,a,pn %icc, 1f /* CTI */ + clr %g2 /* IEU0 */ + lduwa [%src] %asi, %g7 /* Load */ + add %src, 4, %src /* IEU0 Group */ + add %dst, 4, %dst /* IEU1 */ + sllx %g7, 32, %g2 /* IEU0 Group */ + stw %g7, [%dst - 4] /* Store */ +1: andcc %len, 2, %g0 /* IEU1 */ + be,a,pn %icc, 1f /* CTI */ + clr %o4 /* IEU0 Group */ + lduha [%src] %asi, %g7 /* Load */ + add %src, 2, %src /* IEU1 */ + add %dst, 2, %dst /* IEU0 Group */ + sll %g7, 16, %o4 /* IEU0 Group */ + sth %g7, [%dst - 2] /* Store */ +1: andcc %len, 1, %g0 /* IEU1 */ + be,a,pn %icc, 1f /* CTI */ + clr %o5 /* IEU0 Group */ + lduba [%src] %asi, %g7 /* Load */ + sll %g7, 8, %o5 /* IEU0 Group */ + stb %g7, [%dst] /* Store */ +1: or %g2, %o4, %o4 /* IEU1 */ + or %o5, %o4, %o4 /* IEU0 Group (regdep) */ + addcc %o4, %sum, %sum /* IEU1 Group (regdep) */ + bcs,a,pn %xcc, 1f /* CTI */ + add %sum, 1, %sum /* IEU0 */ +1: ba,pt %xcc, 25b /* CTI Group */ + sllx %sum, 32, %g1 /* IEU0 */ + +#ifdef __KERNEL__ +end: + + .section __ex_table + .align 4 + .word csum_partial_copy_vis, 0, end, cpc_handler +#endif diff --git a/arch/sparc64/lib/VISmemset.S b/arch/sparc64/lib/VISmemset.S index d674f2a6e..4c24931ba 100644 --- a/arch/sparc64/lib/VISmemset.S +++ b/arch/sparc64/lib/VISmemset.S @@ -1,4 +1,4 @@ -/* $Id: VISmemset.S,v 1.1 1997/07/18 06:26:49 ralf Exp $ +/* $Id: VISmemset.S,v 1.7 1997/08/22 15:54:56 jj Exp $ * VISmemset.S: High speed memset operations utilizing the UltraSparc * Visual Instruction Set. * @@ -171,11 +171,22 @@ memset: 12: #ifdef __KERNEL__ wr %g0, 0, %fprs +#else +#ifndef REGS_64BIT + wr %g0, FPRS_FEF, %fprs #endif - membar #Sync +#endif + membar #StoreLoad | #StoreStore 9: andcc %o2, 0x78, %g5 be,pn %xcc, 13f andcc %o2, 7, %o2 +#ifdef __KERNEL__ +14: srl %g5, 1, %o3 + sethi %hi(13f), %o4 + sub %o4, %o3, %o4 + jmpl %o4 + %lo(13f), %g0 + add %o0, %g5, %o0 +#else 14: rd %pc, %o4 #ifdef REGS_64BIT srl %g5, 1, %o3 @@ -185,6 +196,7 @@ memset: #endif jmpl %o4 + (13f - 14b), %g0 add %o0, %g5, %o0 +#endif 12: SET_BLOCKS(%o0, 0x68, %o1) SET_BLOCKS(%o0, 0x48, %o1) SET_BLOCKS(%o0, 0x28, %o1) diff --git a/arch/sparc64/lib/blockops.S b/arch/sparc64/lib/blockops.S index 59083aa02..7d5b240ad 100644 --- a/arch/sparc64/lib/blockops.S +++ b/arch/sparc64/lib/blockops.S @@ -1,4 +1,4 @@ -/* $Id: blockops.S,v 1.10 1997/06/24 17:29:10 jj Exp $ +/* $Id: blockops.S,v 1.11 1997/07/29 09:35:36 davem Exp $ * arch/sparc64/lib/blockops.S: UltraSparc block zero optimized routines. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -15,7 +15,7 @@ __bfill64: /* %o0 = buf, %o1= ptr to pattern */ wr %g0, FPRS_FEF, %fprs ! FPU Group ldd [%o1], %f48 ! Load Group wr %g0, ASI_BLK_P, %asi ! LSU Group - membar #StoreStore | #LoadStore ! LSU Group + membar #StoreLoad | #StoreStore | #LoadStore ! LSU Group mov 32, %g2 ! IEU0 Group /* Cannot perform real arithmatic on the pattern, that can @@ -36,7 +36,7 @@ __bfill64: /* %o0 = buf, %o1= ptr to pattern */ subcc %g2, 1, %g2 ! IEU1 Group bne,pt %icc, 1b ! CTI add %o0, 0x100, %o0 ! IEU0 - membar #Sync ! LSU Group + membar #StoreLoad | #StoreStore ! LSU Group jmpl %o7 + 0x8, %g0 ! CTI Group brk forced wr %g0, 0, %fprs ! FPU Group @@ -56,7 +56,7 @@ __bzero_1page: faddd %f0, %f2, %f12 ! FPA Group fmuld %f0, %f2, %f14 ! FPM wr %g0, ASI_BLK_P, %asi ! LSU Group - membar #StoreStore | #LoadStore ! LSU Group + membar #StoreLoad | #StoreStore | #LoadStore ! LSU Group 1: stda %f0, [%o0 + 0x00] %asi ! Store Group stda %f0, [%o0 + 0x40] %asi ! Store Group stda %f0, [%o0 + 0x80] %asi ! Store Group @@ -65,6 +65,6 @@ __bzero_1page: subcc %g1, 1, %g1 ! IEU1 bne,pt %icc, 1b ! CTI add %o0, 0x100, %o0 ! IEU0 Group - membar #Sync ! LSU Group + membar #StoreLoad | #StoreStore ! LSU Group jmpl %o7 + 0x8, %g0 ! CTI Group brk forced wr %g0, 0, %fprs ! FPU Group diff --git a/arch/sparc64/lib/checksum.S b/arch/sparc64/lib/checksum.S index 703370fc6..5f35f136b 100644 --- a/arch/sparc64/lib/checksum.S +++ b/arch/sparc64/lib/checksum.S @@ -23,456 +23,456 @@ * are two fold. Firstly, they cannot pair with jack shit, * and also they only add in the 32-bit carry condition bit * into the accumulated sum. The following is much better. - * - * This should run at max bandwidth for ecache hits, a better - * technique is to use VIS and fpu operations. This is already - * done for csum_partial, needs to be written for the copy stuff - * still. + * For larger chunks we use VIS code, which is faster ;) */ - .text - .globl __csum_partial_copy_start, __csum_partial_copy_end -__csum_partial_copy_start: +#define src o0 +#define dst o1 +#define len o2 +#define sum o3 + .text /* I think I have an erection... Once _AGAIN_ the SunSoft * engineers are caught asleep at the keyboard, tsk tsk... */ -#define CSUMCOPY_ECACHE_LOAD(src, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldxa [src + off + 0x00] %asi, t0; \ - ldxa [src + off + 0x08] %asi, t1; \ - ldxa [src + off + 0x10] %asi, t2; \ - ldxa [src + off + 0x18] %asi, t3; \ - ldxa [src + off + 0x20] %asi, t4; \ - ldxa [src + off + 0x28] %asi, t5; \ - ldxa [src + off + 0x30] %asi, t6; \ - ldxa [src + off + 0x38] %asi, t7; \ +#define CSUMCOPY_ECACHE_LOAD(off, t0, t1, t2, t3, t4, t5, t6, t7) \ + ldxa [%src + off + 0x00] %asi, t0; \ + ldxa [%src + off + 0x08] %asi, t1; \ + ldxa [%src + off + 0x10] %asi, t2; \ + ldxa [%src + off + 0x18] %asi, t3; \ + ldxa [%src + off + 0x20] %asi, t4; \ + ldxa [%src + off + 0x28] %asi, t5; \ + ldxa [%src + off + 0x30] %asi, t6; \ + ldxa [%src + off + 0x38] %asi, t7; \ nop; nop; /* DO NOT TOUCH THIS!!!!! */ -#define CSUMCOPY_EC_STALIGNED_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\ - stx t0, [dest + off - 0x40]; \ - addcc sum, t0, sum; \ +#define CSUMCOPY_EC_STALIGNED_LDNXT(off, t0, t1, t2, t3, t4, t5, t6, t7) \ + stx t0, [%dst + off - 0x40]; \ + addcc %sum, t0, %sum; \ bcc,pt %xcc, 11f; \ - ldxa [src + off + 0x00] %asi, t0; \ - add sum, 1, sum; \ -11: stx t1, [dest + off - 0x38]; \ - addcc sum, t1, sum; \ + ldxa [%src + off + 0x00] %asi, t0; \ + add %sum, 1, %sum; \ +11: stx t1, [%dst + off - 0x38]; \ + addcc %sum, t1, %sum; \ bcc,pt %xcc, 12f; \ - ldxa [src + off + 0x08] %asi, t1; \ - add sum, 1, sum; \ -12: stx t2, [dest + off - 0x30]; \ - addcc sum, t2, sum; \ + ldxa [%src + off + 0x08] %asi, t1; \ + add %sum, 1, %sum; \ +12: stx t2, [%dst + off - 0x30]; \ + addcc %sum, t2, %sum; \ bcc,pt %xcc, 13f; \ - ldxa [src + off + 0x10] %asi, t2; \ - add sum, 1, sum; \ -13: stx t3, [dest + off - 0x28]; \ - addcc sum, t3, sum; \ + ldxa [%src + off + 0x10] %asi, t2; \ + add %sum, 1, %sum; \ +13: stx t3, [%dst + off - 0x28]; \ + addcc %sum, t3, %sum; \ bcc,pt %xcc, 14f; \ - ldxa [src + off + 0x18] %asi, t3; \ - add sum, 1, sum; \ -14: stx t4, [dest + off - 0x20]; \ - addcc sum, t4, sum; \ + ldxa [%src + off + 0x18] %asi, t3; \ + add %sum, 1, %sum; \ +14: stx t4, [%dst + off - 0x20]; \ + addcc %sum, t4, %sum; \ bcc,pt %xcc, 15f; \ - ldxa [src + off + 0x20] %asi, t4; \ - add sum, 1, sum; \ -15: stx t5, [dest + off - 0x18]; \ - addcc sum, t5, sum; \ + ldxa [%src + off + 0x20] %asi, t4; \ + add %sum, 1, %sum; \ +15: stx t5, [%dst + off - 0x18]; \ + addcc %sum, t5, %sum; \ bcc,pt %xcc, 16f; \ - ldxa [src + off + 0x28] %asi, t5; \ - add sum, 1, sum; \ -16: stx t6, [dest + off - 0x10]; \ - addcc sum, t6, sum; \ + ldxa [%src + off + 0x28] %asi, t5; \ + add %sum, 1, %sum; \ +16: stx t6, [%dst + off - 0x10]; \ + addcc %sum, t6, %sum; \ bcc,pt %xcc, 17f; \ - ldxa [src + off + 0x30] %asi, t6; \ - add sum, 1, sum; \ -17: stx t7, [dest + off - 0x08]; \ - addcc sum, t7, sum; \ + ldxa [%src + off + 0x30] %asi, t6; \ + add %sum, 1, %sum; \ +17: stx t7, [%dst + off - 0x08]; \ + addcc %sum, t7, %sum; \ bcc,pt %xcc, 18f; \ - ldxa [src + off + 0x38] %asi, t7; \ - add sum, 1, sum; \ + ldxa [%src + off + 0x38] %asi, t7; \ + add %sum, 1, %sum; \ 18: -#define CSUMCOPY_EC_STUNALIGN_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\ - stw t0, [dest + off - 0x3c]; \ - addcc sum, t0, sum; \ +#define CSUMCOPY_EC_STUNALIGN_LDNXT(off, t0, t1, t2, t3, t4, t5, t6, t7) \ + stw t0, [%dst + off - 0x3c]; \ + addcc %sum, t0, %sum; \ srlx t0, 32, t0; \ - stw t0, [dest + off - 0x40]; \ + stw t0, [%dst + off - 0x40]; \ bcc,pt %xcc, 21f; \ - ldxa [src + off + 0x00] %asi, t0; \ - add sum, 1, sum; \ -21: stw t1, [dest + off - 0x34]; \ - addcc sum, t1, sum; \ + ldxa [%src + off + 0x00] %asi, t0; \ + add %sum, 1, %sum; \ +21: stw t1, [%dst + off - 0x34]; \ + addcc %sum, t1, %sum; \ srlx t1, 32, t1; \ - stw t1, [dest + off - 0x38]; \ + stw t1, [%dst + off - 0x38]; \ bcc,pt %xcc, 22f; \ - ldxa [src + off + 0x08] %asi, t1; \ - add sum, 1, sum; \ -22: stw t2, [dest + off - 0x2c]; \ - addcc sum, t2, sum; \ + ldxa [%src + off + 0x08] %asi, t1; \ + add %sum, 1, %sum; \ +22: stw t2, [%dst + off - 0x2c]; \ + addcc %sum, t2, %sum; \ srlx t2, 32, t2; \ - stw t2, [dest + off - 0x30]; \ + stw t2, [%dst + off - 0x30]; \ bcc,pt %xcc, 23f; \ - ldxa [src + off + 0x10] %asi, t2; \ - add sum, 1, sum; \ -23: stw t3, [dest + off - 0x24]; \ - addcc sum, t3, sum; \ + ldxa [%src + off + 0x10] %asi, t2; \ + add %sum, 1, %sum; \ +23: stw t3, [%dst + off - 0x24]; \ + addcc %sum, t3, %sum; \ srlx t3, 32, t3; \ - stw t3, [dest + off - 0x28]; \ + stw t3, [%dst + off - 0x28]; \ bcc,pt %xcc, 24f; \ - ldxa [src + off + 0x18] %asi, t3; \ - add sum, 1, sum; \ -24: stw t4, [dest + off - 0x1c]; \ - addcc sum, t4, sum; \ + ldxa [%src + off + 0x18] %asi, t3; \ + add %sum, 1, %sum; \ +24: stw t4, [%dst + off - 0x1c]; \ + addcc %sum, t4, %sum; \ srlx t4, 32, t4; \ - stw t4, [dest + off - 0x20]; \ + stw t4, [%dst + off - 0x20]; \ bcc,pt %xcc, 25f; \ - ldxa [src + off + 0x20] %asi, t4; \ - add sum, 1, sum; \ -25: stw t5, [dest + off - 0x14]; \ - addcc sum, t5, sum; \ + ldxa [%src + off + 0x20] %asi, t4; \ + add %sum, 1, %sum; \ +25: stw t5, [%dst + off - 0x14]; \ + addcc %sum, t5, %sum; \ srlx t5, 32, t5; \ - stw t5, [dest + off - 0x18]; \ + stw t5, [%dst + off - 0x18]; \ bcc,pt %xcc, 26f; \ - ldxa [src + off + 0x28] %asi, t5; \ - add sum, 1, sum; \ -26: stw t6, [dest + off - 0x0c]; \ - addcc sum, t6, sum; \ + ldxa [%src + off + 0x28] %asi, t5; \ + add %sum, 1, %sum; \ +26: stw t6, [%dst + off - 0x0c]; \ + addcc %sum, t6, %sum; \ srlx t6, 32, t6; \ - stw t6, [dest + off - 0x10]; \ + stw t6, [%dst + off - 0x10]; \ bcc,pt %xcc, 27f; \ - ldxa [src + off + 0x30] %asi, t6; \ - add sum, 1, sum; \ -27: stw t7, [dest + off - 0x04]; \ - addcc sum, t7, sum; \ + ldxa [%src + off + 0x30] %asi, t6; \ + add %sum, 1, %sum; \ +27: stw t7, [%dst + off - 0x04]; \ + addcc %sum, t7, %sum; \ srlx t7, 32, t7; \ - stw t7, [dest + off - 0x08]; \ + stw t7, [%dst + off - 0x08]; \ bcc,pt %xcc, 28f; \ - ldxa [src + off + 0x38] %asi, t7; \ - add sum, 1, sum; \ + ldxa [%src + off + 0x38] %asi, t7; \ + add %sum, 1, %sum; \ 28: -#define CSUMCOPY_EC_STALIGNED(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ - addcc sum, t0, sum; \ +#define CSUMCOPY_EC_STALIGNED(off, t0, t1, t2, t3, t4, t5, t6, t7) \ + addcc %sum, t0, %sum; \ bcc,pt %xcc, 31f; \ - stx t0, [dest + off + 0x00]; \ - add sum, 1, sum; \ -31: addcc sum, t1, sum; \ + stx t0, [%dst + off + 0x00]; \ + add %sum, 1, %sum; \ +31: addcc %sum, t1, %sum; \ bcc,pt %xcc, 32f; \ - stx t1, [dest + off + 0x08]; \ - add sum, 1, sum; \ -32: addcc sum, t2, sum; \ + stx t1, [%dst + off + 0x08]; \ + add %sum, 1, %sum; \ +32: addcc %sum, t2, %sum; \ bcc,pt %xcc, 33f; \ - stx t2, [dest + off + 0x10]; \ - add sum, 1, sum; \ -33: addcc sum, t3, sum; \ + stx t2, [%dst + off + 0x10]; \ + add %sum, 1, %sum; \ +33: addcc %sum, t3, %sum; \ bcc,pt %xcc, 34f; \ - stx t3, [dest + off + 0x18]; \ - add sum, 1, sum; \ -34: addcc sum, t4, sum; \ + stx t3, [%dst + off + 0x18]; \ + add %sum, 1, %sum; \ +34: addcc %sum, t4, %sum; \ bcc,pt %xcc, 35f; \ - stx t4, [dest + off + 0x20]; \ - add sum, 1, sum; \ -35: addcc sum, t5, sum; \ + stx t4, [%dst + off + 0x20]; \ + add %sum, 1, %sum; \ +35: addcc %sum, t5, %sum; \ bcc,pt %xcc, 36f; \ - stx t5, [dest + off + 0x28]; \ - add sum, 1, sum; \ -36: addcc sum, t6, sum; \ + stx t5, [%dst + off + 0x28]; \ + add %sum, 1, %sum; \ +36: addcc %sum, t6, %sum; \ bcc,pt %xcc, 37f; \ - stx t6, [dest + off + 0x30]; \ - add sum, 1, sum; \ -37: addcc sum, t7, sum; \ + stx t6, [%dst + off + 0x30]; \ + add %sum, 1, %sum; \ +37: addcc %sum, t7, %sum; \ bcc,pt %xcc, 38f; \ - stx t7, [dest + off + 0x38]; \ - add sum, 1, sum; \ + stx t7, [%dst + off + 0x38]; \ + add %sum, 1, %sum; \ 38: -#define CSUMCOPY_EC_STUNALIGN(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ - stw t0, [dest + off + 0x04]; \ - addcc sum, t0, sum; \ +#define CSUMCOPY_EC_STUNALIGN(off, t0, t1, t2, t3, t4, t5, t6, t7) \ + stw t0, [%dst + off + 0x04]; \ + addcc %sum, t0, %sum; \ srlx t0, 32, t0; \ bcc,pt %xcc, 41f; \ - stw t0, [dest + off + 0x00]; \ - add sum, 1, sum; \ -41: stw t1, [dest + off + 0x0c]; \ - addcc sum, t1, sum; \ + stw t0, [%dst + off + 0x00]; \ + add %sum, 1, %sum; \ +41: stw t1, [%dst + off + 0x0c]; \ + addcc %sum, t1, %sum; \ srlx t1, 32, t1; \ bcc,pt %xcc, 42f; \ - stw t1, [dest + off + 0x08]; \ - add sum, 1, sum; \ -42: stw t2, [dest + off + 0x14]; \ - addcc sum, t2, sum; \ + stw t1, [%dst + off + 0x08]; \ + add %sum, 1, %sum; \ +42: stw t2, [%dst + off + 0x14]; \ + addcc %sum, t2, %sum; \ srlx t2, 32, t2; \ bcc,pt %xcc, 43f; \ - stw t2, [dest + off + 0x10]; \ - add sum, 1, sum; \ -43: stw t3, [dest + off + 0x1c]; \ - addcc sum, t3, sum; \ + stw t2, [%dst + off + 0x10]; \ + add %sum, 1, %sum; \ +43: stw t3, [%dst + off + 0x1c]; \ + addcc %sum, t3, %sum; \ srlx t3, 32, t3; \ bcc,pt %xcc, 44f; \ - stw t3, [dest + off + 0x18]; \ - add sum, 1, sum; \ -44: stw t4, [dest + off + 0x24]; \ - addcc sum, t4, sum; \ + stw t3, [%dst + off + 0x18]; \ + add %sum, 1, %sum; \ +44: stw t4, [%dst + off + 0x24]; \ + addcc %sum, t4, %sum; \ srlx t4, 32, t4; \ bcc,pt %xcc, 45f; \ - stw t4, [dest + off + 0x20]; \ - add sum, 1, sum; \ -45: stw t5, [dest + off + 0x2c]; \ - addcc sum, t5, sum; \ + stw t4, [%dst + off + 0x20]; \ + add %sum, 1, %sum; \ +45: stw t5, [%dst + off + 0x2c]; \ + addcc %sum, t5, %sum; \ srlx t5, 32, t5; \ bcc,pt %xcc, 46f; \ - stw t5, [dest + off + 0x28]; \ - add sum, 1, sum; \ -46: stw t6, [dest + off + 0x34]; \ - addcc sum, t6, sum; \ + stw t5, [%dst + off + 0x28]; \ + add %sum, 1, %sum; \ +46: stw t6, [%dst + off + 0x34]; \ + addcc %sum, t6, %sum; \ srlx t6, 32, t6; \ bcc,pt %xcc, 47f; \ - stw t6, [dest + off + 0x30]; \ - add sum, 1, sum; \ -47: stw t7, [dest + off + 0x3c]; \ - addcc sum, t7, sum; \ + stw t6, [%dst + off + 0x30]; \ + add %sum, 1, %sum; \ +47: stw t7, [%dst + off + 0x3c]; \ + addcc %sum, t7, %sum; \ srlx t7, 32, t7; \ bcc,pt %xcc, 48f; \ - stw t7, [dest + off + 0x38]; \ - add sum, 1, sum; \ + stw t7, [%dst + off + 0x38]; \ + add %sum, 1, %sum; \ 48: -#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1) \ - ldxa [src - off - 0x08] %asi, t0; \ - ldxa [src - off - 0x00] %asi, t1; \ +#define CSUMCOPY_LASTCHUNK(off, t0, t1) \ + ldxa [%src - off - 0x08] %asi, t0; \ + ldxa [%src - off - 0x00] %asi, t1; \ nop; nop; \ - addcc t0, sum, sum; \ - stw t0, [dst - off - 0x04]; \ + addcc t0, %sum, %sum; \ + stw t0, [%dst - off - 0x04]; \ srlx t0, 32, t0; \ bcc,pt %xcc, 51f; \ - stw t0, [dst - off - 0x08]; \ - add sum, 1, sum; \ -51: addcc t1, sum, sum; \ - stw t1, [dst - off + 0x04]; \ + stw t0, [%dst - off - 0x08]; \ + add %sum, 1, %sum; \ +51: addcc t1, %sum, %sum; \ + stw t1, [%dst - off + 0x04]; \ srlx t1, 32, t1; \ bcc,pt %xcc, 52f; \ - stw t1, [dst - off - 0x00]; \ - add sum, 1, sum; \ + stw t1, [%dst - off - 0x00]; \ + add %sum, 1, %sum; \ 52: +cpc_start: cc_end_cruft: - andcc %o3, 8, %g0 ! IEU1 Group + andcc %g7, 8, %g0 ! IEU1 Group be,pn %icc, 1f ! CTI - and %o3, 4, %g5 ! IEU0 - ldxa [%o0 + 0x00] %asi, %g2 ! Load Group - add %o1, 8, %o1 ! IEU0 - add %o0, 8, %o0 ! IEU1 - addcc %g2, %g7, %g7 ! IEU1 Group + 2 bubbles - stw %g2, [%o1 - 0x04] ! Store + and %g7, 4, %g5 ! IEU0 + ldxa [%src + 0x00] %asi, %g2 ! Load Group + add %dst, 8, %dst ! IEU0 + add %src, 8, %src ! IEU1 + addcc %g2, %sum, %sum ! IEU1 Group + 2 bubbles + stw %g2, [%dst - 0x04] ! Store srlx %g2, 32, %g2 ! IEU0 bcc,pt %xcc, 1f ! CTI Group - stw %g2, [%o1 - 0x08] ! Store - add %g7, 1, %g7 ! IEU0 + stw %g2, [%dst - 0x08] ! Store + add %sum, 1, %sum ! IEU0 1: brz,pt %g5, 1f ! CTI Group clr %g2 ! IEU0 - lduwa [%o0 + 0x00] %asi, %g2 ! Load - add %o1, 4, %o1 ! IEU0 Group - add %o0, 4, %o0 ! IEU1 - stw %g2, [%o1 - 0x04] ! Store Group + 2 bubbles + lduwa [%src + 0x00] %asi, %g2 ! Load + add %dst, 4, %dst ! IEU0 Group + add %src, 4, %src ! IEU1 + stw %g2, [%dst - 0x04] ! Store Group + 2 bubbles sllx %g2, 32, %g2 ! IEU0 -1: andcc %o3, 2, %g0 ! IEU1 +1: andcc %g7, 2, %g0 ! IEU1 be,pn %icc, 1f ! CTI Group clr %o4 ! IEU1 - lduha [%o0 + 0x00] %asi, %o4 ! Load - add %o0, 2, %o0 ! IEU0 Group - add %o1, 2, %o1 ! IEU1 - sth %o4, [%o1 - 0x2] ! Store Group + 2 bubbles + lduha [%src + 0x00] %asi, %o4 ! Load + add %src, 2, %src ! IEU0 Group + add %dst, 2, %dst ! IEU1 + sth %o4, [%dst - 0x2] ! Store Group + 2 bubbles sll %o4, 16, %o4 ! IEU0 -1: andcc %o3, 1, %g0 ! IEU1 +1: andcc %g7, 1, %g0 ! IEU1 be,pn %icc, 1f ! CTI Group clr %o5 ! IEU0 - lduba [%o0 + 0x00] %asi, %o5 ! Load - stb %o5, [%o1 + 0x00] ! Store Group + 2 bubbles + lduba [%src + 0x00] %asi, %o5 ! Load + stb %o5, [%dst + 0x00] ! Store Group + 2 bubbles sll %o5, 8, %o5 ! IEU0 1: or %g2, %o4, %o4 ! IEU1 or %o5, %o4, %o4 ! IEU0 Group - addcc %o4, %g7, %g7 ! IEU1 + addcc %o4, %sum, %sum ! IEU1 bcc,pt %xcc, ccfold ! CTI sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 Group b,pt %xcc, ccfold ! CTI - add %g7, 1, %g7 ! IEU1 + add %sum, 1, %sum ! IEU1 cc_fixit: bl,a,pn %icc, ccte ! CTI - andcc %g1, 0xf, %o3 ! IEU1 Group - andcc %o0, 1, %g0 ! IEU1 Group - bne,pn %icc, ccslow ! CTI - andcc %o0, 2, %g0 ! IEU1 Group + andcc %len, 0xf, %g7 ! IEU1 Group + andcc %src, 2, %g0 ! IEU1 Group be,pn %icc, 1f ! CTI - andcc %o0, 0x4, %g0 ! IEU1 Group - lduha [%o0 + 0x00] %asi, %g4 ! Load - sub %g1, 2, %g1 ! IEU0 - add %o0, 2, %o0 ! IEU0 Group - add %o1, 2, %o1 ! IEU1 + andcc %src, 0x4, %g0 ! IEU1 Group + lduha [%src + 0x00] %asi, %g4 ! Load + sub %len, 2, %len ! IEU0 + add %src, 2, %src ! IEU0 Group + add %dst, 2, %dst ! IEU1 sll %g4, 16, %g3 ! IEU0 Group + 1 bubble - addcc %g3, %g7, %g7 ! IEU1 + addcc %g3, %sum, %sum ! IEU1 bcc,pt %xcc, 0f ! CTI - srl %g7, 16, %g3 ! IEU0 Group + srl %sum, 16, %g3 ! IEU0 Group add %g3, 1, %g3 ! IEU0 4 clocks (mispredict) -0: andcc %o0, 0x4, %g0 ! IEU1 Group - sth %g4, [%o1 - 0x2] ! Store - sll %g7, 16, %g7 ! IEU0 +0: andcc %src, 0x4, %g0 ! IEU1 Group + sth %g4, [%dst - 0x2] ! Store + sll %sum, 16, %sum ! IEU0 sll %g3, 16, %g3 ! IEU0 Group - srl %g7, 16, %g7 ! IEU0 Group - or %g3, %g7, %g7 ! IEU0 Group (regdep) + srl %sum, 16, %sum ! IEU0 Group + or %g3, %sum, %sum ! IEU0 Group (regdep) 1: be,pt %icc, cc_dword_aligned ! CTI - andn %g1, 0xff, %g2 ! IEU1 - lduwa [%o0 + 0x00] %asi, %g4 ! Load Group - sub %g1, 4, %g1 ! IEU0 - add %o0, 4, %o0 ! IEU1 - add %o1, 4, %o1 ! IEU0 Group - addcc %g4, %g7, %g7 ! IEU1 Group + 1 bubble - stw %g4, [%o1 - 0x4] ! Store + andn %len, 0xff, %g2 ! IEU1 + lduwa [%src + 0x00] %asi, %g4 ! Load Group + sub %len, 4, %len ! IEU0 + add %src, 4, %src ! IEU1 + add %dst, 4, %dst ! IEU0 Group + addcc %g4, %sum, %sum ! IEU1 Group + 1 bubble + stw %g4, [%dst - 0x4] ! Store bcc,pt %xcc, cc_dword_aligned ! CTI - andn %g1, 0xff, %g2 ! IEU0 Group + andn %len, 0xff, %g2 ! IEU0 Group b,pt %xcc, cc_dword_aligned ! CTI 4 clocks (mispredict) - add %g7, 1, %g7 ! IEU0 + add %sum, 1, %sum ! IEU0 .align 32 - .globl __csum_partial_copy_sparc_generic, csum_partial_copy -csum_partial_copy: -__csum_partial_copy_sparc_generic: /* %o0=src, %o1=dest, %g1=len, %g7=sum */ - xorcc %o0, %o1, %o4 ! IEU1 Group - srl %g7, 0, %g7 ! IEU0 + .globl csum_partial_copy_sparc64 +csum_partial_copy_sparc64: /* %o0=src, %o1=dest, %o2=len, %o3=sum */ + xorcc %src, %dst, %o4 ! IEU1 Group + srl %sum, 0, %sum ! IEU0 andcc %o4, 3, %g0 ! IEU1 Group - srl %g1, 0, %g1 ! IEU0 + srl %len, 0, %len ! IEU0 + bne,pn %icc, ccslow ! CTI + andcc %src, 1, %g0 ! IEU1 Group bne,pn %icc, ccslow ! CTI - andcc %o0, 7, %g0 ! IEU1 Group + cmp %len, 256 ! IEU1 Group + bgeu,pt %icc, csum_partial_copy_vis ! CTI + andcc %src, 7, %g0 ! IEU1 Group be,pt %icc, cc_dword_aligned ! CTI - andn %g1, 0xff, %g2 ! IEU0 + andn %len, 0xff, %g2 ! IEU0 b,pt %xcc, cc_fixit ! CTI Group - cmp %g1, 6 ! IEU1 + cmp %len, 6 ! IEU1 cc_dword_aligned: brz,pn %g2, 3f ! CTI Group - andcc %o1, 4, %g0 ! IEU1 Group (brz uses IEU1) + andcc %dst, 4, %g0 ! IEU1 Group (brz uses IEU1) be,pn %icc, ccdbl + 4 ! CTI -5: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STUNALIGN( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) +5: CSUMCOPY_ECACHE_LOAD( 0x00,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STUNALIGN_LDNXT(0x40,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STUNALIGN_LDNXT(0x80,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STUNALIGN_LDNXT(0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STUNALIGN( 0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) 10: - sub %g1, 256, %g1 ! IEU0 Group - add %o0, 256, %o0 ! IEU1 - andncc %g1, 0xff, %g0 ! IEU1 Group + sub %len, 256, %len ! IEU0 Group + add %src, 256, %src ! IEU1 + andncc %len, 0xff, %g0 ! IEU1 Group bne,pt %icc, 5b ! CTI - add %o1, 256, %o1 ! IEU0 -3: andcc %g1, 0xf0, %o2 ! IEU1 Group + add %dst, 256, %dst ! IEU0 +3: andcc %len, 0xf0, %g1 ! IEU1 Group ccmerge:be,pn %icc, ccte ! CTI - andcc %g1, 0xf, %o3 ! IEU1 Group - sll %o2, 2, %o4 ! IEU0 -13: rd %pc, %o5 ! LSU Group + 4 clocks - add %o0, %o2, %o0 ! IEU0 Group - sub %o5, %o4, %o5 ! IEU1 Group - jmpl %o5 + (12f - 13b), %g0 ! CTI Group brk forced - add %o1, %o2, %o1 ! IEU0 Group -cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xe8,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xd8,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xc8,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xb8,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xa8,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x98,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x88,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x78,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3) + andcc %len, 0xf, %g7 ! IEU1 Group + sll %g1, 2, %o4 ! IEU0 +13: sethi %hi(12f), %o5 ! IEU0 Group + add %src, %g1, %src ! IEU1 + sub %o5, %o4, %o5 ! IEU0 Group + jmpl %o5 + %lo(12f), %g0 ! CTI Group brk forced + add %dst, %g1, %dst ! IEU0 Group +cctbl: CSUMCOPY_LASTCHUNK(0xe8,%g2,%g3) + CSUMCOPY_LASTCHUNK(0xd8,%g2,%g3) + CSUMCOPY_LASTCHUNK(0xc8,%g2,%g3) + CSUMCOPY_LASTCHUNK(0xb8,%g2,%g3) + CSUMCOPY_LASTCHUNK(0xa8,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x98,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x88,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x78,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x68,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x58,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x48,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x38,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x28,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x18,%g2,%g3) + CSUMCOPY_LASTCHUNK(0x08,%g2,%g3) 12: - andcc %g1, 0xf, %o3 ! IEU1 Group + andcc %len, 0xf, %g7 ! IEU1 Group ccte: bne,pn %icc, cc_end_cruft ! CTI sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 -ccfold: sllx %g7, 32, %o0 ! IEU0 Group - addcc %g7, %o0, %o0 ! IEU1 Group (regdep) +ccfold: sllx %sum, 32, %o0 ! IEU0 Group + addcc %sum, %o0, %o0 ! IEU1 Group (regdep) srlx %o0, 32, %o0 ! IEU0 Group (regdep) bcs,a,pn %xcc, 1f ! CTI add %o0, 1, %o0 ! IEU1 4 clocks (mispredict) 1: retl ! CTI Group brk forced sllx %g4, 32,%g4 ! IEU0 Group -ccdbl: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_EC_STALIGNED( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) +ccdbl: CSUMCOPY_ECACHE_LOAD( 0x00,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STALIGNED_LDNXT(0x40,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STALIGNED_LDNXT(0x80,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STALIGNED_LDNXT(0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) + CSUMCOPY_EC_STALIGNED( 0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) 11: - sub %g1, 256, %g1 ! IEU0 Group - add %o0, 256, %o0 ! IEU1 - andncc %g1, 0xff, %g0 ! IEU1 Group + sub %len, 256, %len ! IEU0 Group + add %src, 256, %src ! IEU1 + andncc %len, 0xff, %g0 ! IEU1 Group bne,pt %icc, ccdbl ! CTI - add %o1, 256, %o1 ! IEU0 + add %dst, 256, %dst ! IEU0 b,pt %xcc, ccmerge ! CTI Group - andcc %g1, 0xf0, %o2 ! IEU1 + andcc %len, 0xf0, %g1 ! IEU1 ccslow: mov 0, %g5 - brlez,pn %g1, 4f - andcc %o0, 1, %o5 + brlez,pn %len, 4f + andcc %src, 1, %o5 be,a,pt %icc, 1f - srl %g1, 1, %o3 - sub %g1, 1, %g1 - lduba [%o0] %asi, %g5 - add %o0, 1, %o0 - stb %g5, [%o1] - srl %g1, 1, %o3 - add %o1, 1, %o1 -1: brz,a,pn %o3, 3f - andcc %g1, 1, %g0 - andcc %o0, 2, %g0 + srl %len, 1, %g7 + sub %len, 1, %len + lduba [%src] %asi, %g5 + add %src, 1, %src + stb %g5, [%dst] + srl %len, 1, %g7 + add %dst, 1, %dst +1: brz,a,pn %g7, 3f + andcc %len, 1, %g0 + andcc %src, 2, %g0 be,a,pt %icc, 1f - srl %o3, 1, %o3 - lduha [%o0] %asi, %o4 - sub %g1, 2, %g1 + srl %g7, 1, %g7 + lduha [%src] %asi, %o4 + sub %len, 2, %len srl %o4, 8, %g2 - sub %o3, 1, %o3 - stb %g2, [%o1] + sub %g7, 1, %g7 + stb %g2, [%dst] add %o4, %g5, %g5 - stb %o4, [%o1 + 1] - add %o0, 2, %o0 - srl %o3, 1, %o3 - add %o1, 2, %o1 -1: brz,a,pn %o3, 2f - andcc %g1, 2, %g0 - lda [%o0] %asi, %o4 + stb %o4, [%dst + 1] + add %src, 2, %src + srl %g7, 1, %g7 + add %dst, 2, %dst +1: brz,a,pn %g7, 2f + andcc %len, 2, %g0 + lduwa [%src] %asi, %o4 5: srl %o4, 24, %g2 srl %o4, 16, %g3 - stb %g2, [%o1] + stb %g2, [%dst] srl %o4, 8, %g2 - stb %g3, [%o1 + 1] - add %o0, 4, %o0 - stb %g2, [%o1 + 2] + stb %g3, [%dst + 1] + add %src, 4, %src + stb %g2, [%dst + 2] addcc %o4, %g5, %g5 - stb %o4, [%o1 + 3] - addc %g5, %g0, %g5 ! I am now to lazy to optimize this (question is if it - add %o1, 4, %o1 ! is worthy). Maybe some day - with the sll/srl - subcc %o3, 1, %o3 ! tricks + stb %o4, [%dst + 3] + addc %g5, %g0, %g5 + add %dst, 4, %dst + subcc %g7, 1, %g7 bne,a,pt %icc, 5b - lda [%o0] %asi, %o4 + lduwa [%src] %asi, %o4 sll %g5, 16, %g2 srl %g5, 16, %g5 srl %g2, 16, %g2 - andcc %g1, 2, %g0 + andcc %len, 2, %g0 add %g2, %g5, %g5 2: be,a,pt %icc, 3f - andcc %g1, 1, %g0 - lduha [%o0] %asi, %o4 - andcc %g1, 1, %g0 + andcc %len, 1, %g0 + lduha [%src] %asi, %o4 + andcc %len, 1, %g0 srl %o4, 8, %g2 - add %o0, 2, %o0 - stb %g2, [%o1] + add %src, 2, %src + stb %g2, [%dst] add %g5, %o4, %g5 - stb %o4, [%o1 + 1] - add %o1, 2, %o1 + stb %o4, [%dst + 1] + add %dst, 2, %dst 3: be,a,pt %icc, 1f sll %g5, 16, %o4 - lduba [%o0] %asi, %g2 + lduba [%src] %asi, %g2 sll %g2, 8, %o4 - stb %g2, [%o1] + stb %g2, [%dst] add %g5, %o4, %g5 sll %g5, 16, %o4 1: addcc %o4, %g5, %g5 @@ -484,8 +484,22 @@ ccslow: mov 0, %g5 and %o4, 0xff, %o4 sll %g2, 8, %g2 or %g2, %o4, %g5 -4: addcc %g7, %g5, %g7 - addc %g0, %g7, %o0 +4: addcc %sum, %g5, %sum + addc %g0, %sum, %o0 retl srl %o0, 0, %o0 -__csum_partial_copy_end: +cpc_end: + + .globl cpc_handler +cpc_handler: + ldx [%sp + 0x7ff + 128], %g1 + sub %g0, EFAULT, %g2 + brnz,a,pt %g1, 1f + st %g2, [%g1] +1: retl + nop + + .section __ex_table + .align 4 + .word cpc_start, 0, cpc_end, cpc_handler + diff --git a/arch/sparc64/lib/locks.S b/arch/sparc64/lib/locks.S index 74054f63a..7b2bdba62 100644 --- a/arch/sparc64/lib/locks.S +++ b/arch/sparc64/lib/locks.S @@ -1,4 +1,4 @@ -/* $Id: locks.S,v 1.3 1997/07/22 05:51:42 davem Exp $ +/* $Id: locks.S,v 1.5 1997/07/31 05:28:16 davem Exp $ * locks.S: SMP low-level lock primitives on Sparc64. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -12,9 +12,9 @@ .align 32 ___lk_busy_spin: - orcc %g2, 0, %g0 - bne,pt %icc, ___lk_busy_spin - ldub [%g1 + 0], %g2 + ldub [%g1 + 0], %g2 + brnz,pt %g2, ___lk_busy_spin + membar #LoadLoad b,pt %xcc, 1f ldstub [%g1 + 0], %g2 @@ -23,16 +23,15 @@ ___lock_kernel: addcc %g2, -1, %g2 rdpr %pil, %g3 bcs,a,pn %icc, 9f - st %g2, [%g6 + AOFF_task_lock_depth] + stw %g2, [%g6 + AOFF_task_lock_depth] wrpr %g0, 15, %pil ldstub [%g1 + 0], %g2 -1: brnz,a,pn %g2, ___lk_busy_spin - ldub [%g1 + 0], %g2 +1: brnz,pn %g2, ___lk_busy_spin + membar #StoreLoad | #StoreStore lduw [%g6 + AOFF_task_processor], %g2 - membar #LoadLoad | #LoadStore stb %g2, [%g1 + 1] 2: mov -1, %g2 - st %g2, [%g6 + AOFF_task_lock_depth] + stw %g2, [%g6 + AOFF_task_lock_depth] wrpr %g3, 0, %pil 9: jmpl %o7 + 0x8, %g0 mov %g5, %o7 @@ -41,16 +40,16 @@ ___lock_kernel: ___lock_reacquire_kernel: rdpr %pil, %g3 wrpr %g0, 15, %pil - st %g2, [%g6 + AOFF_task_lock_depth] + stw %g2, [%g6 + AOFF_task_lock_depth] ldstub [%g1 + 0], %g2 1: brz,pt %g2, 3f - ldub [%g1 + 0], %g2 -2: brnz,a,pt %g2, 2b - ldub [%g1 + 0], %g2 + membar #StoreLoad | #StoreStore +2: ldub [%g1 + 0], %g2 + brnz,pt %g2, 2b + membar #LoadLoad b,pt %xcc, 1b ldstub [%g1 + 0], %g2 3: lduw [%g6 + AOFF_task_processor], %g2 - membar #LoadLoad | #LoadStore stb %g2, [%g1 + 1] wrpr %g3, 0, %pil jmpl %o7 + 0x8, %g0 diff --git a/arch/sparc64/lib/strlen_user.S b/arch/sparc64/lib/strlen_user.S index ef6cee5a6..9ae3d0381 100644 --- a/arch/sparc64/lib/strlen_user.S +++ b/arch/sparc64/lib/strlen_user.S @@ -78,11 +78,11 @@ __strlen_user: clr %o0 .section __ex_table,#alloc - .align 8 + .align 4 - .xword 10b, 30b - .xword 11b, 30b - .xword 12b, 30b - .xword 15b, 30b - .xword 13b, 30b - .xword 14b, 30b + .word 10b, 30b + .word 11b, 30b + .word 12b, 30b + .word 15b, 30b + .word 13b, 30b + .word 14b, 30b diff --git a/arch/sparc64/lib/strncpy_from_user.S b/arch/sparc64/lib/strncpy_from_user.S index 7a5dc768f..e0fb0f09b 100644 --- a/arch/sparc64/lib/strncpy_from_user.S +++ b/arch/sparc64/lib/strncpy_from_user.S @@ -49,6 +49,6 @@ __strncpy_from_user: mov -EFAULT, %o0 .section __ex_table,#alloc - .align 8 - .xword 10b, 4b - .xword 11b, 4b + .align 4 + .word 10b, 4b + .word 11b, 4b diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 5e16e1218..6bc52f3eb 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.18 1997/07/17 02:20:56 davem Exp $ +/* $Id: fault.c,v 1.20 1997/08/04 16:16:51 davem Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -217,56 +217,3 @@ bad_area: out: unlock_kernel(); } - -void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, pte_t pte) -{ - struct vm_area_struct *vmaring; - struct inode *inode; - unsigned long vaddr, offset, start; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int alias_found = 0; - - inode = vma->vm_dentry->d_inode; - if(!inode) - return; - - offset = (address & PAGE_MASK) - vma->vm_start; - vmaring = inode->i_mmap; - do { - vaddr = vmaring->vm_start + offset; - - /* This conditional is misleading... */ - if((vaddr ^ address) & PAGE_SIZE) { - alias_found++; - start = vmaring->vm_start; - while(start < vmaring->vm_end) { - pgdp = pgd_offset(vmaring->vm_mm, start); - if(!pgdp) goto next; - pmdp = pmd_offset(pgdp, start); - if(!pmdp) goto next; - ptep = pte_offset(pmdp, start); - if(!ptep) goto next; - - if(pte_val(*ptep) & _PAGE_PRESENT) { - flush_cache_page(vmaring, start); - *ptep = __pte(pte_val(*ptep) & - ~(_PAGE_CV)); - flush_tlb_page(vmaring, start); - } - next: - start += PAGE_SIZE; - } - } - } while((vmaring = vmaring->vm_next_share) != NULL); - - if(alias_found && (pte_val(pte) & _PAGE_CV)) { - pgdp = pgd_offset(vma->vm_mm, address); - pmdp = pmd_offset(pgdp, address); - ptep = pte_offset(pmdp, address); - flush_cache_page(vma, address); - *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV)); - flush_tlb_page(vma, address); - } -} diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 386c8540f..4a7a1bce1 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.40 1997/07/24 16:48:27 davem Exp $ +/* $Id: init.c,v 1.55 1997/08/24 01:22:29 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -37,6 +37,18 @@ unsigned long tlb_context_cache = CTX_FIRST_VERSION; /* References to section boundaries */ extern char __init_begin, __init_end, etext, __bss_start; +extern void __bfill64(void *, unsigned long *); + +static __inline__ void __init_pmd(pmd_t *pmdp) +{ + __bfill64((void *)pmdp, &null_pte_table); +} + +static __inline__ void __init_pgd(pgd_t *pgdp) +{ + __bfill64((void *)pgdp, &null_pmd_table); +} + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -103,48 +115,72 @@ void show_mem(void) /* IOMMU support, the ideas are right, the code should be cleaned a bit still... */ -/* XXX Also, play with the streaming buffers at some point, both - * XXX Fusion and Sunfire both have them aparently... -DaveM - */ - /* This keeps track of pages used in sparc_alloc_dvma() invocations. */ static unsigned long dvma_map_pages[0x10000000 >> 16] = { 0, }; static unsigned long dvma_pages_current_offset = 0; static int dvma_pages_current_index = 0; +/* #define E3000_DEBUG */ + __initfunc(unsigned long iommu_init(int iommu_node, unsigned long memory_start, unsigned long memory_end, struct linux_sbus *sbus)) { struct iommu_struct *iommu; struct sysio_regs *sregs; - struct linux_prom_registers rprop[2]; + struct linux_prom64_registers rprop; unsigned long impl, vers; unsigned long control, tsbbase; unsigned long *iopte; + u32 rlow, rhigh; int err, i; - err = prom_getproperty(iommu_node, "reg", (char *)rprop, +#ifdef E3000_DEBUG + prom_printf("\niommu_init: [%x:%016lx:%016lx:%p] ", + iommu_node, memory_start, memory_end, sbus); +#endif + err = prom_getproperty(iommu_node, "reg", (char *)&rprop, sizeof(rprop)); if(err == -1) { prom_printf("iommu_init: Cannot map SYSIO control registers.\n"); prom_halt(); } - sregs = (struct sysio_regs *) sparc_alloc_io(rprop[0].phys_addr, - (void *)0, + rlow = (rprop.phys_addr & 0xffffffff); + rhigh = (rprop.phys_addr >> 32); +#ifdef E3000_DEBUG + prom_printf("rlow[%08x] rhigh[%08x] ", rlow, rhigh); +#endif + sregs = (struct sysio_regs *) sparc_alloc_io(rlow, (void *)0, sizeof(struct sysio_regs), - "SYSIO Regs", - rprop[0].which_io, 0x0); + "SYSIO Regs", rhigh, 0x0); +#ifdef E3000_DEBUG + prom_printf("sregs[%p]\n"); +#endif + if(!sregs) { + prom_printf("iommu_init: Fatal error, sysio regs not mapped\n"); + prom_halt(); + } memory_start = (memory_start + 7) & ~7; iommu = (struct iommu_struct *) memory_start; memory_start += sizeof(struct iommu_struct); + +#ifdef E3000_DEBUG + prom_printf("iommu_init: iommu[%p] ", iommu); +#endif + + spin_lock_init(&iommu->iommu_lock); iommu->sysio_regs = sregs; sbus->iommu = iommu; control = sregs->iommu_control; impl = (control & IOMMU_CTRL_IMPL) >> 60; vers = (control & IOMMU_CTRL_VERS) >> 56; - printk("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n", +#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); control &= ~(IOMMU_CTRL_TSBSZ); @@ -168,7 +204,8 @@ __initfunc(unsigned long iommu_init(int iommu_node, unsigned long memory_start, /* Setup aliased mappings... */ for(i = 0; i < (65536 - 4096); i++) { - *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); + *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_STBUF | + IOPTE_CACHE | IOPTE_WRITE); *iopte |= (i << 16); iopte++; } @@ -177,15 +214,47 @@ __initfunc(unsigned long iommu_init(int iommu_node, unsigned long memory_start, for( ; i < 65536; 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] ", + (unsigned int)impl, (unsigned int)vers); + printk("FlushFLAG[%p,%016lx] ... ", + (iommu->sbuf_flushflag_va = (unsigned int *)memory_start), + (iommu->sbuf_flushflag_pa = __pa(memory_start))); + *(iommu->sbuf_flushflag_va) = 0; + memory_start += sizeof(unsigned long); /* yes, unsigned long, for alignment */ + + sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN); + +#ifdef E3000_DEBUG + prom_printf("done, returning %016lx\n", memory_start); +#endif + printk("ENABLED\n"); + + /* Finally enable DVMA arbitration for all devices, just in case. */ + sregs->sbus_control |= SYSIO_SBCNTRL_AEN; + return memory_start; } -void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr) +void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr, + struct linux_sbus *sbus) { - struct iommu_struct *iommu = SBus_chain->iommu; /* GROSS ME OUT! */ pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; @@ -193,6 +262,7 @@ void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr) /* Find out if we need to grab some pages. */ if(!dvma_map_pages[dvma_pages_current_index] || ((dvma_pages_current_offset + len) > (1 << 16))) { + struct linux_sbus *sbus; unsigned long *iopte; unsigned long newpages = __get_free_pages(GFP_KERNEL, 3, 0); int i; @@ -212,9 +282,16 @@ void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr) /* Stick it in the IOMMU. */ i = (65536 - 4096) + i; - iopte = (unsigned long *)(iommu->page_table + i); - *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); - *iopte |= __pa(newpages); + for_each_sbus(sbus) { + struct iommu_struct *iommu = sbus->iommu; + unsigned long flags; + + spin_lock_irqsave(&iommu->iommu_lock, flags); + iopte = (unsigned long *)(iommu->page_table + i); + *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); + *iopte |= __pa(newpages); + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } } /* Get this out of the way. */ @@ -258,6 +335,33 @@ __u32 mmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus) return (__u32)0; } +void mmu_release_scsi_one(u32 vaddr, unsigned long len, struct linux_sbus *sbus) +{ + 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; + unsigned int *sync_word; + + start &= PAGE_MASK; + + spin_lock_irqsave(&iommu->iommu_lock, flags); + + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + 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) { @@ -273,6 +377,36 @@ void mmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) } } +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; + + spin_lock_irqsave(&iommu->iommu_lock, flags); + + 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--; + } + 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); +} + static char sfmmuinfo[512]; char *mmu_info(void) @@ -340,7 +474,7 @@ int prom_itlb_ent, prom_dtlb_ent; unsigned long prom_itlb_tag, prom_itlb_data; unsigned long prom_dtlb_tag, prom_dtlb_data; -static inline void inherit_locked_prom_mappings(void) +void inherit_locked_prom_mappings(int save_p) { int i; int dtlb_seen = 0; @@ -367,9 +501,12 @@ static inline void inherit_locked_prom_mappings(void) data = spitfire_get_dtlb_data(i); if(!dtlb_seen && (data & _PAGE_L)) { unsigned long tag = spitfire_get_dtlb_tag(i); - prom_dtlb_ent = i; - prom_dtlb_tag = tag; - prom_dtlb_data = data; + + if(save_p) { + prom_dtlb_ent = i; + prom_dtlb_tag = tag; + prom_dtlb_data = data; + } __asm__ __volatile__("stxa %%g0, [%0] %1" : : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU)); membar("#Sync"); @@ -390,9 +527,12 @@ static inline void inherit_locked_prom_mappings(void) data = spitfire_get_itlb_data(i); if(!itlb_seen && (data & _PAGE_L)) { unsigned long tag = spitfire_get_itlb_tag(i); - prom_itlb_ent = i; - prom_itlb_tag = tag; - prom_itlb_data = data; + + if(save_p) { + prom_itlb_ent = i; + prom_itlb_tag = tag; + prom_itlb_data = data; + } __asm__ __volatile__("stxa %%g0, [%0] %1" : : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU)); membar("#Sync"); @@ -443,10 +583,14 @@ void __flush_cache_all(void) /* If not locked, zap it. */ void __flush_tlb_all(void) { - unsigned long flags; + unsigned long pstate; int i; - save_flags(flags); cli(); + __asm__ __volatile__("rdpr %%pstate, %0\n\t" + "wrpr %0, %1, %%pstate\n\t" + "flushw" + : "=r" (pstate) + : "i" (PSTATE_IE)); for(i = 0; i < 64; i++) { if(!(spitfire_get_dtlb_data(i) & _PAGE_L)) { __asm__ __volatile__("stxa %%g0, [%0] %1" @@ -465,19 +609,67 @@ void __flush_tlb_all(void) membar("#Sync"); } } - restore_flags(flags); + __asm__ __volatile__("wrpr %0, 0, %%pstate" + : : "r" (pstate)); } -void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx) +/* We are always protected by scheduler_lock under SMP. */ +void get_new_mmu_context(struct mm_struct *mm, unsigned long *ctx) { - if((ctx & ~(CTX_VERSION_MASK)) == 0) { - flush_tlb_all(); - ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION; - if(ctx == 1) - ctx = CTX_FIRST_VERSION; + unsigned int new_ctx = *ctx; + + if((new_ctx & ~(CTX_VERSION_MASK)) == 0) { + new_ctx += CTX_FIRST_VERSION; + if(new_ctx == 1) + new_ctx = CTX_FIRST_VERSION; + *ctx = new_ctx; + DO_LOCAL_FLUSH(smp_processor_id()); } - tlb_context_cache = ctx + 1; - mm->context = ctx; + mm->context = new_ctx; + mm->cpu_vm_mask = 0; /* Callers sets it properly. */ + (*ctx)++; +} + +#ifndef __SMP__ +unsigned long *pgd_quicklist = NULL; +unsigned long *pmd_quicklist = NULL; +unsigned long *pte_quicklist = NULL; +unsigned long pgtable_cache_size = 0; +#endif + +pgd_t *get_pgd_slow(void) +{ + pgd_t *pgd; + + pgd = (pgd_t *) __get_free_page(GFP_KERNEL); + if(pgd) + __init_pgd(pgd); + return pgd; +} + +pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset) +{ + pmd_t *pmd; + + pmd = (pmd_t *) __get_free_page(GFP_KERNEL); + if(pmd) { + __init_pmd(pmd); + pgd_set(pgd, pmd); + return pmd + offset; + } + return NULL; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) get_free_page(GFP_KERNEL); + if(pte) { + pmd_set(pmd, pte); + return pte + offset; + } + return NULL; } __initfunc(static void @@ -595,7 +787,7 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) */ pt = phys_base | _PAGE_VALID | _PAGE_SZ4MB; pt |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W; - save_flags(flags); cli(); + __save_and_cli(flags); __asm__ __volatile__(" stxa %1, [%0] %3 stxa %2, [%5] %4 @@ -608,15 +800,18 @@ 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"); - restore_flags(flags); + __restore_flags(flags); /* Now set kernel pgd to upper alias so physical page computations * work. */ init_mm.pgd += ((shift) / (sizeof(pgd_t *))); - null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + shift); - null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + shift); + /* The funny offsets are to make page table operations much quicker and + * requite less state, see pgtable.h for gory details. + */ + null_pmd_table=__pa(((unsigned long)&empty_null_pmd_table)+shift); + null_pte_table=__pa(((unsigned long)&empty_null_pte_table)+shift); pmdp = (pmd_t *) &empty_null_pmd_table; for(i = 0; i < 1024; i++) @@ -658,7 +853,7 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) flushi((long)&empty_zero_page); membar("#Sync"); - inherit_locked_prom_mappings(); + inherit_locked_prom_mappings(1); flush_tlb_all(); diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S index 18f9a363d..ec96e8871 100644 --- a/arch/sparc64/mm/ultra.S +++ b/arch/sparc64/mm/ultra.S @@ -1,4 +1,4 @@ -/* $Id: ultra.S,v 1.9 1997/07/24 12:15:08 davem Exp $ +/* $Id: ultra.S,v 1.3 1997/08/30 04:53:20 ralf Exp $ * ultra.S: Don't expand these all over the place... * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -13,28 +13,26 @@ .align 32 .globl __flush_tlb_mm, __flush_tlb_range, __flush_tlb_page __flush_tlb_mm: /* %o0 == (mm->context & 0x1fff) */ - rdpr %otherwin, %g1 - brz,pt %g1, 1f - mov %o7, %g3 - call __flushw_user - clr %g2 -1: rdpr %pil, %g1 -9: mov SECONDARY_CONTEXT, %g7 - wrpr %g0, 15, %pil - - ldxa [%g7] ASI_DMMU, %g2 + mov SECONDARY_CONTEXT, %g7 +9: ldxa [%g7] ASI_DMMU, %g2 cmp %g2, %o0 - be,pt %icc, 1f + bne,pn %icc, 1f mov 0x50, %g3 + stxa %g0, [%g3] ASI_DMMU_DEMAP + stxa %g0, [%g3] ASI_IMMU_DEMAP + retl + flush %g6 +1: rdpr %pstate, %g1 + wrpr %g1, PSTATE_IE, %pstate stxa %o0, [%g7] ASI_DMMU -1: stxa %g0, [%g3] ASI_DMMU_DEMAP - be,pt %icc, 1f - stxa %g0, [%g3] ASI_IMMU_DEMAP - + stxa %g0, [%g3] ASI_DMMU_DEMAP + stxa %g0, [%g3] ASI_IMMU_DEMAP + flush %g6 stxa %g2, [%g7] ASI_DMMU -1: wrpr %g1, 0x0, %pil + flush %g6 retl - flush %g6 + wrpr %g1, 0, %pstate + nop __flush_tlb_range: /* %o0 == (mm->context & 0x1fff), %o1 == start, %o2 == end */ sethi %hi(8192 - 1), %g5 or %g5, %lo(8192 - 1), %g5 @@ -43,19 +41,13 @@ __flush_tlb_range: /* %o0 == (mm->context & 0x1fff), %o1 == start, %o2 == end */ sub %o2, %o1, %o3 add %g5, 1, %g5 - orcc %o1, 0x50, %o1 + orcc %o1, 0x10, %o1 srlx %o3, 13, %o4 - rdpr %otherwin, %g1 - brz,pt %g1, 1f - mov %o7, %g3 - call __flushw_user - - clr %g2 -1: cmp %o4, 96 + cmp %o4, 96 bgu,pn %icc, 9b - rdpr %pil, %g1 - mov SECONDARY_CONTEXT, %g7 - wrpr %g0, 15, %pil + mov SECONDARY_CONTEXT, %g7 + rdpr %pstate, %g1 + wrpr %g1, PSTATE_IE, %pstate ldxa [%g7] ASI_DMMU, %g2 cmp %g2, %o0 @@ -66,37 +58,37 @@ __flush_tlb_range: /* %o0 == (mm->context & 0x1fff), %o1 == start, %o2 == end */ stxa %g0, [%o1 + %o3] ASI_IMMU_DEMAP brnz,pt %o3, 1b sub %o3, %g5, %o3 - nop + flush %g6 - be,pt %icc, 1f - wrpr %g1, 0x0, %pil + be,a,pt %icc, 1f + nop stxa %g2, [%g7] ASI_DMMU -1: retl - flush %g6 +1: flush %g6 + wrpr %g1, 0, %pstate + retl + nop .align 32 __flush_tlb_page: /* %o0 == (mm->context & 0x1fff), %o1 == page & PAGE_MASK */ - rdpr %otherwin, %g1 - brz,pt %g1, 1f - mov %o7, %g3 - call __flushw_user - clr %g2 -1: rdpr %pil, %g1 mov SECONDARY_CONTEXT, %g7 - wrpr %g0, 15, %pil - ldxa [%g7] ASI_DMMU, %g2 cmp %g2, %o0 be,pt %icc, 1f or %o1, 0x10, %g3 + stxa %g0, [%g3] ASI_DMMU_DEMAP + stxa %g0, [%g3] ASI_IMMU_DEMAP + retl + flush %g6 +1: rdpr %pstate, %g1 + wrpr %g1, PSTATE_IE, %pstate stxa %o0, [%g7] ASI_DMMU -1: stxa %g0, [%g3] ASI_DMMU_DEMAP - be,pt %icc, 1f - stxa %g0, [%g3] ASI_IMMU_DEMAP + stxa %g0, [%g3] ASI_DMMU_DEMAP + stxa %g0, [%g3] ASI_IMMU_DEMAP + flush %g6 stxa %g2, [%g7] ASI_DMMU -1: wrpr %g1, 0x0, %pil + flush %g6 retl - flush %g6 + wrpr %g1, 0, %pstate #ifdef __SMP__ /* These are all called by the slaves of a cross call, at @@ -111,50 +103,29 @@ __flush_tlb_page: /* %o0 == (mm->context & 0x1fff), %o1 == page & PAGE_MASK */ * %g2 scratch 1 * %g3 scratch 2 * %g4 scratch 3 - * - * NOTE: We do not acknowledge the UPA until we are done - * with the service. This is what tells the master - * that he can consider the effects of the flush - * "complete" on this cpu. */ .align 32 - .globl xcall_flush_tlb_page + .globl xcall_flush_tlb_page, xcall_flush_tlb_mm, xcall_flush_tlb_range xcall_flush_tlb_page: mov SECONDARY_CONTEXT, %g2 - nop + or %g6, 0x10, %g4 ldxa [%g2] ASI_DMMU, %g3 - cmp %g3, %g5 - be,pt %icc, 1f - or %g6, 0x10, %g4 stxa %g5, [%g2] ASI_DMMU -1: stxa %g0, [%g4] ASI_DMMU_DEMAP - - be,pt %icc, 1f - stxa %g0, [%g4] ASI_IMMU_DEMAP + stxa %g0, [%g4] ASI_DMMU_DEMAP + stxa %g0, [%g4] ASI_IMMU_DEMAP stxa %g3, [%g2] ASI_DMMU -1: b,pt %xcc, do_ivec_return - flush %g1 + retry - .align 32 - .globl xcall_flush_tlb_mm xcall_flush_tlb_mm: mov SECONDARY_CONTEXT, %g2 - nop + mov 0x50, %g4 ldxa [%g2] ASI_DMMU, %g3 - cmp %g3, %g5 - be,pt %icc, 1f - mov 0x50, %g4 stxa %g5, [%g2] ASI_DMMU -1: stxa %g0, [%g4] ASI_DMMU_DEMAP - - be,pt %icc, 1f - stxa %g0, [%g4] ASI_IMMU_DEMAP + stxa %g0, [%g4] ASI_DMMU_DEMAP + stxa %g0, [%g4] ASI_IMMU_DEMAP stxa %g3, [%g2] ASI_DMMU -1: b,pt %xcc, do_ivec_return - flush %g1 + retry - .align 32 - .globl xcall_flush_tlb_range xcall_flush_tlb_range: sethi %hi(8192 - 1), %g2 or %g2, %lo(8192 - 1), %g2 @@ -162,26 +133,54 @@ xcall_flush_tlb_range: andn %g7, %g2, %g7 sub %g7, %g6, %g3 add %g2, 1, %g2 - orcc %g6, 0x50, %g6 + orcc %g6, 0x10, %g6 srlx %g3, 13, %g4 cmp %g4, 96 bgu,pn %icc, xcall_flush_tlb_mm mov SECONDARY_CONTEXT, %g4 ldxa [%g4] ASI_DMMU, %g7 - cmp %g7, %g5 - be,pt %icc, 1f - sub %g3, %g2, %g3 + sub %g3, %g2, %g3 stxa %g5, [%g4] ASI_DMMU + nop + nop 1: stxa %g0, [%g6 + %g3] ASI_DMMU_DEMAP stxa %g0, [%g6 + %g3] ASI_IMMU_DEMAP brnz,pt %g3, 1b sub %g3, %g2, %g3 - bne,a,pn %icc, 1f - stxa %g7, [%g4] ASI_DMMU -1: b,pt %xcc, do_ivec_return - flush %g1 + stxa %g7, [%g4] ASI_DMMU + retry + nop + nop + + .globl xcall_report_regs +xcall_report_regs: + rdpr %pstate, %g2 + wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate + rdpr %pil, %g2 + wrpr %g0, 15, %pil + sethi %hi(109f), %g7 + b,pt %xcc, etrap_irq +109: or %g7, %lo(109b), %g7 + call __show_regs + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + b,pt %xcc, rtrap + clr %l6 + + .globl xcall_capture +xcall_capture: + rdpr %pstate, %g2 + wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate + rdpr %pil, %g2 + wrpr %g0, 15, %pil + sethi %hi(109f), %g7 + b,pt %xcc, etrap_irq +109: or %g7, %lo(109b), %g7 + call smp_penguin_jailcell + nop + b,pt %xcc, rtrap + clr %l6 /* These two are not performance critical... */ .globl xcall_flush_tlb_all @@ -209,8 +208,8 @@ xcall_flush_tlb_all: cmp %g2, 63 ble,pt %icc, 1b sll %g2, 3, %g3 - b,pt %xcc, do_ivec_return - flush %g1 + flush %g1 + retry .globl xcall_flush_cache_all xcall_flush_cache_all: @@ -222,6 +221,6 @@ xcall_flush_cache_all: cmp %g3, %g2 bleu,pt %xcc, 1b nop - b,pt %xcc, do_ivec_return - flush %g1 + flush %g1 + retry #endif /* __SMP__ */ diff --git a/arch/sparc64/prom/console.c b/arch/sparc64/prom/console.c index 40b33da4b..80ba5ec10 100644 --- a/arch/sparc64/prom/console.c +++ b/arch/sparc64/prom/console.c @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.7 1997/07/19 08:28:29 ecd Exp $ +/* $Id: console.c,v 1.8 1997/08/16 08:00:16 davem Exp $ * console.c: Routines that deal with sending and receiving IO * to/from the current console device using the PROM. * @@ -85,13 +85,13 @@ prom_query_input_device() if(prom_node_has_property(st_p, "keyboard")) return PROMDEV_IKBD; prom_getproperty(st_p, "device_type", propb, sizeof(propb)); - if(strncmp(propb, "serial", sizeof("serial"))) + if(strncmp(propb, "serial", 6)) return PROMDEV_I_UNK; /* FIXME: Is there any better way how to find out? */ memset(propb, 0, sizeof(propb)); st_p = prom_finddevice ("/options"); prom_getproperty(st_p, "input-device", propb, sizeof(propb)); - if (strncmp (propb, "tty", 3) || !propb[3] || propb[4]) + if (strncmp (propb, "tty", 3) || !propb[3]) return PROMDEV_I_UNK; switch (propb[3]) { case 'a': return PROMDEV_ITTYA; @@ -114,13 +114,13 @@ prom_query_output_device() if (propl >= 0 && propl == sizeof("display") && strncmp("display", propb, sizeof("display")) == 0) return PROMDEV_OSCREEN; - if(strncmp("serial", propb, sizeof("serial"))) + if(strncmp("serial", propb, 6)) return PROMDEV_O_UNK; /* FIXME: Is there any better way how to find out? */ memset(propb, 0, sizeof(propb)); st_p = prom_finddevice ("/options"); prom_getproperty(st_p, "output-device", propb, sizeof(propb)); - if (strncmp (propb, "tty", 3) || !propb[3] || propb[4]) + if (strncmp (propb, "tty", 3) || !propb[3]) return PROMDEV_O_UNK; switch (propb[3]) { case 'a': return PROMDEV_OTTYA; diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c index 0703b5cd7..9c5bce294 100644 --- a/arch/sparc64/prom/p1275.c +++ b/arch/sparc64/prom/p1275.c @@ -1,4 +1,4 @@ -/* $Id: p1275.c,v 1.11 1997/07/24 12:15:11 davem Exp $ +/* $Id: p1275.c,v 1.12 1997/07/26 18:39:01 davem Exp $ * p1275.c: Sun IEEE 1275 PROM low level interface routines * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -64,7 +64,7 @@ long p1275_cmd (char *service, long fmt, ...) long ctx = 0; p = p1275buf.prom_buffer; - save_and_cli(flags); + __save_and_cli(flags); ctx = spitfire_get_primary_context (); if (ctx) { flushw_user (); @@ -149,7 +149,7 @@ long p1275_cmd (char *service, long fmt, ...) if (ctx) spitfire_set_primary_context (ctx); - restore_flags(flags); + __restore_flags(flags); return x; } diff --git a/arch/sparc64/prom/ranges.c b/arch/sparc64/prom/ranges.c index 323539237..83f860d45 100644 --- a/arch/sparc64/prom/ranges.c +++ b/arch/sparc64/prom/ranges.c @@ -1,15 +1,21 @@ -/* $Id: ranges.c,v 1.3 1997/03/21 12:33:36 jj Exp $ +/* $Id: ranges.c,v 1.8 1997/08/17 22:39:45 ecd Exp $ * ranges.c: Handle ranges in newer proms for obio/sbus. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include <linux/config.h> #include <linux/init.h> #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/sbus.h> +#include <asm/fhc.h> #include <asm/system.h> +#ifdef CONFIG_PCI +#include <asm/pbm.h> +#include <asm/ebus.h> +#endif struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX]; int num_obio_ranges; @@ -62,6 +68,25 @@ void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers } } +/* Apply probed fhc ranges to registers passed, if no ranges return. */ +void prom_apply_fhc_ranges(struct linux_fhc *fhc, + struct linux_prom_registers *regs, + int nregs) +{ + if(fhc->num_fhc_ranges) + prom_adjust_regs(regs, nregs, fhc->fhc_ranges, + fhc->num_fhc_ranges); +} + +/* Apply probed central ranges to registers passed, if no ranges return. */ +void prom_apply_central_ranges(struct linux_central *central, + struct linux_prom_registers *regs, int nregs) +{ + if(central->num_central_ranges) + prom_adjust_regs(regs, nregs, central->central_ranges, + central->num_central_ranges); +} + __initfunc(void prom_ranges_init(void)) { } @@ -78,6 +103,56 @@ __initfunc(void prom_sbus_ranges_init(int iommund, struct linux_sbus *sbus)) sbus->num_sbus_ranges = (success/sizeof(struct linux_prom_ranges)); } +__initfunc(void prom_central_ranges_init(int cnode, struct linux_central *central)) +{ + int success; + + central->num_central_ranges = 0; + success = prom_getproperty(central->prom_node, "ranges", + (char *) central->central_ranges, + sizeof (central->central_ranges)); + if (success != -1) + central->num_central_ranges = (success/sizeof(struct linux_prom_ranges)); +} + +__initfunc(void prom_fhc_ranges_init(int fnode, struct linux_fhc *fhc)) +{ + int success; + + fhc->num_fhc_ranges = 0; + success = prom_getproperty(fhc->prom_node, "ranges", + (char *) fhc->fhc_ranges, + sizeof (fhc->fhc_ranges)); + if (success != -1) + fhc->num_fhc_ranges = (success/sizeof(struct linux_prom_ranges)); +} + +#ifdef CONFIG_PCI +__initfunc(void prom_ebus_ranges_init(struct linux_ebus *ebus)) +{ + int success; + + ebus->num_ebus_ranges = 0; + success = prom_getproperty(ebus->prom_node, "ranges", + (char *)ebus->ebus_ranges, + sizeof(ebus->ebus_ranges)); + if (success != -1) + ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges)); +} + +__initfunc(void prom_pbm_ranges_init(int pnode, struct linux_pbm_info *pbm)) +{ + int success; + + pbm->num_pbm_ranges = 0; + success = prom_getproperty(pbm->prom_node, "ranges", + (char *)&pbm->pbm_ranges, + sizeof(pbm->pbm_ranges)); + if(success != -1) + pbm->num_pbm_ranges = (success/sizeof(struct linux_prom_pci_ranges)); +} +#endif + void prom_apply_generic_ranges (int node, int parent, struct linux_prom_registers *regs, int nregs) { diff --git a/arch/sparc64/prom/tree.c b/arch/sparc64/prom/tree.c index ae4baf858..c2b386641 100644 --- a/arch/sparc64/prom/tree.c +++ b/arch/sparc64/prom/tree.c @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.5 1997/03/24 17:44:01 jj Exp $ +/* $Id: tree.c,v 1.6 1997/08/12 16:32:48 davem Exp $ * tree.c: Basic device tree traversal/scanning for the Linux * prom library. * @@ -26,7 +26,7 @@ __prom_getchild(int node) __inline__ int prom_getchild(int node) { - long cnode; + int cnode; if(node == -1) return 0; cnode = __prom_getchild(node); @@ -37,7 +37,7 @@ prom_getchild(int node) __inline__ int prom_getparent(int node) { - long cnode; + int cnode; if(node == -1) return 0; cnode = p1275_cmd ("parent", P1275_INOUT(1, 1), node); @@ -57,12 +57,12 @@ __prom_getsibling(int node) __inline__ int prom_getsibling(int node) { - long sibnode; + int sibnode; if(node == -1) return 0; sibnode = __prom_getsibling(node); if(sibnode == -1) return 0; - return (int)sibnode; + return sibnode; } /* Return the length in bytes of property 'prop' at node 'node'. diff --git a/arch/sparc64/solaris/Makefile b/arch/sparc64/solaris/Makefile new file mode 100644 index 000000000..56d252962 --- /dev/null +++ b/arch/sparc64/solaris/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for the Solaris binary emulation. +# +# 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 definition is now in the main makefile... + +O_TARGET := solaris.o +O_OBJS := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o +ifeq ($(CONFIG_SOLARIS_EMUL),m) +M_OBJS := $(O_TARGET) +endif + +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + +ifneq ($(CONFIG_SOLARIS_EMUL),y) +do_it_all: +endif + +include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/solaris/conv.h b/arch/sparc64/solaris/conv.h new file mode 100644 index 000000000..c806d3dc1 --- /dev/null +++ b/arch/sparc64/solaris/conv.h @@ -0,0 +1,28 @@ +/* $Id: conv.h,v 1.2 1997/09/03 12:29:13 jj Exp $ + * conv.h: Utility macros for Solaris emulation + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +/* #define DEBUG_SOLARIS */ + +#ifndef __ASSEMBLY__ + +#include <asm/unistd.h> + +/* As gcc will warn about casting u32 to some ptr, we have to cast it to + * unsigned long first, and that's what is A() for. + * You just do (void *)A(x), instead of having to + * type (void *)((unsigned long)x) or instead of just (void *)x, which will + * produce warnings. + */ +#define A(x) ((unsigned long)x) + +extern unsigned sys_call_table[]; +extern unsigned sys_call_table32[]; +extern unsigned sunos_sys_table[]; + +#define SYS(name) ((long)sys_call_table[__NR_##name]) +#define SUNOS(x) ((long)sunos_sys_table[x]) + +#endif /* __ASSEMBLY__ */ diff --git a/arch/sparc64/solaris/entry64.S b/arch/sparc64/solaris/entry64.S new file mode 100644 index 000000000..e983010d1 --- /dev/null +++ b/arch/sparc64/solaris/entry64.S @@ -0,0 +1,202 @@ +/* $Id: entry64.S,v 1.3 1997/09/03 12:29:12 jj Exp $ + * entry64.S: Solaris syscall emulation entry point. + * + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + */ + +#include <linux/errno.h> + +#include <asm/head.h> +#include <asm/asi.h> +#include <asm/smp.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/signal.h> +#include <asm/pgtable.h> +#include <asm/processor.h> + +#include "conv.h" + +#define NR_SYSCALLS 256 + + .text +solaris_syscall_trace: + call syscall_trace + nop + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + mov %i3, %o3 + b,pt %xcc, 2f + mov %i4, %o4 + +solaris_sucks: +/* Solaris is a big system which needs to be able to do all the things + * in Inf+1 different ways */ + add %i6, 0x5c, %o0 + mov %i0, %g1 + mov %i1, %i0 + mov %i2, %i1 + srl %o0, 0, %o0 + mov %i3, %i2 + mov %i4, %i3 + mov %i5, %i4 + ba,pt %xcc, solaris_sparc_syscall +exen: lduwa [%o0] ASI_S, %i5 + +exenf: ba,pt %xcc, solaris_sparc_syscall + clr %i5 + +/* For shared binaries, binfmt_elf32 already sets up personality + and exec_domain. This is to handle static binaries as well */ +solaris_reg: + call solaris_register + nop + ba,pt %xcc, 1f + mov %i0, %o0 + +linux_syscall_for_solaris: + sll %l7, 2, %l4 + ba,pt %xcc, 10f + lduw [%l6 + %l4], %l7 + + /* Solaris system calls enter here... */ + .align 32 + .globl solaris_sparc_syscall +solaris_sparc_syscall: +#ifdef DEBUG_SOLARIS + mov %g1, %o0 + call entry_printk + add %sp, STACK_BIAS + REGWIN_SZ, %o1 +#endif + ldub [%g6 + AOFF_task_personality + ASIZ_task_personality - 1], %l0 + cmp %g1, 255 + bg,pn %icc, solaris_unimplemented + srl %g1, 0, %g1 + sethi %hi(solaris_sys_table), %l7 + brz,pn %g1, solaris_sucks + mov %i0, %o0 + sll %g1, 2, %l4 + cmp %l0, 1 + bne,pn %icc, solaris_reg +1: mov %i1, %o1 + lduw [%l7 + %l4], %l7 + mov %i2, %o2 + ldx [%g6 + AOFF_task_flags], %l5 +! st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] + cmp %l7, NR_SYSCALLS + bleu,a,pn %xcc, linux_syscall_for_solaris + sethi %hi(sys_call_table32), %l6 + andcc %l7, 1, %g0 + bne,a,pn %icc, 10f + add %sp, STACK_BIAS + REGWIN_SZ, %o0 +10: mov %i3, %o3 + mov %i4, %o4 + andn %l7, 3, %l7 + andcc %l5, 0x20, %g0 + bne,pn %icc, solaris_syscall_trace + mov %i0, %l5 +2: call %l7 + mov %i5, %o5 +ret_from_solaris: +#ifdef DEBUG_SOLARIS + call exit_printk + mov %o0, %l0 + mov %l0, %o0 +#endif + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + ldx [%g6 + AOFF_task_flags], %l6 + sra %o0, 0, %o0 + mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3 + cmp %o0, -ENOIOCTLCMD + sllx %g2, 32, %g2 + bgeu,pn %xcc, 1f + andcc %l6, 0x20, %l6 + + /* System call success, clear Carry condition code. */ + andn %g3, %g2, %g3 + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] + bne,pn %icc, solaris_syscall_trace2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc + add %l1, 0x4, %l2 !npc = npc+4 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + clr %l6 + call rtrap + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] +1: + /* System call failure, set Carry condition code. + * Also, get abs(errno) to return to the process. + */ + sub %g0, %o0, %o0 + or %g3, %g2, %g3 + cmp %o0, ERANGE /* 0-ERANGE are identity mapped */ + bleu,pt %icc, 1f + cmp %o0, EMEDIUMTYPE + bgu,pn %icc, 1f + sethi %hi(solaris_err_table), %l6 + sll %o0, 2, %o0 + or %l6, %lo(solaris_err_table), %l6 + ldsw [%l6 + %o0], %o0 +1: stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + mov 1, %l6 + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] + bne,pn %icc, solaris_syscall_trace2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc + add %l1, 0x4, %l2 !npc = npc+4 + + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + call rtrap + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] +solaris_syscall_trace2: + call syscall_trace + add %l1, 0x4, %l2 /* npc = npc+4 */ + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + call rtrap + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] + + /* This one is tricky, so that's why we do it in assembly */ + .globl solaris_sigsuspend +solaris_sigsuspend: + call do_sol_sigsuspend + nop + brlz,pn %o0, ret_from_solaris + nop + call sys_sigsuspend + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + + .globl solaris_getpid +solaris_getpid: + call sys_getppid /* This is tricky, so don't do it in assembly */ + nop + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_from_solaris + lduw [%g6 + AOFF_task_pid], %o0 + + .globl solaris_getuid +solaris_getuid: + lduh [%g6 + AOFF_task_euid], %o1 + lduh [%g6 + AOFF_task_uid], %o0 + b,pt %xcc, ret_from_solaris + stx %o1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + + .globl solaris_getgid +solaris_getgid: + lduh [%g6 + AOFF_task_egid], %o1 + lduh [%g6 + AOFF_task_gid], %o0 + b,pt %xcc, ret_from_solaris + stx %o1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + + .globl solaris_unimplemented +solaris_unimplemented: + call do_sol_unimplemented + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, ret_from_solaris + nop + + .section __ex_table,#alloc + .align 4 + .word exen, exenf + diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c new file mode 100644 index 000000000..9d0905adc --- /dev/null +++ b/arch/sparc64/solaris/fs.c @@ -0,0 +1,711 @@ +/* $Id: fs.c,v 1.4 1997/09/04 15:46:26 jj Exp $ + * fs.c: fs related syscall emulation for Solaris + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/smp_lock.h> +#include <linux/limits.h> +#include <linux/resource.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/ptrace.h> + +#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) +#define R4_MINOR(DEV) ((DEV) & 0x3ffff) +#define R3_VERSION 1 +#define R4_VERSION 2 + +typedef struct { + s32 tv_sec; + s32 tv_nsec; +} timestruct_t; + +struct sol_stat { + u32 st_dev; + s32 st_pad1[3]; /* network id */ + u32 st_ino; + u32 st_mode; + u32 st_nlink; + u32 st_uid; + u32 st_gid; + u32 st_rdev; + s32 st_pad2[2]; + s32 st_size; + s32 st_pad3; /* st_size, off_t expansion */ + timestruct_t st_atime; + timestruct_t st_mtime; + timestruct_t st_ctime; + s32 st_blksize; + s32 st_blocks; + char st_fstype[16]; + s32 st_pad4[8]; /* expansion area */ +}; + +#define UFSMAGIC (((unsigned)'u'<<24)||((unsigned)'f'<<16)||((unsigned)'s'<<8)) + +static inline int putstat(struct sol_stat *ubuf, struct stat *kbuf) +{ + if (put_user (R4_DEV(kbuf->st_dev), &ubuf->st_dev) || + __put_user (kbuf->st_ino, &ubuf->st_ino) || + __put_user (kbuf->st_mode, &ubuf->st_mode) || + __put_user (kbuf->st_nlink, &ubuf->st_nlink) || + __put_user (kbuf->st_uid, &ubuf->st_uid) || + __put_user (kbuf->st_gid, &ubuf->st_gid) || + __put_user (R4_DEV(kbuf->st_rdev), &ubuf->st_rdev) || + __put_user (kbuf->st_size, &ubuf->st_size) || + __put_user (kbuf->st_atime, &ubuf->st_atime.tv_sec) || + __put_user (0, &ubuf->st_atime.tv_nsec) || + __put_user (kbuf->st_mtime, &ubuf->st_mtime.tv_sec) || + __put_user (0, &ubuf->st_mtime.tv_nsec) || + __put_user (kbuf->st_ctime, &ubuf->st_ctime.tv_sec) || + __put_user (0, &ubuf->st_ctime.tv_nsec) || + __put_user (kbuf->st_blksize, &ubuf->st_blksize) || + __put_user (kbuf->st_blocks, &ubuf->st_blocks) || + __put_user (UFSMAGIC, (unsigned *)ubuf->st_fstype)) + return -EFAULT; + return 0; +} + +asmlinkage int solaris_stat(u32 filename, u32 statbuf) +{ + int ret; + struct stat s; + char *filenam; + unsigned long old_fs = get_fs(); + int (*sys_newstat)(char *,struct stat *) = + (int (*)(char *,struct stat *))SYS(stat); + + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + set_fs (KERNEL_DS); + ret = sys_newstat(filenam, &s); + set_fs (old_fs); + putname32 (filenam); + if (putstat ((struct sol_stat *)A(statbuf), &s)) + return -EFAULT; + } + return ret; +} + +asmlinkage int solaris_xstat(int vers, u32 filename, u32 statbuf) +{ + /* Solaris doesn't bother with looking at vers, so we do neither */ + return solaris_stat(filename, statbuf); +} + +asmlinkage int solaris_lstat(u32 filename, u32 statbuf) +{ + int ret; + struct stat s; + char *filenam; + unsigned long old_fs = get_fs(); + int (*sys_newlstat)(char *,struct stat *) = + (int (*)(char *,struct stat *))SYS(lstat); + + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + set_fs (KERNEL_DS); + ret = sys_newlstat(filenam, &s); + set_fs (old_fs); + putname32 (filenam); + if (putstat ((struct sol_stat *)A(statbuf), &s)) + return -EFAULT; + } + return ret; +} + +asmlinkage int solaris_lxstat(int vers, u32 filename, u32 statbuf) +{ + return solaris_lstat(filename, statbuf); +} + +asmlinkage int solaris_fstat(unsigned int fd, u32 statbuf) +{ + int ret; + struct stat s; + unsigned long old_fs = get_fs(); + int (*sys_newfstat)(unsigned,struct stat *) = + (int (*)(unsigned,struct stat *))SYS(fstat); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat ((struct sol_stat *)A(statbuf), &s)) + return -EFAULT; + return ret; +} + +asmlinkage int solaris_fxstat(int vers, u32 fd, u32 statbuf) +{ + return solaris_fstat(fd, statbuf); +} + +asmlinkage int solaris_mknod(u32 path, u32 mode, s32 dev) +{ + int (*sys_mknod)(const char *,int,dev_t) = + (int (*)(const char *,int,dev_t))SYS(mknod); + int major, minor; + + if ((major = R4_MAJOR(dev)) > 255 || + (minor = R4_MINOR(dev)) > 255) return -EINVAL; + return sys_mknod((const char *)A(path), mode, MKDEV(major,minor)); +} + +asmlinkage int solaris_xmknod(int vers, u32 path, u32 mode, s32 dev) +{ + return solaris_mknod(path, mode, dev); +} + +/* This statfs thingie probably will go in the near future, but... */ + +struct sol_statfs { + short f_type; + s32 f_bsize; + s32 f_frsize; + s32 f_blocks; + s32 f_bfree; + u32 f_files; + u32 f_ffree; + char f_fname[6]; + char f_fpack[6]; +}; + +asmlinkage int solaris_statfs(u32 path, u32 buf, int len, int fstype) +{ + int ret; + struct statfs s; + unsigned long old_fs = get_fs(); + int (*sys_statfs)(const char *,struct statfs *) = + (int (*)(const char *,struct statfs *))SYS(statfs); + struct sol_statfs *ss = (struct sol_statfs *)A(buf); + + if (len != sizeof(struct sol_statfs)) return -EINVAL; + if (!fstype) { + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)A(path), &s); + set_fs (old_fs); + if (!ret) { + if (put_user (s.f_type, &ss->f_type) || + __put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __clear_user (&ss->f_fname, 12)) + return -EFAULT; + } + return ret; + } +/* Linux can't stat unmounted filesystems so we + * simply lie and claim 100MB of 1GB is free. Sorry. + */ + if (put_user (fstype, &ss->f_type) || + __put_user (1024, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (1024*1024, &ss->f_blocks) || + __put_user (100*1024, &ss->f_bfree) || + __put_user (60000, &ss->f_files) || + __put_user (50000, &ss->f_ffree) || + __clear_user (&ss->f_fname, 12)) + return -EFAULT; + return 0; +} + +asmlinkage int solaris_fstatfs(u32 fd, u32 buf, int len, int fstype) +{ + int ret; + struct statfs s; + unsigned long old_fs = get_fs(); + int (*sys_fstatfs)(unsigned,struct statfs *) = + (int (*)(unsigned,struct statfs *))SYS(fstatfs); + struct sol_statfs *ss = (struct sol_statfs *)A(buf); + + if (len != sizeof(struct sol_statfs)) return -EINVAL; + if (!fstype) { + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (!ret) { + if (put_user (s.f_type, &ss->f_type) || + __put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __clear_user (&ss->f_fname, 12)) + return -EFAULT; + } + return ret; + } + /* Otherwise fstatfs is the same as statfs */ + return solaris_statfs(0, buf, len, fstype); +} + +struct sol_statvfs { + u32 f_bsize; + u32 f_frsize; + u32 f_blocks; + u32 f_bfree; + u32 f_bavail; + u32 f_files; + u32 f_ffree; + u32 f_favail; + u32 f_fsid; + char f_basetype[16]; + u32 f_flag; + u32 f_namemax; + char f_fstr[32]; + u32 f_filler[16]; +}; + +static int report_statvfs(struct inode *inode, u32 buf) +{ + struct statfs s; + unsigned long old_fs = get_fs(); + int error; + struct sol_statvfs *ss = (struct sol_statvfs *)A(buf); + + set_fs (KERNEL_DS); + error = inode->i_sb->s_op->statfs(inode->i_sb, &s, sizeof(struct statfs)); + set_fs (old_fs); + if (!error) { + const char *p = inode->i_sb->s_type->name; + int i = 0; + int j = strlen (p); + + if (j > 15) j = 15; + if (IS_RDONLY(inode)) i = 1; + if (IS_NOSUID(inode)) i |= 2; + if (put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_bavail, &ss->f_bavail) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __put_user (s.f_ffree, &ss->f_favail) || + __put_user (R4_DEV(inode->i_sb->s_dev), &ss->f_fsid) || + __copy_to_user (ss->f_basetype,p,j) || + __put_user (0, (char *)&ss->f_basetype[j]) || + __put_user (s.f_namelen, &ss->f_namemax) || + __put_user (i, &ss->f_flag) || + __clear_user (&ss->f_fstr, 32)) + return -EFAULT; + } + return error; +} + +asmlinkage int solaris_statvfs(u32 path, u32 buf) +{ + struct dentry * dentry; + int error; + + lock_kernel(); + dentry = namei((const char *)A(path)); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + + error = -ENOSYS; + if (inode->i_sb->s_op->statfs) + error = report_statvfs(inode, buf); + dput(dentry); + } + unlock_kernel(); + return error; +} + +asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf) +{ + struct inode * inode; + struct dentry * dentry; + struct file * file; + int error; + + lock_kernel(); + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + error = -EBADF; + else if (!(dentry = file->f_dentry)) + error = -ENOENT; + else if (!(inode = dentry->d_inode)) + error = -ENOENT; + else if (!inode->i_sb) + error = -ENODEV; + else if (!inode->i_sb->s_op->statfs) + error = -ENOSYS; + else + error = report_statvfs(inode, buf); + unlock_kernel(); + return error; +} + +asmlinkage int solaris_open(u32 filename, int flags, u32 mode) +{ + int (*sys_open)(const char *,int,int) = + (int (*)(const char *,int,int))SYS(open); + int fl = flags & 0xf; + + if (flags & 0x8050) fl |= O_SYNC; + if (flags & 0x80) fl |= O_NONBLOCK; + if (flags & 0x100) fl |= O_CREAT; + if (flags & 0x200) fl |= O_TRUNC; + if (flags & 0x400) fl |= O_EXCL; + if (flags & 0x800) fl |= O_NOCTTY; + return sys_open((const char *)A(filename), fl, mode); +} + +#define SOL_F_SETLK 6 +#define SOL_F_SETLKW 7 +#define SOL_F_FREESP 11 +#define SOL_F_ISSTREAM 13 +#define SOL_F_GETLK 14 +#define SOL_F_PRIV 15 +#define SOL_F_NPRIV 16 +#define SOL_F_QUOTACTL 17 +#define SOL_F_BLOCKS 18 +#define SOL_F_BLKSIZE 19 +#define SOL_F_GETOWN 23 +#define SOL_F_SETOWN 24 + +struct sol_flock { + short l_type; + short l_whence; + u32 l_start; + u32 l_len; + s32 l_sysid; + s32 l_pid; + s32 l_pad[4]; +}; + +asmlinkage int solaris_fcntl(unsigned fd, unsigned cmd, u32 arg) +{ + int (*sys_fcntl)(unsigned,unsigned,unsigned long) = + (int (*)(unsigned,unsigned,unsigned long))SYS(fcntl); + int ret, flags; + + switch (cmd) { + case F_DUPFD: + case F_GETFD: + case F_SETFD: return sys_fcntl(fd, cmd, (unsigned long)arg); + case F_GETFL: + flags = sys_fcntl(fd, cmd, 0); + ret = flags & 0xf; + if (flags & O_SYNC) ret |= 0x8050; + if (flags & O_NONBLOCK) ret |= 0x80; + return ret; + case F_SETFL: + flags = arg & 0xf; + if (arg & 0x8050) flags |= O_SYNC; + if (arg & 0x80) flags |= O_NONBLOCK; + return sys_fcntl(fd, cmd, (long)flags); + case SOL_F_GETLK: + case SOL_F_SETLK: + case SOL_F_SETLKW: + { + struct flock f; + unsigned long old_fs = get_fs(); + + switch (cmd) { + case SOL_F_GETLK: cmd = F_GETLK; break; + case SOL_F_SETLK: cmd = F_SETLK; break; + case SOL_F_SETLKW: cmd = F_SETLKW; break; + } + + get_user_ret (f.l_type, &((struct sol_flock *)A(arg))->l_type, -EFAULT); + __get_user_ret (f.l_whence, &((struct sol_flock *)A(arg))->l_whence, -EFAULT); + __get_user_ret (f.l_start, &((struct sol_flock *)A(arg))->l_start, -EFAULT); + __get_user_ret (f.l_len, &((struct sol_flock *)A(arg))->l_len, -EFAULT); + __get_user_ret (f.l_pid, &((struct sol_flock *)A(arg))->l_sysid, -EFAULT); + set_fs(KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs(old_fs); + __put_user_ret (f.l_type, &((struct sol_flock *)A(arg))->l_type, -EFAULT); + __put_user_ret (f.l_whence, &((struct sol_flock *)A(arg))->l_whence, -EFAULT); + __put_user_ret (f.l_start, &((struct sol_flock *)A(arg))->l_start, -EFAULT); + __put_user_ret (f.l_len, &((struct sol_flock *)A(arg))->l_len, -EFAULT); + __put_user_ret (f.l_pid, &((struct sol_flock *)A(arg))->l_pid, -EFAULT); + __put_user_ret (0, &((struct sol_flock *)A(arg))->l_sysid, -EFAULT); + return ret; + } + } + return -EINVAL; +} + +asmlinkage int solaris_ulimit(int cmd, int val) +{ + switch (cmd) { + case 1: /* UL_GETFSIZE - in 512B chunks */ + return current->rlim[RLIMIT_FSIZE].rlim_cur >> 9; + case 2: /* UL_SETFSIZE */ + if ((unsigned long)val > (LONG_MAX>>9)) return -ERANGE; + val <<= 9; + lock_kernel(); + if (val > current->rlim[RLIMIT_FSIZE].rlim_max) { + if (!suser()) { + unlock_kernel(); + return -EPERM; + } + current->rlim[RLIMIT_FSIZE].rlim_max = val; + } + current->rlim[RLIMIT_FSIZE].rlim_cur = val; + unlock_kernel(); + return 0; + case 3: /* UL_GMEMLIM */ + return current->rlim[RLIMIT_DATA].rlim_cur; + case 4: /* UL_GDESLIM */ + return NR_OPEN; + } + return -EINVAL; +} + +static int chown_common(struct dentry * dentry, uid_t user, gid_t group) +{ + struct inode * inode; + struct iattr newattrs; + int error; + + error = -ENOENT; + if (!(inode = dentry->d_inode)) { + printk("chown_common: NULL inode\n"); + goto out; + } + error = -EROFS; + if (IS_RDONLY(inode)) + goto out; + error = -EPERM; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + goto out; + if (user == (uid_t) -1) + user = inode->i_uid; + if (group == (gid_t) -1) + group = inode->i_gid; + newattrs.ia_mode = inode->i_mode; + newattrs.ia_uid = user; + newattrs.ia_gid = group; + newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; + /* + * If the owner has been changed, remove the setuid bit + */ + if (inode->i_mode & S_ISUID) { + newattrs.ia_mode &= ~S_ISUID; + newattrs.ia_valid |= ATTR_MODE; + } + /* + * If the group has been changed, remove the setgid bit + * + * Don't remove the setgid bit if no group execute bit. + * This is a file marked for mandatory locking. + */ + if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { + newattrs.ia_mode &= ~S_ISGID; + newattrs.ia_valid |= ATTR_MODE; + } + if (inode->i_sb && inode->i_sb->dq_op) { + inode->i_sb->dq_op->initialize(inode, -1); + error = -EDQUOT; + if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) + goto out; + error = notify_change(inode, &newattrs); + if (error) + inode->i_sb->dq_op->transfer(inode, &newattrs, 1); + } else + error = notify_change(inode, &newattrs); +out: + return error; +} + +/* Linux chown works like Solaris lchown. Solaris chown does follow symlink */ +asmlinkage int solaris_chown(u32 filename, s32 user, s32 group) +{ + struct dentry * dentry; + int error; + + lock_kernel(); + dentry = namei((const char *)A(filename)); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = chown_common(dentry, user, group); + dput(dentry); + } + unlock_kernel(); + return error; +} + +/* At least at the time I'm writing this, Linux doesn't have ACLs, so we + just fake this */ +asmlinkage int solaris_acl(u32 filename, int cmd, int nentries, u32 aclbufp) +{ + return -ENOSYS; +} + +asmlinkage int solaris_facl(unsigned int fd, int cmd, int nentries, u32 aclbufp) +{ + return -ENOSYS; +} + +asmlinkage int solaris_pread(int fd, u32 buf, u32 nbyte, s32 offset) +{ + off_t temp; + int retval; + struct file * file; + long (*sys_read)(unsigned int, char *, unsigned long) = + (long (*)(unsigned int, char *, unsigned long))SYS(read); + long (*sys_lseek)(unsigned int, off_t, unsigned int) = + (long (*)(unsigned int, off_t, unsigned int))SYS(lseek); + + lock_kernel(); + retval = -EBADF; + if (fd >= NR_OPEN || + !(file = current->files->fd[fd])) + goto bad; + temp = file->f_pos; + if (temp != offset) { + retval = sys_lseek(fd, offset, 0); + if (retval < 0) goto bad; + } + retval = sys_read(fd, (char *)A(buf), nbyte); + if (file->f_pos != temp) { + if (!retval) + retval = sys_lseek(fd, temp, 0); + else + sys_lseek(fd, temp, 0); + } +bad: + unlock_kernel(); + return retval; +} + +asmlinkage int solaris_pwrite(int fd, u32 buf, u32 nbyte, s32 offset) +{ + off_t temp; + int retval; + struct file * file; + long (*sys_write)(unsigned int, char *, unsigned long) = + (long (*)(unsigned int, char *, unsigned long))SYS(read); + long (*sys_lseek)(unsigned int, off_t, unsigned int) = + (long (*)(unsigned int, off_t, unsigned int))SYS(lseek); + + lock_kernel(); + retval = -EBADF; + if (fd >= NR_OPEN || + !(file = current->files->fd[fd])) + goto bad; + temp = file->f_pos; + if (temp != offset) { + retval = sys_lseek(fd, offset, 0); + if (retval < 0) goto bad; + } + retval = sys_write(fd, (char *)A(buf), nbyte); + if (file->f_pos != temp) { + if (!retval) + retval = sys_lseek(fd, temp, 0); + else + sys_lseek(fd, temp, 0); + } +bad: + unlock_kernel(); + return retval; +} + +/* POSIX.1 names */ +#define _PC_LINK_MAX 1 +#define _PC_MAX_CANON 2 +#define _PC_MAX_INPUT 3 +#define _PC_NAME_MAX 4 +#define _PC_PATH_MAX 5 +#define _PC_PIPE_BUF 6 +#define _PC_NO_TRUNC 7 +#define _PC_VDISABLE 8 +#define _PC_CHOWN_RESTRICTED 9 +/* POSIX.4 names */ +#define _PC_ASYNC_IO 10 +#define _PC_PRIO_IO 11 +#define _PC_SYNC_IO 12 +#define _PC_LAST 12 + +/* This is not a real and complete implementation yet, just to keep + * the easy Solaris binaries happy. + */ +asmlinkage int solaris_fpathconf(int fd, int name) +{ + int ret; + + switch(name) { + case _PC_LINK_MAX: + ret = LINK_MAX; + break; + case _PC_MAX_CANON: + ret = MAX_CANON; + break; + case _PC_MAX_INPUT: + ret = MAX_INPUT; + break; + case _PC_NAME_MAX: + ret = NAME_MAX; + break; + case _PC_PATH_MAX: + ret = PATH_MAX; + break; + case _PC_PIPE_BUF: + ret = PIPE_BUF; + break; + case _PC_CHOWN_RESTRICTED: + ret = 1; + break; + case _PC_NO_TRUNC: + case _PC_VDISABLE: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +asmlinkage int solaris_pathconf(u32 path, int name) +{ + return solaris_fpathconf(0, name); +} + +/* solaris_llseek returns long long - quite difficult */ +asmlinkage long solaris_llseek(struct pt_regs *regs, u32 off_hi, u32 off_lo, int whence) +{ + int (*sys_llseek)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int) = + (int (*)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int))SYS(_llseek); + int ret; + unsigned long old_fs = get_fs(); + loff_t retval; + + set_fs(KERNEL_DS); + ret = sys_llseek((unsigned int)regs->u_regs[UREG_I0], off_hi, off_lo, &retval, whence); + set_fs(old_fs); + if (ret < 0) return ret; + regs->u_regs[UREG_I1] = (u32)retval; + return (retval >> 32); +} + +/* Have to mask out all but lower 3 bits */ +asmlinkage int solaris_access(u32 filename, long mode) +{ + int (*sys_access)(const char *, int) = + (int (*)(const char *, int))SYS(access); + + return sys_access((const char *)A(filename), mode & 7); +} diff --git a/arch/sparc64/solaris/ioctl.c b/arch/sparc64/solaris/ioctl.c new file mode 100644 index 000000000..43b844caf --- /dev/null +++ b/arch/sparc64/solaris/ioctl.c @@ -0,0 +1,316 @@ +/* $Id: ioctl.c,v 1.2 1997/09/04 00:59:22 davem Exp $ + * ioctl.c: Solaris ioctl emulation. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Streams & timod emulation based on code + * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/ioctl.h> +#include <linux/fs.h> + +#include <asm/uaccess.h> +#include <asm/termios.h> + +#include "conv.h" + +extern char * getname32(u32 filename); +#define putname32 putname + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg); +extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, + u32 arg); +asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg); + +/* termio* stuff {{{ */ + +struct solaris_termios { + u32 c_iflag; + u32 c_oflag; + u32 c_cflag; + u32 c_lflag; + u8 c_cc[19]; +}; + +struct solaris_termio { + u16 c_iflag; + u16 c_oflag; + u16 c_cflag; + u16 c_lflag; + s8 c_line; + u8 c_cc[8]; +}; + +struct solaris_termiox { + u16 x_hflag; + u16 x_cflag; + u16 x_rflag[5]; + u16 x_sflag; +}; + +static u32 solaris_to_linux_cflag(u32 cflag) +{ + cflag &= 0x7fdff000; + if (cflag & 0x200000) { + int baud = cflag & 0xf; + cflag &= ~0x20000f; + switch (baud) { + case 0: baud = B57600; break; + case 1: baud = B76800; break; + case 2: baud = B115200; break; + case 3: baud = B153600; break; + case 4: baud = B230400; break; + case 5: baud = B307200; break; + case 6: baud = B460800; break; + } + cflag |= CBAUDEX | baud; + } + return cflag; +} + +static u32 linux_to_solaris_cflag(u32 cflag) +{ + cflag &= ~(CMSPAR | CIBAUD); + if (cflag & CBAUDEX) { + int baud = cflag & CBAUD; + cflag &= ~CBAUD; + switch (baud) { + case B57600: baud = 0; break; + case B76800: baud = 1; break; + case B115200: baud = 2; break; + case B153600: baud = 3; break; + case B230400: baud = 4; break; + case B307200: baud = 5; break; + case B460800: baud = 6; break; + case B614400: baud = 7; break; + case B921600: baud = 8; break; + /* case B1843200: baud = 9; break; */ + } + cflag |= 0x200000 | baud; + } + return cflag; +} + +static inline int linux_to_solaris_termio(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + + ret = sys_ioctl(fd, cmd, A(arg)); + if (!ret) { + u32 cflag; + + if (__get_user (cflag, &((struct solaris_termio *)A(arg))->c_cflag)) + return -EFAULT; + cflag = linux_to_solaris_cflag(cflag); + if (__put_user (cflag, &((struct solaris_termio *)A(arg))->c_cflag)) + return -EFAULT; + } + return ret; +} + +static int solaris_to_linux_termio(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + struct solaris_termio s; + unsigned long old_fs = get_fs(); + + if (copy_from_user (&s, (struct solaris_termio *)A(arg), sizeof(struct solaris_termio))) + return -EFAULT; + s.c_cflag = solaris_to_linux_cflag(s.c_cflag); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)&s); + set_fs(old_fs); + return ret; +} + +static inline int linux_to_solaris_termios(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + struct solaris_termios s; + unsigned long old_fs = get_fs(); + + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)&s); + set_fs(old_fs); + if (!ret) { + if (put_user (s.c_iflag, &((struct solaris_termios *)A(arg))->c_iflag) || + __put_user (s.c_oflag, &((struct solaris_termios *)A(arg))->c_oflag) || + __put_user (linux_to_solaris_cflag(s.c_cflag), &((struct solaris_termios *)A(arg))->c_cflag) || + __put_user (s.c_lflag, &((struct solaris_termios *)A(arg))->c_lflag) || + __copy_to_user (((struct solaris_termios *)A(arg))->c_cc, s.c_cc, 16) || + __clear_user (((struct solaris_termios *)A(arg))->c_cc + 16, 2)) + return -EFAULT; + } + return ret; +} + +static int solaris_to_linux_termios(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + struct solaris_termios s; + unsigned long old_fs = get_fs(); + + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, TCGETS, (unsigned long)&s); + set_fs(old_fs); + if (ret) return ret; + if (put_user (s.c_iflag, &((struct solaris_termios *)A(arg))->c_iflag) || + __put_user (s.c_oflag, &((struct solaris_termios *)A(arg))->c_oflag) || + __put_user (s.c_cflag, &((struct solaris_termios *)A(arg))->c_cflag) || + __put_user (s.c_lflag, &((struct solaris_termios *)A(arg))->c_lflag) || + __copy_from_user (s.c_cc, ((struct solaris_termios *)A(arg))->c_cc, 16)) + return -EFAULT; + s.c_cflag = solaris_to_linux_cflag(s.c_cflag); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)&s); + set_fs(old_fs); + return ret; +} + +static inline int solaris_T(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 1: /* TCGETA */ + return linux_to_solaris_termio(fd, TCGETA, arg); + case 2: /* TCSETA */ + return solaris_to_linux_termio(fd, TCSETA, arg); + case 3: /* TCSETAW */ + return solaris_to_linux_termio(fd, TCSETAW, arg); + case 4: /* TCSETAF */ + return solaris_to_linux_termio(fd, TCSETAF, arg); + case 5: /* TCSBRK */ + return sys_ioctl(fd, TCSBRK, arg); + case 6: /* TCXONC */ + return sys_ioctl(fd, TCXONC, arg); + case 7: /* TCFLSH */ + return sys_ioctl(fd, TCFLSH, arg); + case 13: /* TCGETS */ + return linux_to_solaris_termios(fd, TCGETS, arg); + case 14: /* TCSETS */ + return solaris_to_linux_termios(fd, TCSETS, arg); + case 15: /* TCSETSW */ + return solaris_to_linux_termios(fd, TCSETSW, arg); + case 16: /* TCSETSF */ + return solaris_to_linux_termios(fd, TCSETSF, arg); + case 103: /* TIOCSWINSZ */ + return sys_ioctl(fd, TIOCSWINSZ, arg); + case 104: /* TIOCGWINSZ */ + return sys_ioctl(fd, TIOCGWINSZ, arg); + } + return -ENOSYS; +} + +static inline int solaris_t(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 20: /* TIOCGPGRP */ + return sys_ioctl(fd, TIOCGPGRP, arg); + case 21: /* TIOCSPGRP */ + return sys_ioctl(fd, TIOCSPGRP, arg); + } + return -ENOSYS; +} + +/* }}} */ + +/* A pseudo STREAMS support {{{ */ + +struct strioctl { + int cmd, timeout, len; + u32 data; +}; + +static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg) +{ + char *p; + int ret; + unsigned long old_fs; + struct strioctl si; + + switch (cmd & 0xff) { + case 1: /* I_NREAD */ + return -ENOSYS; + case 2: /* I_PUSH */ + p = getname32 (arg); + if (IS_ERR (p)) + return PTR_ERR(p); + putname32 (p); + return 0; + case 3: /* I_POP */ + return 0; + case 5: /* I_FLUSH */ + return 0; + case 8: /* I_STR */ + if (copy_from_user (&si, (struct strioctl *)A(arg), sizeof(struct strioctl))) + return -EFAULT; + switch ((si.cmd >> 8) & 0xff) { + case 'T': + default: + return solaris_ioctl(fd, si.cmd, si.data); + } + case 9: /* I_SETSIG */ + return sys_ioctl(fd, FIOSETOWN, current->pid); + case 10: /* I_GETSIG */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + sys_ioctl(fd, FIOGETOWN, (unsigned long)&ret); + set_fs(old_fs); + if (ret == current->pid) return 0x3ff; + else return -EINVAL; + case 11: /* I_FIND */ + p = getname32 (arg); + if (IS_ERR (p)) + return PTR_ERR(p); + ret = !strcmp(p, "timod"); + putname32 (p); + return ret; + } + return -ENOSYS; +} +/* }}} */ + +asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct file * filp; + int error = -EBADF; + + lock_kernel(); + if(fd >= NR_OPEN) goto out; + + filp = current->files->fd[fd]; + if(!filp) goto out; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, (unsigned long)arg); + goto out; + } + + error = -EFAULT; + switch ((cmd >> 8) & 0xff) { + case 'S': error = solaris_S(fd, cmd, arg); break; + case 'T': error = solaris_T(fd, cmd, arg); break; + case 't': error = solaris_t(fd, cmd, arg); break; + default: + error = -ENOSYS; + break; + } +out: + if (error == -ENOSYS) { + unsigned char c = cmd>>8; + + if (c < ' ' || c > 126) c = '.'; + printk("solaris_ioctl: Unknown cmd fd(%d) cmd(%08x '%c') arg(%08x)\n", + (int)fd, (unsigned int)cmd, c, (unsigned int)arg); + error = -EINVAL; + } + unlock_kernel(); + return error; +} diff --git a/arch/sparc64/solaris/ipc.c b/arch/sparc64/solaris/ipc.c new file mode 100644 index 000000000..132028447 --- /dev/null +++ b/arch/sparc64/solaris/ipc.c @@ -0,0 +1,122 @@ +/* $Id: ipc.c,v 1.1 1997/09/03 12:29:29 jj Exp $ + * ipc.c: Solaris IPC emulation + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/types.h> +#include <linux/smp_lock.h> +#include <linux/shm.h> +#include <linux/sem.h> +#include <linux/msg.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/ipc.h> + +#include "conv.h" + +struct solaris_ipc_perm { + s32 uid; + s32 gid; + s32 cuid; + s32 cgid; + u32 mode; + u32 seq; + int key; + s32 pad[4]; +}; + +struct solaris_shmid_ds { + struct solaris_ipc_perm shm_perm; + int shm_segsz; + u32 shm_amp; + unsigned short shm_lkcnt; + char __padxx[2]; + s32 shm_lpid; + s32 shm_cpid; + u32 shm_nattch; + u32 shm_cnattch; + s32 shm_atime; + s32 shm_pad1; + s32 shm_dtime; + s32 shm_pad2; + s32 shm_ctime; + s32 shm_pad3; + unsigned short shm_cv; + char shm_pad4[2]; + u32 shm_sptas; + s32 shm_pad5[2]; +}; + +asmlinkage long solaris_shmsys(int cmd, u32 arg1, u32 arg2, u32 arg3) +{ + int (*sys_ipc)(unsigned,int,int,unsigned long,void *,long) = + (int (*)(unsigned,int,int,unsigned long,void *,long))SYS(ipc); + unsigned long old_fs; + unsigned long raddr; + int ret; + + switch (cmd) { + case 0: /* shmat */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ipc(SHMAT, arg1, arg3 & ~0x4000, (unsigned long)&raddr, (void *)A(arg2), 0); + set_fs(old_fs); + if (ret >= 0) return (u32)raddr; + else return ret; + case 1: /* shmctl */ + switch (arg2) { + case 3: /* SHM_LOCK */ + case 4: /* SHM_UNLOCK */ + return sys_ipc(SHMCTL, arg1, (arg2 == 3) ? SHM_LOCK : SHM_UNLOCK, 0, NULL, 0); + case 10: /* IPC_RMID */ + return sys_ipc(SHMCTL, arg1, IPC_RMID, 0, NULL, 0); + case 11: /* IPC_SET */ + { + struct shmid_ds s; + + if (get_user (s.shm_perm.uid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.uid)) || + __get_user (s.shm_perm.gid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.gid)) || + __get_user (s.shm_perm.mode, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.mode))) + return -EFAULT; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0); + set_fs(old_fs); + return ret; + } + case 12: /* IPC_STAT */ + { + struct shmid_ds s; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0); + set_fs(old_fs); + if (get_user (s.shm_perm.uid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.uid)) || + __get_user (s.shm_perm.gid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.gid)) || + __get_user (s.shm_perm.cuid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.cuid)) || + __get_user (s.shm_perm.cgid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.cgid)) || + __get_user (s.shm_perm.mode, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.mode)) || + __get_user (s.shm_perm.seq, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.seq)) || + __get_user (s.shm_perm.key, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.key)) || + __get_user (s.shm_segsz, &(((struct solaris_shmid_ds *)A(arg3))->shm_segsz)) || + __get_user (s.shm_lpid, &(((struct solaris_shmid_ds *)A(arg3))->shm_lpid)) || + __get_user (s.shm_cpid, &(((struct solaris_shmid_ds *)A(arg3))->shm_cpid)) || + __get_user (s.shm_nattch, &(((struct solaris_shmid_ds *)A(arg3))->shm_nattch)) || + __get_user (s.shm_atime, &(((struct solaris_shmid_ds *)A(arg3))->shm_atime)) || + __get_user (s.shm_dtime, &(((struct solaris_shmid_ds *)A(arg3))->shm_dtime)) || + __get_user (s.shm_ctime, &(((struct solaris_shmid_ds *)A(arg3))->shm_ctime))) + return -EFAULT; + return ret; + } + default: return -EINVAL; + } + case 2: /* shmdt */ + return sys_ipc(SHMDT, 0, 0, 0, (void *)A(arg1), 0); + case 3: /* shmget */ + return sys_ipc(SHMGET, arg1, arg2, arg3, NULL, 0); + } + return -EINVAL; +} diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c new file mode 100644 index 000000000..fdabd0e26 --- /dev/null +++ b/arch/sparc64/solaris/misc.c @@ -0,0 +1,463 @@ +/* $Id: misc.c,v 1.4 1997/09/04 14:57:31 jj Exp $ + * misc.c: Miscelaneous syscall emulation for Solaris + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/smp_lock.h> +#include <linux/utsname.h> +#include <linux/limits.h> +#include <linux/mm.h> +#include <linux/smp.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/oplib.h> +#include <asm/idprom.h> +#include <asm/machines.h> + +#include "conv.h" + +/* Conversion from Linux to Solaris errnos. 0-34 are identity mapped. + Some Linux errnos (EPROCLIM, EDOTDOT, ERREMOTE, EUCLEAN, ENOTNAM, + ENAVAIL, EISNAM, EREMOTEIO, ENOMEDIUM, EMEDIUMTYPE) have no Solaris + equivalents. I return EINVAL in that case, which is very wrong. If + someone suggest a better value for them, you're welcomed. + On the other side, Solaris ECANCELED and ENOTSUP have no Linux equivalents, + but that doesn't matter here. --jj */ +int solaris_err_table[] = { +/* 0 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +/* 10 */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +/* 20 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, +/* 30 */ 30, 31, 32, 33, 34, 22, 150, 149, 95, 96, +/* 40 */ 97, 98, 99, 120, 121, 122, 123, 124, 125, 126, +/* 50 */ 127, 128, 129, 130, 131, 132, 133, 134, 143, 144, +/* 60 */ 145, 146, 90, 78, 147, 148, 93, 22, 94, 49, +/* 70 */ 151, 66, 60, 62, 63, 35, 77, 36, 45, 46, +/* 80 */ 64, 22, 67, 68, 69, 70, 71, 74, 22, 82, +/* 90 */ 89, 92, 79, 81, 37, 38, 39, 40, 41, 42, +/* 100 */ 43, 44, 50, 51, 52, 53, 54, 55, 56, 57, +/* 110 */ 87, 61, 84, 65, 83, 80, 91, 22, 22, 22, +/* 120 */ 22, 22, 88, 86, 85, 22, 22, +}; + +asmlinkage u32 solaris_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off) +{ + u32 (*sunos_mmap)(u32,u32,u32,u32,u32,u32) = + (u32 (*)(u32,u32,u32,u32,u32,u32))SUNOS(71); + u32 ret; + + ret = sunos_mmap(addr,len,prot,flags,fd,off); + /* sunos_mmap sets personality to PER_BSD */ + current->personality = PER_SVR4; + return ret; +} + +asmlinkage int solaris_brk(u32 brk) +{ + int (*sunos_brk)(u32) = (int (*)(u32))SUNOS(17); + + return sunos_brk(brk); +} + +#define set_utsfield(to, from, dotchop, countfrom) { \ + char *p; \ + int i, len = (countfrom) ? \ + ((sizeof(to) > sizeof(from) ? \ + sizeof(from) : sizeof(to))) : sizeof(to); \ + copy_to_user_ret(to, from, len, -EFAULT); \ + if (dotchop) \ + for (p=from,i=0; *p && *p != '.' && --len; p++,i++); \ + else \ + i = len - 1; \ + __put_user_ret('\0', (char *)(to+i), -EFAULT); \ +} + +struct sol_uname { + char sysname[9]; + char nodename[9]; + char release[9]; + char version[9]; + char machine[9]; +}; + +struct sol_utsname { + char sysname[257]; + char nodename[257]; + char release[257]; + char version[257]; + char machine[257]; +}; + +static char *machine(void) +{ + switch (sparc_cpu_model) { + case sun4: return "sun4"; + case sun4c: return "sun4c"; + case sun4e: return "sun4e"; + case sun4m: return "sun4m"; + case sun4d: return "sun4d"; + case sun4u: return "sun4u"; + default: return "sparc"; + } +} + +static char *platform(char *buffer) +{ + int i; + struct { + char *platform; + int id_machtype; + } platforms [] = { + { "sun4", (SM_SUN4 | SM_4_110) }, + { "sun4", (SM_SUN4 | SM_4_260) }, + { "sun4", (SM_SUN4 | SM_4_330) }, + { "sun4", (SM_SUN4 | SM_4_470) }, + { "SUNW,Sun_4_60", (SM_SUN4C | SM_4C_SS1) }, + { "SUNW,Sun_4_40", (SM_SUN4C | SM_4C_IPC) }, + { "SUNW,Sun_4_65", (SM_SUN4C | SM_4C_SS1PLUS) }, + { "SUNW,Sun_4_20", (SM_SUN4C | SM_4C_SLC) }, + { "SUNW,Sun_4_75", (SM_SUN4C | SM_4C_SS2) }, + { "SUNW,Sun_4_25", (SM_SUN4C | SM_4C_ELC) }, + { "SUNW,Sun_4_50", (SM_SUN4C | SM_4C_IPX) }, + { "SUNW,Sun_4_600", (SM_SUN4M | SM_4M_SS60) }, + { "SUNW,SPARCstation-5", (SM_SUN4M | SM_4M_SS50) }, + { "SUNW,SPARCstation-20", (SM_SUN4M | SM_4M_SS40) } + }; + + *buffer = 0; + prom_getproperty(prom_root_node, "name", buffer, 256); + if (*buffer) { + char *p; + + for (p = buffer; *p; p++) + if (*p == '/' || *p == ' ') *p = '_'; + return buffer; + } + for (i = 0; i < sizeof (platforms)/sizeof (platforms[0]); i++) + if (platforms[i].id_machtype == idprom->id_machtype) + return platforms[i].platform; + return "sun4c"; +} + +static char *serial(char *buffer) +{ + int node = prom_getchild(prom_root_node); + + node = prom_searchsiblings(node, "options"); + *buffer = 0; + prom_getproperty(node, "system-board-serial#", buffer, 256); + if (!*buffer) + return "4512348717234"; + else + return buffer; +} + +asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2) +{ + switch (which) { + case 0: /* old uname */ + /* Lets cheat */ + set_utsfield(((struct sol_uname *)A(buf))->sysname, + "SunOS", 1, 0); + set_utsfield(((struct sol_uname *)A(buf))->nodename, + system_utsname.nodename, 1, 1); + set_utsfield(((struct sol_uname *)A(buf))->release, + "2.6", 0, 0); + set_utsfield(((struct sol_uname *)A(buf))->version, + "Generic", 0, 0); + set_utsfield(((struct sol_uname *)A(buf))->machine, + machine(), 0, 0); + return 0; + case 2: /* ustat */ + return -ENOSYS; + case 3: /* fusers */ + return -ENOSYS; + default: + return -ENOSYS; + } +} + +asmlinkage int solaris_utsname(u32 buf) +{ + /* Why should we not lie a bit? */ + set_utsfield(((struct sol_utsname *)A(buf))->sysname, + "SunOS", 0, 0); + set_utsfield(((struct sol_utsname *)A(buf))->nodename, + system_utsname.nodename, 1, 1); + set_utsfield(((struct sol_utsname *)A(buf))->release, + "5.6", 0, 0); + set_utsfield(((struct sol_utsname *)A(buf))->version, + "Generic", 0, 0); + set_utsfield(((struct sol_utsname *)A(buf))->machine, + machine(), 0, 0); + return 0; +} + +#define SI_SYSNAME 1 /* return name of operating system */ +#define SI_HOSTNAME 2 /* return name of node */ +#define SI_RELEASE 3 /* return release of operating system */ +#define SI_VERSION 4 /* return version field of utsname */ +#define SI_MACHINE 5 /* return kind of machine */ +#define SI_ARCHITECTURE 6 /* return instruction set arch */ +#define SI_HW_SERIAL 7 /* return hardware serial number */ +#define SI_HW_PROVIDER 8 /* return hardware manufacturer */ +#define SI_SRPC_DOMAIN 9 /* return secure RPC domain */ +#define SI_PLATFORM 513 /* return platform identifier */ + +asmlinkage int solaris_sysinfo(int cmd, u32 buf, s32 count) +{ + char *p, *q, *r; + char buffer[256]; + int len; + + /* Again, we cheat :)) */ + switch (cmd) { + case SI_SYSNAME: r = "SunOS"; break; + case SI_HOSTNAME: + r = buffer + 256; + for (p = system_utsname.nodename, q = buffer; + q < r && *p && *p != '.'; *q++ = *p++); + *q = 0; + r = buffer; + break; + case SI_RELEASE: r = "5.6"; break; + case SI_MACHINE: r = machine(); break; + case SI_ARCHITECTURE: r = "sparc"; break; + case SI_HW_PROVIDER: r = "Sun_Microsystems"; break; + case SI_HW_SERIAL: r = serial(buffer); break; + case SI_PLATFORM: r = platform(buffer); break; + case SI_SRPC_DOMAIN: r = ""; break; + case SI_VERSION: r = "Generic"; break; + default: return -EINVAL; + } + len = strlen(r) + 1; + if (count < len) { + copy_to_user_ret((char *)A(buf), r, count - 1, -EFAULT); + __put_user_ret(0, (char *)A(buf) + count - 1, -EFAULT); + } else + copy_to_user_ret((char *)A(buf), r, len, -EFAULT); + return len; +} + +#define SOLARIS_CONFIG_NGROUPS 2 +#define SOLARIS_CONFIG_CHILD_MAX 3 +#define SOLARIS_CONFIG_OPEN_FILES 4 +#define SOLARIS_CONFIG_POSIX_VER 5 +#define SOLARIS_CONFIG_PAGESIZE 6 +#define SOLARIS_CONFIG_CLK_TCK 7 +#define SOLARIS_CONFIG_XOPEN_VER 8 +#define SOLARIS_CONFIG_PROF_TCK 10 +#define SOLARIS_CONFIG_NPROC_CONF 11 +#define SOLARIS_CONFIG_NPROC_ONLN 12 +#define SOLARIS_CONFIG_AIO_LISTIO_MAX 13 +#define SOLARIS_CONFIG_AIO_MAX 14 +#define SOLARIS_CONFIG_AIO_PRIO_DELTA_MAX 15 +#define SOLARIS_CONFIG_DELAYTIMER_MAX 16 +#define SOLARIS_CONFIG_MQ_OPEN_MAX 17 +#define SOLARIS_CONFIG_MQ_PRIO_MAX 18 +#define SOLARIS_CONFIG_RTSIG_MAX 19 +#define SOLARIS_CONFIG_SEM_NSEMS_MAX 20 +#define SOLARIS_CONFIG_SEM_VALUE_MAX 21 +#define SOLARIS_CONFIG_SIGQUEUE_MAX 22 +#define SOLARIS_CONFIG_SIGRT_MIN 23 +#define SOLARIS_CONFIG_SIGRT_MAX 24 +#define SOLARIS_CONFIG_TIMER_MAX 25 +#define SOLARIS_CONFIG_PHYS_PAGES 26 +#define SOLARIS_CONFIG_AVPHYS_PAGES 27 + +asmlinkage int solaris_sysconf(int id) +{ + switch (id) { + case SOLARIS_CONFIG_NGROUPS: return NGROUPS_MAX; + case SOLARIS_CONFIG_CHILD_MAX: return CHILD_MAX; + case SOLARIS_CONFIG_OPEN_FILES: return OPEN_MAX; + case SOLARIS_CONFIG_POSIX_VER: return 199309; + case SOLARIS_CONFIG_PAGESIZE: return PAGE_SIZE; + case SOLARIS_CONFIG_XOPEN_VER: return 3; + case SOLARIS_CONFIG_CLK_TCK: + case SOLARIS_CONFIG_PROF_TCK: + return prom_getintdefault( + linux_cpus[smp_processor_id()].prom_node, + "clock-frequency", 167000000); +#ifdef __SMP__ + case SOLARIS_CONFIG_NPROC_CONF: return NCPUS; + case SOLARIS_CONFIG_NPROC_ONLN: return smp_num_cpus; +#else + case SOLARIS_CONFIG_NPROC_CONF: return 1; + case SOLARIS_CONFIG_NPROC_ONLN: return 1; +#endif + case SOLARIS_CONFIG_SIGRT_MIN: return 37; + case SOLARIS_CONFIG_SIGRT_MAX: return 44; + case SOLARIS_CONFIG_PHYS_PAGES: + case SOLARIS_CONFIG_AVPHYS_PAGES: + { + struct sysinfo s; + + si_meminfo(&s); + if (id == SOLARIS_CONFIG_PHYS_PAGES) + return s.totalram >>= PAGE_SHIFT; + else + return s.freeram >>= PAGE_SHIFT; + } + /* XXX support these as well -jj */ + case SOLARIS_CONFIG_AIO_LISTIO_MAX: return -EINVAL; + case SOLARIS_CONFIG_AIO_MAX: return -EINVAL; + case SOLARIS_CONFIG_AIO_PRIO_DELTA_MAX: return -EINVAL; + case SOLARIS_CONFIG_DELAYTIMER_MAX: return -EINVAL; + case SOLARIS_CONFIG_MQ_OPEN_MAX: return -EINVAL; + case SOLARIS_CONFIG_MQ_PRIO_MAX: return -EINVAL; + case SOLARIS_CONFIG_RTSIG_MAX: return -EINVAL; + case SOLARIS_CONFIG_SEM_NSEMS_MAX: return -EINVAL; + case SOLARIS_CONFIG_SEM_VALUE_MAX: return -EINVAL; + case SOLARIS_CONFIG_SIGQUEUE_MAX: return -EINVAL; + case SOLARIS_CONFIG_TIMER_MAX: return -EINVAL; + default: return -EINVAL; + } +} + +asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) +{ + int ret; + + switch (cmd) { + case 0: /* getpgrp */ + return current->pgrp; + case 1: /* setpgrp */ + { + int (*sys_setpgid)(pid_t,pid_t) = + (int (*)(pid_t,pid_t))SYS(setpgid); + + /* can anyone explain me the difference between + Solaris setpgrp and setsid? */ + ret = sys_setpgid(0, 0); + if (ret) return ret; + current->tty = NULL; + return current->pgrp; + } + case 2: /* getsid */ + { + int (*sys_getsid)(pid_t) = (int (*)(pid_t))SYS(getsid); + return sys_getsid(pid); + } + case 3: /* setsid */ + { + int (*sys_setsid)(void) = (int (*)(void))SYS(setsid); + return sys_setsid(); + } + case 4: /* getpgid */ + { + int (*sys_getpgid)(pid_t) = (int (*)(pid_t))SYS(getpgid); + return sys_getpgid(pid); + } + case 5: /* setpgid */ + { + int (*sys_setpgid)(pid_t,pid_t) = + (int (*)(pid_t,pid_t))SYS(setpgid); + return sys_setpgid(pid,pgid); + } + } + return -EINVAL; +} + +asmlinkage int do_sol_unimplemented(struct pt_regs *regs) +{ + printk ("Unimplemented Solaris syscall %d %08x %08x %08x %08x\n", + (int)regs->u_regs[UREG_G1], + (int)regs->u_regs[UREG_I0], + (int)regs->u_regs[UREG_I1], + (int)regs->u_regs[UREG_I2], + (int)regs->u_regs[UREG_I3]); + return -ENOSYS; +} + +asmlinkage void solaris_register(void) +{ + lock_kernel(); + current->personality = PER_SVR4; + if (current->exec_domain && current->exec_domain->module) + __MOD_DEC_USE_COUNT(current->exec_domain->module); + current->exec_domain = lookup_exec_domain(current->personality); + if (current->exec_domain && current->exec_domain->module) + __MOD_INC_USE_COUNT(current->exec_domain->module); + unlock_kernel(); +} + +extern long solaris_to_linux_signals[], linux_to_solaris_signals[]; + +struct exec_domain solaris_exec_domain = { + "Solaris", + (lcall7_func)NULL, + 1, 1, /* PER_SVR4 personality */ + solaris_to_linux_signals, + linux_to_solaris_signals, +#ifdef MODULE + &__this_module, +#else + NULL, +#endif + NULL +}; + +#ifdef MODULE + +MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz)"); +MODULE_DESCRIPTION("Solaris binary emulation module"); + +#ifdef __sparc_v9__ +extern u32 tl0_solaris[8]; +#define update_ttable(x) \ + tl0_solaris[3] = (((long)(x) - (long)tl0_solaris - 3) >> 2) | 0x40000000; \ + __asm__ __volatile__ ("membar #StoreStore; flush %0" : : "r" (&tl0_solaris[3])) +#else +#endif + +extern u32 solaris_sparc_syscall[]; +extern u32 solaris_syscall[]; +extern int init_socksys(void); +extern void cleanup_socksys(void); + +int init_module(void) +{ + int ret; + register_exec_domain(&solaris_exec_domain); + if ((ret = init_socksys())) { + unregister_exec_domain(&solaris_exec_domain); + return ret; + } + update_ttable(solaris_sparc_syscall); + return 0; +} + +void cleanup_module(void) +{ + update_ttable(solaris_syscall); + cleanup_socksys(); + unregister_exec_domain(&solaris_exec_domain); +} + +#else +int init_solaris_emul(void) +{ + register_exec_domain(&solaris_exec_domain); + init_socksys(); +} +#endif + +#ifdef DEBUG_SOLARIS +void entry_printk(int sysno, struct pt_regs *regs) +{ + printk ("Entering %d\n", sysno); + printk ("%08x %08x %08x %08x\n", (int)regs->u_regs[UREG_I0], + (int)regs->u_regs[UREG_I1], + (int)regs->u_regs[UREG_I2], + (int)regs->u_regs[UREG_I3]); +} + +void exit_printk(unsigned long ret) +{ + printk ("Returning %016lx\n", ret); +} +#endif diff --git a/arch/sparc64/solaris/signal.c b/arch/sparc64/solaris/signal.c new file mode 100644 index 000000000..6d3081c03 --- /dev/null +++ b/arch/sparc64/solaris/signal.c @@ -0,0 +1,419 @@ +/* $Id: signal.c,v 1.2 1997/09/03 12:29:19 jj Exp $ + * signal.c: Signal emulation for Solaris + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/types.h> +#include <linux/smp_lock.h> + +#include <asm/uaccess.h> +#include <asm/svr4.h> +#include <asm/string.h> + +#include "conv.h" +#include "signal.h" + +#define _S(nr) (1L<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +long linux_to_solaris_signals[] = { + 0, + SOLARIS_SIGHUP, SOLARIS_SIGINT, + SOLARIS_SIGQUIT, SOLARIS_SIGILL, + SOLARIS_SIGTRAP, SOLARIS_SIGIOT, + SOLARIS_SIGEMT, SOLARIS_SIGFPE, + SOLARIS_SIGKILL, SOLARIS_SIGBUS, + SOLARIS_SIGSEGV, SOLARIS_SIGSYS, + SOLARIS_SIGPIPE, SOLARIS_SIGALRM, + SOLARIS_SIGTERM, SOLARIS_SIGURG, + SOLARIS_SIGSTOP, SOLARIS_SIGTSTP, + SOLARIS_SIGCONT, SOLARIS_SIGCLD, + SOLARIS_SIGTTIN, SOLARIS_SIGTTOU, + SOLARIS_SIGPOLL, SOLARIS_SIGXCPU, + SOLARIS_SIGXFSZ, SOLARIS_SIGVTALRM, + SOLARIS_SIGPROF, SOLARIS_SIGWINCH, + SOLARIS_SIGUSR1, SOLARIS_SIGUSR1, + SOLARIS_SIGUSR2, -1, +}; + +long solaris_to_linux_signals[] = { + 0, + SIGHUP, SIGINT, SIGQUIT, SIGILL, + SIGTRAP, SIGIOT, SIGEMT, SIGFPE, + SIGKILL, SIGBUS, SIGSEGV, SIGSYS, + SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, + SIGUSR2, SIGCHLD, -1, SIGWINCH, + SIGURG, SIGPOLL, SIGSTOP, SIGTSTP, + SIGCONT, SIGTTIN, SIGTTOU, SIGVTALRM, + SIGPROF, SIGXCPU, SIGXFSZ, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, +}; + +static inline long mapsig(long sig) +{ + if ((unsigned long)sig > SOLARIS_NSIGNALS) + return -EINVAL; + return solaris_to_linux_signals[sig]; +} + +asmlinkage int solaris_kill(int pid, int sig) +{ + int (*sys_kill)(int,int) = + (int (*)(int,int))SYS(kill); + int s = mapsig(sig); + + if (s < 0) return s; + return sys_kill(pid, s); +} + +static long sig_handler(int sig, u32 arg, int one_shot) +{ + struct sigaction sa, old; + int ret; + unsigned long old_fs = get_fs(); + int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = + (int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction); + + sa.sa_mask = 0L; + sa.sa_restorer = NULL; + sa.sa_handler = (__sighandler_t)A(arg); + sa.sa_flags = 0; + if (one_shot) sa.sa_flags = SA_ONESHOT | SA_NOMASK; + set_fs (KERNEL_DS); + ret = sys_sigaction(sig, &sa, &old); + set_fs (old_fs); + if (ret < 0) return ret; + return (u32)(long)old.sa_handler; +} + +static inline long solaris_signal(int sig, u32 arg) +{ + return sig_handler (sig, arg, 1); +} + +static long solaris_sigset(int sig, u32 arg) +{ + if (arg != 2) /* HOLD */ { + spin_lock_irq(¤t->sigmask_lock); + current->blocked &= ~_S(sig); + spin_unlock_irq(¤t->sigmask_lock); + return sig_handler (sig, arg, 0); + } else { + sigset_t n = _S(sig) & _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + current->blocked |= n; + spin_unlock_irq(¤t->sigmask_lock); + return 0; + } +} + +static inline long solaris_sighold(int sig) +{ + return solaris_sigset(sig, 2); +} + +static inline long solaris_sigrelse(int sig) +{ + spin_lock_irq(¤t->sigmask_lock); + current->blocked &= ~_S(sig); + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} + +static inline long solaris_sigignore(int sig) +{ + return sig_handler (sig, (u32)SIG_IGN, 0); +} + +static inline long solaris_sigpause(int sig) +{ + printk ("Need to support solaris sigpause\n"); + return -ENOSYS; +} + +asmlinkage long solaris_sigfunc(int sig, u32 arg) +{ + int func = sig & ~0xff; + + sig = mapsig(sig & 0xff); + if (sig < 0) return sig; + switch (func) { + case 0: return solaris_signal(sig, arg); + case 0x100: return solaris_sigset(sig, arg); + case 0x200: return solaris_sighold(sig); + case 0x400: return solaris_sigrelse(sig); + case 0x800: return solaris_sigignore(sig); + case 0x1000: return solaris_sigpause(sig); + } + return -EINVAL; +} + +typedef struct { + u32 __sigbits[4]; +} sol_sigset_t; + +static inline int mapin(u32 *p, sigset_t *q) +{ + int i; + u32 x; + int sig; + + *q = 0L; + x = p[0]; + for (i = 1; i <= SOLARIS_NSIGNALS; i++) { + if (x & 1) { + sig = solaris_to_linux_signals[i]; + if (sig == -1) + return -EINVAL; + *q |= 1L << (sig - 1); + } + x >>= 1; + if (i == 32) + x = p[1]; + } + return 0; +} + +static inline int mapout(sigset_t *q, u32 *p) +{ + int i; + sigset_t x; + int sig; + + p[0] = 0; + p[1] = 0; + x = *q; + for (i = 1; i <= 32; i++, x >>= 1) { + if (x & 1) { + sig = linux_to_solaris_signals[i]; + if (sig == -1) + return -EINVAL; + if (sig > 32) + p[1] |= 1L << (sig - 33); + else + p[0] |= 1L << (sig - 1); + } + } + return 0; + +} + +asmlinkage int solaris_sigprocmask(int how, u32 in, u32 out) +{ + sigset_t in_s, *ins, out_s, *outs; + unsigned long old_fs = get_fs(); + int ret; + int (*sys_sigprocmask)(int,sigset_t *,sigset_t *) = + (int (*)(int,sigset_t *,sigset_t *))SYS(sigprocmask); + + ins = NULL; outs = NULL; + if (in) { + u32 tmp[2]; + + if (copy_from_user (tmp, (sol_sigset_t *)A(in), 2*sizeof(u32))) + return -EFAULT; + ins = &in_s; + if (mapin (tmp, ins)) return -EINVAL; + } + if (out) outs = &out_s; + set_fs (KERNEL_DS); + ret = sys_sigprocmask((how == 3) ? SIG_SETMASK : how, ins, outs); + set_fs (old_fs); + if (ret) return ret; + if (out) { + u32 tmp[4]; + + tmp[2] = 0; tmp[3] = 0; + if (mapout (outs, tmp)) return -EINVAL; + if (copy_to_user((sol_sigset_t *)A(out), tmp, 4*sizeof(u32))) + return -EFAULT; + } + return 0; +} + +asmlinkage long do_sol_sigsuspend(u32 mask) +{ + sigset_t s; + u32 tmp[2]; + + if (copy_from_user (tmp, (sol_sigset_t *)A(mask), 2*sizeof(u32))) + return -EFAULT; + if (mapin (tmp, &s)) return -EINVAL; + return (long)s; +} + +struct sol_sigaction { + int sa_flags; + u32 sa_handler; + u32 sa_mask[4]; + int sa_resv[2]; +}; + +asmlinkage int solaris_sigaction(int sig, u32 act, u32 old) +{ + u32 tmp, tmp2[4]; + struct sigaction s, s2; + int ret; + unsigned long old_fs = get_fs(); + int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = + (int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction); + + sig = mapsig(sig); + if (sig < 0) { + /* We cheat a little bit for Solaris only signals */ + if (old && clear_user((struct sol_sigaction *)A(old), sizeof(struct sol_sigaction))) + return -EFAULT; + return 0; + } + if (act) { + if (get_user (tmp, &((struct sol_sigaction *)A(act))->sa_flags)) + return -EFAULT; + s.sa_flags = 0; + if (tmp & SOLARIS_SA_ONSTACK) s.sa_flags |= SA_STACK; + if (tmp & SOLARIS_SA_RESTART) s.sa_flags |= SA_RESTART; + if (tmp & SOLARIS_SA_NODEFER) s.sa_flags |= SA_NOMASK; + if (tmp & SOLARIS_SA_RESETHAND) s.sa_flags |= SA_ONESHOT; + if (tmp & SOLARIS_SA_NOCLDSTOP) s.sa_flags |= SA_NOCLDSTOP; + if (get_user (tmp, &((struct sol_sigaction *)A(act))->sa_handler) || + copy_from_user (tmp2, &((struct sol_sigaction *)A(act))->sa_mask, 2*sizeof(u32))) + return -EFAULT; + s.sa_handler = (__sighandler_t)A(tmp); + if (mapin (tmp2, &s.sa_mask)) return -EINVAL; + s.sa_restorer = 0; + } + set_fs(KERNEL_DS); + ret = sys_sigaction(sig, act ? &s : NULL, old ? &s2 : NULL); + set_fs(old_fs); + if (ret) return ret; + if (old) { + if (mapout (&s2.sa_mask, tmp2)) return -EINVAL; + tmp = 0; tmp2[2] = 0; tmp2[3] = 0; + if (s2.sa_flags & SA_STACK) tmp |= SOLARIS_SA_ONSTACK; + if (s2.sa_flags & SA_RESTART) tmp |= SOLARIS_SA_RESTART; + if (s2.sa_flags & SA_NOMASK) tmp |= SOLARIS_SA_NODEFER; + if (s2.sa_flags & SA_ONESHOT) tmp |= SOLARIS_SA_RESETHAND; + if (s2.sa_flags & SA_NOCLDSTOP) tmp |= SOLARIS_SA_NOCLDSTOP; + if (put_user (tmp, &((struct sol_sigaction *)A(old))->sa_flags) || + __put_user ((u32)(long)s2.sa_handler, &((struct sol_sigaction *)A(old))->sa_handler) || + copy_to_user (&((struct sol_sigaction *)A(old))->sa_mask, tmp2, 4*sizeof(u32))) + return -EFAULT; + } + return 0; +} + +asmlinkage int solaris_sigpending(int which, u32 set) +{ + sigset_t s; + u32 tmp[4]; + switch (which) { + case 1: /* sigpending */ + lock_kernel(); + s = current->blocked & current->signal; + unlock_kernel(); + break; + case 2: /* sigfillset - I just set signals which have linux equivalents */ + s = 0x7fffffff; + break; + default: return -EINVAL; + } + if (mapout (&s, tmp)) return -EINVAL; + tmp[2] = 0; tmp[3] = 0; + if (copy_to_user ((u32 *)A(set), tmp, sizeof(tmp))) + return -EFAULT; + return 0; +} + +asmlinkage int solaris_wait(u32 stat_loc) +{ + int (*sys_wait4)(pid_t,unsigned int *, int, struct rusage *) = + (int (*)(pid_t,unsigned int *, int, struct rusage *))SYS(wait4); + int ret, status; + + ret = sys_wait4(-1, (unsigned int *)A(stat_loc), WUNTRACED, NULL); + if (ret >= 0 && stat_loc) { + if (get_user (status, (unsigned int *)A(stat_loc))) + return -EFAULT; + if (((status - 1) & 0xffff) < 0xff) + status = linux_to_solaris_signals[status & 0x7f] & 0x7f; + else if ((status & 0xff) == 0x7f) + status = (linux_to_solaris_signals[(status >> 8) & 0xff] << 8) | 0x7f; + if (__put_user (status, (unsigned int *)A(stat_loc))) + return -EFAULT; + } + return ret; +} + +asmlinkage int solaris_waitid(int idtype, s32 pid, u32 info, int options) +{ + int (*sys_wait4)(pid_t,unsigned int *, int, struct rusage *) = + (int (*)(pid_t,unsigned int *, int, struct rusage *))SYS(wait4); + int opts, status, ret; + + switch (idtype) { + case 0: /* P_PID */ break; + case 1: /* P_PGID */ pid = -pid; break; + case 7: /* P_ALL */ pid = -1; break; + default: return -EINVAL; + } + opts = 0; + if (options & SOLARIS_WUNTRACED) opts |= WUNTRACED; + if (options & SOLARIS_WNOHANG) opts |= WNOHANG; + current->state = TASK_RUNNING; + ret = sys_wait4(pid, (unsigned int *)A(info), opts, NULL); + if (ret < 0) return ret; + if (info) { + struct sol_siginfo *s = (struct sol_siginfo *)A(info); + + if (get_user (status, (unsigned int *)A(info))) return -EFAULT; + __put_user_ret (SOLARIS_SIGCLD, &s->si_signo, -EFAULT); + __put_user_ret (ret, &s->_data._proc._pid, -EFAULT); + switch (status & 0xff) { + case 0: ret = SOLARIS_CLD_EXITED; + status = (status >> 8) & 0xff; + break; + case 0x7f: + status = (status >> 8) & 0xff; + switch (status) { + case SIGSTOP: + case SIGTSTP: ret = SOLARIS_CLD_STOPPED; + default: ret = SOLARIS_CLD_EXITED; + } + status = linux_to_solaris_signals[status]; + break; + default: + if (status & 0x80) ret = SOLARIS_CLD_DUMPED; + else ret = SOLARIS_CLD_KILLED; + status = linux_to_solaris_signals[status & 0x7f]; + break; + } + __put_user_ret (ret, &s->si_code, -EFAULT); + __put_user_ret (status, &s->_data._proc._pdata._cld._status, -EFAULT); + } + return 0; +} + +extern int svr4_setcontext(svr4_ucontext_t *c, struct pt_regs *regs); +extern int svr4_getcontext(svr4_ucontext_t *c, struct pt_regs *regs); + +asmlinkage int solaris_context(struct pt_regs *regs) +{ + switch ((unsigned)regs->u_regs[UREG_I0]) { + case 0: /* getcontext */ + return svr4_getcontext((svr4_ucontext_t *)(long)(u32)regs->u_regs[UREG_I1], regs); + case 1: /* setcontext */ + return svr4_setcontext((svr4_ucontext_t *)(long)(u32)regs->u_regs[UREG_I1], regs); + default: + return -EINVAL; + + } +} + +asmlinkage int solaris_sigaltstack(u32 ss, u32 oss) +{ +/* XXX Implement this soon */ + return 0; +} diff --git a/arch/sparc64/solaris/signal.h b/arch/sparc64/solaris/signal.h new file mode 100644 index 000000000..2e948c788 --- /dev/null +++ b/arch/sparc64/solaris/signal.h @@ -0,0 +1,109 @@ +/* $Id: signal.h,v 1.2 1997/09/03 12:29:21 jj Exp $ + * signal.h: Signal emulation for Solaris + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#define SOLARIS_SIGHUP 1 +#define SOLARIS_SIGINT 2 +#define SOLARIS_SIGQUIT 3 +#define SOLARIS_SIGILL 4 +#define SOLARIS_SIGTRAP 5 +#define SOLARIS_SIGIOT 6 +#define SOLARIS_SIGEMT 7 +#define SOLARIS_SIGFPE 8 +#define SOLARIS_SIGKILL 9 +#define SOLARIS_SIGBUS 10 +#define SOLARIS_SIGSEGV 11 +#define SOLARIS_SIGSYS 12 +#define SOLARIS_SIGPIPE 13 +#define SOLARIS_SIGALRM 14 +#define SOLARIS_SIGTERM 15 +#define SOLARIS_SIGUSR1 16 +#define SOLARIS_SIGUSR2 17 +#define SOLARIS_SIGCLD 18 +#define SOLARIS_SIGPWR 19 +#define SOLARIS_SIGWINCH 20 +#define SOLARIS_SIGURG 21 +#define SOLARIS_SIGPOLL 22 +#define SOLARIS_SIGSTOP 23 +#define SOLARIS_SIGTSTP 24 +#define SOLARIS_SIGCONT 25 +#define SOLARIS_SIGTTIN 26 +#define SOLARIS_SIGTTOU 27 +#define SOLARIS_SIGVTALRM 28 +#define SOLARIS_SIGPROF 29 +#define SOLARIS_SIGXCPU 30 +#define SOLARIS_SIGXFSZ 31 +#define SOLARIS_SIGWAITING 32 +#define SOLARIS_SIGLWP 33 +#define SOLARIS_SIGFREEZE 34 +#define SOLARIS_SIGTHAW 35 +#define SOLARIS_SIGCANCEL 36 +#define SOLARIS_SIGRTMIN 37 +#define SOLARIS_SIGRTMAX 44 +#define SOLARIS_NSIGNALS 44 + + +#define SOLARIS_SA_ONSTACK 1 +#define SOLARIS_SA_RESETHAND 2 +#define SOLARIS_SA_RESTART 4 +#define SOLARIS_SA_SIGINFO 8 +#define SOLARIS_SA_NODEFER 16 +#define SOLARIS_SA_NOCLDWAIT 0x10000 +#define SOLARIS_SA_NOCLDSTOP 0x20000 + +struct sol_siginfo { + int si_signo; + int si_code; + int si_errno; + union { + char pad[128-3*sizeof(int)]; + struct { + s32 _pid; + union { + struct { + s32 _uid; + s32 _value; + } _kill; + struct { + s32 _utime; + int _status; + s32 _stime; + } _cld; + } _pdata; + } _proc; + struct { /* SIGSEGV, SIGBUS, SIGILL and SIGFPE */ + u32 _addr; + int _trapno; + } _fault; + struct { /* SIGPOLL, SIGXFSZ */ + int _fd; + s32 _band; + } _file; + } _data; +}; + +#define SOLARIS_WUNTRACED 0x04 +#define SOLARIS_WNOHANG 0x40 +#define SOLARIS_WEXITED 0x01 +#define SOLARIS_WTRAPPED 0x02 +#define SOLARIS_WSTOPPED WUNTRACED +#define SOLARIS_WCONTINUED 0x08 +#define SOLARIS_WNOWAIT 0x80 + +#define SOLARIS_TRAP_BRKPT 1 +#define SOLARIS_TRAP_TRACE 2 +#define SOLARIS_CLD_EXITED 1 +#define SOLARIS_CLD_KILLED 2 +#define SOLARIS_CLD_DUMPED 3 +#define SOLARIS_CLD_TRAPPED 4 +#define SOLARIS_CLD_STOPPED 5 +#define SOLARIS_CLD_CONTINUED 6 +#define SOLARIS_POLL_IN 1 +#define SOLARIS_POLL_OUT 2 +#define SOLARIS_POLL_MSG 3 +#define SOLARIS_POLL_ERR 4 +#define SOLARIS_POLL_PRI 5 +#define SOLARIS_POLL_HUP 6 + diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c new file mode 100644 index 000000000..984b1bdb5 --- /dev/null +++ b/arch/sparc64/solaris/socksys.c @@ -0,0 +1,127 @@ +/* $Id: socksys.c,v 1.1 1997/09/03 12:29:27 jj Exp $ + * socksys.c: /dev/inet/ stuff for Solaris emulation. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/poll.h> + +#include <asm/uaccess.h> +#include <asm/termios.h> + +#include "conv.h" + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg); + +static int af_inet_protocols[] = { +IPPROTO_ICMP, IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_TCP, +IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_RAW, +0, 0, 0, 0, 0, 0, +}; + +static struct file_operations socksys_file_ops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ +}; + +static int socksys_open(struct inode * inode, struct file * filp) +{ + int family, type, protocol, fd; + int (*sys_socket)(int,int,int) = + (int (*)(int,int,int))SUNOS(97); + + family = ((MINOR(inode->i_rdev) >> 4) & 0xf); + switch (family) { + case AF_UNIX: + type = SOCK_STREAM; + protocol = 0; + break; + case AF_INET: + protocol = af_inet_protocols[MINOR(inode->i_rdev) & 0xf]; + switch (protocol) { + case IPPROTO_TCP: type = SOCK_STREAM; break; + case IPPROTO_UDP: type = SOCK_DGRAM; break; + default: type = SOCK_RAW; break; + } + break; + default: + type = SOCK_RAW; + protocol = 0; + break; + } + fd = sys_socket(family, type, protocol); + if (fd < 0) return fd; + return 0; +} + +static int socksys_release(struct inode * inode, struct file * filp) +{ + return 0; +} + +static unsigned int socksys_poll(struct file * filp, poll_table * wait) +{ + return 0; +} + +static struct file_operations socksys_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + socksys_open, /* open */ + socksys_release,/* release */ +}; + +__initfunc(int +init_socksys(void)) +{ + int ret; + int (*sys_socket)(int,int,int) = + (int (*)(int,int,int))SUNOS(97); + int (*sys_close)(unsigned int) = + (int (*)(unsigned int))SYS(close); + + ret = register_chrdev (30, "socksys", &socksys_fops); + if (ret < 0) { + printk ("Couldn't register socksys character device\n"); + return ret; + } + ret = sys_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ret < 0) { + printk ("Couldn't create socket\n"); + return ret; + } + socksys_file_ops = *current->files->fd[ret]->f_op; + sys_close(ret); + socksys_file_ops.poll = socksys_poll; + socksys_file_ops.release = socksys_release; + return 0; +} + +void +cleanup_socksys(void) +{ + if (unregister_chrdev (30, "socksys")) + printk ("Couldn't unregister socksys character device\n"); +} diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S new file mode 100644 index 000000000..c425fb721 --- /dev/null +++ b/arch/sparc64/solaris/systbl.S @@ -0,0 +1,289 @@ +/* $Id: systbl.S,v 1.5 1997/09/04 15:46:24 jj Exp $ + * systbl.S: System call entry point table for Solaris compatibility. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + */ + +#include <asm/unistd.h> + +/* Fall back to sys_call_table32 entry */ +#define CHAIN(name) __NR_##name + +/* Pass pt_regs pointer as first argument */ +#define REGS(name) name+1 + +/* Hack till all be implemented */ +#define solaris_getmsg solaris_unimplemented +#define solaris_getpmsg solaris_unimplemented +#define solaris_hrtsys solaris_unimplemented +#define solaris_msgsys solaris_unimplemented +#define solaris_putmsg solaris_unimplemented +#define solaris_putpmsg solaris_unimplemented +#define solaris_semsys solaris_unimplemented + + .data + .align 1024 + .globl solaris_sys_table +solaris_sys_table: + .word solaris_unimplemented /* nosys 0 */ + .word CHAIN(exit) /* exit d 1 */ + .word CHAIN(fork) /* fork 2 */ + .word CHAIN(read) /* read dpd 3 */ + .word CHAIN(write) /* write dpd 4 */ + .word solaris_open /* open soo 5 */ + .word CHAIN(close) /* close d 6 */ + .word solaris_wait /* wait xxx 7 */ + .word CHAIN(creat) /* creat so 8 */ + .word CHAIN(link) /* link ss 9 */ + .word CHAIN(unlink) /* unlink s 10 */ + .word solaris_unimplemented /* exec sxx 11 */ + .word CHAIN(chdir) /* chdir s 12 */ + .word CHAIN(time) /* time 13 */ + .word solaris_mknod /* mknod sox 14 */ + .word CHAIN(chmod) /* chmod so 15 */ + .word solaris_chown /* chown sdd 16 */ + .word solaris_brk /* brk/break x 17 */ + .word solaris_stat /* stat sp 18 */ + .word CHAIN(lseek) /* seek/lseek ddd 19 */ + .word solaris_getpid /* getpid 20 */ + .word solaris_unimplemented /* mount 21 */ + .word CHAIN(umount) /* umount s 22 */ + .word CHAIN(setuid) /* setuid d 23 */ + .word solaris_getuid /* getuid 24 */ + .word CHAIN(stime) /* stime d 25 */ +#if 0 + .word solaris_ptrace /* ptrace xdxx 26 */ +#else + .word CHAIN(ptrace) /* ptrace xdxx 26 */ +#endif + .word CHAIN(alarm) /* alarm d 27 */ + .word solaris_fstat /* fstat dp 28 */ + .word CHAIN(pause) /* pause 29 */ + .word CHAIN(utime) /* utime xx 30 */ + .word solaris_unimplemented /* stty 31 */ + .word solaris_unimplemented /* gtty 32 */ + .word solaris_access /* access so 33 */ + .word CHAIN(nice) /* nice d 34 */ + .word solaris_statfs /* statfs spdd 35 */ + .word CHAIN(sync) /* sync 36 */ + .word solaris_kill /* kill dd 37 */ + .word solaris_fstatfs /* fstatfs dpdd 38 */ + .word solaris_procids /* pgrpsys ddd 39 */ + .word solaris_unimplemented /* xenix 40 */ + .word CHAIN(dup) /* dup d 41 */ + .word CHAIN(pipe) /* pipe 42 */ + .word CHAIN(times) /* times p 43 */ + .word CHAIN(profil) /* prof xxxx 44 */ + .word solaris_unimplemented /* lock/plock 45 */ + .word CHAIN(setgid) /* setgid d 46 */ + .word solaris_getgid /* getgid 47 */ + .word solaris_sigfunc /* sigfunc xx 48 */ + .word REGS(solaris_msgsys) /* msgsys dxddd 49 */ + .word solaris_unimplemented /* syssun/3b 50 */ + .word CHAIN(acct) /* acct/sysacct x 51 */ + .word solaris_shmsys /* shmsys ddxo 52 */ + .word REGS(solaris_semsys) /* semsys dddx 53 */ + .word solaris_ioctl /* ioctl dxx 54 */ + .word solaris_unimplemented /* uadmin xxx 55 */ + .word solaris_unimplemented /* reserved:exch 56 */ + .word solaris_utssys /* utssys x 57 */ + .word CHAIN(fsync) /* fsync d 58 */ + .word CHAIN(execve) /* execv spp 59 */ + .word CHAIN(umask) /* umask o 60 */ + .word CHAIN(chroot) /* chroot s 61 */ + .word solaris_fcntl /* fcntl dxx 62 */ + .word solaris_ulimit /* ulimit xx 63 */ + .word solaris_unimplemented /* ? 64 */ + .word solaris_unimplemented /* ? 65 */ + .word solaris_unimplemented /* ? 66 */ + .word solaris_unimplemented /* ? 67 */ + .word solaris_unimplemented /* ? 68 */ + .word solaris_unimplemented /* ? 69 */ + .word solaris_unimplemented /* advfs 70 */ + .word solaris_unimplemented /* unadvfs 71 */ + .word solaris_unimplemented /* rmount 72 */ + .word solaris_unimplemented /* rumount 73 */ + .word solaris_unimplemented /* rfstart 74 */ + .word solaris_unimplemented /* ? 75 */ + .word solaris_unimplemented /* rdebug 76 */ + .word solaris_unimplemented /* rfstop 77 */ + .word solaris_unimplemented /* rfsys 78 */ + .word CHAIN(rmdir) /* rmdir s 79 */ + .word CHAIN(mkdir) /* mkdir so 80 */ + .word CHAIN(getdents) /* getdents dxd 81 */ + .word solaris_unimplemented /* libattach 82 */ + .word solaris_unimplemented /* libdetach 83 */ + .word CHAIN(sysfs) /* sysfs dxx 84 */ + .word REGS(solaris_getmsg) /* getmsg dxxx 85 */ + .word REGS(solaris_putmsg) /* putmsg dxxd 86 */ + .word CHAIN(poll) /* poll xdd 87 */ + .word solaris_lstat /* lstat sp 88 */ + .word CHAIN(symlink) /* symlink ss 89 */ + .word CHAIN(readlink) /* readlink spd 90 */ + .word CHAIN(setgroups) /* setgroups dp 91 */ + .word CHAIN(getgroups) /* getgroups dp 92 */ + .word CHAIN(fchmod) /* fchmod do 93 */ + .word CHAIN(fchown) /* fchown ddd 94 */ + .word solaris_sigprocmask /* sigprocmask dxx 95 */ + .word solaris_sigsuspend /* sigsuspend x 96 */ + .word solaris_sigaltstack /* sigaltstack xx 97 */ + .word solaris_sigaction /* sigaction dxx 98 */ + .word solaris_sigpending /* sigpending dd 99 */ + .word REGS(solaris_context) /* context 100 */ + .word solaris_unimplemented /* evsys 101 */ + .word solaris_unimplemented /* evtrapret 102 */ + .word solaris_statvfs /* statvfs sp 103 */ + .word solaris_fstatvfs /* fstatvfs dp 104 */ + .word solaris_unimplemented /* unknown 105 */ + .word solaris_unimplemented /* nfssys 106 */ + .word solaris_waitid /* waitid ddxd 107 */ + .word solaris_unimplemented /* sigsendsys ddd 108 */ + .word REGS(solaris_hrtsys) /* hrtsys xxx 109 */ + .word solaris_unimplemented /* acancel dxd 110 */ + .word solaris_unimplemented /* async 111 */ + .word solaris_unimplemented /* priocntlsys 112 */ + .word solaris_pathconf /* pathconf sd 113 */ + .word solaris_unimplemented /* mincore xdx 114 */ + .word solaris_mmap /* mmap xxxxdx 115 */ + .word CHAIN(mprotect) /* mprotect xdx 116 */ + .word CHAIN(munmap) /* munmap xd 117 */ + .word solaris_fpathconf /* fpathconf dd 118 */ + .word CHAIN(fork) /* fork 119 */ + .word solaris_unimplemented /* fchdir d 120 */ + .word CHAIN(readv) /* readv dxd 121 */ + .word CHAIN(writev) /* writev dxd 122 */ + .word solaris_xstat /* xstat dsx 123 */ + .word solaris_lxstat /* lxstat dsx 124 */ + .word solaris_fxstat /* fxstat ddx 125 */ + .word solaris_xmknod /* xmknod dsox 126 */ + .word solaris_unimplemented /* syslocal d 127 */ + .word solaris_unimplemented /* setrlimit 128 */ + .word solaris_unimplemented /* getrlimit 129 */ + .word CHAIN(chown) /* lchown sdd 130 */ + .word solaris_unimplemented /* memcntl 131 */ + .word solaris_getpmsg /* getpmsg dxxxx 132 */ + .word solaris_putpmsg /* putpmsg dxxdd 133 */ + .word CHAIN(rename) /* rename ss 134 */ + .word solaris_utsname /* uname x 135 */ + .word solaris_unimplemented /* setegid 136 */ + .word solaris_sysconf /* sysconfig d 137 */ + .word solaris_unimplemented /* adjtime 138 */ + .word solaris_sysinfo /* systeminfo dsd 139 */ + .word solaris_unimplemented /* ? 140 */ + .word solaris_unimplemented /* seteuid 141 */ + .word solaris_unimplemented /* ? 142 */ + .word solaris_unimplemented /* ? 143 */ + .word solaris_unimplemented /* secsys dx 144 */ + .word solaris_unimplemented /* filepriv sdxd 145 */ + .word solaris_unimplemented /* procpriv dxd 146 */ + .word solaris_unimplemented /* devstat sdx 147 */ + .word solaris_unimplemented /* aclipc ddddx 148 */ + .word solaris_unimplemented /* fdevstat ddx 149 */ + .word solaris_unimplemented /* flvlfile ddx 150 */ + .word solaris_unimplemented /* lvlfile sdx 151 */ + .word solaris_unimplemented /* ? 152 */ + .word solaris_unimplemented /* fchroot d 153 */ + .word solaris_unimplemented /* lvlproc dx 154 */ + .word solaris_unimplemented /* ? 155 */ + .word CHAIN(gettimeofday) /* gettimeofday xx 156 */ + .word CHAIN(getitimer) /* getitimer dx 157 */ + .word CHAIN(setitimer) /* setitimer dxx 158 */ + .word solaris_unimplemented /* lwp-xxx 159 */ + .word solaris_unimplemented /* lwp-xxx 160 */ + .word solaris_unimplemented /* lwp-xxx 161 */ + .word solaris_unimplemented /* lwp-xxx 162 */ + .word solaris_unimplemented /* lwp-xxx 163 */ + .word solaris_unimplemented /* lwp-xxx 164 */ + .word solaris_unimplemented /* lwp-xxx 165 */ + .word solaris_unimplemented /* lwp-xxx 166 */ + .word solaris_unimplemented /* lwp-xxx 167 */ + .word solaris_unimplemented /* lwp-xxx 168 */ + .word solaris_unimplemented /* lwp-xxx 169 */ + .word solaris_unimplemented /* lwp-xxx 170 */ + .word solaris_unimplemented /* lwp-xxx 171 */ + .word solaris_unimplemented /* lwp-xxx 172 */ + .word solaris_pread /* pread dpdd 173 */ + .word solaris_pwrite /* pwrite dpdd 174 */ + .word REGS(solaris_llseek) /* llseek dLd 175 */ + .word solaris_unimplemented /* lwpself 176 */ + .word solaris_unimplemented /* lwpinfo 177 */ + .word solaris_unimplemented /* lwpprivate 178 */ + .word solaris_unimplemented /* processorbind 179 */ + .word solaris_unimplemented /* processorexbind 180 */ + .word solaris_unimplemented /* 181 */ + .word solaris_unimplemented /* sync_mailbox 182 */ + .word solaris_unimplemented /* prepblock 183 */ + .word solaris_unimplemented /* block 184 */ + .word solaris_acl /* acl sddp 185 */ + .word solaris_unimplemented /* unblock 186 */ + .word solaris_unimplemented /* cancelblock 187 */ + .word solaris_unimplemented /* ? 188 */ + .word solaris_unimplemented /* xxxxx 189 */ + .word solaris_unimplemented /* xxxxxe 190 */ + .word solaris_unimplemented /* 191 */ + .word solaris_unimplemented /* 192 */ + .word solaris_unimplemented /* 193 */ + .word solaris_unimplemented /* 194 */ + .word solaris_unimplemented /* 195 */ + .word solaris_unimplemented /* 196 */ + .word solaris_unimplemented /* 197 */ + .word solaris_unimplemented /* 198 */ + .word CHAIN(nanosleep) /* nanosleep dd 199 */ + .word solaris_facl /* facl dddp 200 */ + .word solaris_unimplemented /* 201 */ + .word solaris_unimplemented /* 202 */ + .word solaris_unimplemented /* 203 */ + .word solaris_unimplemented /* 204 */ + .word solaris_unimplemented /* 205 */ + .word solaris_unimplemented /* 206 */ + .word solaris_unimplemented /* 207 */ + .word solaris_unimplemented /* 208 */ + .word solaris_unimplemented /* 209 */ + .word solaris_unimplemented /* 210 */ + .word solaris_unimplemented /* 211 */ + .word solaris_unimplemented /* 212 */ + .word solaris_unimplemented /* 213 */ + .word solaris_unimplemented /* 214 */ + .word solaris_unimplemented /* 215 */ + .word solaris_unimplemented /* 216 */ + .word solaris_unimplemented /* 217 */ + .word solaris_unimplemented /* 218 */ + .word solaris_unimplemented /* 219 */ + .word solaris_unimplemented /* 220 */ + .word solaris_unimplemented /* 221 */ + .word solaris_unimplemented /* 222 */ + .word solaris_unimplemented /* 223 */ + .word solaris_unimplemented /* 224 */ + .word solaris_unimplemented /* 225 */ + .word solaris_unimplemented /* 226 */ + .word solaris_unimplemented /* 227 */ + .word solaris_unimplemented /* 228 */ + .word solaris_unimplemented /* 229 */ + .word solaris_unimplemented /* 230 */ + .word solaris_unimplemented /* 231 */ + .word solaris_unimplemented /* 232 */ + .word solaris_unimplemented /* 233 */ + .word solaris_unimplemented /* 234 */ + .word solaris_unimplemented /* 235 */ + .word solaris_unimplemented /* 236 */ + .word solaris_unimplemented /* 237 */ + .word solaris_unimplemented /* 238 */ + .word solaris_unimplemented /* 239 */ + .word solaris_unimplemented /* 240 */ + .word solaris_unimplemented /* 241 */ + .word solaris_unimplemented /* 242 */ + .word solaris_unimplemented /* 243 */ + .word solaris_unimplemented /* 244 */ + .word solaris_unimplemented /* 245 */ + .word solaris_unimplemented /* 246 */ + .word solaris_unimplemented /* 247 */ + .word solaris_unimplemented /* 248 */ + .word solaris_unimplemented /* 249 */ + .word solaris_unimplemented /* 250 */ + .word solaris_unimplemented /* 251 */ + .word solaris_unimplemented /* 252 */ + .word solaris_unimplemented /* 253 */ + .word solaris_unimplemented /* 254 */ + .word solaris_unimplemented /* 255 */ + diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds index eac8314ca..661acc098 100644 --- a/arch/sparc64/vmlinux.lds +++ b/arch/sparc64/vmlinux.lds @@ -52,12 +52,14 @@ SECTIONS . += 8192; empty_bad_pte_table = .; . += 8192; + empty_bad_page = .; + . += 8192; + . += 0x40; empty_null_pmd_table = .; . += 8192; + . += 0x40; empty_null_pte_table = .; . += 8192; - empty_bad_page = .; - . += 8192; } _end = . ; PROVIDE (end = .); |